xref: /haiku/src/kits/network/libnetapi/SecureSocket.cpp (revision 3b07762c548ec4016dea480d1061577cd15ec614)
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 	// Setup certificate verification
99 	BPath certificateStore;
100 	find_directory(B_SYSTEM_DATA_DIRECTORY, &certificateStore);
101 	certificateStore.Append("ssl/CARootCertificates.pem");
102 	// TODO we may want to add a non-packaged certificate directory?
103 	// (would make it possible to store user-added certificate exceptions
104 	// there)
105 	SSL_CTX_load_verify_locations(sContext, certificateStore.Path(), NULL);
106 	SSL_CTX_set_verify(sContext, SSL_VERIFY_PEER, VerifyCallback);
107 
108 	// Get an unique index number for storing application data in SSL
109 	// structs. We will store a pointer to the BSecureSocket class there.
110 	sDataIndex = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
111 }
112 
113 
114 /* static */ SSL_CTX*
115 BSecureSocket::Private::Context()
116 {
117 	// We use lazy initialisation here, because reading certificates from disk
118 	// and parsing them is a relatively long operation and uses some memory.
119 	// We don't want programs that don't use SSL to waste resources with that.
120 	pthread_once(&sInitOnce, CreateContext);
121 
122 	return sContext;
123 }
124 
125 
126 // This is called each time a certificate verification occurs. It allows us to
127 // catch failures and report them.
128 /* static */ int
129 BSecureSocket::Private::VerifyCallback(int ok, X509_STORE_CTX* ctx)
130 {
131 	// OpenSSL already checked the certificate again the certificate store for
132 	// us, and tells the result of that in the ok parameter.
133 
134 	// If the verification succeeded, no need for any further checks. Let's
135 	// proceed with the connection.
136 	if (ok)
137 		return ok;
138 
139 	// The certificate verification failed. Signal this to the BSecureSocket.
140 
141 	// First of all, get the affected BSecureSocket
142 	SSL* ssl = (SSL*)X509_STORE_CTX_get_ex_data(ctx,
143 		SSL_get_ex_data_X509_STORE_CTX_idx());
144 	BSecureSocket* socket = (BSecureSocket*)SSL_get_ex_data(ssl, sDataIndex);
145 
146 	// Get the certificate that we could not validate (this may not be the one
147 	// we got from the server, but something higher up in the certificate
148 	// chain)
149 	X509* x509 = X509_STORE_CTX_get_current_cert(ctx);
150 	BCertificate::Private* certificate
151 		= new(std::nothrow) BCertificate::Private(x509);
152 
153 	if (certificate == NULL)
154 		return 0;
155 
156 	// Let the BSecureSocket (or subclass) decide if we should continue anyway.
157 	return socket->CertificateVerificationFailed(BCertificate(certificate));
158 }
159 
160 
161 // # pragma mark - BSecureSocket
162 
163 
164 BSecureSocket::BSecureSocket()
165 	:
166 	fPrivate(new(std::nothrow) BSecureSocket::Private())
167 {
168 	fInitStatus = fPrivate != NULL ? fPrivate->InitCheck() : B_NO_MEMORY;
169 }
170 
171 
172 BSecureSocket::BSecureSocket(const BNetworkAddress& peer, bigtime_t timeout)
173 	:
174 	fPrivate(new(std::nothrow) BSecureSocket::Private())
175 {
176 	fInitStatus = fPrivate != NULL ? fPrivate->InitCheck() : B_NO_MEMORY;
177 	Connect(peer, timeout);
178 }
179 
180 
181 BSecureSocket::BSecureSocket(const BSecureSocket& other)
182 	:
183 	BSocket(other)
184 {
185 	fPrivate = new(std::nothrow) BSecureSocket::Private(*other.fPrivate);
186 		// TODO: this won't work this way! - write working copy constructor for
187 		// Private.
188 
189 	if (fPrivate != NULL)
190 		SSL_set_ex_data(fPrivate->fSSL, Private::sDataIndex, this);
191 	else
192 		fInitStatus = B_NO_MEMORY;
193 
194 }
195 
196 
197 BSecureSocket::~BSecureSocket()
198 {
199 	delete fPrivate;
200 }
201 
202 
203 status_t
204 BSecureSocket::Connect(const BNetworkAddress& peer, bigtime_t timeout)
205 {
206 	if (fPrivate == NULL)
207 		return B_NO_MEMORY;
208 
209 	status_t state = fPrivate->InitCheck();
210 	if (state != B_OK)
211 		return state;
212 
213 	status_t status = BSocket::Connect(peer, timeout);
214 	if (status != B_OK)
215 		return status;
216 
217 	// Do this only after BSocket::Connect has checked wether we're already
218 	// connected. We don't want to kill an existing SSL session, as that would
219 	// likely crash the protocol loop for it.
220 	if (fPrivate->fSSL != NULL) {
221 		SSL_free(fPrivate->fSSL);
222 	}
223 
224 	fPrivate->fSSL = SSL_new(BSecureSocket::Private::Context());
225 	if (fPrivate->fSSL == NULL) {
226 		BSocket::Disconnect();
227 		return B_NO_MEMORY;
228 	}
229 
230 	BIO_set_fd(fPrivate->fBIO, fSocket, BIO_NOCLOSE);
231 	SSL_set_bio(fPrivate->fSSL, fPrivate->fBIO, fPrivate->fBIO);
232 	SSL_set_ex_data(fPrivate->fSSL, Private::sDataIndex, this);
233 
234 	int sslStatus = SSL_connect(fPrivate->fSSL);
235 
236 	if (sslStatus <= 0)	{
237 		TRACE("SSLConnection can't connect\n");
238 		BSocket::Disconnect();
239 
240 		switch (SSL_get_error(fPrivate->fSSL, sslStatus)) {
241 			case SSL_ERROR_NONE:
242 				// Shouldn't happen...
243 				return B_NO_ERROR;
244 			case SSL_ERROR_ZERO_RETURN:
245 				// Socket is closed
246 				return B_CANCELED;
247 			case SSL_ERROR_SSL:
248 				// Probably no certificate
249 				return B_NOT_ALLOWED;
250 			default:
251 				return B_ERROR;
252 		}
253 	}
254 
255 	return B_OK;
256 }
257 
258 
259 void
260 BSecureSocket::Disconnect()
261 {
262 	if (IsConnected()) {
263 		if (fPrivate->fSSL != NULL) {
264 			SSL_shutdown(fPrivate->fSSL);
265 		}
266 
267 		BSocket::Disconnect();
268 	}
269 }
270 
271 
272 status_t
273 BSecureSocket::WaitForReadable(bigtime_t timeout) const
274 {
275 	if (fInitStatus != B_OK)
276 		return fInitStatus;
277 	if (!IsConnected())
278 		return B_ERROR;
279 
280 	if (SSL_pending(fPrivate->fSSL) > 0)
281 		return B_OK;
282 
283 	return BSocket::WaitForReadable(timeout);
284 }
285 
286 
287 bool
288 BSecureSocket::CertificateVerificationFailed(BCertificate)
289 {
290 	// Until apps actually make use of the certificate API, let's keep the old
291 	// behavior and accept all connections, even if the certificate validation
292 	// didn't work.
293 	return true;
294 }
295 
296 
297 //	#pragma mark - BDataIO implementation
298 
299 
300 ssize_t
301 BSecureSocket::Read(void* buffer, size_t size)
302 {
303 	if (!IsConnected())
304 		return B_ERROR;
305 
306 	int bytesRead = SSL_read(fPrivate->fSSL, buffer, size);
307 	if (bytesRead >= 0)
308 		return bytesRead;
309 
310 	// TODO: translate SSL error codes!
311 	return B_ERROR;
312 }
313 
314 
315 ssize_t
316 BSecureSocket::Write(const void* buffer, size_t size)
317 {
318 	if (!IsConnected())
319 		return B_ERROR;
320 
321 	int bytesWritten = SSL_write(fPrivate->fSSL, buffer, size);
322 	if (bytesWritten >= 0)
323 		return bytesWritten;
324 
325 	// TODO: translate SSL error codes!
326 	return B_ERROR;
327 }
328 
329 
330 #else	// OPENSSL_ENABLED
331 
332 
333 // #pragma mark - No-SSL stubs
334 
335 
336 BSecureSocket::BSecureSocket()
337 {
338 }
339 
340 
341 BSecureSocket::BSecureSocket(const BNetworkAddress& peer, bigtime_t timeout)
342 {
343 	fInitStatus = B_UNSUPPORTED;
344 }
345 
346 
347 BSecureSocket::BSecureSocket(const BSecureSocket& other)
348 	:
349 	BSocket(other)
350 {
351 }
352 
353 
354 BSecureSocket::~BSecureSocket()
355 {
356 }
357 
358 
359 bool
360 BSecureSocket::CertificateVerificationFailed(BCertificate)
361 {
362 	return false;
363 }
364 
365 
366 status_t
367 BSecureSocket::Connect(const BNetworkAddress& peer, bigtime_t timeout)
368 {
369 	return fInitStatus = B_UNSUPPORTED;
370 }
371 
372 
373 void
374 BSecureSocket::Disconnect()
375 {
376 }
377 
378 
379 status_t
380 BSecureSocket::WaitForReadable(bigtime_t timeout) const
381 {
382 	return B_UNSUPPORTED;
383 }
384 
385 
386 //	#pragma mark - BDataIO implementation
387 
388 
389 ssize_t
390 BSecureSocket::Read(void* buffer, size_t size)
391 {
392 	return B_UNSUPPORTED;
393 }
394 
395 
396 ssize_t
397 BSecureSocket::Write(const void* buffer, size_t size)
398 {
399 	return B_UNSUPPORTED;
400 }
401 
402 
403 #endif	// !OPENSSL_ENABLED
404