xref: /haiku/src/kits/network/libnetapi/SecureSocket.cpp (revision 97dfeb96704e5dbc5bec32ad7b21379d0125e031)
1 /*
2  * Copyright 2013-2015 Haiku, Inc.
3  * Copyright 2011-2015, 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 			status_t			ErrorCode(int returnValue);
42 
43 	static	SSL_CTX*			Context();
44 	static	int					VerifyCallback(int ok, X509_STORE_CTX* ctx);
45 
46 private:
47 	static	void				_CreateContext();
48 
49 public:
50 			SSL*				fSSL;
51 			BIO*				fBIO;
52 	static	int					sDataIndex;
53 
54 private:
55 	static	SSL_CTX*			sContext;
56 		// FIXME When do we SSL_CTX_free it?
57 	static	pthread_once_t		sInitOnce;
58 };
59 
60 
61 /* static */ SSL_CTX* BSecureSocket::Private::sContext = NULL;
62 /* static */ int BSecureSocket::Private::sDataIndex;
63 /* static */ pthread_once_t BSecureSocket::Private::sInitOnce
64 	= PTHREAD_ONCE_INIT;
65 
66 
67 BSecureSocket::Private::Private()
68 	:
69 	fSSL(NULL),
70 	fBIO(BIO_new(BIO_s_socket()))
71 {
72 }
73 
74 
75 BSecureSocket::Private::~Private()
76 {
77 	// SSL_free also frees the underlying BIO.
78 	if (fSSL != NULL)
79 		SSL_free(fSSL);
80 	else {
81 		// The SSL session was never created (Connect() was not called or
82 		// failed). We must free the BIO we created in the constructor.
83 		BIO_free(fBIO);
84 	}
85 }
86 
87 
88 status_t
89 BSecureSocket::Private::InitCheck()
90 {
91 	if (fBIO == NULL)
92 		return B_NO_MEMORY;
93 	return B_OK;
94 }
95 
96 
97 status_t
98 BSecureSocket::Private::ErrorCode(int returnValue)
99 {
100 	int error = SSL_get_error(fSSL, returnValue);
101 	switch (error) {
102 		case SSL_ERROR_NONE:
103 			// Shouldn't happen...
104 			return B_NO_ERROR;
105 		case SSL_ERROR_ZERO_RETURN:
106 			// Socket is closed
107 			return B_CANCELED;
108 		case SSL_ERROR_SSL:
109 			// Probably no certificate
110 			return B_NOT_ALLOWED;
111 
112 		case SSL_ERROR_WANT_READ:
113 		case SSL_ERROR_WANT_WRITE:
114 		case SSL_ERROR_WANT_CONNECT:
115 		case SSL_ERROR_WANT_ACCEPT:
116 		case SSL_ERROR_WANT_X509_LOOKUP:
117 		case SSL_ERROR_SYSCALL:
118 		default:
119 			// TODO: translate SSL error codes!
120 			fprintf(stderr, "SSL error: %d\n", error);
121 			return B_ERROR;
122 	}
123 }
124 
125 
126 /* static */ SSL_CTX*
127 BSecureSocket::Private::Context()
128 {
129 	// We use lazy initialisation here, because reading certificates from disk
130 	// and parsing them is a relatively long operation and uses some memory.
131 	// We don't want programs that don't use SSL to waste resources with that.
132 	pthread_once(&sInitOnce, _CreateContext);
133 
134 	return sContext;
135 }
136 
137 
138 /*!	This is called each time a certificate verification occurs. It allows us to
139 	catch failures and report them.
140 */
141 /* static */ int
142 BSecureSocket::Private::VerifyCallback(int ok, X509_STORE_CTX* ctx)
143 {
144 	// OpenSSL already checked the certificate again the certificate store for
145 	// us, and tells the result of that in the ok parameter.
146 
147 	// If the verification succeeded, no need for any further checks. Let's
148 	// proceed with the connection.
149 	if (ok)
150 		return ok;
151 
152 	// The certificate verification failed. Signal this to the BSecureSocket.
153 
154 	// First of all, get the affected BSecureSocket
155 	SSL* ssl = (SSL*)X509_STORE_CTX_get_ex_data(ctx,
156 		SSL_get_ex_data_X509_STORE_CTX_idx());
157 	BSecureSocket* socket = (BSecureSocket*)SSL_get_ex_data(ssl, sDataIndex);
158 
159 	// Get the certificate that we could not validate (this may not be the one
160 	// we got from the server, but something higher up in the certificate
161 	// chain)
162 	X509* x509 = X509_STORE_CTX_get_current_cert(ctx);
163 	BCertificate::Private* certificate
164 		= new(std::nothrow) BCertificate::Private(x509);
165 
166 	if (certificate == NULL)
167 		return 0;
168 
169 	int error = X509_STORE_CTX_get_error(ctx);
170 	const char* message = X509_verify_cert_error_string(error);
171 
172 	// Let the BSecureSocket (or subclass) decide if we should continue anyway.
173 	BCertificate failedCertificate(certificate);
174 	return socket->CertificateVerificationFailed(failedCertificate, message);
175 }
176 
177 
178 /* static */ void
179 BSecureSocket::Private::_CreateContext()
180 {
181 	sContext = SSL_CTX_new(SSLv23_method());
182 
183 	// Disable legacy protocols. They have known vulnerabilities.
184 	SSL_CTX_set_options(sContext, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
185 
186 	// Don't bother us with ERROR_WANT_READ.
187 	SSL_CTX_set_mode(sContext, SSL_MODE_AUTO_RETRY);
188 
189 	// Setup certificate verification
190 	BPath certificateStore;
191 	find_directory(B_SYSTEM_DATA_DIRECTORY, &certificateStore);
192 	certificateStore.Append("ssl/CARootCertificates.pem");
193 	// TODO we may want to add a non-packaged certificate directory?
194 	// (would make it possible to store user-added certificate exceptions
195 	// there)
196 	SSL_CTX_load_verify_locations(sContext, certificateStore.Path(), NULL);
197 	SSL_CTX_set_verify(sContext, SSL_VERIFY_PEER, VerifyCallback);
198 
199 	// OpenSSL 1.0.2 and later: use the alternate "trusted first" algorithm to validate certificate
200 	// chains. This makes the validation stop as soon as a recognized certificate is found in the
201 	// chain, instead of validating the whole chain, then seeing if the root certificate is known.
202 #ifdef X509_V_FLAG_TRUSTED_FIRST
203 	X509_VERIFY_PARAM* verifyParam = X509_VERIFY_PARAM_new();
204 	X509_VERIFY_PARAM_set_flags(verifyParam, X509_V_FLAG_TRUSTED_FIRST);
205 	SSL_CTX_set1_param(sContext, verifyParam);
206 
207 	// TODO we need to free this after freeing the SSL context (which we currently never do)
208 	// X509_VERIFY_PARAM_free(verifyParam);
209 #endif
210 
211 	// Get an unique index number for storing application data in SSL
212 	// structs. We will store a pointer to the BSecureSocket class there.
213 	sDataIndex = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
214 }
215 
216 
217 // # pragma mark - BSecureSocket
218 
219 
220 BSecureSocket::BSecureSocket()
221 	:
222 	fPrivate(new(std::nothrow) BSecureSocket::Private())
223 {
224 	fInitStatus = fPrivate != NULL ? fPrivate->InitCheck() : B_NO_MEMORY;
225 }
226 
227 
228 BSecureSocket::BSecureSocket(const BNetworkAddress& peer, bigtime_t timeout)
229 	:
230 	fPrivate(new(std::nothrow) BSecureSocket::Private())
231 {
232 	fInitStatus = fPrivate != NULL ? fPrivate->InitCheck() : B_NO_MEMORY;
233 	Connect(peer, timeout);
234 }
235 
236 
237 BSecureSocket::BSecureSocket(const BSecureSocket& other)
238 	:
239 	BSocket(other)
240 {
241 	fPrivate = new(std::nothrow) BSecureSocket::Private(*other.fPrivate);
242 		// TODO: this won't work this way! - write working copy constructor for
243 		// Private.
244 
245 	if (fPrivate != NULL)
246 		SSL_set_ex_data(fPrivate->fSSL, Private::sDataIndex, this);
247 	else
248 		fInitStatus = B_NO_MEMORY;
249 
250 }
251 
252 
253 BSecureSocket::~BSecureSocket()
254 {
255 	delete fPrivate;
256 }
257 
258 
259 status_t
260 BSecureSocket::Connect(const BNetworkAddress& peer, bigtime_t timeout)
261 {
262 	status_t status = InitCheck();
263 	if (status != B_OK)
264 		return status;
265 
266 	status = BSocket::Connect(peer, timeout);
267 	if (status != B_OK)
268 		return status;
269 
270 	return _Setup();
271 }
272 
273 
274 void
275 BSecureSocket::Disconnect()
276 {
277 	if (IsConnected()) {
278 		if (fPrivate->fSSL != NULL)
279 			SSL_shutdown(fPrivate->fSSL);
280 
281 		BSocket::Disconnect();
282 	}
283 }
284 
285 
286 status_t
287 BSecureSocket::WaitForReadable(bigtime_t timeout) const
288 {
289 	if (fInitStatus != B_OK)
290 		return fInitStatus;
291 	if (!IsConnected())
292 		return B_ERROR;
293 
294 	if (SSL_pending(fPrivate->fSSL) > 0)
295 		return B_OK;
296 
297 	return BSocket::WaitForReadable(timeout);
298 }
299 
300 
301 status_t
302 BSecureSocket::InitCheck()
303 {
304 	if (fPrivate == NULL)
305 		return B_NO_MEMORY;
306 
307 	status_t state = fPrivate->InitCheck();
308 	return state;
309 }
310 
311 
312 bool
313 BSecureSocket::CertificateVerificationFailed(BCertificate&, const char*)
314 {
315 	// Until apps actually make use of the certificate API, let's keep the old
316 	// behavior and accept all connections, even if the certificate validation
317 	// didn't work.
318 	return true;
319 }
320 
321 
322 //	#pragma mark - BDataIO implementation
323 
324 
325 ssize_t
326 BSecureSocket::Read(void* buffer, size_t size)
327 {
328 	if (!IsConnected())
329 		return B_ERROR;
330 
331 	int bytesRead = SSL_read(fPrivate->fSSL, buffer, size);
332 	if (bytesRead >= 0)
333 		return bytesRead;
334 
335 	return fPrivate->ErrorCode(bytesRead);
336 }
337 
338 
339 ssize_t
340 BSecureSocket::Write(const void* buffer, size_t size)
341 {
342 	if (!IsConnected())
343 		return B_ERROR;
344 
345 	int bytesWritten = SSL_write(fPrivate->fSSL, buffer, size);
346 	if (bytesWritten >= 0)
347 		return bytesWritten;
348 
349 	return fPrivate->ErrorCode(bytesWritten);
350 }
351 
352 
353 status_t
354 BSecureSocket::_Setup()
355 {
356 	// Do this only after BSocket::Connect has checked wether we're already
357 	// connected. We don't want to kill an existing SSL session, as that would
358 	// likely crash the protocol loop for it.
359 	if (fPrivate->fSSL != NULL) {
360 		SSL_free(fPrivate->fSSL);
361 	}
362 
363 	fPrivate->fSSL = SSL_new(BSecureSocket::Private::Context());
364 	if (fPrivate->fSSL == NULL) {
365 		BSocket::Disconnect();
366 		return B_NO_MEMORY;
367 	}
368 
369 	BIO_set_fd(fPrivate->fBIO, fSocket, BIO_NOCLOSE);
370 	SSL_set_bio(fPrivate->fSSL, fPrivate->fBIO, fPrivate->fBIO);
371 	SSL_set_ex_data(fPrivate->fSSL, Private::sDataIndex, this);
372 
373 	int returnValue = SSL_connect(fPrivate->fSSL);
374 	if (returnValue <= 0) {
375 		TRACE("SSLConnection can't connect\n");
376 		BSocket::Disconnect();
377 		return fPrivate->ErrorCode(returnValue);
378 	}
379 
380 	return B_OK;
381 }
382 
383 
384 #else	// OPENSSL_ENABLED
385 
386 
387 // #pragma mark - No-SSL stubs
388 
389 
390 BSecureSocket::BSecureSocket()
391 {
392 }
393 
394 
395 BSecureSocket::BSecureSocket(const BNetworkAddress& peer, bigtime_t timeout)
396 {
397 	fInitStatus = B_UNSUPPORTED;
398 }
399 
400 
401 BSecureSocket::BSecureSocket(const BSecureSocket& other)
402 	:
403 	BSocket(other)
404 {
405 }
406 
407 
408 BSecureSocket::~BSecureSocket()
409 {
410 }
411 
412 
413 bool
414 BSecureSocket::CertificateVerificationFailed(BCertificate& certificate, const char*)
415 {
416 	(void)certificate;
417 	return false;
418 }
419 
420 
421 status_t
422 BSecureSocket::Connect(const BNetworkAddress& peer, bigtime_t timeout)
423 {
424 	return fInitStatus = B_UNSUPPORTED;
425 }
426 
427 
428 void
429 BSecureSocket::Disconnect()
430 {
431 }
432 
433 
434 status_t
435 BSecureSocket::WaitForReadable(bigtime_t timeout) const
436 {
437 	return B_UNSUPPORTED;
438 }
439 
440 
441 //	#pragma mark - BDataIO implementation
442 
443 
444 ssize_t
445 BSecureSocket::Read(void* buffer, size_t size)
446 {
447 	return B_UNSUPPORTED;
448 }
449 
450 
451 ssize_t
452 BSecureSocket::Write(const void* buffer, size_t size)
453 {
454 	return B_UNSUPPORTED;
455 }
456 
457 
458 status_t
459 BSecureSocket::InitCheck()
460 {
461 	return B_UNSUPPORTED;
462 }
463 
464 
465 status_t
466 BSecureSocket::_Setup()
467 {
468 	return B_UNSUPPORTED;
469 }
470 
471 
472 #endif	// !OPENSSL_ENABLED
473