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.
9 #include <SecureSocket.h>
11 #ifdef OPENSSL_ENABLED
12 # include <openssl/ssl.h>
17 #include <Certificate.h>
18 #include <FindDirectory.h>
21 #include "CertificatePrivate.h"
24 //#define TRACE_SOCKET
26 # define TRACE(x...) printf(x)
28 # define TRACE(x...) ;
32 #ifdef OPENSSL_ENABLED
35 class BSecureSocket::Private
{
41 status_t
ErrorCode(int returnValue
);
43 static SSL_CTX
* Context();
44 static int VerifyCallback(int ok
, X509_STORE_CTX
* ctx
);
47 static void _CreateContext();
52 static int sDataIndex
;
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
67 BSecureSocket::Private::Private()
70 fBIO(BIO_new(BIO_s_socket()))
75 BSecureSocket::Private::~Private()
77 // SSL_free also frees the underlying BIO.
81 // The SSL session was never created (Connect() was not called or
82 // failed). We must free the BIO we created in the constructor.
89 BSecureSocket::Private::InitCheck()
98 BSecureSocket::Private::ErrorCode(int returnValue
)
100 int error
= SSL_get_error(fSSL
, returnValue
);
103 // Shouldn't happen...
105 case SSL_ERROR_ZERO_RETURN
:
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
:
119 // TODO: translate SSL error codes!
120 fprintf(stderr
, "SSL error: %d\n", 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
);
138 /*! This is called each time a certificate verification occurs. It allows us to
139 catch failures and report them.
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.
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
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
)
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
);
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
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
)
226 fPrivate
= new(std::nothrow
) BSecureSocket::Private(*other
.fPrivate
);
227 // TODO: this won't work this way! - write working copy constructor for
230 if (fPrivate
!= NULL
)
231 SSL_set_ex_data(fPrivate
->fSSL
, Private::sDataIndex
, this);
233 fInitStatus
= B_NO_MEMORY
;
238 BSecureSocket::~BSecureSocket()
245 BSecureSocket::Connect(const BNetworkAddress
& peer
, bigtime_t timeout
)
247 if (fPrivate
== NULL
)
250 status_t state
= fPrivate
->InitCheck();
254 status_t status
= BSocket::Connect(peer
, timeout
);
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();
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
);
287 BSecureSocket::Disconnect()
290 if (fPrivate
->fSSL
!= NULL
)
291 SSL_shutdown(fPrivate
->fSSL
);
293 BSocket::Disconnect();
299 BSecureSocket::WaitForReadable(bigtime_t timeout
) const
301 if (fInitStatus
!= B_OK
)
306 if (SSL_pending(fPrivate
->fSSL
) > 0)
309 return BSocket::WaitForReadable(timeout
);
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
323 // #pragma mark - BDataIO implementation
327 BSecureSocket::Read(void* buffer
, size_t size
)
332 int bytesRead
= SSL_read(fPrivate
->fSSL
, buffer
, size
);
336 return fPrivate
->ErrorCode(bytesRead
);
341 BSecureSocket::Write(const void* buffer
, size_t size
)
346 int bytesWritten
= SSL_write(fPrivate
->fSSL
, buffer
, size
);
347 if (bytesWritten
>= 0)
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
)
378 BSecureSocket::~BSecureSocket()
384 BSecureSocket::CertificateVerificationFailed(BCertificate
& certificate
, const char*)
392 BSecureSocket::Connect(const BNetworkAddress
& peer
, bigtime_t timeout
)
394 return fInitStatus
= B_UNSUPPORTED
;
399 BSecureSocket::Disconnect()
405 BSecureSocket::WaitForReadable(bigtime_t timeout
) const
407 return B_UNSUPPORTED
;
411 // #pragma mark - BDataIO implementation
415 BSecureSocket::Read(void* buffer
, size_t size
)
417 return B_UNSUPPORTED
;
422 BSecureSocket::Write(const void* buffer
, size_t size
)
424 return B_UNSUPPORTED
;
428 #endif // !OPENSSL_ENABLED