1 module ssl.openssl; 2 3 import loader.loader; 4 5 import core.stdc.config; 6 import core.stdc..string : strlen; 7 8 struct SSL_CTX; 9 struct SSL; 10 struct SSL_METHOD; 11 struct X509_STORE_CTX; 12 13 // Constants from Deimos bindings 14 enum SSL_VERIFY_NONE = 0x00; 15 enum SSL_ERROR_WANT_READ = 2; 16 enum SSL_ERROR_WANT_WRITE = 3; 17 // End of Deimos bindings 18 19 extern(C) 20 { 21 alias VerifyCallback = int function(int, X509_STORE_CTX*); 22 23 int function() SSL_library_init_p; 24 void function() OPENSSL_add_all_algorithms_noconf_p; 25 void function() SSL_load_error_strings_p; 26 int function(const SSL *ssl, int ret) SSL_get_error_p; 27 28 char* function(c_ulong e, char* buf) ERR_error_string_p; 29 30 SSL_CTX* function(const(SSL_METHOD)* meth) SSL_CTX_new_p; 31 const(SSL_METHOD)* function() SSLv23_client_method_p; /* negotiated */ 32 33 SSL* function(SSL_CTX* ctx) SSL_new_p; 34 int function(SSL* s, int fd) SSL_set_fd_p; 35 void function(SSL *s, int mode, VerifyCallback verify_callback) SSL_set_verify_p; 36 int function(SSL* ssl) SSL_connect_p; 37 int function(SSL* ssl, void* buf, int num) SSL_read_p; 38 int function(SSL* ssl, const(void)* buf, int num) SSL_write_p; 39 } 40 41 version(Windows) 42 { 43 immutable libsslNames = ["ssleay32"]; 44 immutable libcryptoNames = ["libeay32"]; 45 } 46 else version(Posix) 47 { 48 immutable libsslNames = ["libssl.so.1.0.0", "libssl.so"]; 49 immutable libcryptoNames = ["libcrypto.so.1.0.0", "libcrypto.so"]; 50 } 51 else 52 static assert(false, "unrecognized platform"); 53 54 private DynamicLibrary tryLibraries(in string[] names) 55 { 56 if(names.length > 1) 57 { 58 foreach(name; names[0 .. $ - 1]) 59 { 60 try 61 return DynamicLibrary(name); 62 catch(DynamicLoaderException) {} 63 } 64 } 65 66 return DynamicLibrary(names[$ - 1]); 67 } 68 69 void loadOpenSSL() 70 { 71 static bool loaded = false; 72 if(loaded) 73 return; 74 75 auto ssl = tryLibraries(libsslNames); 76 ssl.resolve!SSL_library_init_p; 77 ssl.resolve!SSL_load_error_strings_p; 78 ssl.resolve!SSL_get_error_p; 79 80 ssl.resolve!SSL_CTX_new_p; 81 ssl.resolve!SSLv23_client_method_p; 82 83 ssl.resolve!SSL_new_p; 84 ssl.resolve!SSL_set_fd_p; 85 ssl.resolve!SSL_set_verify_p; 86 ssl.resolve!SSL_connect_p; 87 ssl.resolve!SSL_read_p; 88 ssl.resolve!SSL_write_p; 89 90 auto crypto = tryLibraries(libcryptoNames); 91 crypto.resolve!OPENSSL_add_all_algorithms_noconf_p; 92 crypto.resolve!ERR_error_string_p; 93 94 SSL_library_init_p(); 95 OPENSSL_add_all_algorithms_noconf_p(); 96 SSL_load_error_strings_p(); 97 98 loaded = true; 99 } 100 101 /** 102 * Thrown if an SSL error occurs. 103 */ 104 class OpenSSLException : Exception 105 { 106 private: 107 int error_; 108 109 public: 110 this(string msg, int error, string file = __FILE__, size_t line = __LINE__) 111 { 112 error_ = error; 113 super(msg, file, line); 114 } 115 116 int error() @property 117 { 118 return error_; 119 } 120 } 121 122 int sslEnforce(const SSL* ssl, int result, string file = __FILE__, size_t line = __LINE__) 123 { 124 if(result < 0) 125 { 126 auto error = SSL_get_error_p(ssl, result); 127 128 // std.socket.wouldHaveBlocked is true when the following errors 129 // are reported, so defer to that. 130 if(error != SSL_ERROR_WANT_READ && error != SSL_ERROR_WANT_WRITE) 131 { 132 char* zMsg = ERR_error_string_p(error, null); 133 auto msg = zMsg[0 .. strlen(zMsg)].idup; 134 throw new OpenSSLException(msg, error, file, line); 135 } 136 } 137 138 return result; 139 } 140