xref: /haiku/src/kits/network/libnetapi/SecureSocket.cpp (revision 220c5364ab445eb20376f964ade2f7d3a09e163a)
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 	// Setup certificate verification
187 	BPath certificateStore;
188 	find_directory(B_SYSTEM_DATA_DIRECTORY, &certificateStore);
189 	certificateStore.Append("ssl/CARootCertificates.pem");
190 	// TODO we may want to add a non-packaged certificate directory?
191 	// (would make it possible to store user-added certificate exceptions
192 	// there)
193 	SSL_CTX_load_verify_locations(sContext, certificateStore.Path(), NULL);
194 	SSL_CTX_set_verify(sContext, SSL_VERIFY_PEER, VerifyCallback);
195 
196 	// OpenSSL 1.0.2 and later: use the alternate "trusted first" algorithm to validate certificate
197 	// chains. This makes the validation stop as soon as a recognized certificate is found in the
198 	// chain, instead of validating the whole chain, then seeing if the root certificate is known.
199 #ifdef X509_V_FLAG_TRUSTED_FIRST
200 	X509_VERIFY_PARAM* verifyParam = X509_VERIFY_PARAM_new();
201 	X509_VERIFY_PARAM_set_flags(verifyParam, X509_V_FLAG_TRUSTED_FIRST);
202 	SSL_CTX_set1_param(sContext, verifyParam);
203 
204 	// TODO we need to free this after freeing the SSL context (which we currently never do)
205 	// X509_VERIFY_PARAM_free(verifyParam);
206 #endif
207 
208 	// Get an unique index number for storing application data in SSL
209 	// structs. We will store a pointer to the BSecureSocket class there.
210 	sDataIndex = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
211 }
212 
213 
214 // # pragma mark - BSecureSocket
215 
216 
217 BSecureSocket::BSecureSocket()
218 	:
219 	fPrivate(new(std::nothrow) BSecureSocket::Private())
220 {
221 	fInitStatus = fPrivate != NULL ? fPrivate->InitCheck() : B_NO_MEMORY;
222 }
223 
224 
225 BSecureSocket::BSecureSocket(const BNetworkAddress& peer, bigtime_t timeout)
226 	:
227 	fPrivate(new(std::nothrow) BSecureSocket::Private())
228 {
229 	fInitStatus = fPrivate != NULL ? fPrivate->InitCheck() : B_NO_MEMORY;
230 	Connect(peer, timeout);
231 }
232 
233 
234 BSecureSocket::BSecureSocket(const BSecureSocket& other)
235 	:
236 	BSocket(other)
237 {
238 	fPrivate = new(std::nothrow) BSecureSocket::Private(*other.fPrivate);
239 		// TODO: this won't work this way! - write working copy constructor for
240 		// Private.
241 
242 	if (fPrivate != NULL)
243 		SSL_set_ex_data(fPrivate->fSSL, Private::sDataIndex, this);
244 	else
245 		fInitStatus = B_NO_MEMORY;
246 
247 }
248 
249 
250 BSecureSocket::~BSecureSocket()
251 {
252 	delete fPrivate;
253 }
254 
255 
256 status_t
257 BSecureSocket::Connect(const BNetworkAddress& peer, bigtime_t timeout)
258 {
259 	status_t status = InitCheck();
260 	if (status != B_OK)
261 		return status;
262 
263 	status = BSocket::Connect(peer, timeout);
264 	if (status != B_OK)
265 		return status;
266 
267 	return _Setup();
268 }
269 
270 
271 void
272 BSecureSocket::Disconnect()
273 {
274 	if (IsConnected()) {
275 		if (fPrivate->fSSL != NULL)
276 			SSL_shutdown(fPrivate->fSSL);
277 
278 		BSocket::Disconnect();
279 	}
280 }
281 
282 
283 status_t
284 BSecureSocket::WaitForReadable(bigtime_t timeout) const
285 {
286 	if (fInitStatus != B_OK)
287 		return fInitStatus;
288 	if (!IsConnected())
289 		return B_ERROR;
290 
291 	if (SSL_pending(fPrivate->fSSL) > 0)
292 		return B_OK;
293 
294 	return BSocket::WaitForReadable(timeout);
295 }
296 
297 
298 status_t
299 BSecureSocket::InitCheck()
300 {
301 	if (fPrivate == NULL)
302 		return B_NO_MEMORY;
303 
304 	status_t state = fPrivate->InitCheck();
305 	return state;
306 }
307 
308 
309 bool
310 BSecureSocket::CertificateVerificationFailed(BCertificate&, const char*)
311 {
312 	// Until apps actually make use of the certificate API, let's keep the old
313 	// behavior and accept all connections, even if the certificate validation
314 	// didn't work.
315 	return true;
316 }
317 
318 
319 //	#pragma mark - BDataIO implementation
320 
321 
322 ssize_t
323 BSecureSocket::Read(void* buffer, size_t size)
324 {
325 	if (!IsConnected())
326 		return B_ERROR;
327 
328 	int bytesRead = SSL_read(fPrivate->fSSL, buffer, size);
329 	if (bytesRead >= 0)
330 		return bytesRead;
331 
332 	return fPrivate->ErrorCode(bytesRead);
333 }
334 
335 
336 ssize_t
337 BSecureSocket::Write(const void* buffer, size_t size)
338 {
339 	if (!IsConnected())
340 		return B_ERROR;
341 
342 	int bytesWritten = SSL_write(fPrivate->fSSL, buffer, size);
343 	if (bytesWritten >= 0)
344 		return bytesWritten;
345 
346 	return fPrivate->ErrorCode(bytesWritten);
347 }
348 
349 
350 status_t
351 BSecureSocket::_Setup()
352 {
353 	// Do this only after BSocket::Connect has checked wether we're already
354 	// connected. We don't want to kill an existing SSL session, as that would
355 	// likely crash the protocol loop for it.
356 	if (fPrivate->fSSL != NULL) {
357 		SSL_free(fPrivate->fSSL);
358 	}
359 
360 	fPrivate->fSSL = SSL_new(BSecureSocket::Private::Context());
361 	if (fPrivate->fSSL == NULL) {
362 		BSocket::Disconnect();
363 		return B_NO_MEMORY;
364 	}
365 
366 	BIO_set_fd(fPrivate->fBIO, fSocket, BIO_NOCLOSE);
367 	SSL_set_bio(fPrivate->fSSL, fPrivate->fBIO, fPrivate->fBIO);
368 	SSL_set_ex_data(fPrivate->fSSL, Private::sDataIndex, this);
369 
370 	int returnValue = SSL_connect(fPrivate->fSSL);
371 	if (returnValue <= 0) {
372 		TRACE("SSLConnection can't connect\n");
373 		BSocket::Disconnect();
374 		return fPrivate->ErrorCode(returnValue);
375 	}
376 
377 	return B_OK;
378 }
379 
380 
381 #else	// OPENSSL_ENABLED
382 
383 
384 // #pragma mark - No-SSL stubs
385 
386 
387 BSecureSocket::BSecureSocket()
388 {
389 }
390 
391 
392 BSecureSocket::BSecureSocket(const BNetworkAddress& peer, bigtime_t timeout)
393 {
394 	fInitStatus = B_UNSUPPORTED;
395 }
396 
397 
398 BSecureSocket::BSecureSocket(const BSecureSocket& other)
399 	:
400 	BSocket(other)
401 {
402 }
403 
404 
405 BSecureSocket::~BSecureSocket()
406 {
407 }
408 
409 
410 bool
411 BSecureSocket::CertificateVerificationFailed(BCertificate& certificate, const char*)
412 {
413 	(void)certificate;
414 	return false;
415 }
416 
417 
418 status_t
419 BSecureSocket::Connect(const BNetworkAddress& peer, bigtime_t timeout)
420 {
421 	return fInitStatus = B_UNSUPPORTED;
422 }
423 
424 
425 void
426 BSecureSocket::Disconnect()
427 {
428 }
429 
430 
431 status_t
432 BSecureSocket::WaitForReadable(bigtime_t timeout) const
433 {
434 	return B_UNSUPPORTED;
435 }
436 
437 
438 //	#pragma mark - BDataIO implementation
439 
440 
441 ssize_t
442 BSecureSocket::Read(void* buffer, size_t size)
443 {
444 	return B_UNSUPPORTED;
445 }
446 
447 
448 ssize_t
449 BSecureSocket::Write(const void* buffer, size_t size)
450 {
451 	return B_UNSUPPORTED;
452 }
453 
454 
455 status_t
456 BSecureSocket::InitCheck()
457 {
458 	return B_UNSUPPORTED;
459 }
460 
461 
462 status_t
463 BSecureSocket::_Setup()
464 {
465 	return B_UNSUPPORTED;
466 }
467 
468 
469 #endif	// !OPENSSL_ENABLED
470