Skip to content

feat(drivers): Windows integrated (Kerberos) auth for SQL Server (#1961)#1962

Open
Seha16 wants to merge 3 commits into
TableProApp:linuxfrom
Seha16:feat/linux-mssql-kerberos
Open

feat(drivers): Windows integrated (Kerberos) auth for SQL Server (#1961)#1962
Seha16 wants to merge 3 commits into
TableProApp:linuxfrom
Seha16:feat/linux-mssql-kerberos

Conversation

@Seha16

@Seha16 Seha16 commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds Windows integrated authentication (Kerberos / GSSAPI) to the Linux SQL Server driver — connect with your kinit ticket instead of a SQL login. Fixes #1961.

  • core: AuthMode { Password, Kerberos } + ConnectOptions.auth_mode; DatabaseDriver::supports_integrated_auth() (default false).
  • drivers/mssql: tiberius integrated-auth-gssapi feature; connect() uses AuthMethod::Integrated for Kerberos (ambient credential cache, SPN MSSQLSvc/<host>:<port>).
  • storage: SavedConnection.auth_mode persisted (legacy files default to Password).
  • app: connect-dialog "Method" selector, shown only for integrated-capable drivers; Kerberos hides username/password; validity / label / dedup updated.
  • CI + README: libkrb5-dev and clang (libgssapi-sys links gssapi_krb5 and runs bindgen).

Depends on #1958

This branch is stacked on the SQL Server driver PR (#1958). Until that merges, this PR's diff also contains #1958's commit (feat(drivers): add SQL Server driver via tiberius). Please review/merge after #1958 — the diff then reduces to the single Kerberos commit.

Test plan

  • cargo build --workspace; cargo test --workspace --lib (core + storage auth_mode round-trip/legacy default; mssql supports_integrated_auth)
  • cargo fmt --all -- --check; cargo clippy --all-targets -- -D warnings
  • No regression: mssql integration 4/4 vs. real SQL Server 2022 (SQL auth)
  • UI: connect dialog shows Method = Windows (Kerberos) for SQL Server, credential rows hidden
  • Live Kerberos connection verified against a real cross-realm AD SQL Server (client realm ≠ service realm, cross-realm trust)

Known upstream-crate limitations (with workarounds)

  1. Cross-realm SPN realm — tiberius imports the SPN as a raw Kerberos principal, so it resolves in the client's default realm. For a service in another realm, set default_realm (+ [capaths] / [domain_realm]) so MSSQLSvc/<host>:<port> maps to the service realm. Proper fix: host-based SPN (GSS_C_NT_HOSTBASED_SERVICE) upstream in tiberius.
  2. Debug builds abort — libgssapi 0.4.6 wraps GSSAPI's empty final-step token via slice::from_raw_parts(null, 0), tripping Rust's UB precondition check (debug only). Run a release build (debug-assertions off) for Kerberos. Proper fix: null-guard upstream in libgssapi (0.4.6 is the latest ^0.4.5).

Neither limitation affects CI (build + unit tests; no live handshake) or the SQL-login path.

@Seha16

Seha16 commented Jul 25, 2026

Copy link
Copy Markdown
Contributor Author

Testing evidence on Arch Linux Kernel 7.1.5 with krb5 (Kerberos) with default_realm patched to the needed one:

kerberos-testing.mp4

@datlechin

Copy link
Copy Markdown
Member

Pushed fefd543 with fixes for the review findings. All of it is verified locally with the exact CI commands (cargo fmt --all -- --check, cargo clippy --all-targets -- -D warnings, cargo build --workspace, cargo test --workspace --lib --bins) plus a --no-default-features pass, except the flatpak item called out at the bottom.

Kerberos through an SSH tunnel was asking for the wrong SPN

establish() rewrites opts.host/opts.port to the tunnel's local forward before driver.connect(), and tiberius builds both the SPN and the TLS server name from Config::host/port. A tunnelled connection was asking the KDC for MSSQLSvc/127.0.0.1:<ephemeral>.

The socket and the service name are now separate: ConnectOptions::service_endpoint records the endpoint the tunnel replaced, service_address() returns it, and the driver configures tiberius from that while opening the socket against opts.host. The tuple form of TcpStream::connect also handles IPv6 literals, which config.get_addr()'s host:port string did not.

Kerberos was writing a password into the keyring

run_connect called store_password unconditionally, and AdwPasswordEntryRow keeps its text after the row is hidden. Type a password, switch to Kerberos, connect, and that credential landed in libsecret against a connection that never uses it. The credential rows are now emptied at collection time under Kerberos, the store is skipped, and open_saved no longer reads the keyring back for a Kerberos entry.

Auth mode no longer lives in widget visibility

selected_auth_mode() read auth_combo.is_visible(), and refresh_auth_visibility() called back into it, so state flowed through GTK flags and back. There is now an AuthFormState { file_based, supports_integrated, kerberos_selected } on the model with mode() / shows_method() / shows_credentials(), and the widgets are driven from it. Five unit tests cover the combinations, including a stale Kerberos selection surviving a driver switch and file-based drivers asking for nothing.

That also fixes two things the visibility coupling had introduced:

  • DriverChanged never called refresh_validity(). With SQL Server + Kerberos the form was valid with an empty username; switching back to PostgreSQL brought the username row back but left Connect enabled.
  • Dropping the file_based visibility lines meant refresh_validity() put the .error class on a hidden username row for SQLite.

Kerberos is a cargo feature now

integrated-auth-gssapi was on the workspace tiberius dependency, so libkrb5-dev and libclang were mandatory for anyone building the workspace. It moved to tablepro-driver-mssql's kerberos feature, default on, so --no-default-features drops both. supports_integrated_auth() is cfg!(feature = "kerberos") so the UI never offers a mode the binary cannot perform, and a Kerberos request in a build without it returns the new DriverError::Unsupported instead of silently doing a SQL login. CI checks that configuration so it does not rot.

Debug builds no longer abort

[profile.dev.package.libgssapi]
debug-assertions = false

The UB precondition check is emitted per crate, so turning it off for that one package makes cargo run usable for Kerberos without weakening the check anywhere else. Worth dropping once the null guard lands upstream.

Also

  • cargo test --workspace --lib was skipping the app crate entirely, since tablepro-app has no lib target. 97 existing tests had never run in CI. The step is --lib --bins now; all 97 pass headless.
  • README documents kinit, the SPN, the cross-realm krb5.conf setup, and the no-Kerberos build. Those limitations only existed in the PR description before.
  • AuthMode::Kerberos's doc said drivers without support "fall back to password auth". None did; the driver now rejects it, and the doc says so.
  • The connection label keys off auth_mode rather than an empty username.

Two things I did not do

The flatpak manifest is unverified. org.gnome.Sdk 47 ships gcc, not clang, and bindgen needs libclang, so I added the llvm18 extension with LIBCLANG_PATH. I could not run flatpak-builder from here, and nothing in CI builds the flatpak. Whether the freedesktop 24.08 SDK carries krb5 headers also needs a real build to confirm. Please run it once before merging.

po/tablepro.pot is untouched. "Method" and "Windows (Kerberos)" are still unextractable, but POTFILES.in already lists ui/app.rs, ui/edit_dialog.rs, and ui/insert_dialog.rs, none of which exist any more, so xtr cannot run at all. That predates this PR and fixing it properly is its own change.

Cargo.lock is unchanged: kerberos is default-on, so resolution is identical.

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