From aeb5183df86ba0b321812ab8c34ac5e24254a84e Mon Sep 17 00:00:00 2001 From: David Rose Date: Thu, 17 Sep 2009 13:36:18 +0000 Subject: [PATCH] more WIP: p3DCert --- direct/src/p3d/panda3d.pdef | 8 +- direct/src/plugin/Sources.pp | 15 +- direct/src/plugin/p3dCert.cxx | 403 ++++++++++++++++++++++++++++++ direct/src/plugin/p3dCert.h | 85 +++++++ direct/src/plugin/p3dInstance.cxx | 7 +- 5 files changed, 508 insertions(+), 10 deletions(-) create mode 100644 direct/src/plugin/p3dCert.cxx create mode 100644 direct/src/plugin/p3dCert.h diff --git a/direct/src/p3d/panda3d.pdef b/direct/src/p3d/panda3d.pdef index c3eaaffd28..064c15dcce 100755 --- a/direct/src/p3d/panda3d.pdef +++ b/direct/src/p3d/panda3d.pdef @@ -50,15 +50,21 @@ class images(package): print "Could not locate %s" % (filename) # Also make a few special cases. We use the same default image - # for download, ready, unauth, and launch. + # for download, ready, unauth, launch, and failed. download = configDict.get('download_img', None) if download: configDict['ready_img'] = download configDict['unauth_img'] = download configDict['launch_img'] = download + configDict['failed_img'] = download config(**configDict) +class p3dcert(package): + # This special application, used to pop up a dialog to prompt the + # user to accept or deny unknown applications, is its own package. + file('p3dcert.exe') + class panda3d(package): # The core Panda3D package. Contains Python and most of the graphics # code in Panda3D. diff --git a/direct/src/plugin/Sources.pp b/direct/src/plugin/Sources.pp index 226ecbf5ea..f7bbd9035a 100644 --- a/direct/src/plugin/Sources.pp +++ b/direct/src/plugin/Sources.pp @@ -179,13 +179,14 @@ #end static_lib_target -//#begin bin_target -// #define BUILD_TARGET $[HAVE_WX] -// #define USE_PACKAGES wx openssl -// #define TARGET p3dcert -// -// #define SOURCES p3dCert.cxx -//#end bin_target +#begin bin_target + #define BUILD_TARGET $[HAVE_WX] + #define USE_PACKAGES wx openssl + #define TARGET p3dcert + + #define SOURCES p3dCert.cxx + #define OSX_SYS_FRAMEWORKS Carbon +#end bin_target #include $[THISDIRPREFIX]p3d_plugin_config.h.pp diff --git a/direct/src/plugin/p3dCert.cxx b/direct/src/plugin/p3dCert.cxx new file mode 100644 index 0000000000..991b58da90 --- /dev/null +++ b/direct/src/plugin/p3dCert.cxx @@ -0,0 +1,403 @@ +// Filename: p3dCert.cxx +// Created by: drose (11Sep09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "p3dCert.h" +#include "wx/cmdline.h" + +#ifdef __WXMAC__ +#include +extern "C" { void CPSEnableForegroundOperation(ProcessSerialNumber* psn); } +#endif + +static const wxString +self_signed_cert_text = _T + ("This Panda3D application has been signed by what's known as a " + "self-signed certificate. This means the name on the certificate can't " + "be verified, and you have no way of knowing for sure who wrote it.\n\n" + + "We recommend you click Cancel to avoid running this application."); + +static const wxString +unknown_auth_cert_text = _T + ("This Panda3D application has been signed, but we don't recognize " + "the authority that verifies the signature. This means the name " + "on the certificate can't be trusted, and you have no way of knowing " + "for sure who wrote it.\n\n" + + "We recommend you click Cancel to avoid running this application."); + +static const wxString +verified_cert_text = _T + ("This Panda3D application has been signed by %s. " + "If you trust %s, then click the Run button below " + "to run this application on your computer. This will also " + "automatically approve this and any other applications signed by " + "%s in the future.\n\n" + + "If you are unsure about this application, " + "you should click Cancel instead."); + +static const wxString +expired_cert_text = _T + ("This Panda3D application has been signed by %s, " + "but the certificate has expired.\n\n" + + "You should check the current date set on your computer's clock " + "to make sure it is correct.\n\n" + + "If your computer's date is correct, we recommend " + "you click Cancel to avoid running this application."); + +static const wxString +generic_error_cert_text = _T + ("This Panda3D application has been signed, but there is a problem " + "with the certificate (OpenSSL error code %d).\n\n" + + "We recommend you click Cancel to avoid running this application."); + +static const wxString +no_cert_text = _T + ("This Panda3D application has not been signed. This means you have " + "no way of knowing for sure who wrote it.\n\n" + + "Click Cancel to avoid running this application."); + +// the event tables connect the wxWidgets events with the functions +// (event handlers) which process them. It can be also done at +// run-time, but for the simple menu events like this the static +// method is much simpler. +/* +BEGIN_EVENT_TABLE(MyFrame, wxFrame) + EVT_MENU(Minimal_Quit, MyFrame::OnQuit) + EVT_MENU(Minimal_About, MyFrame::OnAbout) +END_EVENT_TABLE() +*/ + +// Create a new application object: this macro will allow wxWidgets to +// create the application object during program execution (it's better +// than using a static object for many reasons) and also implements +// the accessor function wxGetApp() which will return the reference of +// the right type (i.e. P3DCertApp and not wxApp) +IMPLEMENT_APP(P3DCertApp) + +//////////////////////////////////////////////////////////////////// +// Function: P3DCertApp::OnInit +// Access: Public, Virtual +// Description: The "main" of a wx application. This is the first +// entry point. +//////////////////////////////////////////////////////////////////// +bool P3DCertApp:: +OnInit() { + // call the base class initialization method, currently it only parses a + // few common command-line options but it could be do more in the future + if (!wxApp::OnInit()) { + return false; + } + + OpenSSL_add_all_algorithms(); + +#ifdef __WXMAC__ + // Enable the dialog to go to the foreground on Mac, even without + // having to wrap it up in a bundle. + ProcessSerialNumber psn; + + GetCurrentProcess(&psn); + CPSEnableForegroundOperation(&psn); + SetFrontProcess(&psn); +#endif + + AuthDialog *dialog = new AuthDialog(_cert_filename, _ca_filename); + dialog->Show(true); + + // Return true to enter the main loop and wait for user input. + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DCertApp::OnInitCmdLine +// Access: Public, Virtual +// Description: A callback to initialize the parser with the command +// line options. +//////////////////////////////////////////////////////////////////// +void P3DCertApp:: +OnInitCmdLine(wxCmdLineParser &parser) { + parser.AddParam(); + parser.AddParam(); +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DCertApp::OnCmdLineParsed +// Access: Public, Virtual +// Description: A callback after the successful parsing of the +// command line. +//////////////////////////////////////////////////////////////////// +bool P3DCertApp:: +OnCmdLineParsed(wxCmdLineParser &parser) { + _cert_filename = parser.GetParam(0); + _ca_filename = parser.GetParam(1); + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: AuthDialog::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +AuthDialog:: +AuthDialog(const wxString &cert_filename, const wxString &ca_filename) : + wxDialog(NULL, wxID_ANY, _T("New Panda3D Application"), wxDefaultPosition) +{ + _cert = NULL; + _stack = NULL; + _verify_result = -1; + + read_cert_file(cert_filename); + get_common_name(); + verify_cert(ca_filename); + layout(); +} + +//////////////////////////////////////////////////////////////////// +// Function: AuthDialog::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +AuthDialog:: +~AuthDialog() { + if (_cert != NULL) { + X509_free(_cert); + _cert = NULL; + } + if (_stack != NULL) { + sk_free(_stack); + _stack = NULL; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AuthDialog::read_cert_file +// Access: Private +// Description: Reads the list of certificates in the pem filename +// passed on the command line into _cert and _stack. +//////////////////////////////////////////////////////////////////// +void AuthDialog:: +read_cert_file(const wxString &cert_filename) { + FILE *fp = fopen(cert_filename.mb_str(), "r"); + if (fp == NULL) { + cerr << "Couldn't read " << cert_filename << "\n"; + return; + } + _cert = PEM_read_X509(fp, NULL, NULL, (void *)""); + if (_cert == NULL) { + cerr << "Could not read certificate in " << cert_filename << ".\n"; + fclose(fp); + return; + } + + // Build up a STACK of the remaining certificates in the file. + _stack = sk_new(NULL); + X509 *c = PEM_read_X509(fp, NULL, NULL, (void *)""); + while (c != NULL) { + sk_push(_stack, (char *)c); + c = PEM_read_X509(fp, NULL, NULL, (void *)""); + } + + fclose(fp); +} + +//////////////////////////////////////////////////////////////////// +// Function: AuthDialog::get_common_name +// Access: Public +// Description: Extracts the common_name from the certificate. +//////////////////////////////////////////////////////////////////// +void AuthDialog:: +get_common_name() { + if (_cert == NULL) { + _common_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); + } + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: AuthDialog::verify_cert +// Access: Public +// Description: Checks whether the certificate is valid by the chain +// and initializes _verify_status accordingly. +//////////////////////////////////////////////////////////////////// +void AuthDialog:: +verify_cert(const wxString &ca_filename) { + if (_cert == NULL) { + _verify_result = -1; + return; + } + + // Create a new X509_STORE. + X509_STORE *store = X509_STORE_new(); + X509_STORE_set_default_paths(store); + + // Read the trusted certificates. + FILE *fp = fopen(ca_filename.mb_str(), "r"); + if (fp == NULL) { + cerr << "Couldn't read " << ca_filename << "\n"; + } else { + X509 *c = PEM_read_X509(fp, NULL, NULL, (void *)""); + while (c != NULL) { + X509_STORE_add_cert(store, c); + c = PEM_read_X509(fp, NULL, NULL, (void *)""); + } + fclose(fp); + } + + // Create the X509_STORE_CTX for verifying the cert and chain. + X509_STORE_CTX *ctx = X509_STORE_CTX_new(); + X509_STORE_CTX_init(ctx, store, _cert, _stack); + X509_STORE_CTX_set_cert(ctx, _cert); + + if (X509_verify_cert(ctx)) { + _verify_result = 0; + } else { + _verify_result = X509_STORE_CTX_get_error(ctx); + } + + X509_STORE_CTX_cleanup(ctx); + X509_STORE_CTX_free(ctx); + + X509_STORE_free(store); + + cerr << "Got certificate from " << _common_name + << ", verify_result = " << _verify_result << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: AuthDialog::layout +// Access: Private +// Description: Arranges the text and controls within the dialog. +//////////////////////////////////////////////////////////////////// +void AuthDialog:: +layout() { + wxString header, text; + get_text(header, text); + + wxPanel *panel = new wxPanel(this); + wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL); + + wxFont font = panel->GetFont(); + wxFont *bold_font = wxTheFontList->FindOrCreateFont + (font.GetPointSize() * 1.5, + font.GetFamily(), font.GetStyle(), wxFONTWEIGHT_BOLD); + + if (!header.IsEmpty()) { + wxStaticText *text0 = new wxStaticText + (panel, wxID_ANY, header, wxDefaultPosition, wxDefaultSize, + wxALIGN_CENTER); + vsizer->Add(text0, 0, wxCENTER | wxALL, 10); + } + + wxStaticText *text1 = new wxStaticText + (panel, wxID_ANY, text, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER); + text1->Wrap(400); + vsizer->Add(text1, 0, wxCENTER | wxALL, 10); + + // Create the run / cancel buttons. + wxBoxSizer *bsizer = new wxBoxSizer(wxHORIZONTAL); + + if (_verify_result == 0 && _cert != NULL) { + wxButton *run_button = new wxButton(panel, wxID_OK, _T("Run")); + bsizer->Add(run_button, 0, wxALIGN_CENTER | wxALL, 5); + } + + if (_cert != NULL) { + wxButton *view_button = new wxButton(panel, wxID_ANY, _T("View Certificate")); + bsizer->Add(view_button, 0, wxALIGN_CENTER | wxALL, 5); + } + + wxButton *cancel_button = new wxButton(panel, wxID_CANCEL, _T("Cancel")); + bsizer->Add(cancel_button, 0, wxALIGN_CENTER | wxALL, 5); + + vsizer->Add(bsizer, 0, wxALIGN_CENTER | wxALL, 5); + + panel->SetSizer(vsizer); + panel->SetAutoLayout(true); + vsizer->Fit(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: AuthDialog::get_text +// Access: Private +// Description: Fills in the text appropriate to display in the +// dialog box, based on the certificate read so far. +//////////////////////////////////////////////////////////////////// +void AuthDialog:: +get_text(wxString &header, wxString &text) { + switch (_verify_result) { + case -1: + header = _T("No signature!"); + text = no_cert_text; + break; + + case 0: + text.Printf(verified_cert_text, _common_name.c_str(), _common_name.c_str(), _common_name.c_str()); + break; + + case X509_V_ERR_CERT_NOT_YET_VALID: + case X509_V_ERR_CERT_HAS_EXPIRED: + case X509_V_ERR_CRL_NOT_YET_VALID: + case X509_V_ERR_CRL_HAS_EXPIRED: + header = _T("Expired signatured!"); + text.Printf(expired_cert_text, _common_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()); + break; + + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: + case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: + header = _T("Unverified signature!"); + text = self_signed_cert_text; + break; + + default: + header = _T("Unverified signature!"); + text.Printf(generic_error_cert_text, _verify_result); + } +} diff --git a/direct/src/plugin/p3dCert.h b/direct/src/plugin/p3dCert.h new file mode 100644 index 0000000000..5366aa1c56 --- /dev/null +++ b/direct/src/plugin/p3dCert.h @@ -0,0 +1,85 @@ +// Filename: p3dCert.h +// Created by: drose (11Sep09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef P3DCERT_H +#define P3DCERT_H + +#include "wx/wx.h" + +#define OPENSSL_NO_KRB5 +#include "openssl/x509.h" +#include "openssl/x509_vfy.h" +#include "openssl/pem.h" + +#include +#include +#include +using namespace std; + +//////////////////////////////////////////////////////////////////// +// Class : P3DCertApp +// Description : This is the wxApp that drives this application. +//////////////////////////////////////////////////////////////////// +class P3DCertApp : public wxApp { +public: + virtual bool OnInit(); + virtual void OnInitCmdLine(wxCmdLineParser &parser); + virtual bool OnCmdLineParsed(wxCmdLineParser &parser); + +private: + wxString _cert_filename; + wxString _ca_filename; +}; + +//////////////////////////////////////////////////////////////////// +// Class : AuthDialog +// Description : This is the primary dialog of this application. +// +// This dialog is presented to the user when he/she +// clicks on the red authorization button on the splash +// window. It tells the user the status of the +// application's signature, and invites the user to +// approve the signature or cancel. +//////////////////////////////////////////////////////////////////// +class AuthDialog : public wxDialog { +public: + AuthDialog(const wxString &cert_filename, const wxString &ca_filename); + virtual ~AuthDialog(); + + /* + // event handlers (these functions should _not_ be virtual) + void OnQuit(wxCommandEvent &event); + void OnAbout(wxCommandEvent &event); + */ + +private: + void read_cert_file(const wxString &cert_filename); + void get_common_name(); + void verify_cert(const wxString &ca_filename); + + void layout(); + void get_text(wxString &header, wxString &text); + +private: + // any class wishing to process wxWidgets events must use this macro + // DECLARE_EVENT_TABLE() + + X509 *_cert; + STACK *_stack; + + wxString _common_name; + int _verify_result; +}; + +#endif diff --git a/direct/src/plugin/p3dInstance.cxx b/direct/src/plugin/p3dInstance.cxx index 7a22d80074..087091f731 100644 --- a/direct/src/plugin/p3dInstance.cxx +++ b/direct/src/plugin/p3dInstance.cxx @@ -100,6 +100,9 @@ P3DInstance(P3D_request_ready_func *func, _splash_window = NULL; _instance_window_opened = false; _stuff_to_download = false; + _download_package_index = 0; + _total_download_size = 0; + _total_downloaded = 0; INIT_LOCK(_request_lock); _requested_stop = false; @@ -892,7 +895,6 @@ make_xml() { //////////////////////////////////////////////////////////////////// void P3DInstance:: splash_button_clicked_sub_thread() { - cerr << "splash sub\n"; TiXmlDocument *doc = new TiXmlDocument; TiXmlElement *xrequest = new TiXmlElement("request"); xrequest->SetAttribute("rtype", "notify"); @@ -912,7 +914,6 @@ splash_button_clicked_sub_thread() { //////////////////////////////////////////////////////////////////// void P3DInstance:: splash_button_clicked_main_thread() { - cerr << "splash main\n"; if (!_p3d_trusted) { auth_button_clicked(); } else if (_session == NULL) { @@ -931,6 +932,7 @@ auth_button_clicked() { // Here's where we need to invoke the authorization program. cerr << "auth clicked\n"; + /* assert(_splash_window != NULL); int num_signatures = _mf_reader.get_num_signatures(); if (num_signatures > 0) { @@ -943,6 +945,7 @@ auth_button_clicked() { // attempting to host his own application in a web page. _splash_window->show_auth_dialog(P3DMultifileReader::CertChain()); } + */ /* // After the authorization program has returned, check the signature