Permalink
Browse files

MDL-33791 Portfolio: Fixed security issue with passing file paths.

  • Loading branch information...
1 parent f89a1a8 commit 37743241745f999b855b1b7b2e4b3d3779e19a04 @markn86 markn86 committed with stronk7 Nov 7, 2012
View
@@ -145,7 +145,8 @@
$string['mustsetcallbackoptions'] = 'You must set the callback options either in the portfolio_add_button constructor or using the set_callback_options method';
$string['noavailableplugins'] = 'Sorry, but there are no available portfolios for you to export to';
$string['nocallbackclass'] = 'Could not find the callback class to use ({$a})';
-$string['nocallbackfile'] = 'Something in the module you\'re trying to export from is broken - couldn\'t find a required file ({$a})';
+$string['nocallbackcomponent'] = 'Could not find the component specified {$a}.';
+$string['nocallbackfile'] = 'Something in the module you\'re trying to export from is broken - couldn\'t find a required portfolio file';
$string['noclassbeforeformats'] = 'You must set the callback class before calling set_formats in portfolio_button';
$string['nocommonformats'] = 'No common formats between any available portfolio plugin and the calling location {$a->location} (caller supported {$a->formats})';
$string['noinstanceyet'] = 'Not yet selected';
View
5 lib/db/install.xml 100644 → 100755
@@ -2303,8 +2303,9 @@
<FIELD NAME="time" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="time of transfer (in the case of a queued transfer this is the time the actual transfer ran, not when the user started)" PREVIOUS="userid" NEXT="portfolio"/>
<FIELD NAME="portfolio" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="fk to portfolio_instance" PREVIOUS="time" NEXT="caller_class"/>
<FIELD NAME="caller_class" TYPE="char" LENGTH="150" NOTNULL="true" SEQUENCE="false" COMMENT="the name of the class used to create the transfer" PREVIOUS="portfolio" NEXT="caller_file"/>
- <FIELD NAME="caller_file" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="path to file to include where the class definition lives. (relative to dirroot)" PREVIOUS="caller_class" NEXT="caller_sha1"/>
- <FIELD NAME="caller_sha1" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="sha1 of exported content as far as the caller is concerned (before the portfolio plugin gets a hold of it)" PREVIOUS="caller_file" NEXT="tempdataid"/>
+ <FIELD NAME="caller_file" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="path to file to include where the class definition lives. (relative to dirroot)" PREVIOUS="caller_class" NEXT="caller_component"/>
+ <FIELD NAME="caller_component" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="the component name responsible for exporting" PREVIOUS="caller_file" NEXT="caller_sha1"/>
+ <FIELD NAME="caller_sha1" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="sha1 of exported content as far as the caller is concerned (before the portfolio plugin gets a hold of it)" PREVIOUS="caller_component" NEXT="tempdataid"/>
<FIELD NAME="tempdataid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="old id from portfolio_tempdata. This is so that we can gracefully catch a race condition between an external system requesting a file and causing the tempdata to be deleted, before the user gets the &quot;your transfer is requested&quot; page" PREVIOUS="caller_sha1" NEXT="returnurl"/>
<FIELD NAME="returnurl" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="the original &quot;returnurl&quot; of the export - takes us to the moodle page we started from" PREVIOUS="tempdataid" NEXT="continueurl"/>
<FIELD NAME="continueurl" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="the url the external system has set to view the transfer" PREVIOUS="returnurl"/>
View
@@ -1399,5 +1399,20 @@ function xmldb_main_upgrade($oldversion) {
upgrade_main_savepoint(true, 2012110201.00);
}
+ if ($oldversion < 2012110700.01) {
+
+ // Define field caller_component to be added to portfolio_log.
+ $table = new xmldb_table('portfolio_log');
+ $field = new xmldb_field('caller_component', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'caller_file');
+
+ // Conditionally launch add field caller_component.
+ if (!$dbman->field_exists($table, $field)) {
+ $dbman->add_field($table, $field);
+ }
+
+ // Main savepoint reached.
+ upgrade_main_savepoint(true, 2012110700.01);
+ }
+
return true;
}
View
@@ -66,10 +66,10 @@ class portfolio_exporter {
public $instancefile;
/**
- * @var string the file to include that contains the class definition of
+ * @var string the component that contains the class definition of
* the caller object used to re-waken the object after sleep
*/
- public $callerfile;
+ public $callercomponent;
/** @var int the current stage of the export */
private $stage;
@@ -115,16 +115,16 @@ class portfolio_exporter {
*
* @param portfolio_plugin_base $instance portfolio instance (passed by reference)
* @param portfolio_caller_base $caller portfolio caller (passed by reference)
- * @param string $callerfile path to callerfile (relative to dataroot)
+ * @param string $callercomponent the name of the callercomponent
*/
- public function __construct(&$instance, &$caller, $callerfile) {
+ public function __construct(&$instance, &$caller, $callercomponent) {
$this->instance =& $instance;
$this->caller =& $caller;
if ($instance) {
$this->instancefile = 'portfolio/' . $instance->get('plugin') . '/lib.php';
$this->instance->set('exporter', $this);
}
- $this->callerfile = $callerfile;
+ $this->callercomponent = $callercomponent;
$this->stage = PORTFOLIO_STAGE_CONFIG;
$this->caller->set('exporter', $this);
$this->alreadystolen = array();
@@ -395,7 +395,13 @@ public function process_stage_confirm() {
foreach ($previous as $row) {
$previousstr .= userdate($row->time);
if ($row->caller_class != get_class($this->caller)) {
- require_once($CFG->dirroot . '/' . $row->caller_file);
+ if (!empty($row->caller_file)) {
+ portfolio_include_callback_file($row->caller_file);
+ } else if (!empty($row->caller_component)) {
+ portfolio_include_callback_file($row->caller_component);
+ } else { // Ok, that's weird - this should never happen. Is the apocalypse coming?
+ continue;
+ }
$previousstr .= ' (' . call_user_func(array($row->caller_class, 'display_name')) . ')';
}
$previousstr .= '<br />';
@@ -522,15 +528,16 @@ public function process_stage_send() {
public function log_transfer() {
global $DB;
$l = array(
- 'userid' => $this->user->id,
- 'portfolio' => $this->instance->get('id'),
- 'caller_file' => $this->callerfile,
- 'caller_sha1' => $this->caller->get_sha1(),
- 'caller_class' => get_class($this->caller),
- 'continueurl' => $this->instance->get_static_continue_url(),
- 'returnurl' => $this->caller->get_return_url(),
- 'tempdataid' => $this->id,
- 'time' => time(),
+ 'userid' => $this->user->id,
+ 'portfolio' => $this->instance->get('id'),
+ 'caller_file'=> '',
+ 'caller_component' => $this->callercomponent,
+ 'caller_sha1' => $this->caller->get_sha1(),
+ 'caller_class' => get_class($this->caller),
+ 'continueurl' => $this->instance->get_static_continue_url(),
+ 'returnurl' => $this->caller->get_return_url(),
+ 'tempdataid' => $this->id,
+ 'time' => time(),
);
$DB->insert_record('portfolio_log', $l);
}
@@ -681,7 +688,14 @@ public static function rewaken_object($id) {
if ($exporter->instancefile) {
require_once($CFG->dirroot . '/' . $exporter->instancefile);
}
- require_once($CFG->dirroot . '/' . $exporter->callerfile);
+ if (!empty($exporter->callerfile)) {
+ portfolio_include_callback_file($exporter->callerfile);
+ } else if (!empty($exporter->callercomponent)) {
+ portfolio_include_callback_file($exporter->callercomponent);
+ } else {
+ return; // Should never get here!
+ }
+
$exporter = unserialize(serialize($exporter));
if (!$exporter->get('id')) {
// workaround for weird case
Oops, something went wrong.

0 comments on commit 3774324

Please sign in to comment.