s2n_locking.c 3.3 KB

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