-
Notifications
You must be signed in to change notification settings - Fork 4.8k
/
Copy pathopensslshim.c
255 lines (211 loc) · 7.82 KB
/
opensslshim.c
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
//
#include <assert.h>
#include <dlfcn.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include "opensslshim.h"
#include "pal_atomic.h"
// Define pointers to all the used OpenSSL functions
#define REQUIRED_FUNCTION(fn) TYPEOF(fn) fn##_ptr;
#define REQUIRED_FUNCTION_110(fn) TYPEOF(fn) fn##_ptr;
#define LIGHTUP_FUNCTION(fn) TYPEOF(fn) fn##_ptr;
#define FALLBACK_FUNCTION(fn) TYPEOF(fn) fn##_ptr;
#define RENAMED_FUNCTION(fn,oldfn) TYPEOF(fn) fn##_ptr;
#define LEGACY_FUNCTION(fn) TYPEOF(fn) fn##_ptr;
FOR_ALL_OPENSSL_FUNCTIONS
#undef LEGACY_FUNCTION
#undef RENAMED_FUNCTION
#undef FALLBACK_FUNCTION
#undef LIGHTUP_FUNCTION
#undef REQUIRED_FUNCTION_110
#undef REQUIRED_FUNCTION
#if defined(TARGET_ARM) && defined(TARGET_LINUX)
TYPEOF(OPENSSL_gmtime) OPENSSL_gmtime_ptr;
#endif
// x.x.x, considering the max number of decimal digits for each component
#define MaxVersionStringLength 32
static void* volatile libssl = NULL;
#ifdef __APPLE__
#define DYLIBNAME_PREFIX "libssl."
#define DYLIBNAME_SUFFIX ".dylib"
#define MAKELIB(v) DYLIBNAME_PREFIX v DYLIBNAME_SUFFIX
#else
#define LIBNAME "libssl.so"
#define SONAME_BASE LIBNAME "."
#define MAKELIB(v) SONAME_BASE v
#endif
#if defined(TARGET_ARM) && defined(TARGET_LINUX)
// We support ARM32 linux distros that have Y2038-compatible glibc (those which support _TIME_BITS).
// Some such distros have not yet switched to _TIME_BITS=64 by default, so we may be running against an openssl
// that expects 32-bit time_t even though our time_t is 64-bit.
// This can be deleted once the minimum supported Linux Arm32 distros are
// at least Debian 13 and Ubuntu 24.04.
bool g_libSslUses32BitTime = false;
#endif
static void DlOpen(const char* libraryName)
{
void* libsslNew = dlopen(libraryName, RTLD_LAZY);
// check is someone else has opened and published libssl already
if (!pal_atomic_cas_ptr(&libssl, libsslNew, NULL))
{
dlclose(libsslNew);
}
}
static void OpenLibraryOnce(void)
{
// If there is an override of the version specified using the DOTNET_OPENSSL_VERSION_OVERRIDE
// env variable, try to load that first.
// The format of the value in the env variable is expected to be the version numbers,
// like 1.0.0, 1.0.2 etc.
char* versionOverride = getenv("DOTNET_OPENSSL_VERSION_OVERRIDE");
if ((versionOverride != NULL) && strnlen(versionOverride, MaxVersionStringLength + 1) <= MaxVersionStringLength)
{
#ifdef __APPLE__
char soName[sizeof(DYLIBNAME_PREFIX) + MaxVersionStringLength + sizeof(DYLIBNAME_SUFFIX)] =
DYLIBNAME_PREFIX;
strcat(soName, versionOverride);
strcat(soName, DYLIBNAME_SUFFIX);
#else
char soName[sizeof(SONAME_BASE) + MaxVersionStringLength] = SONAME_BASE;
strcat(soName, versionOverride);
#endif
DlOpen(soName);
}
#ifdef TARGET_ANDROID
if (libssl == NULL)
{
// Android OpenSSL has no soname
DlOpen(LIBNAME);
}
#endif
if (libssl == NULL)
{
// Prefer OpenSSL 3.x
DlOpen(MAKELIB("3"));
}
if (libssl == NULL)
{
DlOpen(MAKELIB("1.1"));
}
if (libssl == NULL)
{
// Debian 9 has dropped support for SSLv3 and so they have bumped their soname. Let's try it
// before trying the version 1.0.0 to make it less probable that some of our other dependencies
// end up loading conflicting version of libssl.
DlOpen(MAKELIB("1.0.2"));
}
if (libssl == NULL)
{
// Now try the default versioned so naming as described in the OpenSSL doc
DlOpen(MAKELIB("1.0.0"));
}
if (libssl == NULL)
{
// Fedora derived distros use different naming for the version 1.0.0
DlOpen(MAKELIB("10"));
}
#ifdef __FreeBSD__
// The ports version of OpenSSL is used over base where possible
if (libssl == NULL)
{
// OpenSSL 3.0 from ports
DlOpen(MAKELIB("12"));
}
if (libssl == NULL)
{
// OpenSSL 3.0 from base as found in FreeBSD 14.0
DlOpen(MAKELIB("30"));
}
// Fallbacks for OpenSSL 1.1.x
if (libssl == NULL)
{
DlOpen(MAKELIB("11"));
}
if (libssl == NULL)
{
DlOpen(MAKELIB("111"));
}
#endif
}
static pthread_once_t g_openLibrary = PTHREAD_ONCE_INIT;
int OpenLibrary(void)
{
pthread_once(&g_openLibrary, OpenLibraryOnce);
if (libssl != NULL)
{
return 1;
}
else
{
return 0;
}
}
void InitializeOpenSSLShim(void)
{
if (!OpenLibrary())
{
fprintf(stderr, "No usable version of libssl was found\n");
abort();
}
// A function defined in libcrypto.so.1.0.0/libssl.so.1.0.0 that is not defined in
// libcrypto.so.1.1.0/libssl.so.1.1.0
const void* v1_0_sentinel = dlsym(libssl, "SSL_state");
// Only permit a single assignment here so that two assemblies both triggering the initializer doesn't cause a
// race where the fn_ptr is nullptr, then properly bound, then goes back to nullptr right before being used (then bound again).
void* volatile tmp_ptr;
// Get pointers to all the functions that are needed
#define REQUIRED_FUNCTION(fn) \
if (!(fn##_ptr = (TYPEOF(fn))(dlsym(libssl, #fn)))) { fprintf(stderr, "Cannot get required symbol " #fn " from libssl\n"); abort(); }
#define REQUIRED_FUNCTION_110(fn) \
if (!v1_0_sentinel && !(fn##_ptr = (TYPEOF(fn))(dlsym(libssl, #fn)))) { fprintf(stderr, "Cannot get required symbol " #fn " from libssl\n"); abort(); }
#define LIGHTUP_FUNCTION(fn) \
fn##_ptr = (TYPEOF(fn))(dlsym(libssl, #fn));
#define FALLBACK_FUNCTION(fn) \
if (!(tmp_ptr = dlsym(libssl, #fn))) { tmp_ptr = (void*)local_##fn; } \
fn##_ptr = (TYPEOF(fn))tmp_ptr;
#define RENAMED_FUNCTION(fn,oldfn) \
tmp_ptr = dlsym(libssl, #fn);\
if (!tmp_ptr && !(tmp_ptr = dlsym(libssl, #oldfn))) { fprintf(stderr, "Cannot get required symbol " #oldfn " from libssl\n"); abort(); } \
fn##_ptr = (TYPEOF(fn))tmp_ptr;
#define LEGACY_FUNCTION(fn) \
if (v1_0_sentinel && !(fn##_ptr = (TYPEOF(fn))(dlsym(libssl, #fn)))) { fprintf(stderr, "Cannot get required symbol " #fn " from libssl\n"); abort(); }
FOR_ALL_OPENSSL_FUNCTIONS
#undef LEGACY_FUNCTION
#undef RENAMED_FUNCTION
#undef FALLBACK_FUNCTION
#undef LIGHTUP_FUNCTION
#undef REQUIRED_FUNCTION_110
#undef REQUIRED_FUNCTION
#if defined(TARGET_ARM) && defined(TARGET_LINUX)
if (!(OPENSSL_gmtime_ptr = (TYPEOF(OPENSSL_gmtime))(dlsym(libssl, "OPENSSL_gmtime")))) { fprintf(stderr, "Cannot get required symbol OPENSSL_gmtime from libssl\n"); abort(); }
#endif
// Sanity check that we have at least one functioning way of reporting errors.
if (ERR_put_error_ptr == &local_ERR_put_error)
{
if (ERR_new_ptr == NULL || ERR_set_debug_ptr == NULL || ERR_set_error_ptr == NULL)
{
fprintf(stderr, "Cannot determine the error reporting routine from libssl\n");
abort();
}
}
#if defined(TARGET_ARM) && defined(TARGET_LINUX)
c_static_assert_msg(sizeof(time_t) == 8, "Build requires 64-bit time_t.");
// This value will represent a time in year 2038 if 64-bit time is used,
// or 1901 if the lower 32 bits are interpreted as a 32-bit time_t value.
time_t timeVal = (time_t)0x80000000U;
struct tm tmVal = { 0 };
// Detect whether openssl is using 32-bit or 64-bit time_t.
// If it uses 32-bit time_t, little-endianness means that the pointer
// will be interpreted as a pointer to the lower 32 bits of timeVal.
// tm_year is the number of years since 1900.
if (!OPENSSL_gmtime(&timeVal, &tmVal) || (tmVal.tm_year != 138 && tmVal.tm_year != 1))
{
fprintf(stderr, "Cannot determine the time_t size used by libssl\n");
abort();
}
g_libSslUses32BitTime = (tmVal.tm_year == 1);
#endif
}