Browse Source

http-preapproved-server-certificate-name

David Rose 15 years ago
parent
commit
3293a6574a

+ 0 - 3
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().  "

+ 0 - 2
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;

+ 85 - 85
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;
-      _state = S_failure;
-      return false;
+    if (_client->get_verify_ssl() != HTTPClient::VS_no_verify && !cert_preapproved) {
+      cert_valid = 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;
+  
+  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;
     }
-
-  } 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

+ 2 - 1
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();

+ 323 - 0
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

+ 25 - 0
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<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;

+ 1 - 1
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();