Bläddra i källkod

common_name -> friendly_name

David Rose 16 år sedan
förälder
incheckning
2552864754

+ 44 - 31
direct/src/plugin/p3dCert.cxx

@@ -165,7 +165,7 @@ AuthDialog(const wxString &cert_filename, const wxString &cert_dir) :
   _verify_result = -1;
 
   read_cert_file(cert_filename);
-  get_common_name();
+  get_friendly_name();
   verify_cert();
   layout();
 }
@@ -295,39 +295,52 @@ read_cert_file(const wxString &cert_filename) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: AuthDialog::get_common_name
+//     Function: AuthDialog::get_friendly_name
 //       Access: Private
-//  Description: Extracts the common_name from the certificate.
+//  Description: Extracts the "friendly name" from the certificate:
+//               the common name or email name.
 ////////////////////////////////////////////////////////////////////
 void AuthDialog::
-get_common_name() {
+get_friendly_name() {
   if (_cert == NULL) {
-    _common_name.clear();
+    _friendly_name.clear();
     return;
   }
 
-  // A complex OpenSSL interface to extract out the common name in
-  // utf-8.
-  X509_NAME *xname = X509_get_subject_name(_cert);
-  if (xname != NULL) {
-    int pos = X509_NAME_get_index_by_NID(xname, NID_commonName, -1);
-    if (pos != -1) {
-      // We just get the first common name.  I guess it's possible to
-      // have more than one; not sure what that means in this context.
-      X509_NAME_ENTRY *xentry = X509_NAME_get_entry(xname, pos);
-      if (xentry != NULL) {
-        ASN1_STRING *data = X509_NAME_ENTRY_get_data(xentry);
-        if (data != NULL) {
-          // We use "print" to dump the output to a memory BIO.  Is
-          // there an easier way to decode the ASN1_STRING?  Curse
-          // these incomplete docs.
-          BIO *mbio = BIO_new(BIO_s_mem());
-          ASN1_STRING_print_ex(mbio, data, ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB);
-
-          char *pp;
-          long pp_size = BIO_get_mem_data(mbio, &pp);
-          _common_name = wxString(pp, wxConvUTF8, pp_size);
-          BIO_free(mbio);
+
+  static const int nid_choices[] = {
+    NID_pkcs9_emailAddress,
+    NID_commonName,
+    -1,
+  };
+
+  // Choose the first NID that exists on the cert.
+  for (int ni = 0; nid_choices[ni] != -1; ++ni) {
+    int nid = nid_choices[ni];
+
+    // A complex OpenSSL interface to extract out the name in utf-8.
+    X509_NAME *xname = X509_get_subject_name(_cert);
+    if (xname != NULL) {
+      int pos = X509_NAME_get_index_by_NID(xname, nid, -1);
+      if (pos != -1) {
+        // We just get the first common name.  I guess it's possible to
+        // have more than one; not sure what that means in this context.
+        X509_NAME_ENTRY *xentry = X509_NAME_get_entry(xname, pos);
+        if (xentry != NULL) {
+          ASN1_STRING *data = X509_NAME_ENTRY_get_data(xentry);
+          if (data != NULL) {
+            // We use "print" to dump the output to a memory BIO.  Is
+            // there an easier way to decode the ASN1_STRING?  Curse
+            // these incomplete docs.
+            BIO *mbio = BIO_new(BIO_s_mem());
+            ASN1_STRING_print_ex(mbio, data, ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB);
+            
+            char *pp;
+            long pp_size = BIO_get_mem_data(mbio, &pp);
+            _friendly_name = wxString(pp, wxConvUTF8, pp_size);
+            BIO_free(mbio);
+            return;
+          }
         }
       }
     }
@@ -388,7 +401,7 @@ verify_cert() {
 
   X509_STORE_free(store);
 
-  cerr << "Got certificate from " << _common_name
+  cerr << "Got certificate from " << _friendly_name
        << ", verify_result = " << _verify_result << "\n";
 }
 
@@ -461,7 +474,7 @@ get_text(wxString &header, wxString &text) {
     break;
 
   case 0:
-    text.Printf(verified_cert_text, _common_name.c_str(), _common_name.c_str(), _common_name.c_str());
+    text.Printf(verified_cert_text, _friendly_name.c_str(), _friendly_name.c_str(), _friendly_name.c_str());
     break;
 
   case X509_V_ERR_CERT_NOT_YET_VALID:
@@ -469,12 +482,12 @@ get_text(wxString &header, wxString &text) {
   case X509_V_ERR_CRL_NOT_YET_VALID:
   case X509_V_ERR_CRL_HAS_EXPIRED:
     header = _T("Expired signature!");
-    text.Printf(expired_cert_text, _common_name.c_str());
+    text.Printf(expired_cert_text, _friendly_name.c_str());
     break;
 
   case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
     header = _T("Unverified signature!");
-    text.Printf(unknown_auth_cert_text, _common_name.c_str());
+    text.Printf(unknown_auth_cert_text, _friendly_name.c_str());
     break;
       
   case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:

+ 2 - 2
direct/src/plugin/p3dCert.h

@@ -67,7 +67,7 @@ public:
 
 private:
   void read_cert_file(const wxString &cert_filename);
-  void get_common_name();
+  void get_friendly_name();
   void verify_cert();
 
   void layout();
@@ -84,7 +84,7 @@ private:
   X509 *_cert;
   STACK *_stack;
 
-  wxString _common_name;
+  wxString _friendly_name;
   int _verify_result;
 };
 

+ 32 - 24
panda/src/downloadertools/multify.cxx

@@ -224,17 +224,19 @@ help() {
     "      generate slightly smaller files, but compression takes longer.  The\n"
     "      default is -" << default_compression_level << ".\n\n"
 
-    "  -S file.crt,[chain.crt],file.key[,\"password\"]\n"
+    "  -S file.crt[,chain.crt[,file.key[,\"password\"]]]\n"
     "      Sign the multifile.  The signing certificate should be in PEM form in\n"
     "      file.crt, with its private key in PEM form in file.key.  If the key\n"
     "      is encrypted on-disk, specify the decryption password as the third\n"
     "      option.  If a certificate chain is required, chain.crt should also\n"
-    "      be specified; note that the commas should be supplied even if this\n"
-    "      optional filename is omitted.\n"
+    "      be specified; note that the separating commas should be supplied\n"
+    "      even if this optional filename is omitted.\n"
+    "      You may also provide a single composite file that contains the\n"
+    "      certificate, chain, and key in the same file.\n"
     "      PEM form is the form accepted by the Apache web server.  The\n"
     "      signature is written to the multifile to prove it is unchanged; any\n"
     "      subsequent change to the multifile will invalidate the signature.\n"
-    "      This parameter may be repeated to sign the multifile with different\n"
+    "      This parameter may be repeated to sign the multifile with additional\n"
     "      certificates.\n\n";
 }
 
@@ -391,7 +393,13 @@ add_files(const vector_string &params) {
 
   bool okflag = do_add_files(multifile, filenames);
 
-  if (multifile->needs_repack()) {
+  bool needs_repack = multifile->needs_repack();
+  if (append) {
+    // If we specified -r mode, we always repack.
+    needs_repack = true;
+  }
+
+  if (needs_repack) {
     if (!multifile->repack()) {
       cerr << "Failed to write " << multifile_name << ".\n";
       okflag = false;
@@ -529,27 +537,27 @@ sign_multifile() {
   vector_string::iterator si;
   for (si = sign_params.begin(); si != sign_params.end(); ++si) {
     const string &param = (*si);
+    Filename certificate, chain, pkey;
+    string password;
+
     size_t comma1 = param.find(',');
     if (comma1 == string::npos) {
-      cerr << "Signing parameter requires two commas: " << param << "\n";
-      return false;
-    }
-    size_t comma2 = param.find(',', comma1 + 1);
-    if (comma2 == string::npos) {
-      cerr << "Signing parameter requires two commas: " << param << "\n";
-      return false;
-    }
-    size_t comma3 = param.find(',', comma2 + 1);
-
-    Filename certificate = Filename::from_os_specific(param.substr(0, comma1));
-    Filename chain = Filename::from_os_specific(param.substr(comma1 + 1, comma2 - comma1 - 1));
-    Filename pkey;
-    string password;
-    if (comma3 != string::npos) {
-      pkey = Filename::from_os_specific(param.substr(comma2 + 1, comma3 - comma2 - 1));
-      password = param.substr(comma3 + 1);
+      certificate = Filename::from_os_specific(param);
     } else {
-      pkey = Filename::from_os_specific(param.substr(comma2 + 1));
+      certificate = Filename::from_os_specific(param.substr(0, comma1));
+      size_t comma2 = param.find(',', comma1 + 1);
+      if (comma2 == string::npos) {
+        chain = Filename::from_os_specific(param.substr(comma1 + 1));
+      } else {
+        chain = Filename::from_os_specific(param.substr(comma1 + 1, comma2 - comma1 - 1));
+        size_t comma3 = param.find(',', comma2 + 1);
+        if (comma3 == string::npos) {
+          pkey = Filename::from_os_specific(param.substr(comma2 + 1));
+        } else {
+          pkey = Filename::from_os_specific(param.substr(comma2 + 1, comma3 - comma2 - 1));
+          password = param.substr(comma3 + 1);
+        }
+      }
     }
 
     if (!multifile->add_signature(certificate, chain, pkey, password)) {
@@ -675,7 +683,7 @@ list_files(const vector_string &params) {
   if (num_signatures != 0) {
     cout << "\n";
     for (i = 0; i < num_signatures; ++i) {
-      cout << "Signed by " << multifile->get_signature_common_name(i);
+      cout << "Signed by " << multifile->get_signature_friendly_name(i);
       int verify_result = multifile->validate_signature_certificate(i);
       if (verify_result == 0) {
         cout << " (certificate validated)\n";

+ 11 - 0
panda/src/express/multifile.I

@@ -568,3 +568,14 @@ INLINE Multifile::CertRecord::
 ~CertRecord() {
   X509_free(_cert);
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: Multifile::CertRecord::Copy Assignment
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void Multifile::CertRecord::
+operator = (const Multifile::CertRecord &other) {
+  X509_free(_cert);
+  _cert = X509_dup(other._cert);
+}

+ 161 - 30
panda/src/express/multifile.cxx

@@ -598,6 +598,13 @@ add_signature(const Filename &certificate, const Filename &chain,
               const Filename &pkey, const string &password) {
   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
 
+  if (chain.empty() && pkey.empty()) {
+    // If the second two filenames are empty, assume we're going for
+    // the composite mode, where everything's stuffed into the first
+    // file.
+    return add_signature(certificate, password);
+  }
+
   CertChain cert_chain;
 
   // Read the certificate file from VFS.  First, read the complete
@@ -673,6 +680,98 @@ add_signature(const Filename &certificate, const Filename &chain,
   return result;
 }
 #endif  // HAVE_OPENSSL
+
+#ifdef HAVE_OPENSSL
+////////////////////////////////////////////////////////////////////
+//     Function: Multifile::add_signature
+//       Access: Published
+//  Description: Adds a new signature to the Multifile.  This
+//               signature associates the indicated certificate with
+//               the current contents of the Multifile.  When the
+//               Multifile is read later, the signature will still be
+//               present only if the Multifile is unchanged; any
+//               subsequent changes to the Multifile will
+//               automatically invalidate and remove the signature.
+//
+//               This flavor of add_signature() reads the certificate,
+//               private key, and certificate chain from the same
+//               PEM-formatted file.  It takes the first private key
+//               found as the intended key, and then uses the first
+//               certificate found that matches that key as the
+//               signing certificate.  Any other certificates in the
+//               file are taken to be part of the chain.
+////////////////////////////////////////////////////////////////////
+bool Multifile::
+add_signature(const Filename &composite, const string &password) {
+  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
+
+  // First, read the complete file into memory.
+  string composite_data;
+  if (!vfs->read_file(composite, composite_data, true)) {
+    express_cat.info()
+      << "Could not read " << composite << ".\n";
+    return false;
+  }
+
+  // Get the private key.
+  BIO *pkey_mbio = BIO_new_mem_buf((void *)composite_data.data(), composite_data.size());
+  EVP_PKEY *evp_pkey = PEM_read_bio_PrivateKey(pkey_mbio, NULL, NULL,
+                                               (void *)password.c_str());
+  BIO_free(pkey_mbio);
+  if (evp_pkey == NULL) {
+    express_cat.info()
+      << "Could not read private key in " << composite << ".\n";
+    return false;
+  }
+
+  // Now read all of the certificates.
+  CertChain cert_chain;
+
+  BIO *chain_mbio = BIO_new_mem_buf((void *)composite_data.data(), composite_data.size());
+  X509 *c = PEM_read_bio_X509(chain_mbio, NULL, NULL, (void *)"");
+  while (c != NULL) {
+    cert_chain.push_back(c);
+    c = PEM_read_bio_X509(chain_mbio, NULL, NULL, (void *)"");
+  }
+  BIO_free(chain_mbio);
+
+  if (cert_chain.empty()) {
+    express_cat.info()
+      << "Could not read certificates in " << composite << ".\n";
+    return false;
+  }
+
+  // Now find the certificate that matches the signature, and move it
+  // to the front of the chain.
+  size_t i;
+  bool found_match = false;
+  for (i = 0; i < cert_chain.size(); ++i) {
+    X509 *c = cert_chain[i]._cert;
+    if (X509_check_private_key(c, evp_pkey)) {
+      found_match = true;
+      if (i != 0) {
+        // Move this entry to the beginning.
+        cert_chain.insert(cert_chain.begin(), cert_chain[i]);
+        cert_chain.erase(cert_chain.begin() + i + 1);
+      }
+      break;
+    }
+  }
+
+  if (!found_match) {
+    express_cat.info()
+      << "No certificates in " << composite << " match key.\n";
+    return false;
+  }
+
+  bool result = add_signature(cert_chain, evp_pkey);
+
+  EVP_PKEY_free(evp_pkey);
+
+  return result;
+}
+#endif  // HAVE_OPENSSL
+
 #ifdef HAVE_OPENSSL
 ////////////////////////////////////////////////////////////////////
 //     Function: Multifile::add_signature
@@ -756,6 +855,24 @@ add_signature(const Multifile::CertChain &cert_chain, EVP_PKEY *pkey) {
       return false;
     }
   }
+
+  if (cert_chain.empty()) {
+    express_cat.info()
+      << "No certificate given.\n";
+    return false;
+  }
+
+  if (pkey == NULL) {
+    express_cat.info()
+      << "No private key given.\n";
+    return false;
+  }
+
+  if (!X509_check_private_key(cert_chain[0]._cert, pkey)) {
+    express_cat.info()
+      << "Private key does not match certificate.\n"; 
+    return false;
+  }
   
   // Now encode that list of certs to a stream in DER form.
   stringstream der_stream;
@@ -871,42 +988,54 @@ get_signature_subject_name(int n) const {
 
 #ifdef HAVE_OPENSSL
 ////////////////////////////////////////////////////////////////////
-//     Function: Multifile::get_signature_common_name
+//     Function: Multifile::get_signature_friendly_name
 //       Access: Published
-//  Description: Returns the "common name" for the nth signature found
-//               on the Multifile, encoded in utf-8.  This is a subset
-//               of the subject_name, but it is probably a friendlier
-//               string to present to a user.  See the comments in
-//               get_num_signatures().
+//  Description: Returns a "friendly name" for the nth signature found
+//               on the Multifile.  This attempts to extract out the
+//               most meaningful part of the subject name.  It returns
+//               the emailAddress, if it is defined; otherwise, it
+//               returns the commonName.
+
+//               See the comments in get_num_signatures().
 ////////////////////////////////////////////////////////////////////
 string Multifile::
-get_signature_common_name(int n) const {
+get_signature_friendly_name(int n) const {
   const CertChain &cert_chain = get_signature(n);
   nassertr(!cert_chain.empty(), string());
 
-  // A complex OpenSSL interface to extract out the common name in
-  // utf-8.
-  X509_NAME *xname = X509_get_subject_name(cert_chain[0]._cert);
-  if (xname != NULL) {
-    int pos = X509_NAME_get_index_by_NID(xname, NID_commonName, -1);
-    if (pos != -1) {
-      // We just get the first common name.  I guess it's possible to
-      // have more than one; not sure what that means in this context.
-      X509_NAME_ENTRY *xentry = X509_NAME_get_entry(xname, pos);
-      if (xentry != NULL) {
-        ASN1_STRING *data = X509_NAME_ENTRY_get_data(xentry);
-        if (data != NULL) {
-          // We use "print" to dump the output to a memory BIO.  Is
-          // there an easier way to decode the ASN1_STRING?  Curse
-          // these incomplete docs.
-          BIO *mbio = BIO_new(BIO_s_mem());
-          ASN1_STRING_print_ex(mbio, data, ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB);
-
-          char *pp;
-          long pp_size = BIO_get_mem_data(mbio, &pp);
-          string name(pp, pp_size);
-          BIO_free(mbio);
-          return name;
+  static const int nid_choices[] = {
+    NID_pkcs9_emailAddress,
+    NID_commonName,
+    -1,
+  };
+
+  // Choose the first NID that exists on the cert.
+  for (int ni = 0; nid_choices[ni] != -1; ++ni) {
+    int nid = nid_choices[ni];
+
+    // A complex OpenSSL interface to extract out the name in utf-8.
+    X509_NAME *xname = X509_get_subject_name(cert_chain[0]._cert);
+    if (xname != NULL) {
+      int pos = X509_NAME_get_index_by_NID(xname, nid, -1);
+      if (pos != -1) {
+        // We just get the first common name.  I guess it's possible to
+        // have more than one; not sure what that means in this context.
+        X509_NAME_ENTRY *xentry = X509_NAME_get_entry(xname, pos);
+        if (xentry != NULL) {
+          ASN1_STRING *data = X509_NAME_ENTRY_get_data(xentry);
+          if (data != NULL) {
+            // We use "print" to dump the output to a memory BIO.  Is
+            // there an easier way to decode the ASN1_STRING?  Curse
+            // these incomplete docs.
+            BIO *mbio = BIO_new(BIO_s_mem());
+            ASN1_STRING_print_ex(mbio, data, ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB);
+            
+            char *pp;
+            long pp_size = BIO_get_mem_data(mbio, &pp);
+            string name(pp, pp_size);
+            BIO_free(mbio);
+            return name;
+          }
         }
       }
     }
@@ -2081,6 +2210,8 @@ clear_subfiles() {
     delete subfile;
   }
   _cert_special.clear();
+
+  _signatures.clear();
 #endif  // HAVE_OPENSSL
 
   Subfiles::iterator fi;

+ 4 - 1
panda/src/express/multifile.h

@@ -89,6 +89,7 @@ PUBLISHED:
     INLINE CertRecord(X509 *cert);
     INLINE CertRecord(const CertRecord &copy);
     INLINE ~CertRecord();
+    INLINE void operator = (const CertRecord &other);
     X509 *_cert;
   };
   typedef pvector<CertRecord> CertChain;
@@ -97,13 +98,15 @@ PUBLISHED:
                      const Filename &chain,
                      const Filename &pkey,
                      const string &password = "");
+  bool add_signature(const Filename &composite, 
+                     const string &password = "");
   bool add_signature(X509 *certificate, STACK *chain, EVP_PKEY *pkey);
   bool add_signature(const CertChain &chain, EVP_PKEY *pkey);
 
   int get_num_signatures() const;
   const CertChain &get_signature(int n) const;
   string get_signature_subject_name(int n) const;
-  string get_signature_common_name(int n) const;
+  string get_signature_friendly_name(int n) const;
   string get_signature_public_key(int n) const;
   void write_signature_certificate(int n, ostream &out) const;