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