|
|
@@ -0,0 +1,404 @@
|
|
|
+// Filename: P3DAuthSession.cxx
|
|
|
+// Created by: drose (17Sep09)
|
|
|
+//
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+//
|
|
|
+// 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 "p3dAuthSession.h"
|
|
|
+#include "p3dInstance.h"
|
|
|
+#include "p3dInstanceManager.h"
|
|
|
+#include "p3dMultifileReader.h"
|
|
|
+#include "p3d_plugin_config.h"
|
|
|
+#include "mkdir_complete.h"
|
|
|
+
|
|
|
+#include <ctype.h>
|
|
|
+
|
|
|
+#ifndef _WIN32
|
|
|
+#include <fcntl.h>
|
|
|
+#include <sys/wait.h>
|
|
|
+#include <sys/select.h>
|
|
|
+#include <signal.h>
|
|
|
+#include <dlfcn.h>
|
|
|
+#endif
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: P3DAuthSession::Constructor
|
|
|
+// Access: Public
|
|
|
+// Description:
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+P3DAuthSession::
|
|
|
+P3DAuthSession(P3DInstance *inst) :
|
|
|
+ _inst(inst)
|
|
|
+{
|
|
|
+ P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
|
|
|
+
|
|
|
+ _p3dcert_started = false;
|
|
|
+ _p3dcert_running = false;
|
|
|
+ _started_wait_thread = false;
|
|
|
+ INIT_THREAD(_wait_thread);
|
|
|
+
|
|
|
+ // Write the cert chain to a temporary file.
|
|
|
+ _cert_filename = new P3DTemporaryFile(".crt");
|
|
|
+ string filename = _cert_filename->get_filename();
|
|
|
+ FILE *fp = fopen(filename.c_str(), "w");
|
|
|
+ if (fp == NULL) {
|
|
|
+ nout << "Couldn't write temporary file\n";
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (inst->_mf_reader.get_num_signatures() > 0) {
|
|
|
+ const P3DMultifileReader::CertChain &cert_chain =
|
|
|
+ inst->_mf_reader.get_signature(0);
|
|
|
+
|
|
|
+ if (cert_chain.size() > 0) {
|
|
|
+ // Save the cert_dir, this is where the p3dcert program will
|
|
|
+ // need to write the cert when it is approved.
|
|
|
+ P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
|
|
|
+ _cert_dir = inst_mgr->get_cert_dir(cert_chain[0]._cert);
|
|
|
+ }
|
|
|
+
|
|
|
+ P3DMultifileReader::CertChain::const_iterator ci;
|
|
|
+ for (ci = cert_chain.begin(); ci != cert_chain.end(); ++ci) {
|
|
|
+ X509 *c = (*ci)._cert;
|
|
|
+ PEM_write_X509(fp, c);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ fclose(fp);
|
|
|
+
|
|
|
+ start_p3dcert();
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: P3DAuthSession::Destructor
|
|
|
+// Access: Public
|
|
|
+// Description:
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+P3DAuthSession::
|
|
|
+~P3DAuthSession() {
|
|
|
+ shutdown(false);
|
|
|
+
|
|
|
+ if (_cert_filename != NULL) {
|
|
|
+ delete _cert_filename;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: P3DAuthSession::shutdown
|
|
|
+// Access: Public
|
|
|
+// Description: Terminates the session by killing the subprocess.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+void P3DAuthSession::
|
|
|
+shutdown(bool send_message) {
|
|
|
+ if (!send_message) {
|
|
|
+ // If we're not to send the instance the shutdown message as a
|
|
|
+ // result of this, then clear the _inst pointer now.
|
|
|
+ _inst = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (_p3dcert_started) {
|
|
|
+ nout << "Killing p3dcert process\n";
|
|
|
+#ifdef _WIN32
|
|
|
+ TerminateProcess(_p3dcert_handle, 2);
|
|
|
+ CloseHandle(_p3dcert_handle);
|
|
|
+
|
|
|
+#else // _WIN32
|
|
|
+ kill(_p3dcert_pid, SIGKILL);
|
|
|
+#endif // _WIN32
|
|
|
+
|
|
|
+ _p3dcert_running = false;
|
|
|
+ _p3dcert_started = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Now that the process has stopped, the thread should stop itself
|
|
|
+ // quickly too.
|
|
|
+ join_wait_thread();
|
|
|
+
|
|
|
+ // We're no longer bound to any particular instance.
|
|
|
+ _inst = NULL;
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: P3DAuthSession::start_p3dcert
|
|
|
+// Access: Private
|
|
|
+// Description: Starts the p3dcert program running in a child
|
|
|
+// process.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+void P3DAuthSession::
|
|
|
+start_p3dcert() {
|
|
|
+ if (_p3dcert_started) {
|
|
|
+ // Already started.
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (_inst->_p3dcert_package == NULL) {
|
|
|
+ nout << "Couldn't start Python: no p3dcert package.\n";
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
|
|
|
+
|
|
|
+ _start_dir = inst_mgr->get_root_dir() + "/start";
|
|
|
+
|
|
|
+ string root_dir = _inst->_p3dcert_package->get_package_dir();
|
|
|
+ mkdir_complete(_start_dir, nout);
|
|
|
+
|
|
|
+ _p3dcert_exe = root_dir + "/p3dcert";
|
|
|
+#ifdef _WIN32
|
|
|
+ _p3dcert_exe += ".exe";
|
|
|
+#endif
|
|
|
+
|
|
|
+ // Populate the new process' environment.
|
|
|
+ _env = string();
|
|
|
+
|
|
|
+ // These are the enviroment variables we forward from the current
|
|
|
+ // environment, if they are set.
|
|
|
+ const char *keep[] = {
|
|
|
+ "TMP", "TEMP", "HOME", "USER",
|
|
|
+#ifdef _WIN32
|
|
|
+ "SYSTEMROOT", "USERPROFILE", "COMSPEC",
|
|
|
+#endif
|
|
|
+#ifdef HAVE_X11
|
|
|
+ "DISPLAY",
|
|
|
+#endif
|
|
|
+ NULL
|
|
|
+ };
|
|
|
+
|
|
|
+ for (int ki = 0; keep[ki] != NULL; ++ki) {
|
|
|
+ char *value = getenv(keep[ki]);
|
|
|
+ if (value != NULL) {
|
|
|
+ _env += keep[ki];
|
|
|
+ _env += "=";
|
|
|
+ _env += value;
|
|
|
+ _env += '\0';
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Define some new environment variables.
|
|
|
+ _env += "PATH=";
|
|
|
+ _env += root_dir;
|
|
|
+ _env += '\0';
|
|
|
+
|
|
|
+ _env += "LD_LIBRARY_PATH=";
|
|
|
+ _env += root_dir;
|
|
|
+ _env += '\0';
|
|
|
+
|
|
|
+ _env += "DYLD_LIBRARY_PATH=";
|
|
|
+ _env += root_dir;
|
|
|
+ _env += '\0';
|
|
|
+
|
|
|
+ _env += "P3DCERT_ROOT=";
|
|
|
+ _env += root_dir;
|
|
|
+ _env += '\0';
|
|
|
+
|
|
|
+ nout << "Attempting to start p3dcert from " << _p3dcert_exe << "\n";
|
|
|
+
|
|
|
+ bool started_p3dcert = false;
|
|
|
+#ifdef _WIN32
|
|
|
+ _p3dcert_handle = win_create_process();
|
|
|
+ started_p3dcert = (_p3dcert_handle != INVALID_HANDLE_VALUE);
|
|
|
+#else
|
|
|
+ _p3dcert_pid = posix_create_process();
|
|
|
+ started_p3dcert = (_p3dcert_pid > 0);
|
|
|
+#endif
|
|
|
+ if (!started_p3dcert) {
|
|
|
+ nout << "Failed to create process.\n";
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ _p3dcert_started = true;
|
|
|
+ _p3dcert_running = true;
|
|
|
+
|
|
|
+ spawn_wait_thread();
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: P3DAuthSession::spawn_wait_thread
|
|
|
+// Access: Private
|
|
|
+// Description: Starts the wait thread. This thread is responsible
|
|
|
+// for waiting for the process to finish, and notifying
|
|
|
+// the instance when it does.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+void P3DAuthSession::
|
|
|
+spawn_wait_thread() {
|
|
|
+ SPAWN_THREAD(_wait_thread, wt_thread_run, this);
|
|
|
+ _started_wait_thread = true;
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: P3DAuthSession::join_wait_thread
|
|
|
+// Access: Private
|
|
|
+// Description: Waits for the wait thread to stop.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+void P3DAuthSession::
|
|
|
+join_wait_thread() {
|
|
|
+ if (!_started_wait_thread) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ JOIN_THREAD(_wait_thread);
|
|
|
+ _started_wait_thread = false;
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: P3DAuthSession::wt_thread_run
|
|
|
+// Access: Private
|
|
|
+// Description: The main function for the wait thread.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+void P3DAuthSession::
|
|
|
+wt_thread_run() {
|
|
|
+ // All we do here is wait for the process to terminate.
|
|
|
+ nout << "wt_thread_run in " << this << " beginning\n";
|
|
|
+#ifdef _WIN32
|
|
|
+ DWORD result = WaitForSingleObject(_p3dcert_handle, INFINITE);
|
|
|
+ if (result != 0) {
|
|
|
+ nout << "Wait for process failed: " << GetLastError() << "\n";
|
|
|
+ }
|
|
|
+ nout << "p3dcert process has successfully stopped.\n";
|
|
|
+#else
|
|
|
+ int status;
|
|
|
+ pid_t result = waitpid(_p3dcert_pid, &status, WNOHANG);
|
|
|
+ if (result == -1) {
|
|
|
+ perror("waitpid");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ nout << "p3dcert process has successfully stopped.\n";
|
|
|
+ if (WIFEXITED(status)) {
|
|
|
+ nout << " exited normally, status = "
|
|
|
+ << WEXITSTATUS(status) << "\n";
|
|
|
+ } else if (WIFSIGNALED(status)) {
|
|
|
+ nout << " signalled by " << WTERMSIG(status) << ", core = "
|
|
|
+ << WCOREDUMP(status) << "\n";
|
|
|
+ } else if (WIFSTOPPED(status)) {
|
|
|
+ nout << " stopped by " << WSTOPSIG(status) << "\n";
|
|
|
+ }
|
|
|
+#endif // _WIN32
|
|
|
+
|
|
|
+ // Notify the instance that we're done.
|
|
|
+ P3DInstance *inst = _inst;
|
|
|
+ if (inst != NULL) {
|
|
|
+ inst->auth_finished_sub_thread();
|
|
|
+ }
|
|
|
+
|
|
|
+ nout << "Exiting wt_thread_run in " << this << "\n";
|
|
|
+}
|
|
|
+
|
|
|
+#ifdef _WIN32
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: P3DAuthSession::win_create_process
|
|
|
+// Access: Private
|
|
|
+// Description: Creates a sub-process to run _p3dcert_exe, with
|
|
|
+// the appropriate command-line arguments, and the
|
|
|
+// environment string defined in _env.
|
|
|
+//
|
|
|
+// Returns the handle to the created process on success,
|
|
|
+// or INVALID_HANDLE_VALUE on falure.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+HANDLE P3DAuthSession::
|
|
|
+win_create_process() {
|
|
|
+ // Make sure we see an error dialog if there is a missing DLL.
|
|
|
+ SetErrorMode(0);
|
|
|
+
|
|
|
+ STARTUPINFO startup_info;
|
|
|
+ ZeroMemory(&startup_info, sizeof(STARTUPINFO));
|
|
|
+ startup_info.cb = sizeof(startup_info);
|
|
|
+
|
|
|
+ // Make sure the initial window is *shown* for this graphical app.
|
|
|
+ startup_info.wShowWindow = SW_SHOW;
|
|
|
+ startup_info.dwFlags |= STARTF_USESHOWWINDOW;
|
|
|
+
|
|
|
+ const char *start_dir_cstr = _start_dir.c_str();
|
|
|
+
|
|
|
+ // Construct the command-line string, containing the quoted
|
|
|
+ // command-line arguments.
|
|
|
+ ostringstream stream;
|
|
|
+ stream << "\"" << _p3dcert_exe << "\" \""
|
|
|
+ << _cert_filename->get_filename() << "\" \""
|
|
|
+ << _cert_dir << "\"";
|
|
|
+
|
|
|
+ // I'm not sure why CreateProcess wants a non-const char pointer for
|
|
|
+ // its command-line string, but I'm not taking chances. It gets a
|
|
|
+ // non-const char array that it can modify.
|
|
|
+ string command_line_str = stream.str();
|
|
|
+ nout << "command is: " << command_line_str << "\n";
|
|
|
+ char *command_line = new char[command_line_str.size() + 1];
|
|
|
+ strcpy(command_line, command_line_str.c_str());
|
|
|
+
|
|
|
+ PROCESS_INFORMATION process_info;
|
|
|
+ BOOL result = CreateProcess
|
|
|
+ (_p3dcert_exe.c_str(), command_line, NULL, NULL, TRUE, 0,
|
|
|
+ (void *)_env.c_str(), start_dir_cstr,
|
|
|
+ &startup_info, &process_info);
|
|
|
+ bool started_program = (result != 0);
|
|
|
+
|
|
|
+ delete[] command_line;
|
|
|
+
|
|
|
+ if (!started_program) {
|
|
|
+ return INVALID_HANDLE_VALUE;
|
|
|
+ }
|
|
|
+
|
|
|
+ CloseHandle(process_info.hThread);
|
|
|
+ return process_info.hProcess;
|
|
|
+}
|
|
|
+#endif // _WIN32
|
|
|
+
|
|
|
+
|
|
|
+#ifndef _WIN32
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: P3DAuthSession::posix_create_process
|
|
|
+// Access: Private
|
|
|
+// Description: Creates a sub-process to run _p3dcert_exe, with
|
|
|
+// the appropriate command-line arguments, and the
|
|
|
+// environment string defined in _env.
|
|
|
+//
|
|
|
+// Returns the pid of the created process on success, or
|
|
|
+// -1 on falure.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+int P3DAuthSession::
|
|
|
+posix_create_process() {
|
|
|
+ // Fork and exec.
|
|
|
+ pid_t child = fork();
|
|
|
+ if (child < 0) {
|
|
|
+ perror("fork");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (child == 0) {
|
|
|
+ // Here we are in the child process.
|
|
|
+
|
|
|
+ if (chdir(_start_dir.c_str()) < 0) {
|
|
|
+ nout << "Could not chdir to " << _start_dir << "\n";
|
|
|
+ // This is a warning, not an error. We don't actually care
|
|
|
+ // that much about the starting directory.
|
|
|
+ }
|
|
|
+
|
|
|
+ // build up an array of char strings for the environment.
|
|
|
+ vector<const char *> ptrs;
|
|
|
+ size_t p = 0;
|
|
|
+ size_t zero = _env.find('\0', p);
|
|
|
+ while (zero != string::npos) {
|
|
|
+ ptrs.push_back(_env.data() + p);
|
|
|
+ p = zero + 1;
|
|
|
+ zero = _env.find('\0', p);
|
|
|
+ }
|
|
|
+ ptrs.push_back((char *)NULL);
|
|
|
+
|
|
|
+ execle(_p3dcert_exe.c_str(), _p3dcert_exe.c_str(),
|
|
|
+ _cert_filename->get_filename().c_str(), _cert_dir.c_str(),
|
|
|
+ (char *)0, &ptrs[0]);
|
|
|
+ nout << "Failed to exec " << _p3dcert_exe << "\n";
|
|
|
+ _exit(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ return child;
|
|
|
+}
|
|
|
+#endif // _WIN32
|