Skip to content

feat: added sspi ntlm without kerberos using sspi-rs#408

Open
carlvoller wants to merge 4 commits intoprisma:mainfrom
carlvoller:main
Open

feat: added sspi ntlm without kerberos using sspi-rs#408
carlvoller wants to merge 4 commits intoprisma:mainfrom
carlvoller:main

Conversation

@carlvoller
Copy link

@carlvoller carlvoller commented Mar 4, 2026

Tiberius currently doesn't support SSPI NTLM authentication on Linux/MacOS systems without using Kerberos. This limits the use of Tiberius in older or more restrictive environments operated by some companies.

This PR adds the sspi-rs feature. When enabled, Linux and MacOS users can use AuthMethod::Windows just like Windows users currently can. This is done through the optional dependency on sspi-rs. Enabling this feature on Windows currently does nothing, and does not overwrite the existing winauth behaviour.

There's no breaking changes with this PR (as far as I'm aware). I have tested these changes locally and was successfully able to authenticate with SSPI NTLM without Kerberos.

Summary by CodeRabbit

  • New Features

    • Optional SSPI-based authentication (sspi-rs) added, enabling SSPI/NTLM-style integrated security on Unix and aligning Unix and Windows authentication paths.
    • New feature flag to opt into Unix SSPI support for integrated-security scenarios.
  • Bug Fixes

    • Improved SSPI-related error handling and fixed a typo in an error conversion message.

@coderabbitai
Copy link

coderabbitai bot commented Mar 4, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9b16a0c9-1059-4684-bd3c-053458ca83f3

📥 Commits

Reviewing files that changed from the base of the PR and between 5ab7325 and 965a66d.

📒 Files selected for processing (2)
  • src/client/config.rs
  • src/error.rs

Walkthrough

Adds optional Unix SSPI support via a new sspi dependency and sspi-rs feature; expands cfg gates across client, TDS, and error modules to enable SSPI-based NTLM authentication on Unix, adds an SspiRs error variant, and implements the Unix SSPI auth path and related imports.

Changes

Cohort / File(s) Summary
Dependency & features
Cargo.toml
Adds optional Unix-only dependency sspi = "0.18" and new feature sspi-rs = ["sspi"].
Client auth types & cfgs
src/client/auth.rs
Expands cfg/doc attributes for WindowsAuth, AuthMethod::Windows, and related constructors to include Unix when sspi-rs is enabled.
Client config branching
src/client/config.rs
Adds Unix sspi-rs conditional branch and adjusts integrated-auth selection to choose Windows-style SSPI, Integrated, or SQL auth based on platform/features and provided credentials.
Connection auth implementation
src/client/connection.rs
Adds Unix sspi-rs imports and implements an NTLM-style SSPI auth flow on Unix (credentials, SSPI context init/exchange, token handling, TLS integration) under all(unix, feature = "sspi-rs").
Error variants & conversions
src/error.rs
Adds Error::SspiRs(String) gated by unix + sspi-rs, implements From<sspi::Error> for Error, and fixes a uuid error message typo.
TDS codec & token cfgs
src/tds/codec/login.rs, src/tds/codec/token/token_sspi.rs, src/tds/context.rs, src/tds/stream/token.rs
Broadens cfg attributes to include sspi-rs alongside integrated-auth-gssapi and Windows for LoginMessage::integrated_security, TokenSspi::new, spn(), and flush_sspi.
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding SSPI NTLM authentication support on Unix platforms using the sspi-rs crate.
Docstring Coverage ✅ Passed Docstring coverage is 82.35% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/client/config.rs (1)

313-319: ⚠️ Potential issue | 🟠 Major

Fix integratedsecurity routing to enable SSPI NTLM on Unix with sspi-rs.

The current routing has two problems:

  1. unix + sspi-rs only (no integrated-auth-gssapi): integratedsecurity is ignored. Line 313–316 doesn't execute (requires integrated-auth-gssapi), so the request falls through to SQL auth at line 319, never reaching the NTLM handler at connection.rs:392.

  2. unix + both sspi-rs and integrated-auth-gssapi: Line 315 unconditionally returns AuthMethod::Integrated (Kerberos), preventing use of the NTLM path that exists at connection.rs:392 for credentials-based flows.

Route integratedsecurity=true with credentials to AuthMethod::windows() for unix+sspi-rs, and reserve AuthMethod::Integrated for unix+integrated-auth-gssapi when credentials are absent.

Proposed fix
-            #[cfg(feature = "integrated-auth-gssapi")]
-            Some(val) if val.to_lowercase() == "sspi" || Self::parse_bool(val)? => {
-                Ok(AuthMethod::Integrated)
-            }
-            
-            // Should sspi-rs take over the default behaviour here if enabled?
+            #[cfg(all(unix, feature = "sspi-rs"))]
+            Some(val) if val.to_lowercase() == "sspi" || Self::parse_bool(val)? => match (user, pw) {
+                (Some(user), Some(pw)) => Ok(AuthMethod::windows(user, pw)),
+                #[cfg(feature = "integrated-auth-gssapi")]
+                (None, None) => Ok(AuthMethod::Integrated),
+                _ => Err(crate::Error::Conversion(
+                    "Connection string: uid/pwd required for SSPI NTLM on Unix".into(),
+                )),
+            },
+            #[cfg(all(unix, feature = "integrated-auth-gssapi", not(feature = "sspi-rs")))]
+            Some(val) if val.to_lowercase() == "sspi" || Self::parse_bool(val)? => {
+                Ok(AuthMethod::Integrated)
+            }

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1111a8b5-b377-4996-a533-7ad5bcbe319a

