curl / Mailing Lists / curl-library / Single Mail


Re: Multi-threading, NSS, client certificates and Linux problem

From: Kamil Dudka <>
Date: Wed, 25 Jan 2017 21:11:46 +0100

On Friday, January 20, 2017 16:41:35 Pawel Veselov wrote:
> On Fri, Jan 20, 2017 at 3:35 AM, Kamil Dudka <> wrote:
> > On Thursday, January 19, 2017 13:39:44 Pawel Veselov wrote:
> >> Things work perfectly fine if I use the database, i.e. no MT problems.
> >> Unfortunately, this is not a workaround to what I'm trying to achieve,
> >> but it is pointing to nss-pem as a culprit, isn't it?
> >
> > Exactly.
> I tried using PKCS#12 format for authenticating, but I can't make it work
> either. I set the certificate type to P12, but simply get "NSS error -8018
> (SEC_ERROR_UNKNOWN_PKCS11_ERROR)". Curl application simply rejects P12
> certificate type, so don't know what's up with that.

PKCS #12 is not supported by nss-pem (and not fully supported by nss either):

> >> Anywhere in particular you'd like me to dig in there?
> >
> > The nss-pem module maintains a global array of objects named pem_objs. I
> > guess it could be a problem if accesses to the array were not
> > synchronized.
> > But they should be thanks to the patch I referenced in my previous reply.
> Well, I'm not familiar with curl or nss code so it's not easy for me to
> understand this.
> I'm looking at the code in nss_create_object, and is already a bit confused.
> On the face of it it looks like the method creates some sort of "slots"
> with NSS library, and there are ever only two potential names for these
> slots - "PEM Token #0" or "PEM Token #1", depending on whether the
> certificate being loaded is CA or not.

Those are PKCS #11 slots -- an abstraction designed mostly for HW devices.
nss-pem uses the PKCS #11 API to access certificates/keys from files. The
slots are created during nss-pem module load. #0 is used for CA certificates
and #1 is used for client certificates (and the corresponding private keys).
I believe there can be multiple certificates loaded in each slot. I was told
that there could be a problem if multiple _encrypted_ private keys were used
at a time but I have not tested this scenario yet.

> The function uses this slot during its execution, only locking it out when
> the slot is being "found" (in nss_find_slot_by_name). I see that
> PK11_CreateGenericObject() function uses a full lock when working with the
> slot (EnterSlotMonitor/ExitSlotMonitor). The private key is loaded
> separately. I haven't figured out all the logic, but the following sequence
> strikes me as odd, I don't understand how things would work in this
> case.

PK11_CreateGenericObject() is used just to load the certificate/key from file.
They are later accessed from the SelectClientCert() hook during the handshake.

> Consider nss_load_key (it doesn't matter what's going here, I just see that
> the slot object is freely used by multiple threads that seem to store some
> information into it, which can be easily overwritten by other threads).
> thread#1 calls nss_create_object on key#1
> thread#2 calls nss_create_object on key#2
> thread#1 calls PK11_Authenticate (password)
> At that last point, the only input is slot ID and password, I don't
> see how it can possible know to operate on key#1, and not key#2...

Good point. That is exactly the reason why I asked whether the private keys
are encrypted or not. The password is only used for encrypted private keys.

> If you could please help me understand how the MT is realized on these
> slots, and where does it happen that certificate/keys are actually
> written out to the TCP socket, it would be very helpful. The
> documentation for PK11 is scarce to
> come about (if you have a pointer to that, it'd be very helpful as well :) )
> > Are your private keys encrypted?
> They are not encrypted.
> >> P.S. I also discovered that if database is used (even if it just
> >> exists, may be it needs to have a CA, may be it doesn't), then
> >> CURLOPT_CAINFO is ignored, or at least the cert that is provided by it
> >> is no longer trusted. May be because the same cert is in the DB, and
> >> without the C flag. If it is the latter, it is probably still a
> >> problem, because if I say I need to trust cert X, there is no other
> >> way for me to do it (if it is a system database, for example).
> >
> > You can try it with your own (e.g. empty) NSS database. You can set the
> > $SSL_DIR environment variable to make libcurl use a different database.
> It's a stretch, sure, but the problem is:
> * server's cert is signed with caXX
> * client's cert is signed with caXX (same CA)
> * you import client's chain into the cert database (CA gets imported, but
> left in there without trust)
> * You don't want to enable trust for this CA for all connections
> * You only want to enable trusting the CA explicitly when you need to.
> * NSS always picks caXX from the database, even if it is specified as
> "trusted" separately, and because it's not marked as trusted in the DB,
> doesn't accept server's certificate.

Could be. I am definitely not an expert in this area. I would suggest to
open a bug against NSS regarding the issue.

> P.P.S.
> I also noticed this other thing in the code, may be I'm wrong.
> The documentation says that to avoid conflicts between using file names and
> certificate aliases for NSS, the use should prepend './' to the relative
> file names. However, the code (nss_load_cert) checks if the file exists
> first. So if there is a file at CWD/nick, it's not possible to use the
> nickname, no?

It should not happen. If you pass a certificate name without a slash,
dup_nickname() will return a non-NULL pointer, which prevents cert_stuff()
and consequently nss_load_cert() from being called at all. If you have a
reproducer for the above mentioned issue, I would be certainly interested.

Received on 2017-01-25