1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
|
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
#include "crypto/s2n_locking.h"
#include <openssl/crypto.h>
#include <pthread.h>
#include "crypto/s2n_openssl.h"
#include "utils/s2n_mem.h"
#include "utils/s2n_safety.h"
/* Writing multithreaded applications using Openssl-1.0.2
* requires calling CRYPTO_set_locking_callback.
* If the callback is not set, locks are no-ops and unexpected
* behavior may occur, particularly for RSA and X509.
*
* In the past s2n-tls relied on customers setting the callback
* themselves, but that seems unnecessary since other parts of
* the library (like fork detection) already rely on the pthreads library.
*
* For more information:
* https://www.openssl.org/blog/blog/2017/02/21/threads/
* https://www.openssl.org/docs/man1.0.2/man3/threads.html
*/
#define S2N_MUTEXES(mem) ((pthread_mutex_t *) (void *) (mem).data)
/* While the locking-related APIs "exist" in later versions of
* Openssl, they tend to be placeholders or hardcoded values like:
* #define CRYPTO_get_locking_callback() (NULL)
* So the code will compile with strange warnings / errors like
* loop conditions always being false.
*/
#if !(S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0))
static struct s2n_blob mutexes_mem = { 0 };
static size_t mutexes_count = 0;
static void s2n_locking_cb(int mode, int n, char *file, int line)
{
pthread_mutex_t *mutexes = S2N_MUTEXES(mutexes_mem);
if (!mutexes_mem.data || n < 0 || (size_t) n >= mutexes_count) {
return;
}
if (mode & CRYPTO_LOCK) {
pthread_mutex_lock(&(mutexes[n]));
} else {
pthread_mutex_unlock(&(mutexes[n]));
}
}
S2N_RESULT s2n_locking_init(void)
{
if (CRYPTO_get_locking_callback() != NULL) {
return S2N_RESULT_OK;
}
int num_locks = CRYPTO_num_locks();
RESULT_ENSURE_GTE(num_locks, 0);
RESULT_GUARD_POSIX(s2n_realloc(&mutexes_mem, num_locks * sizeof(pthread_mutex_t)));
pthread_mutex_t *mutexes = S2N_MUTEXES(mutexes_mem);
mutexes_count = 0;
for (size_t i = 0; i < (size_t) num_locks; i++) {
RESULT_ENSURE_EQ(pthread_mutex_init(&(mutexes[i]), NULL), 0);
mutexes_count++;
}
CRYPTO_set_locking_callback((void (*)()) s2n_locking_cb);
return S2N_RESULT_OK;
}
S2N_RESULT s2n_locking_cleanup(void)
{
if (CRYPTO_get_locking_callback() == (void (*)()) s2n_locking_cb) {
CRYPTO_set_locking_callback(NULL);
}
pthread_mutex_t *mutexes = S2N_MUTEXES(mutexes_mem);
if (mutexes) {
while (mutexes_count > 0) {
RESULT_ENSURE_EQ(pthread_mutex_destroy(&(mutexes[mutexes_count - 1])), 0);
mutexes_count--;
}
RESULT_GUARD_POSIX(s2n_free(&mutexes_mem));
}
return S2N_RESULT_OK;
}
#else
S2N_RESULT s2n_locking_init(void)
{
return S2N_RESULT_OK;
}
S2N_RESULT s2n_locking_cleanup(void)
{
return S2N_RESULT_OK;
}
#endif
|