1 module smtp.ssl;
2 
3 version(ssl) {
4 
5 import std.algorithm;
6 import std.stdio;
7 import std.conv;
8 import std.socket;
9 
10 import deimos.openssl.ssl;
11 import deimos.openssl.conf;
12 import deimos.openssl.err;
13 import deimos.openssl.ssl;
14 
15 }
16 
17 /++
18  Encryption methods for use with SSL
19  +/
20 enum EncryptionMethod : uint {
21 	None    = 0, // No encryption is used
22 
23 	SSLv2   = 1, // SSL version 2 encryption
24 	SSLv23  = 2, // SSL version 23 encryption
25 	SSLv3   = 3, // SSL version 3 encryption
26 
27 	TLSv1   = 4, // TLS version 1 encryption
28 }
29 
30 version(ssl) {
31 /++
32  Initializes OpenSSL library
33  +/
34 void initializeSSL() {
35 	OPENSSL_config("");
36 	SSL_library_init();
37 	SSL_load_error_strings();
38 }
39 
40 /++
41  SSL-encrypted socket-based transport
42  +/
43 class SocketSSL {
44 
45 private:
46 	bool m_verified;
47 	bool m_ready;
48 
49 	SSL_METHOD *encmethod;
50 	SSL_CTX* ctx;
51 	SSL *ssl;
52 	X509* certificate;
53 
54 public:
55 
56 	@property bool ready() { return m_ready; }
57 	@property bool certificateIsVerified() { return m_verified; }
58 
59 	this(Socket socket, EncryptionMethod enctype = EncryptionMethod.SSLv3) {
60 	 	initializeSSL();
61 
62 	 	// Creating SSL context
63 	 	final switch (enctype) {
64 	 	case EncryptionMethod.SSLv2:
65  			version(ssl_no_ssl2) { return; } else {
66  			encmethod = cast(SSL_METHOD*)SSLv2_client_method();
67  			break;
68  			}
69 	 	case EncryptionMethod.SSLv23:
70 	 		encmethod = cast(SSL_METHOD*)SSLv23_client_method();
71 	 		break;
72 	 	case EncryptionMethod.SSLv3:
73 	 		encmethod = cast(SSL_METHOD*)SSLv3_client_method();
74 	 		break;
75 	 	case EncryptionMethod.TLSv1:
76 	 		encmethod = cast(SSL_METHOD*)TLSv1_client_method();
77 	 		break;
78 	 	case EncryptionMethod.None:
79 	 		return;
80 	 	}
81 		ctx = SSL_CTX_new(cast(const(SSL_METHOD*))(encmethod));
82 		if (ctx == null) {
83 	 		writeln("SSL context creation error");
84 	 		return;
85 	 	}
86 
87 	 	// Creating secure data stream
88 	 	ssl = SSL_new(ctx);
89 	 	if (ssl == null) {
90 	 		return;
91 	 	}
92 	 	SSL_set_fd(ssl, socket.handle);
93 
94 	 	// Making SSL handshake
95 	 	auto ret = SSL_connect(ssl);
96 	 	if (ret != 1) {
97 	 		return;
98 	 	}
99 
100 	 	// Get certificate
101 	 	certificate = SSL_get_peer_certificate(ssl);
102 	 	if (certificate == null) {
103 	 		return;
104 	 	}
105 
106 	 	m_ready = true;
107 
108 	 	// Verify certificate
109 	 	long verificationResult = SSL_get_verify_result(ssl);
110 	 	if (verificationResult != X509_V_OK) {
111 	 		m_verified = false;
112 	 	} else {
113 	 		m_verified = true;
114 	 	}
115 	}
116 
117 	/++
118 	 Method encrypts data and writes it into channel
119 	 +/
120 	bool write(in char[] data) {
121 		return SSL_write(ssl, data.ptr, to!(int)(data.length)) >= 0;
122 	}
123 
124 	/++
125 	 Methods reads data from channel and returns it in an unencrypted presentation
126 	 +/
127 	string read() {
128 		char[1024] buf;
129 		int ret = SSL_read(ssl, buf.ptr, buf.length);
130 		if (ret < 0) {
131 			return "";
132 		} else {
133 			for(int index = buf.length - 1; index--; index > 0) {
134 				if (buf[index] == '\n' && buf[index - 1] == '\r') {
135 					return to!string(buf[0 .. index + 1]);
136 				}
137 			}
138 			return to!string(buf);
139 		}
140 	}
141 
142 	~ this() {
143 		if (m_ready) SSL_shutdown(this.ssl);
144 
145 		if (certificate != null) X509_free(certificate);
146 		if (ssl != null) SSL_free(ssl);
147 		if (ctx != null) SSL_CTX_free(ctx);
148 	}
149 };
150 }