[krbdev.mit.edu #6413] pkinit thread safety with OpenSSL
Greg Hudson via RT
rt-comment at krbdev.mit.edu
Thu Mar 27 14:29:09 EDT 2014
#7889 fixes part of the problem, by avoiding using the OpenSSL mutable
OID table.
The major remaining issue has to do with library initialization. At
module initialization time, we call:
CRYPTO_malloc_init();
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
We try to do this only once, using a static variable, but we don't use a
mutex around the static variable, so with threads we could conceivably
call the functions multiple times in close proximity. Other uses of
OpenSSL in the same process could also call these functions at the same
time as we do, whether or not we used a mutex or pthread_once.
We do not register locking callbacks as described in
https://www.openssl.org/support/faq.html#PROG1. In theory, OpenSSL has
the right to be non-thread-safe in arbitrary ways because of this,
unless the application or another OpenSSL user registers callbacks. I
am not aware of specific problems outside of initialization, but they
may still exist.
We never call cleanup functions like EVP_cleanup, which is good for
friendliness to other users of OpenSSL but means OpenSSL could leak heap
memory if it were repeatedly loaded and unloaded. Russ has noted that
this scenario could happen because of PAM loading libkrb5 which then
loads PKINIT.
CRYPTO_malloc_init is a macro which passes the malloc/realloc/free
pointers from the caller's namespace into the library. On a typical
Unix build, this has no impact, since the library uses
malloc/realloc/free from its own namespace by default. On Windows
(which we currently don't build PKINIT for, but might in the future),
CRYPTO_malloc_init is supposed to help with the case where the caller
was built in a different compiler mode from OpenSSL and has a different
malloc implementation (https://www.openssl.org/support/faq.html#PROG2).
Obviously this can't work simultaneously for multiple users of OpenSSL
in the same process if they aren't all compiled in the same mode.
ERR_load_crypto_strings is necessary to produce text output from error
messages (https://www.openssl.org/support/faq.html#PROG7). Accesses to
the OpenSSL global error table during initialization are protected by
locks if locking callbacks are registered.
OpenSSL_add_all_algorithms is necessary to use ciphers and digests
(https://www.openssl.org/support/faq.html#PROG8). As with
ERR_load_crypto_strings, accesses to the OpenSSL global error table
during initialization are protected by locks if locking callbacks are
registered.
We are somewhat limited in how much we can insure thread-safe
initialization within PKINIT. Using a k5-platform.h library initializer
to initialize OpenSSL would help avoid some thread safety issues for the
specific case where PKINIT is used in multiple threads but the process
contains no other user of OpenSSL. Registering our own locking
callbacks would also work for that specific case, but could be very
unfriendly to other users of OpenSSL. Checking first if locking
callbacks are already registered is itself not thread-safe.
Nico has patches at
https://github.com/nicowilliams/openssl/commits/thread_safety to address
thread-safe initialization within OpenSSL, which he believes could be
accepted upstream if they were cleaned up and submitted. I can't be
sure if or when that will happen.
I don't think we can take any responsibility for preventing memory leaks
on unload, unless OpenSSL adds new APIs for registering and
unregistering callers like NSS has. OpenSSL could perhaps address the
issue through a library finalizer, if it is willing to take on the
associated portability issues.
More information about the krb5-bugs
mailing list