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