📥 Commits

Reviewing files that changed from the base of the PR and between a5dffa0 and beeaf2c.

📒 Files selected for processing (9)
  • Cargo.toml
  • src/client/auth.rs
  • src/client/config.rs
  • src/client/connection.rs
  • src/error.rs
  • src/tds/codec/login.rs
  • src/tds/codec/token/token_sspi.rs
  • src/tds/context.rs
  • src/tds/stream/token.rs

Comment on lines +30 to +33
#[cfg_attr(
feature = "docs",
doc(any(all(windows, feature = "winauth"), all(unix, feature = "sspi-rs")))
)]
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Incorrect doc attribute syntax — missing cfg() wrapper.

The doc(any(...)) syntax is invalid. It should be doc(cfg(any(...))) to properly display the "Available on..." badge in rustdoc. Compare with the existing Integrated variant at line 76 which correctly uses doc(cfg(any(...))).

This same issue appears at lines 41-44, 62-65, and 97.

Proposed fix for all occurrences
 #[cfg_attr(
     feature = "docs",
-    doc(any(all(windows, feature = "winauth"), all(unix, feature = "sspi-rs")))
+    doc(cfg(any(all(windows, feature = "winauth"), all(unix, feature = "sspi-rs"))))
 )]

Apply the same pattern to lines 41-44, 62-65, and fix line 97:

-    #[cfg_attr(feature = "docs", doc(any(all(windows, feature = "winauth"), all(unix, feature = "sspi-rs"))))]
+    #[cfg_attr(
+        feature = "docs",
+        doc(cfg(any(all(windows, feature = "winauth"), all(unix, feature = "sspi-rs"))))
+    )]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#[cfg_attr(
feature = "docs",
doc(any(all(windows, feature = "winauth"), all(unix, feature = "sspi-rs")))
)]
#[cfg_attr(
feature = "docs",
doc(cfg(any(all(windows, feature = "winauth"), all(unix, feature = "sspi-rs"))))
)]

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
src/client/auth.rs (1)

62-65: ⚠️ Potential issue | 🟡 Minor

Use doc(cfg(...)) instead of doc(any(...)) for rustdoc availability annotations.

Line 64 and Line 97 still use doc(any(...)); these should be doc(cfg(any(...))) for correct rustdoc cfg badges.

Proposed fix
     #[cfg_attr(
         feature = "docs",
-        doc(any(all(windows, feature = "winauth"), all(unix, feature = "sspi-rs")))
+        doc(cfg(any(all(windows, feature = "winauth"), all(unix, feature = "sspi-rs"))))
     )]
-    #[cfg_attr(feature = "docs", doc(any(all(windows, feature = "winauth"), all(unix, feature = "sspi-rs"))))]
+    #[cfg_attr(
+        feature = "docs",
+        doc(cfg(any(all(windows, feature = "winauth"), all(unix, feature = "sspi-rs"))))
+    )]
#!/bin/bash
# Verify unresolved rustdoc cfg syntax in src/client/auth.rs.
rg -nP 'doc\(\s*any\(' src/client/auth.rs
rg -nP 'doc\(\s*cfg\(' src/client/auth.rs

Expected result: the first command returns no matches after the fix.

Also applies to: 97-97


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ea811eef-e961-4a7b-85d5-08f3f441fbab

📥 Commits

Reviewing files that changed from the base of the PR and between beeaf2c and 9fc93bc.

📒 Files selected for processing (3)
  • src/client/auth.rs
  • src/client/config.rs
  • src/error.rs

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 64d83021-972c-4036-b619-e950f20bd2f9

📥 Commits

Reviewing files that changed from the base of the PR and between 9fc93bc and 5ab7325.

📒 Files selected for processing (2)
  • src/client/config.rs
  • src/error.rs

Comment on lines +170 to +175
#[cfg(all(unix, feature = "sspi-rs"))]
impl From<sspi::Error> for Error {
fn from(err: sspi::Error) -> Self {
Error::SspiRs(format!("{}", err))
}
} No newline at end of file
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Add doc cfg attribute for consistency with GSSAPI impl.

The From<libgssapi::error::Error> impl (lines 159-168) includes a #[cfg_attr(feature = "docs", doc(cfg(...)))] attribute, but this impl is missing it.

📚 Proposed fix to add doc cfg attribute
 #[cfg(all(unix, feature = "sspi-rs"))]
+#[cfg_attr(
+    feature = "docs",
+    doc(cfg(all(unix, feature = "sspi-rs")))
+)]
 impl From<sspi::Error> for Error {
     fn from(err: sspi::Error) -> Self {
         Error::SspiRs(format!("{}", err))
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#[cfg(all(unix, feature = "sspi-rs"))]
impl From<sspi::Error> for Error {
fn from(err: sspi::Error) -> Self {
Error::SspiRs(format!("{}", err))
}
}
#[cfg(all(unix, feature = "sspi-rs"))]
#[cfg_attr(
feature = "docs",
doc(cfg(all(unix, feature = "sspi-rs")))
)]
impl From<sspi::Error> for Error {
fn from(err: sspi::Error) -> Self {
Error::SspiRs(format!("{}", err))
}
}

@carlvoller
Copy link
Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Mar 4, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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.

1 participant