-
Type:
Bug
-
Resolution: Fixed
-
Priority:
Critical - P2
-
Affects Version/s: None
-
Component/s: None
-
Server Security
-
Fully Compatible
-
ALL
-
v9.0, v8.3, v8.0, v7.0, v8.2
-
Server Security 2026-07-03, Server Security 2026-07-17, Server Security 2026-07-31, Server Security 2026-08-14, Server Security 2026-08-28, Server Security 2026-09-11, Server Security 2026-09-25, Server Security 2026-10-09, Server Security 2026-10-23, Server Security 2026-11-06, Server Security 2026-11-20
-
None
-
None
-
None
-
None
-
None
-
None
-
None
Security Context
See SECBUG-1099 for problem description, security impact, preconditions, reproduction steps, and severity rationale.
Root Cause
- src/mongo/db/modules/enterprise/src/sasl/mongo_sspi.cpp:92: SspiConnContext initialises haveCred=false.
- src/mongo/db/modules/enterprise/src/sasl/mongo_sspi.cpp:97-101: the destructor calls FreeCredentialsHandle only when haveCred is true.
- src/mongo/db/modules/enterprise/src/sasl/mongo_sspi.cpp:153-169: sspiServerMechNew calls AcquireCredentialsHandle, checks the return status, then immediately calls pcctx.release() and returns SASL_OK — without ever setting pcctx->haveCred = true. The live Windows credential handle stored in pcctx->cred is handed to the SASL framework, but the destructor's cleanup guard is permanently disabled.
- src/mongo/db/modules/enterprise/src/sasl/mongo_sspi.cpp:442-444: sspiServerMechDispose deletes the context via the destructor, which skips FreeCredentialsHandle because haveCred is still false. One LSA credential handle is leaked per authentication attempt.
A grep of the file confirms haveCred is never assigned true anywhere — only read at the destructor (line 99) and initialised to false in the constructor (line 94).
For contrast: haveCtxt follows the correct pattern — it is set to true at line 425 after AcceptSecurityContext succeeds, and the destructor correctly calls DeleteSecurityContext when it is true.
Because saslStart requires no prior authentication and authFailedDelay defaults to 0 ms, an unauthenticated attacker can loop saslStart/GSSAPI commands at wire speed, each deterministically consuming one handle, until the per-process Windows handle limit is reached and all Kerberos authentications fail.
Proposed Fix
Add the missing assignment in sspiServerMechNew immediately after the AcquireCredentialsHandle success check, mirroring the haveCtxt = true pattern us
ed for the security context:
// mongo_sspi.cpp — sspiServerMechNew, after the AcquireCredentialsHandle success check if (status != SEC_E_OK) { HandleLastError(sparams->utils, status, "AcquireCredentialsHandle"); return SASL_FAIL; } pcctx->haveCred = true; // ADD THIS LINE — enables FreeCredentialsHandle in destructor *conn_context = pcctx.release(); return SASL_OK;
No other changes are needed. The destructor already has the correct cleanup path; it is simply never reached because the flag is not set.
Acceptance Criteria
- After the fix, repeated unauthenticated saslStart/GSSAPI commands do not accumulate Windows LSA credential handles — handle count remains stable as measured by a Windows handle-counting tool (e.g. Process Explorer or GetProcessHandleCount).
- A legitimate GSSAPI authentication handshake still succeeds end-to-end.
- The server does not require a restart after a burst of failed GSSAPI authentication attempts.
Test Coverage
- Add a test that initiates and abandons multiple GSSAPI authentication sessions and then asserts that a subsequent legitimate Kerberos authentication succeeds (i.e. handle exhaustion has not occurred).
- Verify on Windows Enterprise builds only; this code is compiled conditionally and is not reachable on Linux/macOS builds.
Notes
The complete sspiServerMechNew function on master showing the missing assignment (mongo_sspi.cpp:135-171):
int sspiServerMechNew(void* glob_context, sasl_server_params_t* sparams, const char* challenge, unsigned challen, void** conn_context) throw() { TimeStamp ignored; std::unique_ptr<SspiConnContext> pcctx(new SspiConnContext()); // ...principal name setup... SECURITY_STATUS status = AcquireCredentialsHandle( const_cast<wchar_t*>(principalName.c_str()), const_cast<LPWSTR>(L"kerberos"), SECPKG_CRED_INBOUND, nullptr, nullptr, nullptr, nullptr, &pcctx->cred, &ignored); if (status != SEC_E_OK) { HandleLastError(sparams->utils, status, "AcquireCredentialsHandle"); return SASL_FAIL; } // pcctx->haveCred = true; <-- MISSING: destructor will never free pcctx->cred *conn_context = pcctx.release(); return SASL_OK; }
The correct pattern already used for the security context in sspiServerStep (mongo_sspi.cpp:425):
// After AcceptSecurityContext succeeds: pcctx->haveCtxt = true; // destructor will call DeleteSecurityContext
This is a Windows Enterprise-only code path. The file is conditionally compiled; it is not present in Linux or macOS builds and does not affect non-Ke rberos authentication mechanisms.
- related to
-
SERVER-132045 Add an end-to-end Evergreen test for Windows GSSAPI/SSPI authentication
-
- Closed
-