1 module requests.ssl_adapter;
2 
3 version(staticssl) {
4     public import requests.ssl_adapter_static;
5 } else:
6 
7 import std.stdio;
8 import std.string;
9 import std.format;
10 import std.typecons;
11 import core.stdc.stdlib;
12 import core.sys.posix.dlfcn;
13 import std.experimental.logger;
14 import core.stdc.config;
15 
16 version(Windows) {
17     import core.sys.windows.windows;
18     alias DLSYM = GetProcAddress;
19 } else {
20     alias DLSYM = dlsym;
21 }
22 
23 version(RequestsSkipSSL)
24 {
25     enum enableSSL = false;
26 }
27 else
28 {
29     enum enableSSL = true;
30 }
31 
32 /*
33  * /usr/include/openssl/tls1.h:# define TLS_ANY_VERSION 0x10000
34  */
35 
36 immutable int TLS_ANY_VERSION = 0x10000;
37 immutable int TLS1_VERSION = 0x0301;
38 immutable int TLS1_2_VERSION = 0x0303;
39 
40 struct SSL {};
41 struct SSL_CTX {};
42 struct SSL_METHOD {};
43 
44 //
45 // N  - function name, R - return type, A - args
46 //
47 string SSL_Function_decl(string N, R, A...)() {
48     string F = "extern (C) @nogc nothrow %s function %s adapter_%s;".format(R.stringof, A.stringof, N);
49     return F;
50 }
51 string SSL_Function_set_i(string N, R, A...)() {
52     string F = "openssl.adapter_%s = cast(typeof(openssl.adapter_%s))DLSYM(cast(void*)openssl._libssl, \"%s\");".format(N, N, N);
53     return F;
54 }
55 string CRYPTO_Function_set_i(string N, R, A...)() {
56     string F = "openssl.adapter_%s = cast(typeof(openssl.adapter_%s))DLSYM(cast(void*)openssl._libcrypto, \"%s\");".format(N, N, N);
57     return F;
58 }
59 
60 private alias Version = Tuple!(int, "major", int, "minor");
61 
62 immutable static OpenSSL openssl;
63 
64 shared static this() {
65     version(OSX) {
66         enum loadFunction = "dlopen(lib.ptr, RTLD_LAZY)";
67         immutable string[] libsslname = [
68             "libssl.46.dylib",
69             "libssl.44.dylib",
70             "libssl.43.dylib",
71             "libssl.35.dylib",
72             "libssl.dylib",
73         ];
74         immutable string[] libcryptoname = [
75             "libcrypto.44.dylib",
76             "libcrypto.42.dylib",
77             "libcrypto.41.dylib",
78             "libcrypto.35.dylib",
79             "libcrypto.dylib",
80         ];
81     } else
82     version(linux) {
83         enum loadFunction = "dlopen(lib.ptr, RTLD_LAZY)";
84         immutable string[] libsslname = [
85             "libssl.so.3",
86             "libssl.so.1.1",
87             "libssl.so.1.0.2",
88             "libssl.so.1.0.1",
89             "libssl.so.1.0.0",
90             "libssl.so",
91         ];
92         immutable string[] libcryptoname = [
93             "libcrypto.so.3",
94             "libcrypto.so.1.1",
95             "libcrypto.so.1.0.2",
96             "libcrypto.so.1.0.1",
97             "libcrypto.so.1.0.0",
98             "libcrypto.so",
99         ];
100     } else
101     version(FreeBSD) {
102         enum loadFunction = "dlopen(lib.ptr, RTLD_LAZY)";
103         immutable string[] libsslname = [
104             "libssl.so.1.1",
105             "libssl.so.1.0.2",
106             "libssl.so.1.0.1",
107             "libssl.so.1.0.0",
108             "libssl.so",
109         ];
110         immutable string[] libcryptoname = [
111             "libcrypto.so.1.1",
112             "libcrypto.so.1.0.2",
113             "libcrypto.so.1.0.1",
114             "libcrypto.so.1.0.0",
115             "libcrypto.so",
116         ];
117     } else
118     version(Windows) {
119         enum loadFunction = "LoadLibrary(lib.ptr)";
120         immutable wstring[] libsslname = [
121              "libssl32.dll"w,
122              "libssl-1_1"w,
123              "libssl-1_1-x64"w,
124              "libssl-3"w,
125              "libssl-3-x64"w,
126          ];
127          immutable wstring[] libcryptoname = [
128              "libeay32.dll"w,
129              "libcrypto-1_1"w,
130              "libcrypto-1_1-x64"w,
131              "libcrypto-3"w,
132              "libcrypto-3-x64"w,
133         ];
134     } else {
135         debug(requests) trace("error loading openssl: unsupported system - first access over https will fail");
136         return;
137     }
138 
139     static if ( enableSSL && is(typeof(loadFunction)) ) {
140         foreach(lib; libsslname) {
141             openssl._libssl = cast(typeof(openssl._libssl))mixin(loadFunction);
142             if ( openssl._libssl !is null ) {
143                 debug(requests) tracef("will use %s".format(lib));
144                 break;
145             }
146         }
147         foreach(lib; libcryptoname) {
148             openssl._libcrypto = cast(typeof(openssl._libcrypto))mixin(loadFunction);
149             if ( openssl._libcrypto !is null ) {
150                 debug(requests) tracef("will use %s".format(lib));
151                 break;
152             }
153         }
154     }
155 
156     if ( openssl._libssl is null ) {
157         debug(requests) trace("warning: failed to load libssl - first access over https will fail");
158         return;
159     }
160     if ( openssl._libcrypto is null ) {
161         debug(requests) trace("warning: failed to load libcrypto - first access over https will fail");
162         return;
163     }
164     openssl._ver = openssl.OpenSSL_version_detect();
165 
166     mixin(SSL_Function_set_i!("SSL_library_init", int));
167     mixin(CRYPTO_Function_set_i!("OpenSSL_add_all_ciphers", void));
168     mixin(CRYPTO_Function_set_i!("OpenSSL_add_all_digests", void));
169     mixin(SSL_Function_set_i!("SSL_load_error_strings", void));
170 
171     mixin(SSL_Function_set_i!("OPENSSL_init_ssl", int, ulong, void*));
172     mixin(CRYPTO_Function_set_i!("OPENSSL_init_crypto", int, ulong, void*));
173 
174     mixin(SSL_Function_set_i!("TLSv1_client_method", SSL_METHOD*));
175     mixin(SSL_Function_set_i!("TLSv1_2_client_method", SSL_METHOD*));
176     mixin(SSL_Function_set_i!("TLS_method", SSL_METHOD*));
177     mixin(SSL_Function_set_i!("SSLv23_client_method", SSL_METHOD*));
178     mixin(SSL_Function_set_i!("SSL_CTX_new", SSL_CTX*, SSL_METHOD*));
179     mixin(SSL_Function_set_i!("SSL_CTX_set_default_verify_paths", int, SSL_CTX*));
180     mixin(SSL_Function_set_i!("SSL_CTX_load_verify_locations", int, SSL_CTX*, char*, char*));
181     mixin(SSL_Function_set_i!("SSL_CTX_set_verify", void, SSL_CTX*, int, void*));
182     mixin(SSL_Function_set_i!("SSL_CTX_use_PrivateKey_file", int, SSL_CTX*, const char*, int));
183     mixin(SSL_Function_set_i!("SSL_CTX_use_certificate_file", int, SSL_CTX*, const char*, int));
184     mixin(SSL_Function_set_i!("SSL_CTX_set_cipher_list", int, SSL_CTX*, const char*));
185     mixin(SSL_Function_set_i!("SSL_CTX_ctrl", c_long, SSL_CTX*, int, c_long, void*));
186     mixin(SSL_Function_set_i!("SSL_new", SSL*, SSL_CTX*));
187     mixin(SSL_Function_set_i!("SSL_set_fd", int, SSL*, int));
188     mixin(SSL_Function_set_i!("SSL_connect", int, SSL*));
189     mixin(SSL_Function_set_i!("SSL_write", int, SSL*, const void*, int));
190     mixin(SSL_Function_set_i!("SSL_read", int, SSL*, void*, int));
191     mixin(SSL_Function_set_i!("SSL_free", void, SSL*));
192     mixin(SSL_Function_set_i!("SSL_CTX_free", void, SSL_CTX*));
193     mixin(SSL_Function_set_i!("SSL_get_error", int, SSL*, int));
194     mixin(SSL_Function_set_i!("SSL_ctrl", c_long, SSL*, int, c_long, void*));
195     mixin(CRYPTO_Function_set_i!("ERR_reason_error_string", char*, c_ulong));
196     mixin(CRYPTO_Function_set_i!("ERR_get_error", c_ulong));
197 
198     void delegate()[Version] init_matrix;
199     init_matrix[Version(1,0)] = &openssl.init1_0;
200     init_matrix[Version(1,1)] = &openssl.init1_1;
201     init_matrix[Version(2,0)] = &openssl.init1_1;
202     init_matrix[Version(0,2)] = &openssl.init1_1; // libressl >= 2.7.1
203     init_matrix[Version(0,3)] = &openssl.init1_1; // libressl >= 3.0.0
204     init_matrix[Version(3,0)] = &openssl.init1_1; // >=3.0
205     auto initVer = (ver) {
206         if (ver.major == 3 && ver.minor >= 1) // set 3.x to 3.0 for the init matrix
207             return Version(3, 0);
208         else
209             return ver;
210     }(openssl._ver);
211     auto init = init_matrix.get(initVer, null);
212     if ( init is null ) {
213         throw new Exception("loading openssl: unknown version %s for init".format(openssl._ver));
214     }
215     init();
216 }
217 
218 struct OpenSSL {
219 
220     private {
221         Version         _ver;
222         void*           _libssl;
223         void*           _libcrypto;
224 
225         // openssl 1.0.x init functions
226         mixin(SSL_Function_decl!("SSL_library_init", int));
227         mixin(SSL_Function_decl!("OpenSSL_add_all_ciphers", void));
228         mixin(SSL_Function_decl!("OpenSSL_add_all_digests", void));
229         mixin(SSL_Function_decl!("SSL_load_error_strings", void));
230 
231         // openssl 1.1.x init functions
232         mixin(SSL_Function_decl!("OPENSSL_init_ssl", int, ulong, void*)); // fixed width 64 bit arg
233         mixin(SSL_Function_decl!("OPENSSL_init_crypto", int, ulong, void*)); // fixed width 64 bit arg
234 
235         // all other functions
236         mixin(SSL_Function_decl!("TLSv1_client_method", SSL_METHOD*));
237         mixin(SSL_Function_decl!("TLSv1_2_client_method", SSL_METHOD*));
238         mixin(SSL_Function_decl!("TLS_method", SSL_METHOD*));
239         mixin(SSL_Function_decl!("SSLv23_client_method", SSL_METHOD*));
240         mixin(SSL_Function_decl!("SSL_CTX_new", SSL_CTX*, SSL_METHOD*));
241         mixin(SSL_Function_decl!("SSL_CTX_set_default_verify_paths", int, SSL_CTX*));
242         mixin(SSL_Function_decl!("SSL_CTX_load_verify_locations", int, SSL_CTX*, char*, char*));
243         mixin(SSL_Function_decl!("SSL_CTX_set_verify", void, SSL_CTX*, int, void*));
244         mixin(SSL_Function_decl!("SSL_CTX_use_PrivateKey_file", int, SSL_CTX*, const char*, int));
245         mixin(SSL_Function_decl!("SSL_CTX_use_certificate_file", int, SSL_CTX*, const char*, int));
246         mixin(SSL_Function_decl!("SSL_CTX_set_cipher_list", int, SSL_CTX*, const char*));
247         mixin(SSL_Function_decl!("SSL_CTX_ctrl", c_long, SSL_CTX*, int, c_long, void*));
248         mixin(SSL_Function_decl!("SSL_new", SSL*, SSL_CTX*));
249         mixin(SSL_Function_decl!("SSL_set_fd", int, SSL*, int));
250         mixin(SSL_Function_decl!("SSL_connect", int, SSL*));
251         mixin(SSL_Function_decl!("SSL_write", int, SSL*, const void*, int));
252         mixin(SSL_Function_decl!("SSL_read", int, SSL*, void*, int));
253         mixin(SSL_Function_decl!("SSL_free", void, SSL*));
254         mixin(SSL_Function_decl!("SSL_CTX_free", void, SSL_CTX*));
255         mixin(SSL_Function_decl!("SSL_get_error", int, SSL*, int));
256         mixin(SSL_Function_decl!("SSL_ctrl", c_long, SSL*, int, c_long, void*));
257         mixin(SSL_Function_decl!("ERR_reason_error_string", char*, c_ulong));
258         mixin(SSL_Function_decl!("ERR_get_error", c_ulong));
259     }
260 
261     Version reportVersion() const @nogc nothrow pure {
262         return _ver;
263     };
264 
265     private Version OpenSSL_version_detect() const {
266         c_ulong function() OpenSSL_version_num = cast(c_ulong function())DLSYM(cast(void*)_libcrypto, "OpenSSL_version_num".ptr);
267         if ( OpenSSL_version_num ) {
268             auto v = OpenSSL_version_num() & 0xffffffff;
269             return Version((v>>>28) & 0xff, (v>>>20) & 0xff);
270         }
271         return Version(1, 0);
272     }
273 
274     private void init1_0() const {
275         adapter_SSL_library_init();
276         adapter_OpenSSL_add_all_ciphers();
277         adapter_OpenSSL_add_all_digests();
278         adapter_SSL_load_error_strings();
279     }
280     private void init1_1() const {
281         /**
282         Standard initialisation options
283 
284         #define OPENSSL_INIT_LOAD_SSL_STRINGS       0x00200000L
285 
286         # define OPENSSL_INIT_LOAD_CRYPTO_STRINGS    0x00000002L
287         # define OPENSSL_INIT_ADD_ALL_CIPHERS        0x00000004L
288         # define OPENSSL_INIT_ADD_ALL_DIGESTS        0x00000008L
289         **/
290         enum OPENSSL_INIT_LOAD_SSL_STRINGS = 0x00200000L;
291         enum OPENSSL_INIT_LOAD_CRYPTO_STRINGS = 0x00000002L;
292         enum OPENSSL_INIT_ADD_ALL_CIPHERS = 0x00000004L;
293         enum OPENSSL_INIT_ADD_ALL_DIGESTS = 0x00000008L;
294         adapter_OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, null);
295         adapter_OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, null);
296     }
297 
298     SSL_METHOD* TLSv1_client_method() const {
299         if ( adapter_TLSv1_client_method is null ) {
300             throw new Exception("openssl not initialized - is it installed?");
301         }
302         return adapter_TLSv1_client_method();
303     }
304     SSL_METHOD* TLSv1_2_client_method() const {
305         if ( adapter_TLSv1_2_client_method is null ) {
306             throw new Exception("openssl not initialized - is it installed?");
307         }
308         return adapter_TLSv1_2_client_method();
309     }
310     SSL_METHOD* SSLv23_client_method() const {
311         if ( adapter_SSLv23_client_method is null ) {
312             throw new Exception("can't complete call to SSLv23_client_method");
313         }
314         return adapter_SSLv23_client_method();
315     }
316     SSL_METHOD* TLS_method() const {
317         if ( adapter_TLS_method !is null ) {
318             return adapter_TLS_method();
319         }
320         if ( adapter_SSLv23_client_method !is null ) {
321             return adapter_SSLv23_client_method();
322         }
323         throw new Exception("can't complete call to TLS_method");
324     }
325     SSL_CTX* SSL_CTX_new(SSL_METHOD* method) const {
326         if ( adapter_SSL_CTX_new is null ) {
327             throw new Exception("openssl not initialized - is it installed?");
328         }
329         return adapter_SSL_CTX_new(method);
330     }
331     int SSL_CTX_set_default_verify_paths(SSL_CTX* ctx) const @nogc nothrow {
332         return adapter_SSL_CTX_set_default_verify_paths(ctx);
333     }
334     int SSL_CTX_load_verify_locations(SSL_CTX* ctx, char* CAFile, char* CAPath) const @nogc nothrow {
335         return adapter_SSL_CTX_load_verify_locations(ctx, CAFile, CAPath);
336     }
337     void SSL_CTX_set_verify(SSL_CTX* ctx, int mode, void* callback) const @nogc nothrow {
338         adapter_SSL_CTX_set_verify(ctx, mode, callback);
339     }
340     int SSL_CTX_use_PrivateKey_file(SSL_CTX* ctx, const char* file, int type) const @nogc nothrow {
341         return adapter_SSL_CTX_use_PrivateKey_file(ctx, file, type);
342     }
343     int SSL_CTX_use_certificate_file(SSL_CTX* ctx, const char* file, int type) const @nogc nothrow {
344         return adapter_SSL_CTX_use_certificate_file(ctx, file, type);
345     }
346     int SSL_CTX_set_cipher_list(SSL_CTX* ssl_ctx, const char* c) const @nogc nothrow {
347         return adapter_SSL_CTX_set_cipher_list(ssl_ctx, c);
348     }
349     /*
350      *
351      * # define SSL_CTRL_SET_MIN_PROTO_VERSION          123
352      * # define SSL_CTRL_SET_MAX_PROTO_VERSION          124
353     */
354     enum int SSL_CTRL_SET_MIN_PROTO_VERSION = 123;
355     enum int SSL_CTRL_SET_MAX_PROTO_VERSION = 124;
356     int SSL_CTX_set_min_proto_version(SSL_CTX* ctx, int v) const @nogc nothrow {
357         int r = cast(int)adapter_SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MIN_PROTO_VERSION, cast(c_long)v, null);
358         return r;
359     }
360     int SSL_CTX_set_max_proto_version(SSL_CTX* ctx, int v) const @nogc nothrow {
361         int r = cast(int)adapter_SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MAX_PROTO_VERSION, cast(c_long)v, null);
362         return r;
363     }
364     SSL* SSL_new(SSL_CTX* ctx) const @nogc nothrow {
365         return adapter_SSL_new(ctx);
366     }
367     int SSL_set_fd(SSL* ssl, int fd) const @nogc nothrow {
368         return adapter_SSL_set_fd(ssl, fd);
369     }
370     int SSL_connect(SSL* ssl) const @nogc nothrow {
371         return adapter_SSL_connect(ssl);
372     }
373     int SSL_read(SSL* ssl, void *b, int n) const @nogc nothrow {
374         return adapter_SSL_read(ssl, b, n);
375     }
376     int SSL_write(SSL* ssl, const void *b, int n) const @nogc nothrow {
377         return adapter_SSL_write(ssl, b, n);
378     }
379     void SSL_free(SSL* ssl) const @nogc nothrow @trusted {
380         adapter_SSL_free(ssl);
381     }
382     void SSL_CTX_free(SSL_CTX* ctx) const @nogc nothrow @trusted {
383         adapter_SSL_CTX_free(ctx);
384     }
385     int SSL_get_error(SSL* ssl, int err) const @nogc nothrow {
386         return adapter_SSL_get_error(ssl, err);
387     }
388     c_long SSL_set_tlsext_host_name(SSL* ssl, const char* host) const @nogc nothrow {
389         enum SSL_CTRL_SET_TLSEXT_HOSTNAME = 55;
390         enum TLSEXT_NAMETYPE_host_name = 0;
391         return adapter_SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME,TLSEXT_NAMETYPE_host_name, cast(void*)host);
392     }
393     char* ERR_reason_error_string(c_ulong code) const @nogc nothrow {
394         return adapter_ERR_reason_error_string(code);
395     }
396     c_ulong ERR_get_error() const @nogc nothrow {
397         return adapter_ERR_get_error();
398     }
399 }
400 /*
401 int main() {
402     import std.socket;
403 
404     auto v = openssl.reportVersion();
405     writefln("openSSL v.%s.%s", v.major, v.minor);
406     openssl.SSL_library_init();
407     writeln("InitSSL - ok");
408     SSL_CTX* ctx = openssl.SSL_CTX_new(openssl.TLSv1_client_method());
409     writefln("SSL_CTX_new = %x", ctx);
410     int r = openssl.adapter_SSL_CTX_set_default_verify_paths(ctx);
411     writefln("SSL_CTX_set_default_verify_paths = %d(%s)", r, r==1?"ok":"fail");
412     r = openssl.adapter_SSL_CTX_load_verify_locations(ctx, cast(char*)null, cast(char*)null);
413     writefln("SSL_CTX_load_verify_locations - ok");
414     openssl.SSL_CTX_set_verify(ctx, 0, null);
415     writefln("SSL_CTX_set_verify - ok");
416     //r = openssl.SSL_CTX_use_PrivateKey_file(ctx, null, 0);
417     //writefln("SSL_CTX_use_PrivateKey_file = %d(%s)", r, r==1?"ok":"fail");
418     //r = openssl.SSL_CTX_use_certificate_file(ctx, cast(char*), 0);
419     //writefln("SSL_CTX_use_certificate_file = %d(%s)", r, r==1?"ok":"fail");
420     SSL* ssl = openssl.SSL_new(ctx);
421     writefln("SSL_new = %x", ssl);
422     auto s = new Socket(AddressFamily.INET, SocketType.STREAM, ProtocolType.TCP);
423     Address[] a = getAddress("ns.od.ua", 443);
424     writeln(a[0]);
425     s.connect(a[0]);
426     r = openssl.SSL_set_fd(ssl, s.handle);
427     writefln("SSL_set_fd = %d(%s)", r, r==1?"ok":"fail");
428     r = openssl.SSL_connect(ssl);
429     writefln("SSL_connect = %d(%s)", r, r==1?"ok":"fail");
430     if ( r < 0 ) {
431         writefln("code: %d", openssl.SSL_get_error(ssl, r));
432     }
433     string req = "GET / HTTP/1.0\n\n";
434     r = openssl.SSL_write(ssl, cast(void*)req.ptr, cast(int)req.length);
435     writefln("SSL_write = %d", r);
436     do {
437         ubyte[]  resp = new ubyte[](1024);
438         r = openssl.SSL_read(ssl, cast(void*)resp.ptr, cast(int)1024);
439         writefln("SSL_read = %d", r);
440         if ( r > 0 ) {
441             writeln(cast(string)resp);
442         }
443     } while(r > 0);
444     openssl.SSL_free(ssl);
445     openssl.SSL_CTX_free(ctx);
446     s.close();
447     return 0;
448 }
449 */