xref: /haiku/src/kits/network/libnetapi/SecureSocket.cpp (revision 83b1a68c52ba3e0e8796282759f694b7fdddf06d)
1 /*
2  * Copyright 2014 Haiku, Inc.
3  * Copyright 2011, Axel Dörfler, axeld@pinc-software.de.
4  * Copyright 2010, Clemens Zeidler <haiku@clemens-zeidler.de>
5  * Distributed under the terms of the MIT License.
6  */
7 
8 
9 #include <SecureSocket.h>
10 
11 #ifdef OPENSSL_ENABLED
12 #	include <openssl/ssl.h>
13 #endif
14 
15 #include <pthread.h>
16 
17 #include <Certificate.h>
18 #include <FindDirectory.h>
19 #include <Path.h>
20 
21 #include "CertificatePrivate.h"
22 
23 
24 //#define TRACE_SOCKET
25 #ifdef TRACE_SOCKET
26 #	define TRACE(x...) printf(x)
27 #else
28 #	define TRACE(x...) ;
29 #endif
30 
31 
32 #ifdef OPENSSL_ENABLED
33 
34 
35 class BSecureSocket::Private {
36 public:
37 								Private();
38 								~Private();
39 
40 			status_t			InitCheck();
41 
42 	static	SSL_CTX*			Context();
43 	static	int					VerifyCallback(int ok, X509_STORE_CTX* ctx);
44 private:
45 	static	void				CreateContext();
46 public:
47 			SSL*				fSSL;
48 			BIO*				fBIO;
49 	static	int					sDataIndex;
50 private:
51 	static	SSL_CTX*			sContext;
52 		// FIXME When do we SSL_CTX_free it?
53 	static	pthread_once_t		sInitOnce;
54 };
55 
56 
57 /* static */ SSL_CTX* BSecureSocket::Private::sContext = NULL;
58 /* static */ int BSecureSocket::Private::sDataIndex;
59 /* static */ pthread_once_t BSecureSocket::Private::sInitOnce
60 	= PTHREAD_ONCE_INIT;
61 
62 
63 BSecureSocket::Private::Private()
64 	:
65 	fSSL(NULL),
66 	fBIO(BIO_new(BIO_s_socket()))
67 {
68 }
69 
70 
71 BSecureSocket::Private::~Private()
72 {
73 	// SSL_free also frees the underlying BIO.
74 	if (fSSL != NULL)
75 		SSL_free(fSSL);
76 	else {
77 		// The SSL session was never created (Connect() was not called or
78 		// failed). We must free the BIO we created in the constructor.
79 		BIO_free(fBIO);
80 	}
81 }
82 
83 
84 status_t
85 BSecureSocket::Private::InitCheck()
86 {
87 	if (fBIO == NULL)
88 		return B_NO_MEMORY;
89 	return B_OK;
90 }
91 
92 
93 /* static */ void
94 BSecureSocket::Private::CreateContext()
95 {
96 	sContext = SSL_CTX_new(SSLv23_method());
97 
98 	// Disable legacy protocols. They have known vulnerabilities.
99 	SSL_CTX_set_options(sContext, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
100 
101 	// Setup certificate verification
102 	BPath certificateStore;
103 	find_directory(B_SYSTEM_DATA_DIRECTORY, &certificateStore);
104 	certificateStore.Append("ssl/CARootCertificates.pem");
105 	// TODO we may want to add a non-packaged certificate directory?
106 	// (would make it possible to store user-added certificate exceptions
107 	// there)
108 	SSL_CTX_load_verify_locations(sContext, certificateStore.Path(), NULL);
109 	SSL_CTX_set_verify(sContext, SSL_VERIFY_PEER, VerifyCallback);
110 
111 	// Get an unique index number for storing application data in SSL
112 	// structs. We will store a pointer to the BSecureSocket class there.
113 	sDataIndex = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
114 }
115 
116 
117 /* static */ SSL_CTX*
118 BSecureSocket::Private::Context()
119 {
120 	// We use lazy initialisation here, because reading certificates from disk
121 	// and parsing them is a relatively long operation and uses some memory.
122 	// We don't want programs that don't use SSL to waste resources with that.
123 	pthread_once(&sInitOnce, CreateContext);
124 
125 	return sContext;
126 }
127 
128 
129 // This is called each time a certificate verification occurs. It allows us to
130 // catch failures and report them.
131 /* static */ int
132 BSecureSocket::Private::VerifyCallback(int ok, X509_STORE_CTX* ctx)
133 {
134 	// OpenSSL already checked the certificate again the certificate store for
135 	// us, and tells the result of that in the ok parameter.
136 
137 	// If the verification succeeded, no need for any further checks. Let's
138 	// proceed with the connection.
139 	if (ok)
140 		return ok;
141 
142 	// The certificate verification failed. Signal this to the BSecureSocket.
143 
144 	// First of all, get the affected BSecureSocket
145 	SSL* ssl = (SSL*)X509_STORE_CTX_get_ex_data(ctx,
146 		SSL_get_ex_data_X509_STORE_CTX_idx());
147 	BSecureSocket* socket = (BSecureSocket*)SSL_get_ex_data(ssl, sDataIndex);
148 
149 	// Get the certificate that we could not validate (this may not be the one
150 	// we got from the server, but something higher up in the certificate
151 	// chain)
152 	X509* x509 = X509_STORE_CTX_get_current_cert(ctx);
153 	BCertificate::Private* certificate
154 		= new(std::nothrow) BCertificate::Private(x509);
155 
156 	if (certificate == NULL)
157 		return 0;
158 
159 	int error = X509_STORE_CTX_get_error(ctx);
160 	const char* message = X509_verify_cert_error_string(error);
161 
162 	// Let the BSecureSocket (or subclass) decide if we should continue anyway.
163 	BCertificate failedCertificate(certificate);
164 	return socket->CertificateVerificationFailed(failedCertificate, message);
165 }
166 
167 
168 // # pragma mark - BSecureSocket
169 
170 
171 BSecureSocket::BSecureSocket()
172 	:
173 	fPrivate(new(std::nothrow) BSecureSocket::Private())
174 {
175 	fInitStatus = fPrivate != NULL ? fPrivate->InitCheck() : B_NO_MEMORY;
176 }
177 
178 
179 BSecureSocket::BSecureSocket(const BNetworkAddress& peer, bigtime_t timeout)
180 	:
181 	fPrivate(new(std::nothrow) BSecureSocket::Private())
182 {
183 	fInitStatus = fPrivate != NULL ? fPrivate->InitCheck() : B_NO_MEMORY;
184 	Connect(peer, timeout);
185 }
186 
187 
188 BSecureSocket::BSecureSocket(const BSecureSocket& other)
189 	:
190 	BSocket(other)
191 {
192 	fPrivate = new(std::nothrow) BSecureSocket::Private(*other.fPrivate);
193 		// TODO: this won't work this way! - write working copy constructor for
194 		// Private.
195 
196 	if (fPrivate != NULL)
197 		SSL_set_ex_data(fPrivate->fSSL, Private::sDataIndex, this);
198 	else
199 		fInitStatus = B_NO_MEMORY;
200 
201 }
202 
203 
204 BSecureSocket::~BSecureSocket()
205 {
206 	delete fPrivate;
207 }
208 
209 
210 status_t
211 BSecureSocket::Connect(const BNetworkAddress& peer, bigtime_t timeout)
212 {
213 	if (fPrivate == NULL)
214 		return B_NO_MEMORY;
215 
216 	status_t state = fPrivate->InitCheck();
217 	if (state != B_OK)
218 		return state;
219 
220 	status_t status = BSocket::Connect(peer, timeout);
221 	if (status != B_OK)
222 		return status;
223 
224 	// Do this only after BSocket::Connect has checked wether we're already
225 	// connected. We don't want to kill an existing SSL session, as that would
226 	// likely crash the protocol loop for it.
227 	if (fPrivate->fSSL != NULL) {
228 		SSL_free(fPrivate->fSSL);
229 	}
230 
231 	fPrivate->fSSL = SSL_new(BSecureSocket::Private::Context());
232 	if (fPrivate->fSSL == NULL) {
233 		BSocket::Disconnect();
234 		return B_NO_MEMORY;
235 	}
236 
237 	BIO_set_fd(fPrivate->fBIO, fSocket, BIO_NOCLOSE);
238 	SSL_set_bio(fPrivate->fSSL, fPrivate->fBIO, fPrivate->fBIO);
239 	SSL_set_ex_data(fPrivate->fSSL, Private::sDataIndex, this);
240 
241 	int sslStatus = SSL_connect(fPrivate->fSSL);
242 
243 	if (sslStatus <= 0)	{
244 		TRACE("SSLConnection can't connect\n");
245 		BSocket::Disconnect();
246 
247 		switch (SSL_get_error(fPrivate->fSSL, sslStatus)) {
248 			case SSL_ERROR_NONE:
249 				// Shouldn't happen...
250 				return B_NO_ERROR;
251 			case SSL_ERROR_ZERO_RETURN:
252 				// Socket is closed
253 				return B_CANCELED;
254 			case SSL_ERROR_SSL:
255 				// Probably no certificate
256 				return B_NOT_ALLOWED;
257 			default:
258 				return B_ERROR;
259 		}
260 	}
261 
262 	return B_OK;
263 }
264 
265 
266 void
267 BSecureSocket::Disconnect()
268 {
269 	if (IsConnected()) {
270 		if (fPrivate->fSSL != NULL) {
271 			SSL_shutdown(fPrivate->fSSL);
272 		}
273 
274 		BSocket::Disconnect();
275 	}
276 }
277 
278 
279 status_t
280 BSecureSocket::WaitForReadable(bigtime_t timeout) const
281 {
282 	if (fInitStatus != B_OK)
283 		return fInitStatus;
284 	if (!IsConnected())
285 		return B_ERROR;
286 
287 	if (SSL_pending(fPrivate->fSSL) > 0)
288 		return B_OK;
289 
290 	return BSocket::WaitForReadable(timeout);
291 }
292 
293 
294 bool
295 BSecureSocket::CertificateVerificationFailed(BCertificate&, const char*)
296 {
297 	// Until apps actually make use of the certificate API, let's keep the old
298 	// behavior and accept all connections, even if the certificate validation
299 	// didn't work.
300 	return true;
301 }
302 
303 
304 //	#pragma mark - BDataIO implementation
305 
306 
307 ssize_t
308 BSecureSocket::Read(void* buffer, size_t size)
309 {
310 	if (!IsConnected())
311 		return B_ERROR;
312 
313 	int bytesRead = SSL_read(fPrivate->fSSL, buffer, size);
314 	if (bytesRead >= 0)
315 		return bytesRead;
316 
317 	// TODO: translate SSL error codes!
318 	return B_ERROR;
319 }
320 
321 
322 ssize_t
323 BSecureSocket::Write(const void* buffer, size_t size)
324 {
325 	if (!IsConnected())
326 		return B_ERROR;
327 
328 	int bytesWritten = SSL_write(fPrivate->fSSL, buffer, size);
329 	if (bytesWritten >= 0)
330 		return bytesWritten;
331 
332 	// TODO: translate SSL error codes!
333 	return B_ERROR;
334 }
335 
336 
337 #else	// OPENSSL_ENABLED
338 
339 
340 // #pragma mark - No-SSL stubs
341 
342 
343 BSecureSocket::BSecureSocket()
344 {
345 }
346 
347 
348 BSecureSocket::BSecureSocket(const BNetworkAddress& peer, bigtime_t timeout)
349 {
350 	fInitStatus = B_UNSUPPORTED;
351 }
352 
353 
354 BSecureSocket::BSecureSocket(const BSecureSocket& other)
355 	:
356 	BSocket(other)
357 {
358 }
359 
360 
361 BSecureSocket::~BSecureSocket()
362 {
363 }
364 
365 
366 bool
367 BSecureSocket::CertificateVerificationFailed(BCertificate& certificate, const char*)
368 {
369 	(void)certificate;
370 	return false;
371 }
372 
373 
374 status_t
375 BSecureSocket::Connect(const BNetworkAddress& peer, bigtime_t timeout)
376 {
377 	return fInitStatus = B_UNSUPPORTED;
378 }
379 
380 
381 void
382 BSecureSocket::Disconnect()
383 {
384 }
385 
386 
387 status_t
388 BSecureSocket::WaitForReadable(bigtime_t timeout) const
389 {
390 	return B_UNSUPPORTED;
391 }
392 
393 
394 //	#pragma mark - BDataIO implementation
395 
396 
397 ssize_t
398 BSecureSocket::Read(void* buffer, size_t size)
399 {
400 	return B_UNSUPPORTED;
401 }
402 
403 
404 ssize_t
405 BSecureSocket::Write(const void* buffer, size_t size)
406 {
407 	return B_UNSUPPORTED;
408 }
409 
410 
411 #endif	// !OPENSSL_ENABLED
412