more security work
This commit is contained in:
parent
9f734e3cbf
commit
b52dfe14dd
|
|
@ -474,9 +474,16 @@ class AppRunner(DirectObject):
|
|||
self.instanceId = instanceId
|
||||
|
||||
self.tokens = tokens
|
||||
self.tokenDict = dict(tokens)
|
||||
self.argv = argv
|
||||
|
||||
# We build up a token dictionary with care, so that if a given
|
||||
# token appears twice in the token list, we record only the
|
||||
# first value, not the second or later. This is consistent
|
||||
# with the internal behavior of the core API.
|
||||
self.tokenDict = {}
|
||||
for token, keyword in tokens:
|
||||
self.tokenDict.setdefault(token, keyword)
|
||||
|
||||
# Also store the arguments on sys, for applications that
|
||||
# aren't instance-ready.
|
||||
sys.argv = argv
|
||||
|
|
|
|||
|
|
@ -46,10 +46,11 @@ class images(package):
|
|||
print "Could not locate %s" % (filename)
|
||||
|
||||
# Also make a few special cases. We use the same default image
|
||||
# for download, ready, and launch.
|
||||
# for download, ready, unauth, and launch.
|
||||
download = configDict.get('download_img', None)
|
||||
if download:
|
||||
configDict['ready_img'] = download
|
||||
configDict['unauth_img'] = download
|
||||
configDict['launch_img'] = download
|
||||
|
||||
config(**configDict)
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ load_plugin(const string &p3d_plugin_filename,
|
|||
const string &contents_filename, const string &download_url,
|
||||
bool verify_contents, const string &platform,
|
||||
const string &log_directory, const string &log_basename,
|
||||
bool keep_cwd) {
|
||||
bool trusted_environment) {
|
||||
string filename = p3d_plugin_filename;
|
||||
if (filename.empty()) {
|
||||
// Look for the plugin along the path.
|
||||
|
|
@ -301,7 +301,7 @@ load_plugin(const string &p3d_plugin_filename,
|
|||
if (!P3D_initialize(P3D_API_VERSION, contents_filename.c_str(),
|
||||
download_url.c_str(), verify_contents, platform.c_str(),
|
||||
log_directory.c_str(), log_basename.c_str(),
|
||||
keep_cwd)) {
|
||||
trusted_environment)) {
|
||||
// Oops, failure to initialize.
|
||||
cerr << "Failed to initialize plugin (wrong API version?)\n";
|
||||
unload_plugin();
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ load_plugin(const string &p3d_plugin_filename,
|
|||
const string &contents_filename, const string &download_url,
|
||||
bool verify_contents, const string &platform,
|
||||
const string &log_directory, const string &log_basename,
|
||||
bool keep_cwd);
|
||||
bool trusted_environment);
|
||||
void unload_plugin();
|
||||
bool is_plugin_loaded();
|
||||
|
||||
|
|
|
|||
|
|
@ -71,17 +71,3 @@ inline bool P3DHost::
|
|||
has_contents_file() const {
|
||||
return (_xcontents != NULL);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DHost::encode_hexdigit
|
||||
// Access: Private
|
||||
// Description: Returns the hex digit corresponding to the
|
||||
// indicated integer value.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
inline char P3DHost::
|
||||
encode_hexdigit(int c) {
|
||||
if (c >= 10) {
|
||||
return c - 10 + 'a';
|
||||
}
|
||||
return c + '0';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -293,8 +293,8 @@ determine_host_dir() {
|
|||
for (size_t i = 0; i < keep_hash; ++i) {
|
||||
int high = (md[i] >> 4) & 0xf;
|
||||
int low = md[i] & 0xf;
|
||||
_host_dir += encode_hexdigit(high);
|
||||
_host_dir += encode_hexdigit(low);
|
||||
_host_dir += P3DInstanceManager::encode_hexdigit(high);
|
||||
_host_dir += P3DInstanceManager::encode_hexdigit(low);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,6 @@ private:
|
|||
|
||||
static string standardize_filename(const string &filename);
|
||||
static bool copy_file(const string &from_filename, const string &to_filename);
|
||||
static inline char encode_hexdigit(int c);
|
||||
|
||||
private:
|
||||
string _host_dir;
|
||||
|
|
|
|||
|
|
@ -55,6 +55,10 @@ const char *P3DInstance::_image_type_names[P3DInstance::IT_num_image_types] = {
|
|||
"play_ready",
|
||||
"play_rollover",
|
||||
"play_click",
|
||||
"unauth",
|
||||
"auth_ready",
|
||||
"auth_rollover",
|
||||
"auth_click",
|
||||
"none", // Not really used.
|
||||
};
|
||||
|
||||
|
|
@ -80,6 +84,7 @@ P3DInstance(P3D_request_ready_func *func,
|
|||
_current_button_image = IT_none;
|
||||
_got_fparams = false;
|
||||
_got_wparams = false;
|
||||
_p3d_trusted = false;
|
||||
|
||||
_fparams.set_tokens(tokens, num_tokens);
|
||||
_fparams.set_args(argc, argv);
|
||||
|
|
@ -89,6 +94,7 @@ P3DInstance(P3D_request_ready_func *func,
|
|||
_hidden = false;
|
||||
_allow_python_dev = false;
|
||||
_auto_start = false;
|
||||
_auth_button_approved = false;
|
||||
_session = NULL;
|
||||
_panda3d = NULL;
|
||||
_splash_window = NULL;
|
||||
|
|
@ -116,6 +122,10 @@ P3DInstance(P3D_request_ready_func *func,
|
|||
_panda_script_object->set_string_property("downloadPackageDisplayName", "");
|
||||
_panda_script_object->set_bool_property("downloadComplete", false);
|
||||
_panda_script_object->set_string_property("status", "initial");
|
||||
|
||||
// We'll start off with the "download" image displayed in the splash
|
||||
// window (when it opens), until we get stuff downloaded.
|
||||
set_background_image(IT_download);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -234,50 +244,24 @@ set_p3d_url(const string &p3d_url) {
|
|||
void P3DInstance::
|
||||
set_p3d_filename(const string &p3d_filename) {
|
||||
_fparams.set_p3d_filename(p3d_filename);
|
||||
_got_fparams = true;
|
||||
|
||||
_panda_script_object->set_float_property("instanceDownloadProgress", 1.0);
|
||||
|
||||
// This also sets up some internal data based on the contents of the
|
||||
// above file and the associated tokens.
|
||||
|
||||
// Extract the application desc file from the p3d file.
|
||||
P3DMultifileReader reader;
|
||||
stringstream sstream;
|
||||
if (!reader.extract_one(p3d_filename, sstream, "p3d_info.xml")) {
|
||||
nout << "No p3d_info.xml file found in " << p3d_filename << "\n";
|
||||
} else {
|
||||
sstream.seekg(0);
|
||||
TiXmlDocument doc;
|
||||
sstream >> doc;
|
||||
|
||||
// This also starts required packages downloading. When all
|
||||
// packages have been installed, we will start the instance.
|
||||
scan_app_desc_file(&doc);
|
||||
}
|
||||
|
||||
// For the moment, all sessions will be unique. TODO: support
|
||||
// multiple instances per session.
|
||||
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
|
||||
ostringstream strm;
|
||||
strm << inst_mgr->get_unique_id();
|
||||
_session_key = strm.str();
|
||||
|
||||
// Until we've done all of the above processing, we haven't fully
|
||||
// committed to having fparams. (Setting this flag down here
|
||||
// instead of up there avoids starting the instance in
|
||||
// scan_app_desc_file(), before we've had a chance to finish
|
||||
// processing this method.)
|
||||
_got_fparams = true;
|
||||
|
||||
// Generate a special notification: onpluginload, indicating the
|
||||
// plugin has read its parameters and is ready to be queried (even
|
||||
// if Python has not yet started).
|
||||
send_notify("onpluginload");
|
||||
|
||||
// Now that we're all set up, start the instance if we're fully
|
||||
// downloaded.
|
||||
if (get_packages_ready() && _got_wparams) {
|
||||
ready_to_start();
|
||||
if (!_mf_reader.open_read(_fparams.get_p3d_filename())) {
|
||||
nout << "Couldn't read " << _fparams.get_p3d_filename() << "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
if (check_p3d_signature()) {
|
||||
mark_p3d_trusted();
|
||||
} else {
|
||||
mark_p3d_untrusted();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -356,7 +340,7 @@ set_wparams(const P3DWindowParams &wparams) {
|
|||
_session->send_command(doc);
|
||||
}
|
||||
|
||||
if (get_packages_ready() && _got_fparams) {
|
||||
if (get_packages_ready() && _p3d_trusted) {
|
||||
ready_to_start();
|
||||
}
|
||||
}
|
||||
|
|
@ -904,29 +888,174 @@ make_xml() {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
void P3DInstance::
|
||||
splash_button_clicked() {
|
||||
if (_session == NULL) {
|
||||
if (!_p3d_trusted) {
|
||||
auth_button_clicked();
|
||||
} else if (_session == NULL) {
|
||||
play_button_clicked();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DInstance::auth_button_clicked
|
||||
// Access: Public
|
||||
// Description: Called to authorize the p3d file by the user clicking
|
||||
// the red "auth" button.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DInstance::
|
||||
auth_button_clicked() {
|
||||
// Here's where we need to invoke the authorization program.
|
||||
cerr << "auth clicked\n";
|
||||
|
||||
// After the authorization program has returned, check the signature
|
||||
// again.
|
||||
if (check_p3d_signature()) {
|
||||
// Set this flag to indicate that the user has clicked on the red
|
||||
// "auth" button an successfully approved the application. This
|
||||
// eliminates the need to click on the green "start" button.
|
||||
_auth_button_approved = true;
|
||||
mark_p3d_trusted();
|
||||
} else {
|
||||
mark_p3d_untrusted();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DInstance::play_button_clicked
|
||||
// Access: Public
|
||||
// Description: Called to start the game by the user clicking the
|
||||
// "play" button, or by JavaScript calling start().
|
||||
// green "play" button, or by JavaScript calling
|
||||
// start().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DInstance::
|
||||
play_button_clicked() {
|
||||
if (_session == NULL) {
|
||||
if (_splash_window != NULL) {
|
||||
_splash_window->set_button_active(false);
|
||||
}
|
||||
set_button_image(IT_none);
|
||||
set_background_image(IT_launch);
|
||||
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
|
||||
inst_mgr->start_instance(this);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DInstance::check_p3d_signature
|
||||
// Access: Private
|
||||
// Description: Checks the signature(s) encoded in the p3d file, and
|
||||
// looks to see if any of them are recognized. Returns
|
||||
// true if the signature is recognized and the file
|
||||
// should be trusted, false otherwise.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool P3DInstance::
|
||||
check_p3d_signature() {
|
||||
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
|
||||
if (inst_mgr->get_trusted_environment()) {
|
||||
// If we're in a trusted environment (e.g. the panda3d command
|
||||
// line, where we've already downloaded the p3d file separately),
|
||||
// then everything is approved.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Temporary hack: disabling further security checks until this code
|
||||
// is complete.
|
||||
return true;
|
||||
|
||||
nout << "check_p3d_signature\n";
|
||||
int num_signatures = _mf_reader.get_num_signatures();
|
||||
nout << "file has " << num_signatures << " signatures\n";
|
||||
|
||||
for (int i = 0; i < num_signatures; ++i) {
|
||||
const P3DMultifileReader::CertChain &chain = _mf_reader.get_signature(i);
|
||||
|
||||
// Here's a certificate that has signed this multifile.
|
||||
X509 *cert = chain[0]._cert;
|
||||
|
||||
// Look up the certificate to see if we've stored a copy in our
|
||||
// certs dir.
|
||||
if (inst_mgr->find_cert(cert)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Couldn't find any approved certificates.
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DInstance::mark_p3d_untrusted
|
||||
// Access: Private
|
||||
// Description: This is called internally when it has been determined
|
||||
// that the p3d file can't (yet) be trusted, for
|
||||
// instance because it lacks a signature, or because it
|
||||
// is signed by an unrecognized certificate. This puts
|
||||
// up the red "auth" button and waits for the user to
|
||||
// approve the app before continuing.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DInstance::
|
||||
mark_p3d_untrusted() {
|
||||
// Failed test.
|
||||
nout << "p3d untrusted\n";
|
||||
set_background_image(IT_unauth);
|
||||
set_button_image(IT_auth_ready);
|
||||
make_splash_window();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DInstance::mark_p3d_trusted
|
||||
// Access: Private
|
||||
// Description: This is called internally when it has been determined
|
||||
// that the p3d file can be trusted and started. When
|
||||
// this is called, the p3d file will be examined and
|
||||
// made ready to start; it will not be started until
|
||||
// this is called.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DInstance::
|
||||
mark_p3d_trusted() {
|
||||
nout << "p3d trusted\n";
|
||||
// Only call this once.
|
||||
assert(!_p3d_trusted);
|
||||
|
||||
// Extract the application desc file from the p3d file.
|
||||
stringstream sstream;
|
||||
if (!_mf_reader.extract_one(sstream, "p3d_info.xml")) {
|
||||
nout << "No p3d_info.xml file found in " << _fparams.get_p3d_filename() << "\n";
|
||||
// TODO: fail.
|
||||
|
||||
} else {
|
||||
sstream.seekg(0);
|
||||
TiXmlDocument doc;
|
||||
sstream >> doc;
|
||||
|
||||
// This also starts required packages downloading. When all
|
||||
// packages have been installed, we will start the instance.
|
||||
scan_app_desc_file(&doc);
|
||||
}
|
||||
|
||||
// Now we've got no further need to keep the _mf_reader open.
|
||||
_mf_reader.close();
|
||||
|
||||
// For the moment, all sessions will be unique. TODO: support
|
||||
// multiple instances per session.
|
||||
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
|
||||
ostringstream strm;
|
||||
strm << inst_mgr->get_unique_id();
|
||||
_session_key = strm.str();
|
||||
|
||||
// Until we've done all of the above processing, we haven't fully
|
||||
// committed to setting the trusted flag. (Setting this flag down
|
||||
// here instead of up there avoids starting the instance in
|
||||
// scan_app_desc_file(), before we've had a chance to finish
|
||||
// processing this method.)
|
||||
_p3d_trusted = true;
|
||||
|
||||
// Notify JS that we've accepted the trust of the p3d file.
|
||||
send_notify("ontrust");
|
||||
|
||||
// Now that we're all set up, start the instance if we're fully
|
||||
// downloaded.
|
||||
if (get_packages_ready() && _got_wparams) {
|
||||
ready_to_start();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DInstance::scan_app_desc_file
|
||||
// Access: Private
|
||||
|
|
@ -970,10 +1099,15 @@ scan_app_desc_file(TiXmlDocument *doc) {
|
|||
if (_fparams.lookup_token_int("auto_start") != 0) {
|
||||
_auto_start = true;
|
||||
}
|
||||
if (_auth_button_approved) {
|
||||
// But finally, if the user has already clicked through the red
|
||||
// "auth" button, no need to present him/her with another green
|
||||
// "play" button as well.
|
||||
_auto_start = true;
|
||||
}
|
||||
|
||||
nout << "_auto_start = " << _auto_start << "\n";
|
||||
|
||||
// But auto_start will be set false if the p3d file has not been
|
||||
// signed by an approved signature. TODO.
|
||||
|
||||
if (_hidden && _got_wparams) {
|
||||
_wparams.set_window_type(P3D_WT_hidden);
|
||||
|
|
@ -1284,9 +1418,10 @@ make_splash_window() {
|
|||
return;
|
||||
}
|
||||
if (_wparams.get_window_type() != P3D_WT_embedded &&
|
||||
!_stuff_to_download && _auto_start) {
|
||||
!_stuff_to_download && _auto_start && _p3d_trusted) {
|
||||
// If it's a toplevel or fullscreen window, then we don't want a
|
||||
// splash window until we have stuff to download.
|
||||
// splash window unless we have stuff to download, or a button to
|
||||
// display.
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1296,10 +1431,6 @@ make_splash_window() {
|
|||
|
||||
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
|
||||
|
||||
// Direct the "download" image to the background slot on the splash
|
||||
// window for now, while we perform the download.
|
||||
set_background_image(IT_download);
|
||||
|
||||
// Go get the required images.
|
||||
bool any_standard_images = false;
|
||||
for (int i = 0; i < (int)IT_none; ++i) {
|
||||
|
|
@ -1344,6 +1475,10 @@ make_splash_window() {
|
|||
_image_package->add_instance(this);
|
||||
}
|
||||
}
|
||||
|
||||
if (_current_button_image != IT_none) {
|
||||
_splash_window->set_button_active(true);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -1357,6 +1492,7 @@ make_splash_window() {
|
|||
void P3DInstance::
|
||||
set_background_image(ImageType image_type) {
|
||||
if (image_type != _current_background_image) {
|
||||
nout << "setting background to " << _image_type_names[image_type] << "\n";
|
||||
// Remove the previous image.
|
||||
_image_files[_current_background_image]._image_placement = P3DSplashWindow::IP_none;
|
||||
|
||||
|
|
@ -1387,6 +1523,7 @@ set_background_image(ImageType image_type) {
|
|||
void P3DInstance::
|
||||
set_button_image(ImageType image_type) {
|
||||
if (image_type != _current_button_image) {
|
||||
nout << "setting button to " << _image_type_names[image_type] << "\n";
|
||||
// Remove the previous image.
|
||||
_image_files[_current_button_image]._image_placement = P3DSplashWindow::IP_none;
|
||||
if (_current_button_image != IT_none) {
|
||||
|
|
@ -1408,10 +1545,20 @@ set_button_image(ImageType image_type) {
|
|||
_splash_window->set_image_filename(_image_files[_current_button_image]._filename, P3DSplashWindow::IP_button_ready);
|
||||
_splash_window->set_image_filename(_image_files[_current_button_image + 1]._filename, P3DSplashWindow::IP_button_rollover);
|
||||
_splash_window->set_image_filename(_image_files[_current_button_image + 2]._filename, P3DSplashWindow::IP_button_click);
|
||||
_splash_window->set_button_active(true);
|
||||
} else {
|
||||
_splash_window->set_image_filename(string(), P3DSplashWindow::IP_button_ready);
|
||||
_splash_window->set_image_filename(string(), P3DSplashWindow::IP_button_rollover);
|
||||
_splash_window->set_image_filename(string(), P3DSplashWindow::IP_button_click);
|
||||
_splash_window->set_button_active(false);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// We're not changing the button graphic, but we might be
|
||||
// re-activating it.
|
||||
if (_splash_window != NULL) {
|
||||
if (_current_button_image != IT_none) {
|
||||
_splash_window->set_button_active(true);
|
||||
} else {
|
||||
_splash_window->set_button_active(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1532,7 +1679,7 @@ start_next_download() {
|
|||
_splash_window->set_install_label("");
|
||||
}
|
||||
|
||||
if (_got_wparams && _got_fparams) {
|
||||
if (_got_wparams && _p3d_trusted) {
|
||||
ready_to_start();
|
||||
}
|
||||
}
|
||||
|
|
@ -1556,7 +1703,6 @@ ready_to_start() {
|
|||
// We're fully downloaded, and waiting for the user to click play.
|
||||
set_background_image(IT_ready);
|
||||
set_button_image(IT_play_ready);
|
||||
_splash_window->set_button_active(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include "p3dReferenceCount.h"
|
||||
#include "p3dSplashWindow.h"
|
||||
#include "p3dTemporaryFile.h"
|
||||
#include "p3dMultifileReader.h"
|
||||
#include "get_tinyxml.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
|
@ -101,6 +102,7 @@ public:
|
|||
|
||||
TiXmlElement *make_xml();
|
||||
void splash_button_clicked();
|
||||
void auth_button_clicked();
|
||||
void play_button_clicked();
|
||||
|
||||
private:
|
||||
|
|
@ -126,6 +128,7 @@ private:
|
|||
// The different kinds of image files we download for the splash
|
||||
// window.
|
||||
enum ImageType {
|
||||
// Also update _image_type_names when you update this list.
|
||||
IT_download,
|
||||
IT_ready,
|
||||
IT_failed,
|
||||
|
|
@ -133,10 +136,17 @@ private:
|
|||
IT_play_ready,
|
||||
IT_play_rollover,
|
||||
IT_play_click,
|
||||
IT_unauth,
|
||||
IT_auth_ready,
|
||||
IT_auth_rollover,
|
||||
IT_auth_click,
|
||||
IT_none, // Must be the last value
|
||||
IT_num_image_types, // Not a real value
|
||||
};
|
||||
|
||||
bool check_p3d_signature();
|
||||
void mark_p3d_untrusted();
|
||||
void mark_p3d_trusted();
|
||||
void scan_app_desc_file(TiXmlDocument *doc);
|
||||
|
||||
void send_browser_script_object();
|
||||
|
|
@ -192,16 +202,20 @@ private:
|
|||
|
||||
bool _got_fparams;
|
||||
P3DFileParams _fparams;
|
||||
P3DMultifileReader _mf_reader;
|
||||
|
||||
bool _got_wparams;
|
||||
P3DWindowParams _wparams;
|
||||
|
||||
bool _p3d_trusted;
|
||||
|
||||
int _instance_id;
|
||||
string _session_key;
|
||||
string _log_basename;
|
||||
bool _hidden;
|
||||
bool _allow_python_dev;
|
||||
bool _auto_start;
|
||||
bool _auth_button_approved;
|
||||
|
||||
P3DSession *_session;
|
||||
|
||||
|
|
|
|||
|
|
@ -105,17 +105,21 @@ get_log_pathname() const {
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DInstanceManager::get_keep_cwd
|
||||
// Function: P3DInstanceManager::get_trusted_environment
|
||||
// Access: Public
|
||||
// Description: Returns the value of the keep_cwd flag passed to the
|
||||
// constructor. This is true if the original working
|
||||
// directory was valuable and meaningful, and should be
|
||||
// preserved; or false if it is meaningless and should
|
||||
// be changed.
|
||||
// Description: Returns the value of the trusted_environment flag
|
||||
// passed to the constructor. If this is true, it means
|
||||
// the environment we are running in is trusted and the
|
||||
// p3d file is already vetted. This means the current
|
||||
// working directory will remain unchanged, and the p3d
|
||||
// file will be run without checking its signature.
|
||||
//
|
||||
// This should generally be true only when run by
|
||||
// panda3d.exe, and not when run by the web plugin.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
inline bool P3DInstanceManager::
|
||||
get_keep_cwd() const {
|
||||
return _keep_cwd;
|
||||
get_trusted_environment() const {
|
||||
return _trusted_environment;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -165,3 +169,17 @@ new_bool_object(bool value) {
|
|||
P3D_OBJECT_INCREF(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DInstanceManager::encode_hexdigit
|
||||
// Access: Public
|
||||
// Description: Returns the hex digit corresponding to the
|
||||
// indicated integer value.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
inline char P3DInstanceManager::
|
||||
encode_hexdigit(int c) {
|
||||
if (c >= 10) {
|
||||
return c - 10 + 'a';
|
||||
}
|
||||
return c + '0';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,8 +35,11 @@
|
|||
#else
|
||||
#include <sys/stat.h>
|
||||
#include <signal.h>
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static ofstream logfile;
|
||||
ostream *nout_stream = &logfile;
|
||||
|
||||
|
|
@ -52,7 +55,7 @@ P3DInstanceManager() {
|
|||
_is_initialized = false;
|
||||
_next_temp_filename_counter = 1;
|
||||
_unique_id = 0;
|
||||
_keep_cwd = false;
|
||||
_trusted_environment = false;
|
||||
|
||||
_notify_thread_continue = false;
|
||||
_started_notify_thread = false;
|
||||
|
|
@ -157,8 +160,8 @@ bool P3DInstanceManager::
|
|||
initialize(const string &contents_filename, const string &download_url,
|
||||
bool verify_contents,
|
||||
const string &platform, const string &log_directory,
|
||||
const string &log_basename, bool keep_cwd) {
|
||||
_keep_cwd = keep_cwd;
|
||||
const string &log_basename, bool trusted_environment) {
|
||||
_trusted_environment = trusted_environment;
|
||||
_root_dir = find_root_dir();
|
||||
_verify_contents = verify_contents;
|
||||
_platform = platform;
|
||||
|
|
@ -256,6 +259,12 @@ initialize(const string &contents_filename, const string &download_url,
|
|||
return false;
|
||||
}
|
||||
|
||||
// Make the certificate directory.
|
||||
_certs_dir = _root_dir + "/certs";
|
||||
if (!mkdir_complete(_certs_dir, nout)) {
|
||||
nout << "Couldn't mkdir " << _certs_dir << "\n";
|
||||
}
|
||||
|
||||
_is_initialized = true;
|
||||
|
||||
if (!_verify_contents &&
|
||||
|
|
@ -645,6 +654,176 @@ release_temp_filename(const string &filename) {
|
|||
unlink(filename.c_str());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DInstanceManager::find_cert
|
||||
// Access: Public
|
||||
// Description: Looks for the particular certificate in the cache of
|
||||
// recognized certificates. Returns true if it is
|
||||
// found, false if not.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool P3DInstanceManager::
|
||||
find_cert(X509 *cert) {
|
||||
// First, we need the DER representation.
|
||||
string der = cert_to_der(cert);
|
||||
|
||||
// If we've previously found this certificate, we don't have to hit
|
||||
// disk again.
|
||||
ApprovedCerts::iterator ci = _approved_certs.find(der);
|
||||
if (ci != _approved_certs.end()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Well, we haven't found it already. Look for it on disk. For
|
||||
// this, we hash the cert into a hex string. This is similar to
|
||||
// OpenSSL's get_by_subject() approach, except we hash the whole
|
||||
// cert, not just the subject. (Since we also store self-signed
|
||||
// certs in this list, we can't trust the subject name alone.)
|
||||
|
||||
static const size_t hash_size = 16;
|
||||
unsigned char md[hash_size];
|
||||
|
||||
MD5_CTX ctx;
|
||||
MD5_Init(&ctx);
|
||||
MD5_Update(&ctx, der.data(), der.size());
|
||||
MD5_Final(md, &ctx);
|
||||
|
||||
string basename;
|
||||
static const size_t keep_hash = 6;
|
||||
for (size_t i = 0; i < keep_hash; ++i) {
|
||||
int high = (md[i] >> 4) & 0xf;
|
||||
int low = md[i] & 0xf;
|
||||
basename += P3DInstanceManager::encode_hexdigit(high);
|
||||
basename += P3DInstanceManager::encode_hexdigit(low);
|
||||
}
|
||||
|
||||
string this_cert_dir = _certs_dir + "/" + basename;
|
||||
nout << "looking in " << this_cert_dir << "\n";
|
||||
|
||||
vector<string> contents;
|
||||
scan_directory(this_cert_dir, contents);
|
||||
|
||||
// Now look at each of the files in this directory and see if any of
|
||||
// them matches the certificate.
|
||||
vector<string>::iterator si;
|
||||
for (si = contents.begin(); si != contents.end(); ++si) {
|
||||
string filename = this_cert_dir + "/" + (*si);
|
||||
X509 *x509 = NULL;
|
||||
FILE *fp = fopen(filename.c_str(), "r");
|
||||
if (fp != NULL) {
|
||||
x509 = PEM_read_X509(fp, NULL, NULL, (void *)"");
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
if (x509 != NULL) {
|
||||
string der2 = cert_to_der(x509);
|
||||
// We might as well save this cert in the table for next time,
|
||||
// even if it's not the one we're looking for right now.
|
||||
_approved_certs.insert(der2);
|
||||
|
||||
if (der == der2) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing matched.
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DInstanceManager::cert_to_der
|
||||
// Access: Public, Static
|
||||
// Description: Converts the indicated certificate to its binary DER
|
||||
// representation.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
string P3DInstanceManager::
|
||||
cert_to_der(X509 *cert) {
|
||||
int buffer_size = i2d_X509(cert, NULL);
|
||||
unsigned char *buffer = new unsigned char[buffer_size];
|
||||
unsigned char *p = buffer;
|
||||
i2d_X509(cert, &p);
|
||||
|
||||
string result((char *)buffer, buffer_size);
|
||||
delete[] buffer;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DInstanceManager::scan_directory
|
||||
// Access: Public, Static
|
||||
// Description: Attempts to open the named filename as if it were a
|
||||
// directory and looks for the non-hidden files within
|
||||
// the directory. Fills the given vector up with the
|
||||
// sorted list of filenames that are local to this
|
||||
// directory.
|
||||
//
|
||||
// It is the user's responsibility to ensure that the
|
||||
// contents vector is empty before making this call;
|
||||
// otherwise, the new files will be appended to it.
|
||||
//
|
||||
// Returns true on success, false if the directory could
|
||||
// not be read for some reason.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool P3DInstanceManager::
|
||||
scan_directory(const string &dirname, vector<string> &contents) {
|
||||
#ifdef _WIN32
|
||||
// Use Windows' FindFirstFile() / FindNextFile() to walk through the
|
||||
// list of files in a directory.
|
||||
size_t orig_size = contents.size();
|
||||
|
||||
string match = dirname + "\\*.*";
|
||||
WIN32_FIND_DATA find_data;
|
||||
|
||||
HANDLE handle = FindFirstFile(match.c_str(), &find_data);
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
if (GetLastError() == ERROR_NO_MORE_FILES) {
|
||||
// No matching files is not an error.
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
do {
|
||||
string filename = find_data.cFileName;
|
||||
if (filename != "." && filename != "..") {
|
||||
contents.push_back(filename);
|
||||
}
|
||||
} while (FindNextFile(handle, &find_data));
|
||||
|
||||
bool scan_ok = (GetLastError() == ERROR_NO_MORE_FILES);
|
||||
FindClose(handle);
|
||||
|
||||
sort(contents.begin() + orig_size, contents.end());
|
||||
return scan_ok;
|
||||
|
||||
#else // _WIN32
|
||||
// Use Posix's opendir() / readdir() to walk through the list of
|
||||
// files in a directory.
|
||||
size_t orig_size = contents.size();
|
||||
|
||||
DIR *root = opendir(dirname.c_str());
|
||||
if (root == (DIR *)NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct dirent *d;
|
||||
d = readdir(root);
|
||||
while (d != (struct dirent *)NULL) {
|
||||
if (d->d_name[0] != '.') {
|
||||
contents.push_back(d->d_name);
|
||||
}
|
||||
d = readdir(root);
|
||||
}
|
||||
|
||||
closedir(root);
|
||||
|
||||
sort(contents.begin() + orig_size, contents.end());
|
||||
return true;
|
||||
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DInstanceManager::get_global_ptr
|
||||
// Access: Public, Static
|
||||
|
|
|
|||
|
|
@ -26,6 +26,11 @@
|
|||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
#define OPENSSL_NO_KRB5
|
||||
#include "openssl/x509.h"
|
||||
#include "openssl/pem.h"
|
||||
#include "openssl/md5.h"
|
||||
|
||||
class P3DInstance;
|
||||
class P3DSession;
|
||||
class P3DHost;
|
||||
|
|
@ -49,7 +54,7 @@ public:
|
|||
const string &platform,
|
||||
const string &log_directory,
|
||||
const string &log_basename,
|
||||
bool keep_cwd);
|
||||
bool trusted_environment);
|
||||
|
||||
inline bool is_initialized() const;
|
||||
inline bool get_verify_contents() const;
|
||||
|
|
@ -59,7 +64,7 @@ public:
|
|||
inline const string &get_platform() const;
|
||||
inline const string &get_log_directory() const;
|
||||
inline const string &get_log_pathname() const;
|
||||
inline bool get_keep_cwd() const;
|
||||
inline bool get_trusted_environment() const;
|
||||
|
||||
P3DInstance *
|
||||
create_instance(P3D_request_ready_func *func,
|
||||
|
|
@ -92,9 +97,14 @@ public:
|
|||
string make_temp_filename(const string &extension);
|
||||
void release_temp_filename(const string &filename);
|
||||
|
||||
bool find_cert(X509 *cert);
|
||||
static string cert_to_der(X509 *cert);
|
||||
|
||||
static P3DInstanceManager *get_global_ptr();
|
||||
static void delete_global_ptr();
|
||||
|
||||
static inline char encode_hexdigit(int c);
|
||||
static bool scan_directory(const string &dirname, vector<string> &contents);
|
||||
private:
|
||||
// The notify thread. This thread runs only for the purpose of
|
||||
// generating asynchronous notifications of requests, to callers who
|
||||
|
|
@ -105,19 +115,23 @@ private:
|
|||
private:
|
||||
bool _is_initialized;
|
||||
string _root_dir;
|
||||
string _certs_dir;
|
||||
bool _verify_contents;
|
||||
string _platform;
|
||||
string _log_directory;
|
||||
string _log_basename;
|
||||
string _log_pathname;
|
||||
string _temp_directory;
|
||||
bool _keep_cwd;
|
||||
bool _trusted_environment;
|
||||
|
||||
P3D_object *_undefined_object;
|
||||
P3D_object *_none_object;
|
||||
P3D_object *_true_object;
|
||||
P3D_object *_false_object;
|
||||
|
||||
typedef set<string> ApprovedCerts;
|
||||
ApprovedCerts _approved_certs;
|
||||
|
||||
typedef set<P3DInstance *> Instances;
|
||||
Instances _instances;
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,16 @@
|
|||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DMultifileReader::is_open
|
||||
// Access: Public
|
||||
// Description: Returns true if the reader is open, false otherwise.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
inline bool P3DMultifileReader::
|
||||
is_open() const {
|
||||
return _is_open;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DMultifileReader::read_uint16
|
||||
// Access: Private
|
||||
|
|
@ -38,3 +48,49 @@ read_uint32() {
|
|||
unsigned int d = _in.get();
|
||||
return (d << 24) | (c << 16) | (b << 8) | a;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DMultifileReader::get_last_byte_pos
|
||||
// Access: Public
|
||||
// Description: Returns the byte position within the Multifile of the
|
||||
// last byte that contributes to this Subfile, either in
|
||||
// the index record or in the subfile data.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
inline size_t P3DMultifileReader::Subfile::
|
||||
get_last_byte_pos() const {
|
||||
return max(_index_start + _index_length, _data_start + _data_length) - 1;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DMultifileReader::CertRecord::Constructor
|
||||
// Access: Public
|
||||
// Description: Ownership of the X509 object is passed into the
|
||||
// CertRecord; it will be freed when the CertRecord
|
||||
// destructs.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
inline P3DMultifileReader::CertRecord::
|
||||
CertRecord(X509 *cert) :
|
||||
_cert(cert)
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DMultifileReader::CertRecord::Copy Constructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
inline P3DMultifileReader::CertRecord::
|
||||
CertRecord(const P3DMultifileReader::CertRecord ©) :
|
||||
_cert(X509_dup(copy._cert))
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DMultifileReader::CertRecord::Destructor
|
||||
// Access: Public
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
inline P3DMultifileReader::CertRecord::
|
||||
~CertRecord() {
|
||||
X509_free(_cert);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,15 +45,46 @@ const int P3DMultifileReader::_current_minor_ver = 1;
|
|||
////////////////////////////////////////////////////////////////////
|
||||
P3DMultifileReader::
|
||||
P3DMultifileReader() {
|
||||
_is_open = false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DMultifileReader::open_read
|
||||
// Access: Public
|
||||
// Description: Opens the indicated file for reading. Returns true
|
||||
// on success, false on failure.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool P3DMultifileReader::
|
||||
open_read(const string &pathname) {
|
||||
if (_is_open) {
|
||||
close();
|
||||
}
|
||||
if (!read_header(pathname)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_is_open = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DMultifileReader::close
|
||||
// Access: Public
|
||||
// Description: Closes the previously-opened file.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DMultifileReader::
|
||||
close() {
|
||||
_in.close();
|
||||
_is_open = false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DMultifileReader::extract_all
|
||||
// Access: Public
|
||||
// Description: Reads the named multifile, and extracts all the
|
||||
// expected extractable components within it to the
|
||||
// indicated directory. Returns true on success, false
|
||||
// on failure.
|
||||
// Description: Reads the multifile, and extracts all the expected
|
||||
// extractable components within it to the indicated
|
||||
// directory. Returns true on success, false on
|
||||
// failure.
|
||||
//
|
||||
// The parameters package, start_progress, and
|
||||
// progress_size are provided to make the appropriate
|
||||
|
|
@ -61,9 +92,10 @@ P3DMultifileReader() {
|
|||
// during this operation.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool P3DMultifileReader::
|
||||
extract_all(const string &pathname, const string &to_dir,
|
||||
extract_all(const string &to_dir,
|
||||
P3DPackage *package, double start_progress, double progress_size) {
|
||||
if (!read_header(pathname)) {
|
||||
assert(_is_open);
|
||||
if (_in.fail()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -118,13 +150,14 @@ extract_all(const string &pathname, const string &to_dir,
|
|||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DMultifileReader::extract_one
|
||||
// Access: Public
|
||||
// Description: Reads the named multifile, and extracts only the
|
||||
// named component to the indicated stream. Returns
|
||||
// true on success, false on failure.
|
||||
// Description: Reads the multifile, and extracts only the named
|
||||
// component to the indicated stream. Returns true on
|
||||
// success, false on failure.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool P3DMultifileReader::
|
||||
extract_one(const string &pathname, ostream &out, const string &filename) {
|
||||
if (!read_header(pathname)) {
|
||||
extract_one(ostream &out, const string &filename) {
|
||||
assert(_is_open);
|
||||
if (_in.fail()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -141,6 +174,42 @@ extract_one(const string &pathname, ostream &out, const string &filename) {
|
|||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DMultifileReader::get_num_signatures
|
||||
// Access: Published
|
||||
// Description: Returns the number of matching signatures found on
|
||||
// the Multifile. These signatures may be iterated via
|
||||
// get_signature() and related methods.
|
||||
//
|
||||
// A signature on this list is guaranteed to match the
|
||||
// Multifile contents, proving that the Multifile has
|
||||
// been unmodified since the signature was applied.
|
||||
// However, this does not guarantee that the certificate
|
||||
// itself is actually from who it says it is from; only
|
||||
// that it matches the Multifile contents. See
|
||||
// validate_signature_certificate() to authenticate a
|
||||
// particular certificate.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
int P3DMultifileReader::
|
||||
get_num_signatures() const {
|
||||
assert(_is_open);
|
||||
((P3DMultifileReader *)this)->check_signatures();
|
||||
return _signatures.size();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DMultifileReader::get_signature
|
||||
// Access: Published
|
||||
// Description: Returns the nth signature found on the Multifile.
|
||||
// See the comments in get_num_signatures().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
const P3DMultifileReader::CertChain &P3DMultifileReader::
|
||||
get_signature(int n) const {
|
||||
static CertChain error_chain;
|
||||
assert(n >= 0 && n < (int)_signatures.size());
|
||||
return _signatures[n];
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DMultifileReader::read_header
|
||||
// Access: Private
|
||||
|
|
@ -150,7 +219,10 @@ extract_one(const string &pathname, ostream &out, const string &filename) {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
bool P3DMultifileReader::
|
||||
read_header(const string &pathname) {
|
||||
assert(!_is_open);
|
||||
_subfiles.clear();
|
||||
_cert_special.clear();
|
||||
_signatures.clear();
|
||||
|
||||
_in.open(pathname.c_str(), ios::in | ios::binary);
|
||||
if (!_in) {
|
||||
|
|
@ -228,16 +300,19 @@ read_header(const string &pathname) {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
bool P3DMultifileReader::
|
||||
read_index() {
|
||||
_last_data_byte = 0;
|
||||
unsigned int next_entry = read_uint32();
|
||||
if (!_in) {
|
||||
return false;
|
||||
}
|
||||
while (next_entry != 0) {
|
||||
Subfile s;
|
||||
s._start = read_uint32();
|
||||
s._length = read_uint32();
|
||||
s._index_start = (size_t)_in.tellg();
|
||||
s._index_length = 0;
|
||||
s._data_start = read_uint32();
|
||||
s._data_length = read_uint32();
|
||||
unsigned int flags = read_uint16();
|
||||
if ((flags & 0x18) != 0) {
|
||||
if ((flags & (SF_compressed | SF_encrypted)) != 0) {
|
||||
// Skip over the uncompressed length.
|
||||
read_uint32();
|
||||
}
|
||||
|
|
@ -255,9 +330,19 @@ read_index() {
|
|||
s._filename = string(buffer, name_length);
|
||||
delete[] buffer;
|
||||
|
||||
if (flags == 0) {
|
||||
// We can only support subfiles with no particular flags set.
|
||||
_subfiles.push_back(s);
|
||||
s._index_length = (size_t)_in.tellg() - s._index_start;
|
||||
|
||||
if (flags & SF_signature) {
|
||||
// A subfile with this bit set is a signature.
|
||||
_cert_special.push_back(s);
|
||||
} else {
|
||||
// Otherwise, it's a regular file.
|
||||
_last_data_byte = max(_last_data_byte, s.get_last_byte_pos());
|
||||
|
||||
if (flags == 0) {
|
||||
// We can only support subfiles with no particular flags set.
|
||||
_subfiles.push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
_in.seekg(next_entry);
|
||||
|
|
@ -279,12 +364,12 @@ read_index() {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
bool P3DMultifileReader::
|
||||
extract_subfile(ostream &out, const Subfile &s) {
|
||||
_in.seekg(s._start);
|
||||
_in.seekg(s._data_start);
|
||||
|
||||
static const size_t buffer_size = 1024;
|
||||
static const size_t buffer_size = 4096;
|
||||
char buffer[buffer_size];
|
||||
|
||||
size_t remaining_data = s._length;
|
||||
size_t remaining_data = s._data_length;
|
||||
_in.read(buffer, min(buffer_size, remaining_data));
|
||||
size_t count = _in.gcount();
|
||||
while (count != 0) {
|
||||
|
|
@ -302,3 +387,120 @@ extract_subfile(ostream &out, const Subfile &s) {
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DMultifileReader::check_signatures
|
||||
// Access: Private
|
||||
// Description: Walks through the list of _cert_special entries in
|
||||
// the Multifile, moving any valid signatures found to
|
||||
// _signatures. After this call, _cert_special will be
|
||||
// empty.
|
||||
//
|
||||
// This does not check the validity of the certificates
|
||||
// themselves. It only checks that they correctly sign
|
||||
// the Multifile contents.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void P3DMultifileReader::
|
||||
check_signatures() {
|
||||
Subfiles::iterator pi;
|
||||
|
||||
for (pi = _cert_special.begin(); pi != _cert_special.end(); ++pi) {
|
||||
Subfile *subfile = &(*pi);
|
||||
|
||||
// Extract the signature data and certificate separately.
|
||||
_in.seekg(subfile->_data_start);
|
||||
size_t sig_size = read_uint32();
|
||||
char *sig = new char[sig_size];
|
||||
_in.read(sig, sig_size);
|
||||
if (_in.gcount() != sig_size) {
|
||||
nout << "read failure\n";
|
||||
delete[] sig;
|
||||
return;
|
||||
}
|
||||
|
||||
size_t num_certs = read_uint32();
|
||||
|
||||
// Read the remaining buffer of certificate data.
|
||||
size_t bytes_read = (size_t)_in.tellg() - subfile->_data_start;
|
||||
size_t buffer_size = subfile->_data_length - bytes_read;
|
||||
char *buffer = new char[buffer_size];
|
||||
_in.read(buffer, buffer_size);
|
||||
if (_in.gcount() != buffer_size) {
|
||||
nout << "read failure\n";
|
||||
delete[] sig;
|
||||
delete[] buffer;
|
||||
return;
|
||||
}
|
||||
|
||||
// Now convert each of the certificates to an X509 object, and
|
||||
// store it in our CertChain.
|
||||
CertChain chain;
|
||||
EVP_PKEY *pkey = NULL;
|
||||
if (buffer_size > 0) {
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x00908000L
|
||||
// Beginning in 0.9.8, d2i_X509() accepted a const unsigned char **.
|
||||
const unsigned char *bp, *bp_end;
|
||||
#else
|
||||
// Prior to 0.9.8, d2i_X509() accepted an unsigned char **.
|
||||
unsigned char *bp, *bp_end;
|
||||
#endif
|
||||
bp = (unsigned char *)&buffer[0];
|
||||
bp_end = bp + buffer_size;
|
||||
X509 *x509 = d2i_X509(NULL, &bp, bp_end - bp);
|
||||
while (num_certs > 0 && x509 != NULL) {
|
||||
chain.push_back(CertRecord(x509));
|
||||
--num_certs;
|
||||
x509 = d2i_X509(NULL, &bp, bp_end - bp);
|
||||
}
|
||||
if (num_certs != 0 || x509 != NULL) {
|
||||
nout << "Extra data in signature record.\n";
|
||||
}
|
||||
}
|
||||
|
||||
delete[] buffer;
|
||||
|
||||
if (!chain.empty()) {
|
||||
pkey = X509_get_pubkey(chain[0]._cert);
|
||||
}
|
||||
|
||||
if (pkey != NULL) {
|
||||
EVP_MD_CTX *md_ctx;
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x00907000L
|
||||
md_ctx = EVP_MD_CTX_create();
|
||||
#else
|
||||
md_ctx = new EVP_MD_CTX;
|
||||
#endif
|
||||
EVP_VerifyInit(md_ctx, EVP_sha1());
|
||||
|
||||
// Read and hash the multifile contents, but only up till
|
||||
// _last_data_byte.
|
||||
_in.seekg(0);
|
||||
streampos bytes_remaining = (streampos)_last_data_byte;
|
||||
static const size_t buffer_size = 4096;
|
||||
char buffer[buffer_size];
|
||||
_in.read(buffer, min((streampos)buffer_size, bytes_remaining));
|
||||
size_t count = _in.gcount();
|
||||
while (count != 0) {
|
||||
assert(count <= buffer_size);
|
||||
EVP_VerifyUpdate(md_ctx, buffer, count);
|
||||
bytes_remaining -= count;
|
||||
_in.read(buffer, min((streampos)buffer_size, bytes_remaining));
|
||||
count = _in.gcount();
|
||||
}
|
||||
assert(bytes_remaining == (streampos)0);
|
||||
|
||||
// Now check that the signature matches the hash.
|
||||
int verify_result =
|
||||
EVP_VerifyFinal(md_ctx, (unsigned char *)sig,
|
||||
sig_size, pkey);
|
||||
if (verify_result == 1) {
|
||||
// The signature matches; save the certificate and its chain.
|
||||
_signatures.push_back(chain);
|
||||
}
|
||||
}
|
||||
|
||||
delete[] sig;
|
||||
}
|
||||
|
||||
_cert_special.clear();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#define P3DMULTIFILEREADER_H
|
||||
|
||||
#include "p3d_plugin_common.h"
|
||||
#include "p3dInstanceManager.h" // for openssl
|
||||
|
||||
class P3DPackage;
|
||||
|
||||
|
|
@ -30,13 +31,26 @@ class P3DPackage;
|
|||
class P3DMultifileReader {
|
||||
public:
|
||||
P3DMultifileReader();
|
||||
bool open_read(const string &pathname);
|
||||
inline bool is_open() const;
|
||||
void close();
|
||||
|
||||
bool extract_all(const string &pathname, const string &to_dir,
|
||||
bool extract_all(const string &to_dir,
|
||||
P3DPackage *package, double start_progress,
|
||||
double progress_size);
|
||||
|
||||
bool extract_one(const string &pathname, ostream &out,
|
||||
const string &filename);
|
||||
bool extract_one(ostream &out, const string &filename);
|
||||
|
||||
class CertRecord {
|
||||
public:
|
||||
inline CertRecord(X509 *cert);
|
||||
inline CertRecord(const CertRecord ©);
|
||||
inline ~CertRecord();
|
||||
X509 *_cert;
|
||||
};
|
||||
typedef vector<CertRecord> CertChain;
|
||||
int get_num_signatures() const;
|
||||
const CertChain &get_signature(int n) const;
|
||||
|
||||
private:
|
||||
class Subfile;
|
||||
|
|
@ -45,21 +59,39 @@ private:
|
|||
bool read_index();
|
||||
bool extract_subfile(ostream &out, const Subfile &s);
|
||||
|
||||
void check_signatures();
|
||||
|
||||
inline unsigned int read_uint16();
|
||||
inline unsigned int read_uint32();
|
||||
|
||||
ifstream _in;
|
||||
enum SubfileFlags {
|
||||
SF_compressed = 0x0008,
|
||||
SF_encrypted = 0x0010,
|
||||
SF_signature = 0x0020,
|
||||
};
|
||||
|
||||
class Subfile {
|
||||
public:
|
||||
inline size_t get_last_byte_pos() const;
|
||||
|
||||
string _filename;
|
||||
size_t _start;
|
||||
size_t _length;
|
||||
size_t _index_start;
|
||||
size_t _index_length;
|
||||
size_t _data_start;
|
||||
size_t _data_length;
|
||||
size_t _timestamp;
|
||||
};
|
||||
|
||||
ifstream _in;
|
||||
bool _is_open;
|
||||
|
||||
typedef vector<Subfile> Subfiles;
|
||||
Subfiles _subfiles;
|
||||
Subfiles _cert_special;
|
||||
size_t _last_data_byte;
|
||||
|
||||
typedef vector<CertChain> Certificates;
|
||||
Certificates _signatures;
|
||||
|
||||
static const char _header[];
|
||||
static const size_t _header_size;
|
||||
|
|
|
|||
|
|
@ -702,8 +702,15 @@ void P3DPackage::
|
|||
extract_archive() {
|
||||
string source_pathname = _package_dir + "/" + _uncompressed_archive.get_filename();
|
||||
P3DMultifileReader reader;
|
||||
if (!reader.extract_all(source_pathname, _package_dir,
|
||||
this, download_portion + uncompress_portion, extract_portion)) {
|
||||
if (!reader.open_read(source_pathname)) {
|
||||
nout << "Couldn't read " << _uncompressed_archive.get_filename() << "\n";
|
||||
report_done(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!reader.extract_all(_package_dir, this,
|
||||
download_portion + uncompress_portion,
|
||||
extract_portion)) {
|
||||
nout << "Failure extracting " << _uncompressed_archive.get_filename()
|
||||
<< "\n";
|
||||
report_done(false);
|
||||
|
|
|
|||
|
|
@ -661,7 +661,7 @@ start_p3dpython(P3DInstance *inst) {
|
|||
// Change the current directory to the standard start directory, but
|
||||
// only if the runtime environment told us the original current
|
||||
// directory isn't meaningful.
|
||||
_use_start_dir = !inst_mgr->get_keep_cwd();
|
||||
_use_start_dir = !inst_mgr->get_trusted_environment();
|
||||
if (_use_start_dir) {
|
||||
mkdir_complete(_start_dir, nout);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -410,6 +410,7 @@ set_mouse_data(int mouse_x, int mouse_y, bool mouse_down) {
|
|||
_mouse_down = mouse_down;
|
||||
|
||||
ButtonState bstate = BS_hidden;
|
||||
bool click_detected = false;
|
||||
|
||||
if (!_button_active) {
|
||||
// The button isn't active, so it's hidden, regardless of the
|
||||
|
|
@ -439,7 +440,7 @@ set_mouse_data(int mouse_x, int mouse_y, bool mouse_down) {
|
|||
// after a successful click.
|
||||
bstate = BS_hidden;
|
||||
_button_active = false;
|
||||
button_click_detected();
|
||||
click_detected = true;
|
||||
} else {
|
||||
bstate = BS_rollover;
|
||||
}
|
||||
|
|
@ -455,6 +456,13 @@ set_mouse_data(int mouse_x, int mouse_y, bool mouse_down) {
|
|||
}
|
||||
|
||||
set_bstate(bstate);
|
||||
|
||||
// If we detected a click operation in the above, make the callback
|
||||
// here, at the end of the method, after we have finished updating
|
||||
// the button state.
|
||||
if (click_detected) {
|
||||
button_click_detected();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ P3D_initialize(int api_version, const char *contents_filename,
|
|||
const char *download_url, bool verify_contents,
|
||||
const char *platform,
|
||||
const char *log_directory, const char *log_basename,
|
||||
bool keep_cwd) {
|
||||
bool trusted_environment) {
|
||||
if (api_version != P3D_API_VERSION) {
|
||||
// Can't accept an incompatible version.
|
||||
return false;
|
||||
|
|
@ -74,7 +74,7 @@ P3D_initialize(int api_version, const char *contents_filename,
|
|||
bool result = inst_mgr->initialize(contents_filename, download_url,
|
||||
verify_contents, platform,
|
||||
log_directory, log_basename,
|
||||
keep_cwd);
|
||||
trusted_environment);
|
||||
RELEASE_LOCK(_api_lock);
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,11 +122,11 @@ extern "C" {
|
|||
core API. Note that the individual instances also have their own
|
||||
log_basename values.
|
||||
|
||||
Finally, keep_cwd should be set true if the current working
|
||||
directory is meaningful and valuable to the user (for instance,
|
||||
when this is launched via a command-line tool), or false if it
|
||||
means nothing and can safely be reset. Normally, a browser plugin
|
||||
should set this false.
|
||||
Finally, trusted_environment should be set true to indicate that
|
||||
the environment and p3d file are already trusted. If this is set,
|
||||
the current working directory will remain unchanged, and the p3d
|
||||
file will be run without checking its signature. Normally, a
|
||||
browser plugin should set this false.
|
||||
|
||||
This function returns true if the core API is valid and uses a
|
||||
compatible API, false otherwise. If it returns false, the host
|
||||
|
|
@ -137,7 +137,7 @@ P3D_initialize_func(int api_version, const char *contents_filename,
|
|||
const char *download_url, bool verify_contents,
|
||||
const char *platform,
|
||||
const char *log_directory, const char *log_basename,
|
||||
bool keep_cwd);
|
||||
bool trusted_environment);
|
||||
|
||||
/* This function should be called to unload the core API. It will
|
||||
release all internally-allocated memory and return the core API to
|
||||
|
|
|
|||
Loading…
Reference in New Issue