BPicture: Fix archive constructor.
[haiku.git] / src / kits / network / libnetapi / SecureSocket.cpp
blob52d7edf65b3d2e532977b27c55b160b0908ffb84
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 */
9 #include <SecureSocket.h>
11 #ifdef OPENSSL_ENABLED
12 # include <openssl/ssl.h>
13 #endif
15 #include <pthread.h>
17 #include <Certificate.h>
18 #include <FindDirectory.h>
19 #include <Path.h>
21 #include "CertificatePrivate.h"
24 //#define TRACE_SOCKET
25 #ifdef TRACE_SOCKET
26 # define TRACE(x...) printf(x)
27 #else
28 # define TRACE(x...) ;
29 #endif
32 #ifdef OPENSSL_ENABLED
35 class BSecureSocket::Private {
36 public:
37 Private();
38 ~Private();
40 status_t InitCheck();
41 status_t ErrorCode(int returnValue);
43 static SSL_CTX* Context();
44 static int VerifyCallback(int ok, X509_STORE_CTX* ctx);
46 private:
47 static void _CreateContext();
49 public:
50 SSL* fSSL;
51 BIO* fBIO;
52 static int sDataIndex;
54 private:
55 static SSL_CTX* sContext;
56 // FIXME When do we SSL_CTX_free it?
57 static pthread_once_t sInitOnce;
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;
67 BSecureSocket::Private::Private()
69 fSSL(NULL),
70 fBIO(BIO_new(BIO_s_socket()))
75 BSecureSocket::Private::~Private()
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);
88 status_t
89 BSecureSocket::Private::InitCheck()
91 if (fBIO == NULL)
92 return B_NO_MEMORY;
93 return B_OK;
97 status_t
98 BSecureSocket::Private::ErrorCode(int returnValue)
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;
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;
126 /* static */ SSL_CTX*
127 BSecureSocket::Private::Context()
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);
134 return sContext;
138 /*! This is called each time a certificate verification occurs. It allows us to
139 catch failures and report them.
141 /* static */ int
142 BSecureSocket::Private::VerifyCallback(int ok, X509_STORE_CTX* ctx)
144 // OpenSSL already checked the certificate again the certificate store for
145 // us, and tells the result of that in the ok parameter.
147 // If the verification succeeded, no need for any further checks. Let's
148 // proceed with the connection.
149 if (ok)
150 return ok;
152 // The certificate verification failed. Signal this to the BSecureSocket.
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);
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);
166 if (certificate == NULL)
167 return 0;
169 int error = X509_STORE_CTX_get_error(ctx);
170 const char* message = X509_verify_cert_error_string(error);
172 // Let the BSecureSocket (or subclass) decide if we should continue anyway.
173 BCertificate failedCertificate(certificate);
174 return socket->CertificateVerificationFailed(failedCertificate, message);
178 /* static */ void
179 BSecureSocket::Private::_CreateContext()
181 sContext = SSL_CTX_new(SSLv23_method());
183 // Disable legacy protocols. They have known vulnerabilities.
184 SSL_CTX_set_options(sContext, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
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);
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);
202 // # pragma mark - BSecureSocket
205 BSecureSocket::BSecureSocket()
207 fPrivate(new(std::nothrow) BSecureSocket::Private())
209 fInitStatus = fPrivate != NULL ? fPrivate->InitCheck() : B_NO_MEMORY;
213 BSecureSocket::BSecureSocket(const BNetworkAddress& peer, bigtime_t timeout)
215 fPrivate(new(std::nothrow) BSecureSocket::Private())
217 fInitStatus = fPrivate != NULL ? fPrivate->InitCheck() : B_NO_MEMORY;
218 Connect(peer, timeout);
222 BSecureSocket::BSecureSocket(const BSecureSocket& other)
224 BSocket(other)
226 fPrivate = new(std::nothrow) BSecureSocket::Private(*other.fPrivate);
227 // TODO: this won't work this way! - write working copy constructor for
228 // Private.
230 if (fPrivate != NULL)
231 SSL_set_ex_data(fPrivate->fSSL, Private::sDataIndex, this);
232 else
233 fInitStatus = B_NO_MEMORY;
238 BSecureSocket::~BSecureSocket()
240 delete fPrivate;
244 status_t
245 BSecureSocket::Connect(const BNetworkAddress& peer, bigtime_t timeout)
247 if (fPrivate == NULL)
248 return B_NO_MEMORY;
250 status_t state = fPrivate->InitCheck();
251 if (state != B_OK)
252 return state;
254 status_t status = BSocket::Connect(peer, timeout);
255 if (status != B_OK)
256 return status;
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);
265 fPrivate->fSSL = SSL_new(BSecureSocket::Private::Context());
266 if (fPrivate->fSSL == NULL) {
267 BSocket::Disconnect();
268 return B_NO_MEMORY;
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);
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);
282 return B_OK;
286 void
287 BSecureSocket::Disconnect()
289 if (IsConnected()) {
290 if (fPrivate->fSSL != NULL)
291 SSL_shutdown(fPrivate->fSSL);
293 BSocket::Disconnect();
298 status_t
299 BSecureSocket::WaitForReadable(bigtime_t timeout) const
301 if (fInitStatus != B_OK)
302 return fInitStatus;
303 if (!IsConnected())
304 return B_ERROR;
306 if (SSL_pending(fPrivate->fSSL) > 0)
307 return B_OK;
309 return BSocket::WaitForReadable(timeout);
313 bool
314 BSecureSocket::CertificateVerificationFailed(BCertificate&, const char*)
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;
323 // #pragma mark - BDataIO implementation
326 ssize_t
327 BSecureSocket::Read(void* buffer, size_t size)
329 if (!IsConnected())
330 return B_ERROR;
332 int bytesRead = SSL_read(fPrivate->fSSL, buffer, size);
333 if (bytesRead >= 0)
334 return bytesRead;
336 return fPrivate->ErrorCode(bytesRead);
340 ssize_t
341 BSecureSocket::Write(const void* buffer, size_t size)
343 if (!IsConnected())
344 return B_ERROR;
346 int bytesWritten = SSL_write(fPrivate->fSSL, buffer, size);
347 if (bytesWritten >= 0)
348 return bytesWritten;
350 return fPrivate->ErrorCode(bytesWritten);
354 #else // OPENSSL_ENABLED
357 // #pragma mark - No-SSL stubs
360 BSecureSocket::BSecureSocket()
365 BSecureSocket::BSecureSocket(const BNetworkAddress& peer, bigtime_t timeout)
367 fInitStatus = B_UNSUPPORTED;
371 BSecureSocket::BSecureSocket(const BSecureSocket& other)
373 BSocket(other)
378 BSecureSocket::~BSecureSocket()
383 bool
384 BSecureSocket::CertificateVerificationFailed(BCertificate& certificate, const char*)
386 (void)certificate;
387 return false;
391 status_t
392 BSecureSocket::Connect(const BNetworkAddress& peer, bigtime_t timeout)
394 return fInitStatus = B_UNSUPPORTED;
398 void
399 BSecureSocket::Disconnect()
404 status_t
405 BSecureSocket::WaitForReadable(bigtime_t timeout) const
407 return B_UNSUPPORTED;
411 // #pragma mark - BDataIO implementation
414 ssize_t
415 BSecureSocket::Read(void* buffer, size_t size)
417 return B_UNSUPPORTED;
421 ssize_t
422 BSecureSocket::Write(const void* buffer, size_t size)
424 return B_UNSUPPORTED;
428 #endif // !OPENSSL_ENABLED