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