xref: /haiku/src/kits/network/libnetapi/SecureSocket.cpp (revision 85892ec52f476b254d75e2bb2e6560e72faa567c)
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 	// Get an unique index number for storing application data in SSL
197 	// structs. We will store a pointer to the BSecureSocket class there.
198 	sDataIndex = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
199 }
200 
201 
202 // # pragma mark - BSecureSocket
203 
204 
205 BSecureSocket::BSecureSocket()
206 	:
207 	fPrivate(new(std::nothrow) BSecureSocket::Private())
208 {
209 	fInitStatus = fPrivate != NULL ? fPrivate->InitCheck() : B_NO_MEMORY;
210 }
211 
212 
213 BSecureSocket::BSecureSocket(const BNetworkAddress& peer, bigtime_t timeout)
214 	:
215 	fPrivate(new(std::nothrow) BSecureSocket::Private())
216 {
217 	fInitStatus = fPrivate != NULL ? fPrivate->InitCheck() : B_NO_MEMORY;
218 	Connect(peer, timeout);
219 }
220 
221 
222 BSecureSocket::BSecureSocket(const BSecureSocket& other)
223 	:
224 	BSocket(other)
225 {
226 	fPrivate = new(std::nothrow) BSecureSocket::Private(*other.fPrivate);
227 		// TODO: this won't work this way! - write working copy constructor for
228 		// Private.
229 
230 	if (fPrivate != NULL)
231 		SSL_set_ex_data(fPrivate->fSSL, Private::sDataIndex, this);
232 	else
233 		fInitStatus = B_NO_MEMORY;
234 
235 }
236 
237 
238 BSecureSocket::~BSecureSocket()
239 {
240 	delete fPrivate;
241 }
242 
243 
244 status_t
245 BSecureSocket::Connect(const BNetworkAddress& peer, bigtime_t timeout)
246 {
247 	if (fPrivate == NULL)
248 		return B_NO_MEMORY;
249 
250 	status_t state = fPrivate->InitCheck();
251 	if (state != B_OK)
252 		return state;
253 
254 	status_t status = BSocket::Connect(peer, timeout);
255 	if (status != B_OK)
256 		return status;
257 
258 	// Do this only after BSocket::Connect has checked wether we're already
259 	// connected. We don't want to kill an existing SSL session, as that would
260 	// likely crash the protocol loop for it.
261 	if (fPrivate->fSSL != NULL) {
262 		SSL_free(fPrivate->fSSL);
263 	}
264 
265 	fPrivate->fSSL = SSL_new(BSecureSocket::Private::Context());
266 	if (fPrivate->fSSL == NULL) {
267 		BSocket::Disconnect();
268 		return B_NO_MEMORY;
269 	}
270 
271 	BIO_set_fd(fPrivate->fBIO, fSocket, BIO_NOCLOSE);
272 	SSL_set_bio(fPrivate->fSSL, fPrivate->fBIO, fPrivate->fBIO);
273 	SSL_set_ex_data(fPrivate->fSSL, Private::sDataIndex, this);
274 
275 	int returnValue = SSL_connect(fPrivate->fSSL);
276 	if (returnValue <= 0) {
277 		TRACE("SSLConnection can't connect\n");
278 		BSocket::Disconnect();
279 		return fPrivate->ErrorCode(returnValue);
280 	}
281 
282 	return B_OK;
283 }
284 
285 
286 void
287 BSecureSocket::Disconnect()
288 {
289 	if (IsConnected()) {
290 		if (fPrivate->fSSL != NULL)
291 			SSL_shutdown(fPrivate->fSSL);
292 
293 		BSocket::Disconnect();
294 	}
295 }
296 
297 
298 status_t
299 BSecureSocket::WaitForReadable(bigtime_t timeout) const
300 {
301 	if (fInitStatus != B_OK)
302 		return fInitStatus;
303 	if (!IsConnected())
304 		return B_ERROR;
305 
306 	if (SSL_pending(fPrivate->fSSL) > 0)
307 		return B_OK;
308 
309 	return BSocket::WaitForReadable(timeout);
310 }
311 
312 
313 bool
314 BSecureSocket::CertificateVerificationFailed(BCertificate&, const char*)
315 {
316 	// Until apps actually make use of the certificate API, let's keep the old
317 	// behavior and accept all connections, even if the certificate validation
318 	// didn't work.
319 	return true;
320 }
321 
322 
323 //	#pragma mark - BDataIO implementation
324 
325 
326 ssize_t
327 BSecureSocket::Read(void* buffer, size_t size)
328 {
329 	if (!IsConnected())
330 		return B_ERROR;
331 
332 	int bytesRead = SSL_read(fPrivate->fSSL, buffer, size);
333 	if (bytesRead >= 0)
334 		return bytesRead;
335 
336 	return fPrivate->ErrorCode(bytesRead);
337 }
338 
339 
340 ssize_t
341 BSecureSocket::Write(const void* buffer, size_t size)
342 {
343 	if (!IsConnected())
344 		return B_ERROR;
345 
346 	int bytesWritten = SSL_write(fPrivate->fSSL, buffer, size);
347 	if (bytesWritten >= 0)
348 		return bytesWritten;
349 
350 	return fPrivate->ErrorCode(bytesWritten);
351 }
352 
353 
354 #else	// OPENSSL_ENABLED
355 
356 
357 // #pragma mark - No-SSL stubs
358 
359 
360 BSecureSocket::BSecureSocket()
361 {
362 }
363 
364 
365 BSecureSocket::BSecureSocket(const BNetworkAddress& peer, bigtime_t timeout)
366 {
367 	fInitStatus = B_UNSUPPORTED;
368 }
369 
370 
371 BSecureSocket::BSecureSocket(const BSecureSocket& other)
372 	:
373 	BSocket(other)
374 {
375 }
376 
377 
378 BSecureSocket::~BSecureSocket()
379 {
380 }
381 
382 
383 bool
384 BSecureSocket::CertificateVerificationFailed(BCertificate& certificate, const char*)
385 {
386 	(void)certificate;
387 	return false;
388 }
389 
390 
391 status_t
392 BSecureSocket::Connect(const BNetworkAddress& peer, bigtime_t timeout)
393 {
394 	return fInitStatus = B_UNSUPPORTED;
395 }
396 
397 
398 void
399 BSecureSocket::Disconnect()
400 {
401 }
402 
403 
404 status_t
405 BSecureSocket::WaitForReadable(bigtime_t timeout) const
406 {
407 	return B_UNSUPPORTED;
408 }
409 
410 
411 //	#pragma mark - BDataIO implementation
412 
413 
414 ssize_t
415 BSecureSocket::Read(void* buffer, size_t size)
416 {
417 	return B_UNSUPPORTED;
418 }
419 
420 
421 ssize_t
422 BSecureSocket::Write(const void* buffer, size_t size)
423 {
424 	return B_UNSUPPORTED;
425 }
426 
427 
428 #endif	// !OPENSSL_ENABLED
429