Skip to content

Commit b979c89

Browse files
authored
CPP-871 Fix SSL cleanup on error conditions in mockssandra (#339)
* Also, fix bug in HttpClient unit tests
1 parent ed1d005 commit b979c89

4 files changed

Lines changed: 142 additions & 145 deletions

File tree

cpp-driver/src/ssl/ssl_no_impl.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ class NoSslSession : public SslSession {
3333

3434
class NoSslContext : public SslContext {
3535
public:
36-
virtual SslSession* create_session(const Address& address, const String& hostname, const String& sni_server_name);
36+
virtual SslSession* create_session(const Address& address, const String& hostname,
37+
const String& sni_server_name);
3738

3839
virtual CassError add_trusted_cert(const char* cert, size_t cert_length);
3940
virtual CassError set_cert(const char* cert, size_t cert_length);

cpp-driver/tests/src/unit/http_test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ SocketSettings HttpTest::use_ssl(const String& cn, bool is_server_using_ssl /*=
5050
return settings;
5151
}
5252

53-
void HttpTest::use_ssl(const String& ca_cert, const String& ca_key, const String& cn) {
53+
void HttpTest::use_ssl(const String& ca_key, const String& ca_cert, const String& cn) {
5454
#ifdef HAVE_OPENSSL
5555
key_ = mockssandra::Ssl::generate_key();
5656
cert_ = mockssandra::Ssl::generate_cert(key_, cn, ca_cert, ca_key);

cpp-driver/tests/src/unit/mockssandra.cpp

Lines changed: 138 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,65 @@ using datastax::internal::core::UuidGen;
6363

6464
namespace mockssandra {
6565

66-
static DH* dh_parameters() {
66+
namespace {
67+
68+
template <class T>
69+
struct FreeDeleterImpl {};
70+
71+
#define MAKE_DELETER(type, free_func) \
72+
template <> \
73+
struct FreeDeleterImpl<type> { \
74+
static void free(type* ptr) { free_func(ptr); } \
75+
};
76+
77+
MAKE_DELETER(BIO, BIO_free)
78+
MAKE_DELETER(DH, DH_free)
79+
MAKE_DELETER(EVP_PKEY, EVP_PKEY_free)
80+
MAKE_DELETER(EVP_PKEY_CTX, EVP_PKEY_CTX_free)
81+
MAKE_DELETER(X509, X509_free)
82+
MAKE_DELETER(X509_REQ, X509_REQ_free)
83+
MAKE_DELETER(X509_EXTENSION, X509_EXTENSION_free)
84+
85+
template <class T>
86+
struct FreeDeleter {
87+
void operator()(T* ptr) const { FreeDeleterImpl<T>::free(ptr); }
88+
};
89+
90+
template <class T>
91+
class Scoped : public ScopedPtr<T, FreeDeleter<T> > {
92+
public:
93+
Scoped(T* ptr = NULL)
94+
: ScopedPtr<T, FreeDeleter<T> >(ptr) {}
95+
};
96+
97+
void print_ssl_error() {
98+
unsigned long err = ERR_get_error();
99+
char buf[256];
100+
ERR_error_string_n(err, buf, sizeof(buf));
101+
fprintf(stderr, "%s\n", buf);
102+
}
103+
104+
X509* load_cert(const String& cert) {
105+
X509* x509 = NULL;
106+
Scoped<BIO> bio(BIO_new_mem_buf(const_cast<char*>(cert.c_str()), cert.length()));
107+
if (PEM_read_bio_X509(bio.get(), &x509, NULL, NULL) == NULL) {
108+
print_ssl_error();
109+
return NULL;
110+
}
111+
return x509;
112+
}
113+
114+
EVP_PKEY* load_private_key(const String& key) {
115+
EVP_PKEY* pkey = NULL;
116+
Scoped<BIO> bio(BIO_new_mem_buf(const_cast<char*>(key.c_str()), key.length()));
117+
if (!PEM_read_bio_PrivateKey(bio.get(), &pkey, NULL, NULL)) {
118+
print_ssl_error();
119+
return NULL;
120+
}
121+
return pkey;
122+
}
123+
124+
DH* dh_parameters() {
67125
// Generated using the following command: `openssl dhparam -C 2048`
68126
// Prime length of 2048 chosen to bypass client-side error:
69127
// `SSL3_CHECK_CERT_AND_ALGORITHM:dh key too small`
@@ -80,32 +138,32 @@ static DH* dh_parameters() {
80138
"VYp84xAy2M6mWWqUm/kokN9QjAiT/DZRxZK8VhY7O9+oATo7/YPCMd9Em417O13k\n"
81139
"+F0o/8IMaQvpmtlAsLc2ZKwGqqG+HD2dOwIBAg==\n"
82140
"-----END DH PARAMETERS-----";
83-
BIO* bio = BIO_new_mem_buf(const_cast<char*>(dh_parameters_pem),
84-
-1); // Use null terminator for length
85-
DH* dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
86-
BIO_free(bio);
87-
return dh;
141+
Scoped<BIO> bio(BIO_new_mem_buf(const_cast<char*>(dh_parameters_pem),
142+
-1)); // Use null terminator for length
143+
return PEM_read_bio_DHparams(bio.get(), NULL, NULL, NULL);
88144
}
89145

146+
} // namespace
147+
90148
String Ssl::generate_key() {
91-
EVP_PKEY* pkey = NULL;
92-
EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
93-
EVP_PKEY_keygen_init(pctx);
94-
EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, 2048);
95-
EVP_PKEY_keygen(pctx, &pkey);
96-
EVP_PKEY_CTX_free(pctx);
149+
Scoped<EVP_PKEY_CTX> pctx(EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL));
97150

98-
String result;
99-
BIO* bio = BIO_new(BIO_s_mem());
100-
PEM_write_bio_PrivateKey(bio, pkey, NULL, NULL, 0, NULL, NULL);
101-
BUF_MEM* mem = NULL;
102-
BIO_get_mem_ptr(bio, &mem);
103-
result.append(mem->data, mem->length);
104-
BIO_free(bio);
151+
EVP_PKEY_keygen_init(pctx.get());
152+
EVP_PKEY_CTX_set_rsa_keygen_bits(pctx.get(), 2048);
105153

106-
EVP_PKEY_free(pkey);
154+
Scoped<EVP_PKEY> pkey;
155+
{ // Generate RSA key
156+
EVP_PKEY* temp = NULL;
157+
EVP_PKEY_keygen(pctx.get(), &temp);
158+
pkey.reset(temp);
159+
}
107160

108-
return result;
161+
Scoped<BIO> bio(BIO_new(BIO_s_mem()));
162+
PEM_write_bio_PrivateKey(bio.get(), pkey.get(), NULL, NULL, 0, NULL, NULL);
163+
164+
BUF_MEM* mem = NULL;
165+
BIO_get_mem_ptr(bio.get(), &mem);
166+
return String(mem->data, mem->length);
109167
}
110168

111169
String Ssl::generate_cert(const String& key, String cn, String ca_cert, String ca_key) {
@@ -120,133 +178,75 @@ String Ssl::generate_cert(const String& key, String cn, String ca_cert, String c
120178
#endif
121179
}
122180

123-
EVP_PKEY* pkey = NULL;
124-
{ // Read key from string
125-
BIO* bio = BIO_new_mem_buf(const_cast<char*>(key.c_str()), key.length());
126-
if (!PEM_read_bio_PrivateKey(bio, &pkey, NULL, NULL)) {
127-
BIO_free(bio);
128-
return "";
129-
}
130-
BIO_free(bio);
131-
}
181+
Scoped<EVP_PKEY> pkey(load_private_key(key));
182+
if (!pkey) return "";
132183

133-
X509_REQ* x509_req = NULL;
184+
Scoped<X509_REQ> x509_req;
134185
if (!ca_cert.empty() && !ca_key.empty()) {
135-
x509_req = X509_REQ_new();
136-
X509_REQ_set_version(x509_req, 2);
137-
X509_REQ_set_pubkey(x509_req, pkey);
186+
x509_req.reset(X509_REQ_new());
187+
X509_REQ_set_version(x509_req.get(), 2);
188+
X509_REQ_set_pubkey(x509_req.get(), pkey.get());
138189

139-
X509_NAME* name = X509_REQ_get_subject_name(x509_req);
190+
X509_NAME* name = X509_REQ_get_subject_name(x509_req.get());
140191
X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
141192
reinterpret_cast<const unsigned char*>("US"), -1, -1, 0);
142193
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
143194
reinterpret_cast<const unsigned char*>(cn.c_str()), -1, -1, 0);
144-
X509_REQ_sign(x509_req, pkey, EVP_sha256());
195+
X509_REQ_sign(x509_req.get(), pkey.get(), EVP_sha256());
145196
}
146197

147-
X509* x509 = X509_new();
148-
X509_set_version(x509, 2);
149-
ASN1_INTEGER_set(X509_get_serialNumber(x509), 0);
150-
X509_gmtime_adj(X509_get_notBefore(x509), 0);
151-
X509_gmtime_adj(X509_get_notAfter(x509), static_cast<long>(60 * 60 * 24 * 365));
152-
X509_set_pubkey(x509, pkey);
198+
Scoped<X509> x509(X509_new());
199+
X509_set_version(x509.get(), 2);
200+
ASN1_INTEGER_set(X509_get_serialNumber(x509.get()), 0);
201+
X509_gmtime_adj(X509_get_notBefore(x509.get()), 0);
202+
X509_gmtime_adj(X509_get_notAfter(x509.get()), static_cast<long>(60 * 60 * 24 * 365));
203+
X509_set_pubkey(x509.get(), pkey.get());
153204

154205
if (x509_req) {
155-
X509_set_subject_name(x509, X509_REQ_get_subject_name(x509_req));
156-
157-
X509* x509_ca = NULL;
158-
{ // Read CA from string
159-
BIO* bio = BIO_new_mem_buf(const_cast<char*>(ca_cert.c_str()), ca_cert.length());
160-
if (!PEM_read_bio_X509(bio, &x509_ca, NULL, NULL)) {
161-
X509_free(x509);
162-
X509_REQ_free(x509_req);
163-
BIO_free(bio);
164-
return "";
165-
}
166-
BIO_free(bio);
167-
}
168-
X509_set_issuer_name(x509, X509_get_issuer_name(x509_ca));
169-
170-
EVP_PKEY* pkey_ca = NULL;
171-
{ // Read key from string
172-
BIO* bio = BIO_new_mem_buf(const_cast<char*>(ca_key.c_str()), ca_key.length());
173-
if (!PEM_read_bio_PrivateKey(bio, &pkey_ca, NULL, NULL)) {
174-
BIO_free(bio);
175-
X509_free(x509);
176-
X509_free(x509_ca);
177-
X509_REQ_free(x509_req);
178-
return "";
179-
}
180-
BIO_free(bio);
181-
}
182-
X509_sign(x509, pkey_ca, EVP_sha256());
206+
X509_set_subject_name(x509.get(), X509_REQ_get_subject_name(x509_req.get()));
207+
208+
Scoped<X509> x509_ca(load_cert(ca_cert));
209+
if (!x509_ca) return "";
210+
X509_set_issuer_name(x509.get(), X509_get_issuer_name(x509_ca.get()));
183211

184-
X509_free(x509_ca);
185-
EVP_PKEY_free(pkey_ca);
212+
Scoped<EVP_PKEY> pkey_ca(load_private_key(ca_key));
213+
if (!pkey_ca) return "";
214+
X509_sign(x509.get(), pkey_ca.get(), EVP_sha256());
186215
} else {
187216
if (cn == "CA") { // Set the purpose as a CA certificate.
188-
X509_EXTENSION* x509_ex;
189217
X509V3_CTX x509v3_ctx;
190218
X509V3_set_ctx_nodb(&x509v3_ctx);
191-
X509V3_set_ctx(&x509v3_ctx, x509, x509, NULL, NULL, 0);
192-
x509_ex = X509V3_EXT_conf_nid(NULL, &x509v3_ctx, NID_basic_constraints,
193-
const_cast<char*>("critical,CA:TRUE"));
194-
if (!x509_ex) {
195-
X509_free(x509);
196-
X509_EXTENSION_free(x509_ex);
197-
return "";
198-
}
199-
X509_add_ext(x509, x509_ex, -1);
200-
X509_EXTENSION_free(x509_ex);
219+
X509V3_set_ctx(&x509v3_ctx, x509.get(), x509.get(), NULL, NULL, 0);
220+
221+
Scoped<X509_EXTENSION> x509_ex(X509V3_EXT_conf_nid(NULL, &x509v3_ctx, NID_basic_constraints,
222+
const_cast<char*>("critical,CA:TRUE")));
223+
if (!x509_ex) return "";
224+
225+
X509_add_ext(x509.get(), x509_ex.get(), -1);
201226
}
202-
X509_NAME* name = X509_get_subject_name(x509);
227+
X509_NAME* name = X509_get_subject_name(x509.get());
203228
X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
204229
reinterpret_cast<const unsigned char*>("US"), -1, -1, 0);
205230
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
206231
reinterpret_cast<const unsigned char*>(cn.c_str()), -1, -1, 0);
207-
X509_set_issuer_name(x509, name);
208-
X509_sign(x509, pkey, EVP_sha256());
232+
X509_set_issuer_name(x509.get(), name);
233+
X509_sign(x509.get(), pkey.get(), EVP_sha256());
209234
}
210235

211236
String result;
212237
{ // Write cert into string
213-
BIO* bio = BIO_new(BIO_s_mem());
214-
PEM_write_bio_X509(bio, x509);
238+
Scoped<BIO> bio(BIO_new(BIO_s_mem()));
239+
PEM_write_bio_X509(bio.get(), x509.get());
215240
BUF_MEM* mem = NULL;
216-
BIO_get_mem_ptr(bio, &mem);
241+
BIO_get_mem_ptr(bio.get(), &mem);
217242
result.append(mem->data, mem->length);
218-
BIO_free(bio);
219243
}
220244

221-
X509_free(x509);
222-
if (x509_req) X509_REQ_free(x509_req);
223-
224-
EVP_PKEY_free(pkey);
225-
226245
return result;
227246
}
228247

229248
namespace internal {
230249

231-
static void print_ssl_error() {
232-
unsigned long err = ERR_get_error();
233-
char buf[256];
234-
ERR_error_string_n(err, buf, sizeof(buf));
235-
fprintf(stderr, "%s\n", buf);
236-
}
237-
238-
static X509* load_cert(const String& cert) {
239-
X509* x509 = NULL;
240-
BIO* bio = BIO_new_mem_buf(const_cast<char*>(cert.c_str()), cert.length());
241-
if (PEM_read_bio_X509(bio, &x509, NULL, NULL) == NULL) {
242-
print_ssl_error();
243-
BIO_free(bio);
244-
return NULL;
245-
}
246-
BIO_free(bio);
247-
return x509;
248-
}
249-
250250
struct WriteReq {
251251
WriteReq(const char* data, size_t len, ClientConnection* connection)
252252
: data(data, len)
@@ -499,59 +499,55 @@ bool ServerConnection::use_ssl(const String& key, const String& cert,
499499
SSL_CTX_set_verify(ssl_context_, SSL_VERIFY_NONE, NULL);
500500

501501
{ // Load server certificate
502-
X509* x509 = load_cert(cert);
502+
Scoped<X509> x509(load_cert(cert));
503503
if (!x509) return false;
504-
if (SSL_CTX_use_certificate(ssl_context_, x509) <= 0) {
504+
if (SSL_CTX_use_certificate(ssl_context_, x509.get()) <= 0) {
505505
print_ssl_error();
506-
X509_free(x509);
507506
return false;
508507
}
509-
X509_free(x509);
510508
}
511509

512510
if (!ca_cert.empty()) { // Load CA certificate
513-
X509* x509 = load_cert(ca_cert);
514-
if (!x509) return false;
515-
if (SSL_CTX_add_extra_chain_cert(ssl_context_, x509) <= 0) { // Certificate freed by function
516-
print_ssl_error();
517-
X509_free(x509);
518-
return false;
511+
512+
{ // Add CA certificate to chain to send to the client
513+
Scoped<X509> x509(load_cert(ca_cert));
514+
if (!x509) return false;
515+
516+
if (SSL_CTX_add_extra_chain_cert(ssl_context_, x509.release()) <=
517+
0) { // Certificate freed by function
518+
print_ssl_error();
519+
return false;
520+
}
519521
}
522+
520523
if (require_client_cert) {
524+
Scoped<X509> x509(load_cert(ca_cert));
525+
if (!x509) return false;
526+
527+
// Add CA certificate to chain to validate peer certificate
521528
X509_STORE* cert_store = SSL_CTX_get_cert_store(ssl_context_);
522-
if (X509_STORE_add_cert(cert_store, x509) <= 0) {
529+
if (X509_STORE_add_cert(cert_store, x509.get()) <= 0) {
523530
print_ssl_error();
524531
return false;
525532
}
533+
526534
SSL_CTX_set_verify(ssl_context_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
527535
}
528536
}
529537

530-
EVP_PKEY* pkey = NULL;
531-
{ // Read key from string
532-
BIO* bio = BIO_new_mem_buf(const_cast<char*>(key.c_str()), key.length());
533-
if (PEM_read_bio_PrivateKey(bio, &pkey, on_password, (void*)"") == NULL) {
534-
print_ssl_error();
535-
BIO_free(bio);
536-
return false;
537-
}
538-
BIO_free(bio);
539-
}
538+
Scoped<EVP_PKEY> pkey(load_private_key(key));
539+
if (!pkey) return false;
540540

541-
if (SSL_CTX_use_PrivateKey(ssl_context_, pkey) <= 0) {
541+
if (SSL_CTX_use_PrivateKey(ssl_context_, pkey.get()) <= 0) {
542542
print_ssl_error();
543-
EVP_PKEY_free(pkey);
544543
return false;
545544
}
546-
EVP_PKEY_free(pkey);
547545

548-
DH* dh = dh_parameters();
549-
if (!dh || !SSL_CTX_set_tmp_dh(ssl_context_, dh)) {
546+
Scoped<DH> dh(dh_parameters());
547+
if (!dh || !SSL_CTX_set_tmp_dh(ssl_context_, dh.get())) {
550548
print_ssl_error();
551-
DH_free(dh);
552549
return false;
553550
}
554-
DH_free(dh);
555551

556552
return true;
557553
}

0 commit comments

Comments
 (0)