61
There are two different sets of callbacks that an application is expected to provide to safely operate in a multithreaded environment. Static locks provide a fixed number of mutexes available
for OpenSSLs use. Dynamic locks allow OpenSSL to create mutexes as it needs them. OpenSSL does not currently make use of dynamic locks, but reserves the right to do so in the future. If you
want your applications to continue working with a minimal amount of effort in the future, we recommend that you implement both static and dynamic locks now.
4.1.1 Static Locking Callbacks
The static locking mechanism requires the application to provide two callback functions. In addition to providing an implementation for the functions, the application must tell OpenSSL
about them so that it knows to call them when appropriate. The first callback function is used either to acquire or release a lock, and is defined like this:
void locking_functionint mode, int n, const char file, int line;
mode Determines the action that the locking function should take. When the
CRYPTO_LOCK
flag is set, the lock should be acquired; otherwise, it should be released. n
The number of the lock that should be acquired or released. The number is zero-based, meaning that the first lock is identified by 0. The value will never be greater than or equal
to the return from the
CRYPTO_num_locks
function. file
The name of the source file requesting the locking operation to take place. It is intended to aid in debugging and is usually supplied by the
_ _FILE_ _
preprocessor macro. line
The source line number requesting the locking operation to take place. Like the
file
argument, it is also intended to aid in debugging, and it is usually supplied by the
_ _LINE_ _
preprocessor macro. The next callback function is used to get a unique identifier for the calling thread. For example,
GetCurrentThreadId
on Windows will do just that. For reasons that will soon become clear, it is important the value returned from this function be consistent across calls for the same thread,
but different for each thread within the same process. The return value from the function should be the unique identifier. The function is defined like this:
unsigned long id_functionvoid;
Example 4-1 introduces two new OpenSSL library functions:
CRYPTO_set_id_callback
and
CRYPTO_set_locking_callback
. These two functions are used to tell OpenSSL about the callbacks that weve implemented for the static locking mechanism. We can either pass a pointer
to a function to install a callback or
NULL
to remove a callback.
Example 4-1. Static locking callbacks for WIN32 and POSIX threads systems
int THREAD_setupvoid; int THREAD_cleanupvoid;
62
if definedWIN32 define MUTEX_TYPE HANDLE
define MUTEX_SETUPx x = CreateMutexNULL, FALSE, NULL define MUTEX_CLEANUPx CloseHandlex
define MUTEX_LOCKx WaitForSingleObjectx, INFINITE define MUTEX_UNLOCKx ReleaseMutexx
define THREAD_ID GetCurrentThreadId elif defined_POSIX_THREADS
_POSIX_THREADS is normally defined in unistd.h if pthreads are available on your platform.
define MUTEX_TYPE pthread_mutex_t define MUTEX_SETUPx pthread_mutex_initx, NULL
define MUTEX_CLEANUPx pthread_mutex_destroyx define MUTEX_LOCKx pthread_mutex_lockx
define MUTEX_UNLOCKx pthread_mutex_unlockx define THREAD_ID pthread_self
else error You must define mutex operations appropriate for your
platform endif
This array will store all of the mutexes available to OpenSSL. static MUTEX_TYPE mutex_buf = NULL;
static void locking_functionint mode, int n, const char file, int line
{ if mode CRYPTO_LOCK
MUTEX_LOCKmutex_buf[n]; else
MUTEX_UNLOCKmutex_buf[n]; }
static unsigned long id_functionvoid {
return unsigned longTHREAD_ID; }
int THREAD_setupvoid {
int i; mutex_buf = MUTEX_TYPE mallocCRYPTO_num_locks
sizeofMUTEX_TYPE; if mutex_buf
return 0; for i = 0; i CRYPTO_num_locks; i++
MUTEX_SETUPmutex_buf[i]; CRYPTO_set_id_callbackid_function;
CRYPTO_set_locking_callbacklocking_function; return 1;
} int THREAD_cleanupvoid
{ int i;
if mutex_buf return 0;
CRYPTO_set_id_callbackNULL;
63
CRYPTO_set_locking_callbackNULL; for i = 0; i CRYPTO_num_locks; i++
MUTEX_CLEANUPmutex_buf[i]; freemutex_buf;
mutex_buf = NULL; return 1;
}
To use these static locking functions, we need to make one function call before our program starts threads or calls OpenSSL functions, and we must call
THREAD_setup
, which returns 1 normally and 0 if it is unable to allocate the memory required to hold the mutexes. In our example code, we
do make a potentially unsafe assumption that the initialization of each individual mutex will succeed. You may wish to add additional error handling code to your programs. Once weve called
THREAD_setup
and it returns successfully, we can safely make calls into OpenSSL from multiple threads. After our programs threads are finished, or if we are done using OpenSSL, we
should call
THREAD_cleanup
to reclaim any memory used for the mutex structures.
4.1.2 Dynamic Locking Callbacks