ExportXMLWordPrintableJSON

    • Type: Bug
    • Resolution: Fixed
    • Priority: Critical - P2
    • 8.3.16, 9.0.7, 8.0.37, 7.0.48
    • Affects Version/s: None
    • Component/s: None
    • Server Security
    • Fully Compatible
    • ALL
    • v9.0, v8.3, v8.0, v7.0, v8.2
    • Hide
      1. Start mongod with TLS/X.509 enabled, cluster auth mode allowing X.509, and extension-based cluster membership configured (net.tls.clusterAuthX509.extensionValue or equivalent).
      2. Ensure enforceUserClusterSeparation=true.
      3. Use a client certificate that is valid for cluster-member authentication.
      4. Drive authentication through the X.509 path where separation check calls AuthorizationManager::acquireUser()}} for the subject DN.
      5. Induce a non-OK, non-UserNotFound result during that user lookup (for example via a deterministic failpoint in the user acquisition/read path in a test harness).
      6. Observe that current logic computes userExists = false on that error and does not trigger the separation rejection.
      7. Observe authentication proceeds and grants internal authorization (cluster-member privileges).

      Expected result:

      Non-UserNotFound lookup failures should fail authentication (fail closed).

      Observed vulnerable result:

      Non-UserNotFound lookup failures are treated as "user absent" and authentication continues to internal authorization.

      Show
      Start mongod with TLS/X.509 enabled, cluster auth mode allowing X.509, and extension-based cluster membership configured (net.tls.clusterAuthX509.extensionValue or equivalent). Ensure enforceUserClusterSeparation=true. Use a client certificate that is valid for cluster-member authentication. Drive authentication through the X.509 path where separation check calls AuthorizationManager::acquireUser()}} for the subject DN. Induce a non-OK, non- UserNotFound result during that user lookup (for example via a deterministic failpoint in the user acquisition/read path in a test harness). Observe that current logic computes userExists = false on that error and does not trigger the separation rejection. Observe authentication proceeds and grants internal authorization (cluster-member privileges). Expected result: Non- UserNotFound lookup failures should fail authentication (fail closed). Observed vulnerable result: Non- UserNotFound lookup failures are treated as "user absent" and authentication continues to internal authorization.
    • 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-10-23, Server Security 2026-11-06, Server Security 2026-11-20
    • None
    • None
    • None
    • None
    • None
    • None
    • None

      Related to CVE-690 Engineering Work and SECBUG-1282

      Security Context

      See SECBUG-1282 for problem description, security impact, affected conditions, reproduction steps, and severity rationale.

      Root Cause

      • src/mongo/db/commands/authentication_commands.cpp: user-existence check collapses all non-OK results into "user absent" via .isOK(); ErrorCodes::UserNotFound and transient failures (MaxTimeMSExpired, NotPrimary, lock timeout, Interrupted) are indistinguishable to the caller
      • src/mongo/db/auth/sasl_x509_server_conversation.cpp: same fail-open pattern in the SASL X.509 path
      • src/mongo/db/auth/authorization_router_impl.cpp: the function-try-block in acquireUser catches every DBException and returns it as a Status, so all exceptions reach the caller as a non-OK Status
      • On v7.0 and v8.0 the lookup uses am->getUserDescription(opCtx, request.name, &ignored).isOK() instead of acquireUser; the same fail-open applies because any non-OK status from the underlying findOne maps to userExists = false

      Proposed Fix

      Update both paths to handle the acquireUser() (or getUserDescription()) status explicitly:

      • OK: treat as explicit user exists, reject authentication per separation policy
      • ErrorCodes::UserNotFound: treat as explicit user absent, allow cluster-member path
      • any other error: fail closed (abort authentication, do not grant internal authorization)
      auto acquireResult = am->acquireUser(opCtx, std::move(userRequest));
      if (!acquireResult.isOK() &&
          acquireResult.getStatus().code() != ErrorCodes::UserNotFound) {
          uassertStatusOK(acquireResult.getStatus());
      }
      bool userExists = acquireResult.isOK(); 

      Apply the same pattern to the getUserDescription call sites in v7.0 and v8.0. Prefer a shared helper to keep the command and SASL behavior consistent.

      Acceptance Criteria

      • Authentication must fail closed when separation-check user lookup returns non-UserNotFound errors.
      • Authentication must continue to reject explicit user overlap when lookup returns OK.
      • Authentication must continue to allow cluster-member flow when lookup returns UserNotFound.
      • No code path may call grantInternalAuthorization() after an ambiguous/transient lookup failure.

      Testing

      1. Add unit tests for both:
        • src/mongo/db/commands/authentication_commands.cpp
        • src/mongo/db/auth/sasl_x509_server_conversation.cpp
      2. Validate three-way behavior of separation check:
        • OK}} -> reject as explicit user exists
        • UserNotFound -> allow cluster-member auth
        • Any other error -> reject/fail authentication
      3. Add/extend integration test coverage in X.509 auth tests to assert no internal authorization is granted on transient lookup errors.

      Likely Files

      • src/mongo/db/commands/authentication_commands.cpp
      • src/mongo/db/auth/sasl_x509_server_conversation.cpp
      • src/mongo/db/auth/sasl_x509_test.cpp
      • jstests/ssl/x509_enforce_user_cluster_separation.js (if integration-level repro/assertion is added)

      Note on v7.0/v8.0 backport

      The v7.0 and v8.0 fix targets getUserDescription instead of acquireUser. The fix shape is the same — inspect the returned Status code before assigning userExists — but verify which non-UserNotFound statuses getUserDescription can return on those branches (e.g. storage errors during findOne) and confirm that propagating them as authentication failures is safe.

            Assignee:
            Sam Frank
            Reporter:
            Chye Lin Chee
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: