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