http-preapproved-server-certificate-name
This commit is contained in:
parent
128097e19d
commit
3293a6574a
|
|
@ -64,9 +64,6 @@ ConfigVariableInt patcher_buffer_size
|
|||
"Patcher::run(). Increasing this may help the Patcher "
|
||||
"perform more work before returning."));
|
||||
|
||||
ConfigVariableList expected_ssl_server
|
||||
("expected-ssl-server");
|
||||
|
||||
ConfigVariableBool http_proxy_tunnel
|
||||
("http-proxy-tunnel", false,
|
||||
PRC_DESC("This specifies the default value for HTTPChannel::set_proxy_tunnel(). "
|
||||
|
|
|
|||
|
|
@ -38,8 +38,6 @@ extern ConfigVariableDouble decompressor_step_time;
|
|||
extern ConfigVariableDouble extractor_step_time;
|
||||
extern ConfigVariableInt patcher_buffer_size;
|
||||
|
||||
extern ConfigVariableList expected_ssl_server;
|
||||
|
||||
extern ConfigVariableBool http_proxy_tunnel;
|
||||
extern ConfigVariableDouble http_connect_timeout;
|
||||
extern ConfigVariableDouble http_timeout;
|
||||
|
|
|
|||
|
|
@ -1626,90 +1626,91 @@ run_ssl_handshake() {
|
|||
_bio->set_bio(_sbio);
|
||||
_sbio = NULL;
|
||||
|
||||
X509 *cert = SSL_get_peer_certificate(ssl);
|
||||
if (cert == (X509 *)NULL) {
|
||||
downloader_cat.info()
|
||||
<< "No certificate was presented by server.\n";
|
||||
|
||||
// This shouldn't be possible, per the SSL specs.
|
||||
_status_entry._status_code = SC_ssl_invalid_server_certificate;
|
||||
_state = S_failure;
|
||||
return false;
|
||||
}
|
||||
|
||||
X509_NAME *subject = X509_get_subject_name(cert);
|
||||
if (downloader_cat.is_debug()) {
|
||||
string org_name = get_x509_name_component(subject, NID_organizationName);
|
||||
string org_unit_name = get_x509_name_component(subject, NID_organizationalUnitName);
|
||||
string common_name = get_x509_name_component(subject, NID_commonName);
|
||||
|
||||
downloader_cat.debug()
|
||||
<< "Server is " << common_name << " from " << org_unit_name
|
||||
<< " / " << org_name << "\n";
|
||||
|
||||
if (downloader_cat.is_spam()) {
|
||||
downloader_cat.spam()
|
||||
<< "Received certificate from server:\n" << flush;
|
||||
X509_print_fp(stderr, cert);
|
||||
fflush(stderr);
|
||||
}
|
||||
}
|
||||
|
||||
bool cert_preapproved = false;
|
||||
bool cert_name_preapproved = false;
|
||||
check_preapproved_server_certificate(cert, cert_preapproved, cert_name_preapproved);
|
||||
|
||||
// Now verify the server certificate is valid.
|
||||
long verify_result = SSL_get_verify_result(ssl);
|
||||
bool cert_valid = true;
|
||||
|
||||
if (verify_result == X509_V_ERR_CERT_HAS_EXPIRED) {
|
||||
downloader_cat.info()
|
||||
<< "Expired certificate from " << _request.get_url().get_server_and_port() << "\n";
|
||||
if (_client->get_verify_ssl() == HTTPClient::VS_normal) {
|
||||
_status_entry._status_code = SC_ssl_invalid_server_certificate;
|
||||
_state = S_failure;
|
||||
return false;
|
||||
if (_client->get_verify_ssl() == HTTPClient::VS_normal && !cert_preapproved) {
|
||||
cert_valid = false;
|
||||
}
|
||||
|
||||
} else if (verify_result == X509_V_ERR_CERT_NOT_YET_VALID) {
|
||||
downloader_cat.info()
|
||||
<< "Premature certificate from " << _request.get_url().get_server_and_port() << "\n";
|
||||
if (_client->get_verify_ssl() == HTTPClient::VS_normal) {
|
||||
_status_entry._status_code = SC_ssl_invalid_server_certificate;
|
||||
_state = S_failure;
|
||||
return false;
|
||||
if (_client->get_verify_ssl() == HTTPClient::VS_normal && !cert_preapproved) {
|
||||
cert_valid = false;
|
||||
}
|
||||
|
||||
} else if (verify_result == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
|
||||
verify_result == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) {
|
||||
downloader_cat.info()
|
||||
<< "Self-signed certificate from " << _request.get_url().get_server_and_port() << "\n";
|
||||
if (_client->get_verify_ssl() == HTTPClient::VS_normal) {
|
||||
_status_entry._status_code = SC_ssl_self_signed_server_certificate;
|
||||
_state = S_failure;
|
||||
return false;
|
||||
if (_client->get_verify_ssl() != HTTPClient::VS_no_verify && !cert_preapproved) {
|
||||
cert_valid = false;
|
||||
}
|
||||
|
||||
} else if (verify_result != X509_V_OK) {
|
||||
downloader_cat.info()
|
||||
<< "Unable to verify identity of " << _request.get_url().get_server_and_port()
|
||||
<< ", verify error code " << verify_result << "\n";
|
||||
if (_client->get_verify_ssl() != HTTPClient::VS_no_verify) {
|
||||
_status_entry._status_code = SC_ssl_invalid_server_certificate;
|
||||
if (_client->get_verify_ssl() != HTTPClient::VS_no_verify && !cert_preapproved) {
|
||||
cert_valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!cert_valid) {
|
||||
_status_entry._status_code = SC_ssl_invalid_server_certificate;
|
||||
_state = S_failure;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_client->get_verify_ssl() != HTTPClient::VS_no_verify && !cert_name_preapproved) {
|
||||
// Check that the server is someone we expected to be talking
|
||||
// to.
|
||||
if (!validate_server_name(cert)) {
|
||||
_status_entry._status_code = SC_ssl_unexpected_server;
|
||||
_state = S_failure;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
X509 *cert = SSL_get_peer_certificate(ssl);
|
||||
if (cert == (X509 *)NULL) {
|
||||
downloader_cat.info()
|
||||
<< "No certificate was presented by server.\n";
|
||||
// This shouldn't be possible, per the SSL specs.
|
||||
if (_client->get_verify_ssl() != HTTPClient::VS_no_verify) {
|
||||
_status_entry._status_code = SC_ssl_invalid_server_certificate;
|
||||
_state = S_failure;
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (downloader_cat.is_debug()) {
|
||||
downloader_cat.debug()
|
||||
<< "Received certificate from server:\n" << flush;
|
||||
X509_print_fp(stderr, cert);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
X509_NAME *subject = X509_get_subject_name(cert);
|
||||
|
||||
if (downloader_cat.is_debug()) {
|
||||
string org_name = get_x509_name_component(subject, NID_organizationName);
|
||||
string org_unit_name = get_x509_name_component(subject, NID_organizationalUnitName);
|
||||
string common_name = get_x509_name_component(subject, NID_commonName);
|
||||
|
||||
downloader_cat.debug()
|
||||
<< "Server is " << common_name << " from " << org_unit_name
|
||||
<< " / " << org_name << "\n";
|
||||
}
|
||||
|
||||
if (_client->get_verify_ssl() != HTTPClient::VS_no_verify) {
|
||||
// Check that the server is someone we expected to be talking
|
||||
// to.
|
||||
if (!validate_server_name(cert)) {
|
||||
_status_entry._status_code = SC_ssl_unexpected_server;
|
||||
_state = S_failure;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
X509_free(cert);
|
||||
}
|
||||
|
||||
X509_free(cert);
|
||||
|
||||
_state = S_ready;
|
||||
return false;
|
||||
|
|
@ -3344,6 +3345,34 @@ certificate signing
|
|||
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPChannel::check_preapproved_server_certificate
|
||||
// Access: Private
|
||||
// Description: Checks to see if the indicated certificate is on the
|
||||
// pre-approved list for the current server.
|
||||
//
|
||||
// If the full cert itself (including its key) is on the
|
||||
// pre-approved list, sets both cert_preapproved and
|
||||
// cert_name_preapproved to true.
|
||||
//
|
||||
// If the full cert is not on the pre-approved list, but
|
||||
// its name matches a name on the pre-approved list,
|
||||
// sets cert_name_preapproved to true, and
|
||||
// cert_preapproved to false.
|
||||
//
|
||||
// Otherwise, sets both values to false. This doesn't
|
||||
// mean the cert is necessarily invalid, just that it
|
||||
// wasn't on the pre-approved list (which is usually
|
||||
// empty anyway).
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void HTTPChannel::
|
||||
check_preapproved_server_certificate(X509 *cert, bool &cert_preapproved,
|
||||
bool &cert_name_preapproved) const {
|
||||
return _client->check_preapproved_server_certificate(_request.get_url(),
|
||||
cert, cert_preapproved,
|
||||
cert_name_preapproved);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPChannel::validate_server_name
|
||||
// Access: Private
|
||||
|
|
@ -3481,35 +3510,6 @@ get_x509_name_component(X509_NAME *name, int nid) {
|
|||
return string((char *)data->data, data->length);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPChannel::x509_name_subset
|
||||
// Access: Private, Static
|
||||
// Description: Returns true if name_a is a subset of name_b: each
|
||||
// property of name_a is defined in name_b, and the
|
||||
// defined value is equivalent to that of name_a.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool HTTPChannel::
|
||||
x509_name_subset(X509_NAME *name_a, X509_NAME *name_b) {
|
||||
int count_a = X509_NAME_entry_count(name_a);
|
||||
for (int ai = 0; ai < count_a; ai++) {
|
||||
X509_NAME_ENTRY *na = X509_NAME_get_entry(name_a, ai);
|
||||
|
||||
int bi = X509_NAME_get_index_by_OBJ(name_b, na->object, -1);
|
||||
if (bi < 0) {
|
||||
// This entry in name_a is not defined in name_b.
|
||||
return false;
|
||||
}
|
||||
|
||||
X509_NAME_ENTRY *nb = X509_NAME_get_entry(name_b, bi);
|
||||
if (na->value->length != nb->value->length ||
|
||||
memcmp(na->value->data, nb->value->data, na->value->length) != 0) {
|
||||
// This entry in name_a doesn't match that of name_b.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPChannel::make_header
|
||||
// Access: Private
|
||||
|
|
|
|||
|
|
@ -249,10 +249,11 @@ private:
|
|||
|
||||
void check_socket();
|
||||
|
||||
void check_preapproved_server_certificate(X509 *cert, bool &cert_preapproved,
|
||||
bool &cert_name_preapproved) const;
|
||||
bool validate_server_name(X509 *cert);
|
||||
static bool match_cert_name(const string &cert_name, const string &hostname);
|
||||
static string get_x509_name_component(X509_NAME *name, int nid);
|
||||
static bool x509_name_subset(X509_NAME *name_a, X509_NAME *name_b);
|
||||
|
||||
void make_header();
|
||||
void make_proxy_request_text();
|
||||
|
|
|
|||
|
|
@ -137,6 +137,25 @@ HTTPClient() {
|
|||
("http-client-certificate-passphrase", "",
|
||||
PRC_DESC("This specifies the passphrase to use to decode the certificate named "
|
||||
"by http-client-certificate-filename."));
|
||||
|
||||
|
||||
ConfigVariableList http_preapproved_server_certificate_filename
|
||||
("http-preapproved-server-certificate-filename",
|
||||
PRC_DESC("This specifies a server hostname:port combination, followed by "
|
||||
"at least one space, and the name of a PEM file that contains "
|
||||
"an SSL certificate that we expect this server to offer. If "
|
||||
"it does, we will accept the cert without further question, "
|
||||
"even if the cert does not match any known certificate "
|
||||
"authorities. This option may appear multiple times."));
|
||||
|
||||
ConfigVariableList http_preapproved_server_certificate_name
|
||||
("http-preapproved-server-certificate-name",
|
||||
PRC_DESC("This specifies a server hostname:port combination, followed by "
|
||||
"at least one space, and a string describing the subject name "
|
||||
"of an SSL certificate we expect this server to offer. If it "
|
||||
"it does, we will accept the cert, but only if it also matches "
|
||||
"a known certificate authority. This option may appear "
|
||||
"multiple times."));
|
||||
|
||||
_http_version = HTTPEnum::HV_11;
|
||||
_verify_ssl = verify_ssl ? VS_normal : VS_no_verify;
|
||||
|
|
@ -168,6 +187,23 @@ HTTPClient() {
|
|||
_client_certificate_pub = NULL;
|
||||
_client_certificate_priv = NULL;
|
||||
|
||||
int num_server_certs = http_preapproved_server_certificate_filename.get_num_unique_values();
|
||||
int si;
|
||||
for (si = 0; si < num_server_certs; si++) {
|
||||
string cert_line = http_preapproved_server_certificate_filename.get_unique_value(si);
|
||||
string a, b;
|
||||
split_whitespace(a, b, cert_line);
|
||||
add_preapproved_server_certificate_filename(URLSpec(a, true), b);
|
||||
}
|
||||
|
||||
num_server_certs = http_preapproved_server_certificate_name.get_num_unique_values();
|
||||
for (si = 0; si < num_server_certs; si++) {
|
||||
string cert_line = http_preapproved_server_certificate_name.get_unique_value(si);
|
||||
string a, b;
|
||||
split_whitespace(a, b, cert_line);
|
||||
add_preapproved_server_certificate_name(URLSpec(a, true), b);
|
||||
}
|
||||
|
||||
// The first time we create an HTTPClient, we must initialize the
|
||||
// OpenSSL library. The OpenSSLWrapper object does that.
|
||||
OpenSSLWrapper::get_global_ptr();
|
||||
|
|
@ -900,6 +936,150 @@ load_client_certificate() {
|
|||
_client_certificate_pub != (X509 *)NULL);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPClient::add_preapproved_server_certificate_filename
|
||||
// Access: Published
|
||||
// Description: Adds the certificate defined in the indicated PEM
|
||||
// filename as a "pre-approved" certificate for the
|
||||
// indicated server, defined by the hostname and port
|
||||
// (only) from the given URL.
|
||||
//
|
||||
// If the server offers this particular certificate on a
|
||||
// secure connection, it will be accepted without
|
||||
// question. This is particularly useful for
|
||||
// communicating with a server using a known self-signed
|
||||
// certificate.
|
||||
//
|
||||
// See also the similar
|
||||
// add_preapproved_server_certificate_pem(), and the
|
||||
// weaker add_preapproved_server_certificate_name().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool HTTPClient::
|
||||
add_preapproved_server_certificate_filename(const URLSpec &url, const Filename &filename) {
|
||||
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
||||
string pem;
|
||||
|
||||
if (!vfs->read_file(filename, pem, true)) {
|
||||
// Could not find or read file.
|
||||
downloader_cat.warning()
|
||||
<< "Could not read " << filename << ".\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return add_preapproved_server_certificate_pem(url, pem);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPClient::add_preapproved_server_certificate_pem
|
||||
// Access: Published
|
||||
// Description: Adds the certificate defined in the indicated data
|
||||
// string, formatted as a PEM block, as a "pre-approved"
|
||||
// certificate for the indicated server, defined by the
|
||||
// hostname and port (only) from the given URL.
|
||||
//
|
||||
// If the server offers this particular certificate on a
|
||||
// secure connection, it will be accepted without
|
||||
// question. This is particularly useful for
|
||||
// communicating with a server using a known self-signed
|
||||
// certificate.
|
||||
//
|
||||
// See also the similar
|
||||
// add_preapproved_server_certificate_filename(), and
|
||||
// the weaker add_preapproved_server_certificate_name().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool HTTPClient::
|
||||
add_preapproved_server_certificate_pem(const URLSpec &url, const string &pem) {
|
||||
// Create an in-memory BIO to read the "file" from the memory
|
||||
// buffer, and call the low-level routine to read the
|
||||
// cert from the BIO.
|
||||
BIO *mbio = BIO_new_mem_buf((void *)pem.data(), pem.length());
|
||||
|
||||
ERR_clear_error();
|
||||
X509 *cert = PEM_read_bio_X509(mbio, NULL, NULL, NULL);
|
||||
BIO_free(mbio);
|
||||
|
||||
if (cert == NULL) {
|
||||
downloader_cat.warning()
|
||||
<< "Could not parse PEM data\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
string server_and_port = url.get_server_and_port();
|
||||
PreapprovedServerCerts::iterator psci =
|
||||
_preapproved_server_certs.insert(PreapprovedServerCerts::value_type(server_and_port, PreapprovedServerCert())).first;
|
||||
|
||||
PreapprovedServerCert &psc = (*psci).second;
|
||||
psc._certs.push_back(cert);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPClient::add_preapproved_server_certificate_name
|
||||
// Access: Published
|
||||
// Description: Adds the certificate *name* only, as a "pre-approved"
|
||||
// certificate name for the indicated server, defined by
|
||||
// the hostname and port (only) from the given URL.
|
||||
//
|
||||
// This is a weaker function than
|
||||
// add_preapproved_server_certificate_filename(). This
|
||||
// checks only the subject name of the certificate,
|
||||
// without checking for a particular certificate by key.
|
||||
// This means that a variety of server certificates may
|
||||
// match the indicated name.
|
||||
//
|
||||
// Because this is a weaker verification, it only
|
||||
// applies to server certificates that are signed by a
|
||||
// recognized certificate authority. Thus, it cannot be
|
||||
// used to pre-approve self-signed certificates, but it
|
||||
// can be used to accept a server certificate offered by
|
||||
// a different hostname than the one in the cert itself.
|
||||
//
|
||||
// The certificate name should be formatted in the form
|
||||
// /type0=value0/type1=value1/type2=...
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool HTTPClient::
|
||||
add_preapproved_server_certificate_name(const URLSpec &url, const string &name) {
|
||||
X509_NAME *cert_name = parse_x509_name(name);
|
||||
if (cert_name == NULL) {
|
||||
downloader_cat.warning()
|
||||
<< "Could not parse certificate name " << name << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
string server_and_port = url.get_server_and_port();
|
||||
PreapprovedServerCerts::iterator psci =
|
||||
_preapproved_server_certs.insert(PreapprovedServerCerts::value_type(server_and_port, PreapprovedServerCert())).first;
|
||||
|
||||
PreapprovedServerCert &psc = (*psci).second;
|
||||
psc._cert_names.push_back(cert_name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPClient::clear_preapproved_server_certificates
|
||||
// Access: Published
|
||||
// Description: Removes all preapproved server certificates for the
|
||||
// indicated server and port.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void HTTPClient::
|
||||
clear_preapproved_server_certificates(const URLSpec &url) {
|
||||
string server_and_port = url.get_server_and_port();
|
||||
_preapproved_server_certs.erase(server_and_port);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPClient::clear_all_preapproved_server_certificates
|
||||
// Access: Published
|
||||
// Description: Removes all preapproved server certificates for all
|
||||
// servers.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void HTTPClient::
|
||||
clear_all_preapproved_server_certificates() {
|
||||
_preapproved_server_certs.clear();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPClient::get_http_version_string
|
||||
// Access: Published
|
||||
|
|
@ -1085,6 +1265,72 @@ get_ssl_ctx() {
|
|||
return _ssl_ctx;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPClient::check_preapproved_server_certificate
|
||||
// Access: Private
|
||||
// Description: Checks to see if the indicated certificate is on the
|
||||
// pre-approved list for the indicated server's URL.
|
||||
//
|
||||
// If the full cert itself (including its key) is on the
|
||||
// pre-approved list, sets both cert_preapproved and
|
||||
// cert_name_preapproved to true.
|
||||
//
|
||||
// If the full cert is not on the pre-approved list, but
|
||||
// its name matches a name on the pre-approved list,
|
||||
// sets cert_name_preapproved to true, and
|
||||
// cert_preapproved to false.
|
||||
//
|
||||
// Otherwise, sets both values to false. This doesn't
|
||||
// mean the cert is necessarily invalid, just that it
|
||||
// wasn't on the pre-approved list (which is usually
|
||||
// empty anyway).
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void HTTPClient::
|
||||
check_preapproved_server_certificate(const URLSpec &url, X509 *cert,
|
||||
bool &cert_preapproved, bool &cert_name_preapproved) const {
|
||||
cert_preapproved = false;
|
||||
cert_name_preapproved = false;
|
||||
|
||||
string server_and_port = url.get_server_and_port();
|
||||
PreapprovedServerCerts::const_iterator psci =
|
||||
_preapproved_server_certs.find(server_and_port);
|
||||
|
||||
if (psci == _preapproved_server_certs.end()) {
|
||||
// No preapproved certs defined for this server.
|
||||
return;
|
||||
}
|
||||
|
||||
const PreapprovedServerCert &psc = (*psci).second;
|
||||
|
||||
// See if we have an exact match on the cert itself.
|
||||
ServerCerts::const_iterator sci;
|
||||
for (sci = psc._certs.begin(); sci != psc._certs.end(); ++sci) {
|
||||
if (X509_cmp(cert, *sci) == 0) {
|
||||
cert_preapproved = true;
|
||||
cert_name_preapproved = true;
|
||||
downloader_cat.info()
|
||||
<< "Server certificate is pre-approved.\n";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// OK, look for a match on the cert's name only.
|
||||
X509_NAME *subject = X509_get_subject_name(cert);
|
||||
ServerCertNames::const_iterator scni;
|
||||
for (scni = psc._cert_names.begin(); scni != psc._cert_names.end(); ++scni) {
|
||||
X509_NAME *cert_name = (*scni);
|
||||
if (x509_name_subset(cert_name, subject)) {
|
||||
downloader_cat.info()
|
||||
<< "Server certificate name is pre-approved.\n";
|
||||
cert_name_preapproved = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Didn't find any match.
|
||||
return;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPClient::get_proxies_for_scheme
|
||||
// Access: Private
|
||||
|
|
@ -1421,6 +1667,65 @@ parse_x509_name(const string &source) {
|
|||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPClient::x509_name_subset
|
||||
// Access: Private, Static
|
||||
// Description: Returns true if name_a is a subset of name_b: each
|
||||
// property of name_a is defined in name_b, and the
|
||||
// defined value is equivalent to that of name_a.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool HTTPClient::
|
||||
x509_name_subset(X509_NAME *name_a, X509_NAME *name_b) {
|
||||
int count_a = X509_NAME_entry_count(name_a);
|
||||
for (int ai = 0; ai < count_a; ai++) {
|
||||
X509_NAME_ENTRY *na = X509_NAME_get_entry(name_a, ai);
|
||||
|
||||
int bi = X509_NAME_get_index_by_OBJ(name_b, na->object, -1);
|
||||
if (bi < 0) {
|
||||
// This entry in name_a is not defined in name_b.
|
||||
return false;
|
||||
}
|
||||
|
||||
X509_NAME_ENTRY *nb = X509_NAME_get_entry(name_b, bi);
|
||||
if (na->value->length != nb->value->length ||
|
||||
memcmp(na->value->data, nb->value->data, na->value->length) != 0) {
|
||||
// This entry in name_a doesn't match that of name_b.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPClient::split_whitespace
|
||||
// Access: Private, Static
|
||||
// Description: Puts the first word of c into a, and the remainder
|
||||
// into b.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void HTTPClient::
|
||||
split_whitespace(string &a, string &b, const string &c) {
|
||||
size_t p = 0;
|
||||
|
||||
// Skip initial whitespace
|
||||
while (p < c.length() && isspace(c[p])) {
|
||||
++p;
|
||||
}
|
||||
|
||||
// Find the length of the first word
|
||||
size_t q = p;
|
||||
while (p < c.length() && !isspace(c[p])) {
|
||||
++p;
|
||||
}
|
||||
|
||||
a = c.substr(q, p - q);
|
||||
|
||||
// Skip whitespace between words
|
||||
while (p < c.length() && isspace(c[p])) {
|
||||
++p;
|
||||
}
|
||||
b = c.substr(p);
|
||||
}
|
||||
|
||||
#if defined(SSL_097) && !defined(NDEBUG)
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPClient::ssl_msg_callback
|
||||
|
|
@ -1485,4 +1790,22 @@ ssl_msg_callback(int write_p, int version, int content_type,
|
|||
}
|
||||
#endif // defined(SSL_097) && !defined(NDEBUG)
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: HTTPClient::PreapprovedServerCert::Destructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
HTTPClient::PreapprovedServerCert::
|
||||
~PreapprovedServerCert() {
|
||||
ServerCerts::const_iterator sci;
|
||||
for (sci = _certs.begin(); sci != _certs.end(); ++sci) {
|
||||
X509_free(*sci);
|
||||
}
|
||||
|
||||
ServerCertNames::const_iterator scni;
|
||||
for (scni = _cert_names.begin(); scni != _cert_names.end(); ++scni) {
|
||||
X509_NAME_free(*scni);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HAVE_OPENSSL
|
||||
|
|
|
|||
|
|
@ -98,6 +98,12 @@ PUBLISHED:
|
|||
INLINE void set_client_certificate_passphrase(const string &passphrase);
|
||||
bool load_client_certificate();
|
||||
|
||||
bool add_preapproved_server_certificate_filename(const URLSpec &url, const Filename &filename);
|
||||
bool add_preapproved_server_certificate_pem(const URLSpec &url, const string &pem);
|
||||
bool add_preapproved_server_certificate_name(const URLSpec &url, const string &name);
|
||||
void clear_preapproved_server_certificates(const URLSpec &url);
|
||||
void clear_all_preapproved_server_certificates();
|
||||
|
||||
INLINE void set_http_version(HTTPEnum::HTTPVersion version);
|
||||
INLINE HTTPEnum::HTTPVersion get_http_version() const;
|
||||
string get_http_version_string() const;
|
||||
|
|
@ -131,6 +137,9 @@ public:
|
|||
SSL_CTX *get_ssl_ctx();
|
||||
|
||||
private:
|
||||
void check_preapproved_server_certificate(const URLSpec &url, X509 *cert,
|
||||
bool &cert_preapproved, bool &cert_name_preapproved) const;
|
||||
|
||||
bool get_proxies_for_scheme(const string &scheme,
|
||||
pvector<URLSpec> &proxies) const;
|
||||
|
||||
|
|
@ -146,6 +155,9 @@ private:
|
|||
void unload_client_certificate();
|
||||
|
||||
static X509_NAME *parse_x509_name(const string &source);
|
||||
static bool x509_name_subset(X509_NAME *name_a, X509_NAME *name_b);
|
||||
|
||||
static void split_whitespace(string &a, string &b, const string &c);
|
||||
|
||||
#if defined(SSL_097) && !defined(NDEBUG)
|
||||
static void ssl_msg_callback(int write_p, int version, int content_type,
|
||||
|
|
@ -188,6 +200,19 @@ private:
|
|||
X509 *_client_certificate_pub;
|
||||
EVP_PKEY *_client_certificate_priv;
|
||||
|
||||
typedef pvector<X509 *> ServerCerts;
|
||||
typedef pvector<X509_NAME *> ServerCertNames;
|
||||
class PreapprovedServerCert {
|
||||
public:
|
||||
~PreapprovedServerCert();
|
||||
|
||||
ServerCerts _certs;
|
||||
ServerCertNames _cert_names;
|
||||
};
|
||||
|
||||
typedef pmap<string, PreapprovedServerCert> PreapprovedServerCerts;
|
||||
PreapprovedServerCerts _preapproved_server_certs;
|
||||
|
||||
static PT(HTTPClient) _global_ptr;
|
||||
|
||||
friend class HTTPChannel;
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ do_receive_datagram(Datagram &dg) {
|
|||
}
|
||||
if (_data_expected == 0) {
|
||||
// Read the first two bytes: the datagram length.
|
||||
while (_data_so_far.length() < _tcp_header_size) {
|
||||
while ((int)_data_so_far.length() < _tcp_header_size) {
|
||||
int ch = _istream->get();
|
||||
if (_istream->eof() || _istream->fail()) {
|
||||
_istream->clear();
|
||||
|
|
|
|||
Loading…
Reference in New Issue