ExportXMLWordPrintableJSON

    • 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.

            Assignee:
            Owen Chen
            Reporter:
            Chye Lin Chee
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved: