Implement CONNECT pass-through for HTTPS proxy

* When using a proxy, HTTPS connexion must still go directly to the
  target website. The proxy can then act as a TCP stream relay and just
  transmit the raw SSL stream between the client and website.
* For this, we ask the proxy sending an HTTP request with the CONNECT
  method. If the proxy supports this, we can then send anything as the
  payload and it will be forwarded.
* Untested, as the network here in Dusseldorf doesn't let me use a
  proxy.

ticket : #10973
This commit is contained in:
Adrien Destugues
2015-11-11 01:16:38 +01:00
parent 959355842f
commit c614961364
7 changed files with 235 additions and 61 deletions
+8 -1
View File
@@ -16,6 +16,12 @@
#include <NetworkRequest.h> #include <NetworkRequest.h>
namespace BPrivate {
class CheckedSecureSocket;
class CheckedProxySecureSocket;
};
class BHttpRequest : public BNetworkRequest { class BHttpRequest : public BNetworkRequest {
public: public:
BHttpRequest(const BUrl& url, BHttpRequest(const BUrl& url,
@@ -76,7 +82,8 @@ private:
BString& _ResultStatusText(); BString& _ResultStatusText();
// SSL failure management // SSL failure management
friend class CheckedSecureSocket; friend class BPrivate::CheckedSecureSocket;
friend class BPrivate::CheckedProxySecureSocket;
bool _CertificateVerificationFailed( bool _CertificateVerificationFailed(
BCertificate& certificate, BCertificate& certificate,
const char* message); const char* message);
+32
View File
@@ -0,0 +1,32 @@
/*
* Copyright 2015, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _PROXY_SECURE_SOCKET_H
#define _PROXY_SECURE_SOCKET_H
#include <SecureSocket.h>
class BProxySecureSocket : public BSecureSocket {
public:
BProxySecureSocket(const BNetworkAddress& proxy);
BProxySecureSocket(const BNetworkAddress& proxy,
const BNetworkAddress& peer,
bigtime_t timeout = B_INFINITE_TIMEOUT);
BProxySecureSocket(const BProxySecureSocket& other);
virtual ~BProxySecureSocket();
// BSocket implementation
virtual status_t Connect(const BNetworkAddress& peer,
bigtime_t timeout = B_INFINITE_TIMEOUT);
private:
const BNetworkAddress fProxyAddress;
};
#endif // _PROXY_SECURE_SOCKET_H
+6 -1
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 2011, Haiku, Inc. All Rights Reserved. * Copyright 2011-2015, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
#ifndef _SECURE_SOCKET_H #ifndef _SECURE_SOCKET_H
@@ -23,6 +23,8 @@ public:
virtual bool CertificateVerificationFailed(BCertificate& virtual bool CertificateVerificationFailed(BCertificate&
certificate, const char* message); certificate, const char* message);
status_t InitCheck();
// BSocket implementation // BSocket implementation
virtual status_t Connect(const BNetworkAddress& peer, virtual status_t Connect(const BNetworkAddress& peer,
@@ -37,6 +39,9 @@ public:
virtual ssize_t Read(void* buffer, size_t size); virtual ssize_t Read(void* buffer, size_t size);
virtual ssize_t Write(const void* buffer, size_t size); virtual ssize_t Write(const void* buffer, size_t size);
protected:
status_t _Setup();
private: private:
friend class BCertificate; friend class BCertificate;
class Private; class Private;
+65 -27
View File
@@ -23,6 +23,7 @@
#include <Debug.h> #include <Debug.h>
#include <DynamicBuffer.h> #include <DynamicBuffer.h>
#include <File.h> #include <File.h>
#include <ProxySecureSocket.h>
#include <Socket.h> #include <Socket.h>
#include <SecureSocket.h> #include <SecureSocket.h>
#include <StackOrHeapArray.h> #include <StackOrHeapArray.h>
@@ -32,35 +33,68 @@
static const int32 kHttpBufferSize = 4096; static const int32 kHttpBufferSize = 4096;
class CheckedSecureSocket: public BSecureSocket namespace BPrivate {
{
public:
CheckedSecureSocket(BHttpRequest* request);
bool CertificateVerificationFailed(BCertificate& certificate, class CheckedSecureSocket: public BSecureSocket
const char* message); {
public:
CheckedSecureSocket(BHttpRequest* request);
private: bool CertificateVerificationFailed(BCertificate& certificate,
BHttpRequest* fRequest; const char* message);
private:
BHttpRequest* fRequest;
};
CheckedSecureSocket::CheckedSecureSocket(BHttpRequest* request)
:
BSecureSocket(),
fRequest(request)
{
}
bool
CheckedSecureSocket::CertificateVerificationFailed(BCertificate& certificate,
const char* message)
{
return fRequest->_CertificateVerificationFailed(certificate, message);
}
class CheckedProxySecureSocket: public BProxySecureSocket
{
public:
CheckedProxySecureSocket(const BNetworkAddress& proxy, BHttpRequest* request);
bool CertificateVerificationFailed(BCertificate& certificate,
const char* message);
private:
BHttpRequest* fRequest;
};
CheckedProxySecureSocket::CheckedProxySecureSocket(const BNetworkAddress& proxy,
BHttpRequest* request)
:
BProxySecureSocket(proxy),
fRequest(request)
{
}
bool
CheckedProxySecureSocket::CertificateVerificationFailed(BCertificate& certificate,
const char* message)
{
return fRequest->_CertificateVerificationFailed(certificate, message);
}
}; };
CheckedSecureSocket::CheckedSecureSocket(BHttpRequest* request)
:
BSecureSocket(),
fRequest(request)
{
}
bool
CheckedSecureSocket::CertificateVerificationFailed(BCertificate& certificate,
const char* message)
{
return fRequest->_CertificateVerificationFailed(certificate, message);
}
BHttpRequest::BHttpRequest(const BUrl& url, bool ssl, const char* protocolName, BHttpRequest::BHttpRequest(const BUrl& url, bool ssl, const char* protocolName,
BUrlProtocolListener* listener, BUrlContext* context) BUrlProtocolListener* listener, BUrlContext* context)
: :
@@ -488,9 +522,13 @@ BHttpRequest::_MakeRequest()
{ {
delete fSocket; delete fSocket;
if (fSSL) if (fSSL) {
fSocket = new(std::nothrow) CheckedSecureSocket(this); if (fContext->UseProxy()) {
else BNetworkAddress proxy(fContext->GetProxyHost(), fContext->GetProxyPort());
fSocket = new(std::nothrow) BPrivate::CheckedProxySecureSocket(proxy, this);
} else
fSocket = new(std::nothrow) BPrivate::CheckedSecureSocket(this);
} else
fSocket = new(std::nothrow) BSocket(); fSocket = new(std::nothrow) BSocket();
if (fSocket == NULL) if (fSocket == NULL)
+1
View File
@@ -66,6 +66,7 @@ for architectureObject in [ MultiArchSubDirSetup ] {
DatagramSocket.cpp DatagramSocket.cpp
Socket.cpp Socket.cpp
SecureSocket.cpp SecureSocket.cpp
ProxySecureSocket.cpp
# TODO: another add-on for file:// (a much simpler one) # TODO: another add-on for file:// (a much simpler one)
FileRequest.cpp FileRequest.cpp
@@ -0,0 +1,76 @@
/*
* Copyright 2015 Haiku, Inc.
* Distributed under the terms of the MIT License.
*/
#include <ProxySecureSocket.h>
#include <stdio.h>
BProxySecureSocket::BProxySecureSocket(const BNetworkAddress& proxy)
:
BSecureSocket(),
fProxyAddress(proxy)
{
}
BProxySecureSocket::BProxySecureSocket(const BNetworkAddress& proxy, const BNetworkAddress& peer,
bigtime_t timeout)
:
BSecureSocket(),
fProxyAddress(proxy)
{
Connect(peer, timeout);
}
BProxySecureSocket::BProxySecureSocket(const BProxySecureSocket& other)
:
BSecureSocket(other),
fProxyAddress(other.fProxyAddress)
{
}
BProxySecureSocket::~BProxySecureSocket()
{
}
status_t
BProxySecureSocket::Connect(const BNetworkAddress& peer, bigtime_t timeout)
{
status_t status = InitCheck();
if (status != B_OK)
return status;
BSocket::Connect(fProxyAddress, timeout);
if (status != B_OK)
return status;
BString connectRequest;
connectRequest.SetToFormat("CONNECT %s:%d HTTP/1.0\r\n\r\n",
peer.HostName().String(), peer.Port());
BSocket::Write(connectRequest.String(), connectRequest.Length());
char buffer[256];
ssize_t length = BSocket::Read(buffer, sizeof(buffer) - 1);
if (length <= 0)
return length;
buffer[length] = '\0';
int httpStatus = 0;
int matches = scanf(buffer, "HTTP/1.0 %d %*[^\r\n]\r\n\r\n", httpStatus);
if (matches != 2)
return B_BAD_DATA;
if (httpStatus < 200 || httpStatus > 299)
return B_BAD_VALUE;
return _Setup();
}
+47 -32
View File
@@ -256,42 +256,15 @@ BSecureSocket::~BSecureSocket()
status_t status_t
BSecureSocket::Connect(const BNetworkAddress& peer, bigtime_t timeout) BSecureSocket::Connect(const BNetworkAddress& peer, bigtime_t timeout)
{ {
if (fPrivate == NULL) status_t status = InitCheck();
return B_NO_MEMORY;
status_t state = fPrivate->InitCheck();
if (state != B_OK)
return state;
status_t status = BSocket::Connect(peer, timeout);
if (status != B_OK) if (status != B_OK)
return status; return status;
// Do this only after BSocket::Connect has checked wether we're already status = BSocket::Connect(peer, timeout);
// connected. We don't want to kill an existing SSL session, as that would if (status != B_OK)
// likely crash the protocol loop for it. return status;
if (fPrivate->fSSL != NULL) {
SSL_free(fPrivate->fSSL);
}
fPrivate->fSSL = SSL_new(BSecureSocket::Private::Context()); return _Setup();
if (fPrivate->fSSL == NULL) {
BSocket::Disconnect();
return B_NO_MEMORY;
}
BIO_set_fd(fPrivate->fBIO, fSocket, BIO_NOCLOSE);
SSL_set_bio(fPrivate->fSSL, fPrivate->fBIO, fPrivate->fBIO);
SSL_set_ex_data(fPrivate->fSSL, Private::sDataIndex, this);
int returnValue = SSL_connect(fPrivate->fSSL);
if (returnValue <= 0) {
TRACE("SSLConnection can't connect\n");
BSocket::Disconnect();
return fPrivate->ErrorCode(returnValue);
}
return B_OK;
} }
@@ -322,6 +295,17 @@ BSecureSocket::WaitForReadable(bigtime_t timeout) const
} }
status_t
BSecureSocket::InitCheck()
{
if (fPrivate == NULL)
return B_NO_MEMORY;
status_t state = fPrivate->InitCheck();
return state;
}
bool bool
BSecureSocket::CertificateVerificationFailed(BCertificate&, const char*) BSecureSocket::CertificateVerificationFailed(BCertificate&, const char*)
{ {
@@ -363,6 +347,37 @@ BSecureSocket::Write(const void* buffer, size_t size)
} }
status_t
BSecureSocket::_Setup()
{
// Do this only after BSocket::Connect has checked wether we're already
// connected. We don't want to kill an existing SSL session, as that would
// likely crash the protocol loop for it.
if (fPrivate->fSSL != NULL) {
SSL_free(fPrivate->fSSL);
}
fPrivate->fSSL = SSL_new(BSecureSocket::Private::Context());
if (fPrivate->fSSL == NULL) {
BSocket::Disconnect();
return B_NO_MEMORY;
}
BIO_set_fd(fPrivate->fBIO, fSocket, BIO_NOCLOSE);
SSL_set_bio(fPrivate->fSSL, fPrivate->fBIO, fPrivate->fBIO);
SSL_set_ex_data(fPrivate->fSSL, Private::sDataIndex, this);
int returnValue = SSL_connect(fPrivate->fSSL);
if (returnValue <= 0) {
TRACE("SSLConnection can't connect\n");
BSocket::Disconnect();
return fPrivate->ErrorCode(returnValue);
}
return B_OK;
}
#else // OPENSSL_ENABLED #else // OPENSSL_ENABLED