From 3293a6574a4f4fa44856ef5dada00e2d5f6ed5bc Mon Sep 17 00:00:00 2001 From: David Rose Date: Sat, 3 Apr 2010 01:17:45 +0000 Subject: [PATCH] http-preapproved-server-certificate-name --- panda/src/downloader/config_downloader.cxx | 3 - panda/src/downloader/config_downloader.h | 2 - panda/src/downloader/httpChannel.cxx | 174 +++++------ panda/src/downloader/httpChannel.h | 3 +- panda/src/downloader/httpClient.cxx | 323 +++++++++++++++++++++ panda/src/downloader/httpClient.h | 25 ++ panda/src/downloader/socketStream.cxx | 2 +- 7 files changed, 438 insertions(+), 94 deletions(-) diff --git a/panda/src/downloader/config_downloader.cxx b/panda/src/downloader/config_downloader.cxx index 2a2a9600a8..535a31693b 100644 --- a/panda/src/downloader/config_downloader.cxx +++ b/panda/src/downloader/config_downloader.cxx @@ -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(). " diff --git a/panda/src/downloader/config_downloader.h b/panda/src/downloader/config_downloader.h index 374ba2252a..ffd64f81f6 100644 --- a/panda/src/downloader/config_downloader.h +++ b/panda/src/downloader/config_downloader.h @@ -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; diff --git a/panda/src/downloader/httpChannel.cxx b/panda/src/downloader/httpChannel.cxx index 63ffd5b07f..a2b0708715 100644 --- a/panda/src/downloader/httpChannel.cxx +++ b/panda/src/downloader/httpChannel.cxx @@ -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 diff --git a/panda/src/downloader/httpChannel.h b/panda/src/downloader/httpChannel.h index f1d16f1c19..344b8b60f8 100644 --- a/panda/src/downloader/httpChannel.h +++ b/panda/src/downloader/httpChannel.h @@ -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(); diff --git a/panda/src/downloader/httpClient.cxx b/panda/src/downloader/httpClient.cxx index 391ccb227f..fc3f046781 100644 --- a/panda/src/downloader/httpClient.cxx +++ b/panda/src/downloader/httpClient.cxx @@ -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 diff --git a/panda/src/downloader/httpClient.h b/panda/src/downloader/httpClient.h index 1227f02622..5d6b0c8aea 100644 --- a/panda/src/downloader/httpClient.h +++ b/panda/src/downloader/httpClient.h @@ -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 &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 ServerCerts; + typedef pvector ServerCertNames; + class PreapprovedServerCert { + public: + ~PreapprovedServerCert(); + + ServerCerts _certs; + ServerCertNames _cert_names; + }; + + typedef pmap PreapprovedServerCerts; + PreapprovedServerCerts _preapproved_server_certs; + static PT(HTTPClient) _global_ptr; friend class HTTPChannel; diff --git a/panda/src/downloader/socketStream.cxx b/panda/src/downloader/socketStream.cxx index b375e5479c..88f26762eb 100644 --- a/panda/src/downloader/socketStream.cxx +++ b/panda/src/downloader/socketStream.cxx @@ -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();