OpenSSLAdapter::SSLPostConnectionCheck(SSL* ssl, const char* host) {
if (!host)
return false;
// Checking the return from SSL_get_peer_certificate here is not strictly
// necessary. With our setup, it is not possible for it to return
// NULL. However, it is good form to check the return.
X509* certificate = SSL_get_peer_certificate(ssl);
if (!certificate)
return false;
#ifdef _DEBUG
{
LOG(LS_INFO) << "Certificate from server:";
BIO* mem = BIO_new(BIO_s_mem());
X509_print_ex(mem, certificate, XN_FLAG_SEP_CPLUS_SPC, X509_FLAG_NO_HEADER);
BIO_write(mem, "\0", 1);
char* buffer;
BIO_get_mem_data(mem, &buffer);
LOG(LS_INFO) << buffer;
BIO_free(mem);
char* cipher_description =
SSL_CIPHER_description(SSL_get_current_cipher(ssl), NULL, 128);
LOG(LS_INFO) << "Cipher: " << cipher_description;
OPENSSL_free(cipher_description);
}
#endif
bool ok = false;
int extension_count = X509_get_ext_count(certificate);
for (int i = 0; i < extension_count; ++i) {
X509_EXTENSION* extension = X509_get_ext(certificate, i);
int extension_nid = OBJ_obj2nid(X509_EXTENSION_get_object(extension));
if (extension_nid == NID_subject_alt_name) {
X509V3_EXT_METHOD* meth = X509V3_EXT_get(extension);
if (!meth)
break;
void* ext_str = NULL;
if (meth->it) {
ext_str = ASN1_item_d2i(NULL, &(extension->value->data), extension->value->length,
ASN1_ITEM_ptr(meth->it));
} else {
ext_str = meth->d2i(NULL, &(extension->value->data), extension->value->length);
}
STACK_OF(CONF_VALUE)* value = meth->i2v(meth, ext_str, NULL);
for (int j = 0; j < sk_CONF_VALUE_num(value); ++j) {
CONF_VALUE* nval = sk_CONF_VALUE_value(value, j);
if (!strcmp(nval->name, "DNS") && !strcmp(nval->value, host)) {
ok = true;
break;
}
}
}
if (ok)
break;
}
char data[256];
X509_name_st* subject;
if (!ok
&& (subject = X509_get_subject_name(certificate))
&& (X509_NAME_get_text_by_NID(subject, NID_commonName,
data, sizeof(data)) > 0)) {
data[sizeof(data)-1] = 0;
if (_stricmp(data, host) == 0)
ok = true;
}
X509_free(certificate);
if (!ok && ignore_bad_cert()) {
LOG(LS_WARNING) << "TLS certificate check FAILED. "
<< "Allowing connection anyway.";
ok = true;
}
if (ok)
ok = (SSL_get_verify_result(ssl) == X509_V_OK);
if (!ok && ignore_bad_cert()) {
LOG(LS_INFO) << "Other TLS post connection checks failed.";
ok = true;
}
return ok;
}
#if _DEBUG
// We only use this for tracing and so it is only needed in debug mode
void
OpenSSLAdapter::SSLInfoCallback(const SSL* s, int where, int ret) {
const char* str = "undefined";
int w = where & ~SSL_ST_MASK;
if (w & SSL_ST_CONNECT) {
str = "SSL_connect";
} else if (w & SSL_ST_ACCEPT) {
str = "SSL_accept";
}
if (where & SSL_CB_LOOP) {
LOG(LS_INFO) << str << ":" << SSL_state_string_long(s);
} else if (where & SSL_CB_ALERT) {
str = (where & SSL_CB_READ) ? "read" : "write";
LOG(LS_INFO) << "SSL3 alert " << str
<< ":" << SSL_alert_type_string_long(ret)
<< ":" << SSL_alert_desc_string_long(ret);
} else if (where & SSL_CB_EXIT) {
if (ret == 0) {
LOG(LS_INFO) << str << ":failed in " << SSL_state_string_long(s);
} else if (ret < 0) {
LOG(LS_INFO) << str << ":error in " << SSL_state_string_long(s);
}
}
}
#endif // _DEBUG
int
OpenSSLAdapter::SSLVerifyCallback(int ok, X509_STORE_CTX* store) {
#if _DEBUG
if (!ok) {
char data[256];
X509* cert = X509_STORE_CTX_get_current_cert(store);
int depth = X509_STORE_CTX_get_error_depth(store);
int err = X509_STORE_CTX_get_error(store);
LOG(LS_INFO) << "Error with certificate at depth: " << depth;
X509_NAME_oneline(X509_get_issuer_name(cert), data, sizeof(data));
LOG(LS_INFO) << " issuer = " << data;
X509_NAME_oneline(X509_get_subject_name(cert), data, sizeof(data));
LOG(LS_INFO) << " subject = " << data;
LOG(LS_INFO) << " err = " << err
<< ":" << X509_verify_cert_error_string(err);
}
#endif
// Get our stream pointer from the store
SSL* ssl = reinterpret_cast<SSL*>(
X509_STORE_CTX_get_ex_data(store,
SSL_get_ex_data_X509_STORE_CTX_idx()));
OpenSSLAdapter* stream =
reinterpret_cast<OpenSSLAdapter*>(SSL_get_app_data(ssl));
if (!ok && stream->ignore_bad_cert()) {
LOG(LS_WARNING) << "Ignoring cert error while verifying cert chain";
ok = 1;
}
return ok;
}
SSL_CTX*
OpenSSLAdapter::SetupSSLContext() {
SSL_CTX* ctx = SSL_CTX_new(TLSv1_client_method());
if (ctx == NULL)
return NULL;
// Add the root cert to the SSL context
unsigned char* cert_buffer
= EquifaxSecureGlobalEBusinessCA1_certificate;
size_t cert_buffer_len = sizeof(EquifaxSecureGlobalEBusinessCA1_certificate);
X509* cert = d2i_X509(NULL, &cert_buffer, cert_buffer_len);
if (cert == NULL) {
SSL_CTX_free(ctx);
return NULL;
}
if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ctx), cert)) {
X509_free(cert);
SSL_CTX_free(ctx);
return NULL;
}
#ifdef _DEBUG
SSL_CTX_set_info_callback(ctx, SSLInfoCallback);
#endif
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, SSLVerifyCallback);
SSL_CTX_set_verify_depth(ctx, 4);
SSL_CTX_set_cipher_list(ctx, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
return ctx;
}
} // namespace cricket
OpenSSLAdapter::SSLPostConnectionCheck(SSL* ssl, const char* host) {
if (!host)
return false;
// Checking the return from SSL_get_peer_certificate here is not strictly
// necessary. With our setup, it is not possible for it to return
// NULL. However, it is good form to check the return.
X509* certificate = SSL_get_peer_certificate(ssl);
if (!certificate)
return false;
#ifdef _DEBUG
{
LOG(LS_INFO) << "Certificate from server:";
BIO* mem = BIO_new(BIO_s_mem());
X509_print_ex(mem, certificate, XN_FLAG_SEP_CPLUS_SPC, X509_FLAG_NO_HEADER);
BIO_write(mem, "\0", 1);
char* buffer;
BIO_get_mem_data(mem, &buffer);
LOG(LS_INFO) << buffer;
BIO_free(mem);
char* cipher_description =
SSL_CIPHER_description(SSL_get_current_cipher(ssl), NULL, 128);
LOG(LS_INFO) << "Cipher: " << cipher_description;
OPENSSL_free(cipher_description);
}
#endif
bool ok = false;
int extension_count = X509_get_ext_count(certificate);
for (int i = 0; i < extension_count; ++i) {
X509_EXTENSION* extension = X509_get_ext(certificate, i);
int extension_nid = OBJ_obj2nid(X509_EXTENSION_get_object(extension));
if (extension_nid == NID_subject_alt_name) {
X509V3_EXT_METHOD* meth = X509V3_EXT_get(extension);
if (!meth)
break;
void* ext_str = NULL;
if (meth->it) {
ext_str = ASN1_item_d2i(NULL, &(extension->value->data), extension->value->length,
ASN1_ITEM_ptr(meth->it));
} else {
ext_str = meth->d2i(NULL, &(extension->value->data), extension->value->length);
}
STACK_OF(CONF_VALUE)* value = meth->i2v(meth, ext_str, NULL);
for (int j = 0; j < sk_CONF_VALUE_num(value); ++j) {
CONF_VALUE* nval = sk_CONF_VALUE_value(value, j);
if (!strcmp(nval->name, "DNS") && !strcmp(nval->value, host)) {
ok = true;
break;
}
}
}
if (ok)
break;
}
char data[256];
X509_name_st* subject;
if (!ok
&& (subject = X509_get_subject_name(certificate))
&& (X509_NAME_get_text_by_NID(subject, NID_commonName,
data, sizeof(data)) > 0)) {
data[sizeof(data)-1] = 0;
if (_stricmp(data, host) == 0)
ok = true;
}
X509_free(certificate);
if (!ok && ignore_bad_cert()) {
LOG(LS_WARNING) << "TLS certificate check FAILED. "
<< "Allowing connection anyway.";
ok = true;
}
if (ok)
ok = (SSL_get_verify_result(ssl) == X509_V_OK);
if (!ok && ignore_bad_cert()) {
LOG(LS_INFO) << "Other TLS post connection checks failed.";
ok = true;
}
return ok;
}
#if _DEBUG
// We only use this for tracing and so it is only needed in debug mode
void
OpenSSLAdapter::SSLInfoCallback(const SSL* s, int where, int ret) {
const char* str = "undefined";
int w = where & ~SSL_ST_MASK;
if (w & SSL_ST_CONNECT) {
str = "SSL_connect";
} else if (w & SSL_ST_ACCEPT) {
str = "SSL_accept";
}
if (where & SSL_CB_LOOP) {
LOG(LS_INFO) << str << ":" << SSL_state_string_long(s);
} else if (where & SSL_CB_ALERT) {
str = (where & SSL_CB_READ) ? "read" : "write";
LOG(LS_INFO) << "SSL3 alert " << str
<< ":" << SSL_alert_type_string_long(ret)
<< ":" << SSL_alert_desc_string_long(ret);
} else if (where & SSL_CB_EXIT) {
if (ret == 0) {
LOG(LS_INFO) << str << ":failed in " << SSL_state_string_long(s);
} else if (ret < 0) {
LOG(LS_INFO) << str << ":error in " << SSL_state_string_long(s);
}
}
}
#endif // _DEBUG
int
OpenSSLAdapter::SSLVerifyCallback(int ok, X509_STORE_CTX* store) {
#if _DEBUG
if (!ok) {
char data[256];
X509* cert = X509_STORE_CTX_get_current_cert(store);
int depth = X509_STORE_CTX_get_error_depth(store);
int err = X509_STORE_CTX_get_error(store);
LOG(LS_INFO) << "Error with certificate at depth: " << depth;
X509_NAME_oneline(X509_get_issuer_name(cert), data, sizeof(data));
LOG(LS_INFO) << " issuer = " << data;
X509_NAME_oneline(X509_get_subject_name(cert), data, sizeof(data));
LOG(LS_INFO) << " subject = " << data;
LOG(LS_INFO) << " err = " << err
<< ":" << X509_verify_cert_error_string(err);
}
#endif
// Get our stream pointer from the store
SSL* ssl = reinterpret_cast<SSL*>(
X509_STORE_CTX_get_ex_data(store,
SSL_get_ex_data_X509_STORE_CTX_idx()));
OpenSSLAdapter* stream =
reinterpret_cast<OpenSSLAdapter*>(SSL_get_app_data(ssl));
if (!ok && stream->ignore_bad_cert()) {
LOG(LS_WARNING) << "Ignoring cert error while verifying cert chain";
ok = 1;
}
return ok;
}
SSL_CTX*
OpenSSLAdapter::SetupSSLContext() {
SSL_CTX* ctx = SSL_CTX_new(TLSv1_client_method());
if (ctx == NULL)
return NULL;
// Add the root cert to the SSL context
unsigned char* cert_buffer
= EquifaxSecureGlobalEBusinessCA1_certificate;
size_t cert_buffer_len = sizeof(EquifaxSecureGlobalEBusinessCA1_certificate);
X509* cert = d2i_X509(NULL, &cert_buffer, cert_buffer_len);
if (cert == NULL) {
SSL_CTX_free(ctx);
return NULL;
}
if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ctx), cert)) {
X509_free(cert);
SSL_CTX_free(ctx);
return NULL;
}
#ifdef _DEBUG
SSL_CTX_set_info_callback(ctx, SSLInfoCallback);
#endif
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, SSLVerifyCallback);
SSL_CTX_set_verify_depth(ctx, 4);
SSL_CTX_set_cipher_list(ctx, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
return ctx;
}
} // namespace cricket
To copy to clipboard, switch view to plain text mode
Bookmarks