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