[gnutls-devel] [TLS] multiple clients in one process (was: Re: Deployment ... Re: This working group has failed)

Nico Williams nico at cryptonector.com
Thu Nov 28 01:16:03 CET 2013

On Wed, Nov 27, 2013 at 5:58 PM, Andy Lutomirski <luto at amacapital.net> wrote:
> [stripped tls at ietf.org]
> On Wed, Nov 27, 2013 at 3:54 PM, Nico Williams <nico at cryptonector.com> wrote:
>> All of this is off-topic for this list.  I'll post a reply anyways, and
>> I apologize to the list.
>> On Tue, Nov 19, 2013 at 10:24:03PM -0800, Andy Lutomirski wrote:
>>>                     [...].  gnutls_global_init is documented as being
>>> unsafe if called from multiple threads, which seems silly.
>> Initialization is not thread-safe in OpenSSL either.  This is a terrible
>> thing.  It *can* be made thread-safe, so there's no excuse for it not
>> being thread-safe.
>>> GnuTLS has gnutls_pkcs11_init, which is rather impolite -- it
>>> manipulates global state, and it sometimes causes things to
>>> malfunction after forking.  [...]
>> PKCS#11 is by definition fork-unsafe (see the PKCS#11 docs).
>> Any API dealing with "tokens" (in the PKCS#11 sense) is bound to be
>> fork-unsafe for at least open sessions/objects on tokens that require
>> authentication (PIN).  That's because any library using file descriptors
>> where offset is not a relevant concept will necessarily be fork-unsafe
>> by default.  And: any stateful cryptography library (e.g., an
>> implementation of TLS) will tend to be fork-unsafe (imagine a process
>> trying to use a TLS connection on both sides of a fork()!).
> I agree with all of this, except that I don't think that GnuTLS has
> any business even trying to use PKCS11 unless something explicitly
> requests it.  I've had all kinds of problems with libvmime causing
> GnuTLS to start interfacing with some mysterious GNOME PKCS11 token,
> when I don't want any of the above.  It breaks fork for no good
> reason.  (I'm not even trying to do crypto in the child -- I just want
> to avoid getting all kinds of random errors.)

The thing to do is to arrange to re-initialize on the child-side of
fork().  I don't mean lock-everything in a pre-fork() atfork handler,
then unlock everything in the parent and child handlers -- that's not
safe.  I mean: reset all global state in the PKCS#11 implementation on
the child-side of fork() in a child atfork handler -- yes, this means
leaking memory (and its contents), but hopefully the child will
immediately _exit() or exec*().  Also, O_CLOEXEC should be safely set
on all file descriptors used by the PKCS#11 implementation.

>> Of course, that's all rather POSIX-specific, but the problem is inherent
>> to any fork()-like interface.  Use posix_spawn() or similar, then you
>> won't have to worry about fork-safety at all.  Long ago I used to think
>> fork+exec superior to spawn since spawn was easy to implement in terms
>> of fork+exec, but in truth fork() is a dangerous and difficult-to-use
>> interface -- the only safe things to do on the child side of fork() are:
>> async-signal-safe things, culminating in _exit() or exec*() ASAP.
> Except that glibc's posix_spawn is screwed up wrt atfork.  There are
> some bugs open about it.

That's because it needs a real vfork() (or at least to tell fork() not
to run atfork handlers when called from posix_spawn()).  Anyways, it
should be possible to write atfork handlers that don't screw up a
posix_spawn() user.  If one really does use only async-signal-safe
code in the atfork handlers, then that's really not that hard.  But of
course, you've no idea what atfork handlers have been set...


More information about the Gnutls-devel mailing list