Skip to content

Conversation

@Westwooo
Copy link
Contributor

@Westwooo Westwooo commented Sep 30, 2025

@Westwooo Westwooo force-pushed the ING-662_cbauthx-cert-auth branch 3 times, most recently from 354d590 to 54f79f0 Compare September 30, 2025 14:29
}
}

if slowEntry.ResolveErr != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we can get here without unlocking too

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think that we can as we either unlock in the above if, or slowEntry.PendingCh is nil which means that the checkThread finished, and we unlock at the end of that.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we have code like this elsewhere? Relying on a different "thread" to release a lock seems like a bug in waiting. The way that the locking between the two threads is reliant on the state of the pendingCh just seems like it's going to get broken in the future.

Copy link
Contributor Author

@Westwooo Westwooo Oct 14, 2025

Choose a reason for hiding this comment

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

Do we have code like this elsewhere?

Yeah I copied the logic from authcheck_cached.go

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We do I copied it from authcheck_cached.go

Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't like it but fair enough

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've been thinking about it and I think there is a case where the lock never gets dropped (pls let me know if you disagree):

// Two callers A and B call checkSlow for the same cert
// A acquires the slow lock, so B is blocked
// There is no slow entry so A starts a checkThread and adds an entry to the slowCache
// in checkSlow A slowEntry.PendingCh != nil, because the checkThread is blocked by checkSlowA holding the lock
// checkSlow A drops the lock and waits on pendingCH
// Now either checkThread A or checkSlow B will acquire the lock
// If checkThread A acquires the lock it rebuilds the cache, sets the slowEntry.pendingCh to nil and closes the pendingCh
// checkSlow B now acquires the lock and there is a slow cache entry so no new check thread is started
// slowEntry.PendingCh == nil so we skip the IF and don't drop the lock 
// slowCheck B completes without dropping the lock

I think this can be fixed by moving the unlock to before the if, which I've done.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah that does seem like a potential deadlock, should probably file a bug against authcheck_cached. The very fact this has to be thought about so much makes me think it's too complicated...

Copy link
Member

Choose a reason for hiding this comment

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

There has been a bit of confusion here I think. The original code indeed had a bug where the Unlock was incorrectly placed inside of the if-block when it shouldn't be, but it looks like Jack has moved that Unlock call back outside of the if. There is no case where there is a cross-thread unlock. Both the checkSlow function manages the lock and the checkThread thread manages the lock, each completely independently of the other.

@Westwooo Westwooo force-pushed the ING-662_cbauthx-cert-auth branch 4 times, most recently from 97ec8f7 to b94ae87 Compare October 16, 2025 10:01
@Westwooo Westwooo requested a review from chvck October 20, 2025 06:44
}
}

if slowEntry.ResolveErr != nil {
Copy link
Member

Choose a reason for hiding this comment

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

There has been a bit of confusion here I think. The original code indeed had a bug where the Unlock was incorrectly placed inside of the if-block when it shouldn't be, but it looks like Jack has moved that Unlock call back outside of the if. There is no case where there is a cross-thread unlock. Both the checkSlow function manages the lock and the checkThread thread manages the lock, each completely independently of the other.

cr.fastCache.Store(fastCache)
}

func (a *CertCheckCached) CheckCertificate(ctx context.Context, connState *tls.ConnectionState) (UserInfo, error) {
Copy link
Member

Choose a reason for hiding this comment

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

Why does this accept a *tls.ConnectionState rather than a *tls.Certificate? It looks like the only part of the ConnectionState that is actually used is PeerCertificate[0], which is referenced in a couple of places. In order to prevent a case where the object used to hash the key and the actual object used end up being different, why not 'pick out' the certificate to validate at the top-level and just pass that through? Relatedly, are we use that we only need to send PeerCertificate[0]? There are certificate chain related things that might be relevant here I think.

Copy link
Contributor Author

@Westwooo Westwooo Oct 21, 2025

Choose a reason for hiding this comment

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

I've changed it so that we get the leaf at the top level and then pass that around.

Are we sure that we only need to send PeerCertificate[0].

Yeah for a couple of reasons, first because this is how they use this endpoint in their own implementation: https://github.com/couchbase/cbauth/blob/164ea251e2c977ca0a911b97e60dbdde515f6085/cbauthimpl/impl.go#L1601

Secon this endpoint does not appear to do any tls verification on the given cert. I created an unsigned certificate with CN set to an existing user with:

➜  node_certs openssl genrsa -out unsigned_client.key 2048
➜  node_certs openssl req -x509 -new -nodes -key unsigned_client.key -sha256 -days 3650 -out unsigned_client.crt \
  -subj "/C=US/ST=State/L=City/O=MyOrg/CN=Westwooo"

I manually sent this to the extractUserFromCert endpoint and got a 200 response, with the username and Uuid.


fastCertCache := a.fastCache.Load()
if fastCertCache != nil {
userInfo, wasFound := fastCertCache.users[certHash]
Copy link
Member

Choose a reason for hiding this comment

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

nit: This object should probably be called .certificates or .certs rather than .users I think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think certs would be confusing since it is a map of users, have changed the name to make it more clear though, let me know your thoughts.

@Westwooo Westwooo force-pushed the ING-662_cbauthx-cert-auth branch 2 times, most recently from 436ecf1 to c1be669 Compare October 21, 2025 07:18
@Westwooo Westwooo force-pushed the ING-662_cbauthx-cert-auth branch 4 times, most recently from a98ad68 to d730547 Compare October 21, 2025 12:46
@Westwooo Westwooo force-pushed the ING-662_cbauthx-cert-auth branch from d730547 to 8a7c238 Compare October 21, 2025 12:48
@Westwooo Westwooo merged commit 491da08 into master Oct 22, 2025
15 of 17 checks passed
@Westwooo Westwooo deleted the ING-662_cbauthx-cert-auth branch October 22, 2025 09:57
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.

4 participants