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