Skip to content

Commit abc9cf0

Browse files
committed
Incorporating feedback and tested SSL certs on iOS.
1 parent 807a18e commit abc9cf0

File tree

2 files changed

+87
-82
lines changed

2 files changed

+87
-82
lines changed

Release/src/http/client/http_linux.cpp

Lines changed: 59 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,10 @@ namespace web { namespace http
453453

454454
private:
455455
tcp::resolver m_resolver;
456-
456+
#if defined(__APPLE__)
457+
bool m_openssl_failed;
458+
#endif
459+
457460
static bool _check_streambuf(std::shared_ptr<linux_client_request_context> ctx, concurrency::streams::streambuf<uint8_t> rdbuf, const utility::char_t* msg)
458461
{
459462
if (!rdbuf.is_open())
@@ -487,6 +490,7 @@ namespace web { namespace http
487490
{
488491
ctx->m_ssl_stream->set_verify_mode(boost::asio::ssl::context::verify_peer);
489492
ctx->m_ssl_stream->set_verify_callback(boost::bind(&linux_client::handle_cert_verification, shared_from_this(), _1, _2));
493+
m_openssl_failed = false;
490494
}
491495
else
492496
{
@@ -538,6 +542,7 @@ namespace web { namespace http
538542
{
539543
ctx->m_ssl_stream->set_verify_mode(boost::asio::ssl::context::verify_peer);
540544
ctx->m_ssl_stream->set_verify_callback(boost::bind(&linux_client::handle_cert_verification, shared_from_this(), _1, _2));
545+
m_openssl_failed = false;
541546
}
542547
else
543548
{
@@ -551,47 +556,66 @@ namespace web { namespace http
551556

552557
bool handle_cert_verification(bool preverified, boost::asio::ssl::verify_context &ctx)
553558
{
554-
#if defined(__APPLE__)
555-
// The 'leaf', non-Certificate Authority (CA) certificate, i.e. actual server certificate
556-
// is at the '0' position in the certificate chain, the rest are optional intermediate
557-
// certificates, followed finally by the root CA self signed certificate.
558-
559559
// OpenSSL calls the verification callback once per certificate in the chain,
560-
// starting with the root CA certificate. We will be performing verification all
561-
// at once using the whole certificate chain so wait until the 'leaf' cert.
562-
X509_STORE_CTX *storeContext = ctx.native_handle();
563-
int currentDepth = X509_STORE_CTX_get_error_depth(storeContext);
564-
if(currentDepth == 0)
560+
// starting with the root CA certificate. The 'leaf', non-Certificate Authority (CA)
561+
// certificate, i.e. actual server certificate is at the '0' position in the
562+
// certificate chain, the rest are optional intermediate certificates, followed
563+
// finally by the root CA self signed certificate.
564+
565+
#if defined(__APPLE__)
566+
if(!preverified)
567+
{
568+
m_openssl_failed = true;
569+
}
570+
if(m_openssl_failed)
565571
{
566-
// preverified false means OpenSSL falied to verify the certificate successfully.
567-
// On OS X, iOS, or Android, OpenSSL doesn't have access to where the OS stores keychains.
568-
// To work around this we fall back to the OS facilities to verify the server certificate.
569-
if(!preverified)
572+
// On OS X, iOS, and Android, OpenSSL doesn't have access to where the OS
573+
// stores keychains. If OpenSSL fails we will doing verification at the
574+
// end using the whole certificate chain so wait until the 'leaf' cert.
575+
// For now return true so OpenSSL continues down the certificate chain.
576+
X509_STORE_CTX *storeContext = ctx.native_handle();
577+
int currentDepth = X509_STORE_CTX_get_error_depth(storeContext);
578+
if(currentDepth != 0)
570579
{
571-
STACK_OF(X509) *certStack = X509_STORE_CTX_get_chain(ctx.native_handle());
572-
const int numCerts = sk_X509_num(certStack);
573-
std::vector<std::string> certChain;
574-
575-
for(int i = 0; i < numCerts; ++i)
576-
{
577-
X509 *cert = sk_X509_value(certStack, i);
578-
579-
// Encode into DER format into raw memory.
580-
unsigned char * buffer = nullptr;
581-
const int len = i2d_X509(cert, &buffer);
582-
if(len < 0)
583-
{
584-
return false;
585-
}
580+
return true;
581+
}
582+
583+
STACK_OF(X509) *certStack = X509_STORE_CTX_get_chain(storeContext);
584+
const int numCerts = sk_X509_num(certStack);
585+
if(numCerts < 0)
586+
{
587+
return false;
588+
}
589+
590+
std::vector<std::string> certChain;
591+
certChain.reserve(numCerts);
592+
for(int i = 0; i < numCerts; ++i)
593+
{
594+
X509 *cert = sk_X509_value(certStack, i);
586595

587-
certChain.emplace_back(reinterpret_cast<char *>(buffer), len);
596+
// Encode into DER format into raw memory.
597+
int len = i2d_X509(cert, nullptr);
598+
if(len < 0)
599+
{
600+
return false;
588601
}
589-
590-
return verify_X509_cert_chain(certChain, m_uri.host());
602+
603+
std::string certData;
604+
certData.resize(len);
605+
unsigned char * buffer = reinterpret_cast<unsigned char *>(&certData[0]);
606+
len = i2d_X509(cert, &buffer);
607+
if(len < 0)
608+
{
609+
return false;
610+
}
611+
612+
certChain.push_back(std::move(certData));
591613
}
614+
615+
return verify_X509_cert_chain(certChain, m_uri.host());
592616
}
593617
#endif
594-
618+
595619
boost::asio::ssl::rfc2818_verification rfc2818(m_uri.host());
596620
return rfc2818(preverified, ctx);
597621
}
@@ -604,7 +628,7 @@ namespace web { namespace http
604628
}
605629
else
606630
{
607-
ctx->report_error("Error code in handle_handshake is ", ec, httpclient_errorcode_context::handshake);
631+
ctx->report_error("Error in SSL handshake", ec, httpclient_errorcode_context::handshake);
608632
}
609633
}
610634

Release/src/http/client/x509_cert_utilities.cpp

Lines changed: 28 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -44,103 +44,84 @@ namespace web { namespace http { namespace client { namespace details {
4444

4545
// Simple RAII pattern wrapper to perform CFRelease on objects.
4646
template <typename T>
47-
class CFRef
47+
class cf_ref
4848
{
4949
public:
50-
CFRef(T v) : value(v) {}
51-
CFRef() : value(nullptr) {}
52-
53-
~CFRef()
50+
cf_ref(T v) : value(v) {}
51+
cf_ref() : value(nullptr) {}
52+
cf_ref(cf_ref &&other) : value(other.value) { other.value = nullptr; }
53+
54+
~cf_ref()
5455
{
5556
if(value != nullptr)
5657
{
5758
CFRelease(value);
5859
}
5960
}
6061

61-
T & Get()
62+
T & get()
6263
{
6364
return value;
6465
}
6566
private:
67+
cf_ref(const cf_ref &);
68+
cf_ref & operator=(const cf_ref &);
6669
T value;
6770
};
68-
69-
template <typename T>
70-
class CFVectorRef
71-
{
72-
public:
73-
CFVectorRef() {}
74-
~CFVectorRef()
75-
{
76-
for(auto & item : container)
77-
{
78-
CFRelease(item);
79-
}
80-
}
81-
82-
void push_back(T item)
83-
{
84-
container.push_back(item);
85-
}
86-
std::vector<T> & items()
87-
{
88-
return container;
89-
}
90-
91-
private:
92-
std::vector<T> container;
93-
};
9471

9572
bool verify_X509_cert_chain(const std::vector<std::string> &certChain, const std::string &hostName)
9673
{
9774
// Build up CFArrayRef with all the certificates.
9875
// All this code is basically just to get into the correct structures for the Apple APIs.
9976
// Copies are avoided whenever possible.
100-
CFVectorRef<SecCertificateRef> certs;
77+
std::vector<cf_ref<SecCertificateRef>> certs;
10178
for(const auto & certBuf : certChain)
10279
{
103-
CFRef<CFDataRef> certDataRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
80+
cf_ref<CFDataRef> certDataRef;
81+
certDataRef.get() = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
10482
reinterpret_cast<const unsigned char*>(certBuf.c_str()),
10583
certBuf.size(),
10684
kCFAllocatorNull);
107-
if(certDataRef.Get() == nullptr)
85+
if(certDataRef.get() == nullptr)
10886
{
10987
return false;
11088
}
11189

112-
SecCertificateRef certObj = SecCertificateCreateWithData(nullptr, certDataRef.Get());
113-
if(certObj == nullptr)
90+
cf_ref<SecCertificateRef> certObj = SecCertificateCreateWithData(nullptr, certDataRef.get());
91+
if(certObj.get() == nullptr)
11492
{
11593
return false;
11694
}
117-
certs.push_back(certObj);
95+
certs.push_back(std::move(certObj));
11896
}
119-
CFRef<CFArrayRef> certsArray = CFArrayCreate(kCFAllocatorDefault, (const void **)&certs.items()[0], certs.items().size(), nullptr);
120-
if(certsArray.Get() == nullptr)
97+
cf_ref<CFArrayRef> certsArray;
98+
certsArray.get() = CFArrayCreate(kCFAllocatorDefault, const_cast<const void **>(reinterpret_cast<void **>(&certs[0])), certs.size(), nullptr);
99+
if(certsArray.get() == nullptr)
121100
{
122101
return false;
123102
}
124103

125104
// Create trust management object with certificates and SSL policy.
126105
// Note: SecTrustCreateWithCertificates expects the certificate to be
127106
// verified is the first element.
128-
CFRef<CFStringRef> cfHostName = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault,
129-
&hostName[0],
107+
cf_ref<CFStringRef> cfHostName;
108+
cfHostName.get() = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault,
109+
hostName.c_str(),
130110
kCFStringEncodingASCII,
131111
kCFAllocatorNull);
132-
if(cfHostName.Get() == nullptr)
112+
if(cfHostName.get() == nullptr)
133113
{
134114
return false;
135115
}
136-
CFRef<SecPolicyRef> policy = SecPolicyCreateSSL(true /* client side */, cfHostName.Get());
137-
CFRef<SecTrustRef> trust;
138-
OSStatus status = SecTrustCreateWithCertificates(certsArray.Get(), policy.Get(), &trust.Get());
116+
cf_ref<SecPolicyRef> policy;
117+
policy.get() = SecPolicyCreateSSL(true /* client side */, cfHostName.get());
118+
cf_ref<SecTrustRef> trust;
119+
OSStatus status = SecTrustCreateWithCertificates(certsArray.get(), policy.get(), &trust.get());
139120
if(status == noErr)
140121
{
141122
// Perform actual certificate verification.
142123
SecTrustResultType trustResult;
143-
status = SecTrustEvaluate(trust.Get(), &trustResult);
124+
status = SecTrustEvaluate(trust.get(), &trustResult);
144125
if(status == noErr && (trustResult == kSecTrustResultUnspecified || trustResult == kSecTrustResultProceed))
145126
{
146127
return true;

0 commit comments

Comments
 (0)