/** * If security checks are passed, dispatch the request to the function/method * * The config variable 'mnet_dispatcher_mode' can be: * strict: Only execute functions that are in specific files * off: The default - don't execute anything * * @param string $payload The XML-RPC request * * @throws mnet_server_exception * * @return No return val - just echo the response */ function mnet_server_dispatch($payload) { global $CFG, $DB; $remoteclient = get_mnet_remote_client(); // xmlrpc_decode_request returns an array of parameters, and the $method // variable (which is passed by reference) is instantiated with the value from // the methodName tag in the xml payload // xmlrpc_decode_request($xml, &$method) $params = xmlrpc_decode_request($payload, $method); // $method is something like: "mod/forum/lib.php/forum_add_instance" // $params is an array of parameters. A parameter might itself be an array. // Whitelist characters that are permitted in a method name // The method name must not begin with a / - avoid absolute paths // A dot character . is only allowed in the filename, i.e. something.php if (0 == preg_match("@^[A-Za-z0-9]+/[A-Za-z0-9/_\.-]+(\.php/)?[A-Za-z0-9_-]+$@",$method)) { throw new mnet_server_exception(713, 'nosuchfunction'); } if(preg_match("/^system\./", $method)) { $callstack = explode('.', $method); } else { $callstack = explode('/', $method); // callstack will look like array('mod', 'forum', 'lib.php', 'forum_add_instance'); } /** * What has the site administrator chosen as his dispatcher setting? * strict: Only execute functions that are in specific files * off: The default - don't execute anything */ ////////////////////////////////////// OFF if (!isset($CFG->mnet_dispatcher_mode) ) { set_config('mnet_dispatcher_mode', 'off'); throw new mnet_server_exception(704, 'nosuchservice'); } elseif ('off' == $CFG->mnet_dispatcher_mode) { throw new mnet_server_exception(704, 'nosuchservice'); ////////////////////////////////////// SYSTEM METHODS } elseif ($callstack[0] == 'system') { $functionname = $callstack[1]; $xmlrpcserver = xmlrpc_server_create(); // register all the system methods $systemmethods = array('listMethods', 'methodSignature', 'methodHelp', 'listServices', 'listFiles', 'retrieveFile', 'keyswap'); foreach ($systemmethods as $m) { // I'm adding the canonical xmlrpc references here, however we've // already forbidden that the period (.) should be allowed in the call // stack, so if someone tries to access our XMLRPC in the normal way, // they'll already have received a RPC server fault message. // Maybe we should allow an easement so that regular XMLRPC clients can // call our system methods, and find out what we have to offer? $handler = 'mnet_system'; if ($m == 'keyswap') { $handler = 'mnet_keyswap'; } if ($method == 'system.' . $m || $method == 'system/' . $m) { xmlrpc_server_register_method($xmlrpcserver, $method, $handler); $response = xmlrpc_server_call_method($xmlrpcserver, $payload, $remoteclient, array("encoding" => "utf-8")); $response = mnet_server_prepare_response($response); echo $response; xmlrpc_server_destroy($xmlrpcserver); return; } } throw new mnet_server_exception(7018, 'nosuchfunction'); //////////////////////////////////// NORMAL PLUGIN DISPATCHER } else { // anything else comes from some sort of plugin if ($rpcrecord = $DB->get_record('mnet_rpc', array('xmlrpcpath' => $method))) { $response = mnet_server_invoke_plugin_method($method, $callstack, $rpcrecord, $payload); $response = mnet_server_prepare_response($response); echo $response; return; // if the rpc record isn't found, check to see if dangerous mode is on ////////////////////////////////////// DANGEROUS } else if ('dangerous' == $CFG->mnet_dispatcher_mode && $remoteclient->plaintext_is_ok()) { $functionname = array_pop($callstack); $filename = clean_param(implode('/',$callstack), PARAM_PATH); if (0 == preg_match("/php$/", $filename)) { // Filename doesn't end in 'php'; possible attack? // Generate error response - unable to locate function throw new mnet_server_exception(7012, 'nosuchfunction'); } // The call stack holds the path to any include file $includefile = $CFG->dirroot.'/'.$filename; $response = mnet_server_invoke_dangerous_method($includefile, $functionname, $method, $payload); echo $response; return; } } throw new mnet_server_exception(7012, 'nosuchfunction'); }
/** * If security checks are passed, dispatch the request to the function/method * * The config variable 'mnet_dispatcher_mode' can be: * strict: Only execute functions that are in specific files * off: The default - don't execute anything * * @param string $payload The XML-RPC request * @return No return val - just echo the response */ function mnet_server_dispatch($payload) { global $CFG, $MNET_REMOTE_CLIENT; // xmlrpc_decode_request returns an array of parameters, and the $method // variable (which is passed by reference) is instantiated with the value from // the methodName tag in the xml payload // xmlrpc_decode_request($xml, &$method) $params = xmlrpc_decode_request($payload, $method); // $method is something like: "mod/forum/lib.php/forum_add_instance" // $params is an array of parameters. A parameter might itself be an array. // Whitelist characters that are permitted in a method name // The method name must not begin with a / - avoid absolute paths // A dot character . is only allowed in the filename, i.e. something.php if (0 == preg_match("@^[A-Za-z0-9]+/[A-Za-z0-9/_-]+(\\.php/)?[A-Za-z0-9_-]+\$@", $method)) { exit(mnet_server_fault(713, 'nosuchfunction')); } if (preg_match("/^system\\./", $method)) { $callstack = explode('.', $method); } else { $callstack = explode('/', $method); // callstack will look like array('mod', 'forum', 'lib.php', 'forum_add_instance'); } /** * What has the site administrator chosen as his dispatcher setting? * strict: Only execute functions that are in specific files * off: The default - don't execute anything */ ////////////////////////////////////// OFF if (!isset($CFG->mnet_dispatcher_mode)) { set_config('mnet_dispatcher_mode', 'off'); exit(mnet_server_fault(704, 'nosuchservice')); } elseif ('off' == $CFG->mnet_dispatcher_mode) { exit(mnet_server_fault(704, 'nosuchservice')); ////////////////////////////////////// SYSTEM METHODS } elseif ($callstack[0] == 'system') { $functionname = $callstack[1]; $xmlrpcserver = xmlrpc_server_create(); // I'm adding the canonical xmlrpc references here, however we've // already forbidden that the period (.) should be allowed in the call // stack, so if someone tries to access our XMLRPC in the normal way, // they'll already have received a RPC server fault message. // Maybe we should allow an easement so that regular XMLRPC clients can // call our system methods, and find out what we have to offer? xmlrpc_server_register_method($xmlrpcserver, 'system.listMethods', 'mnet_system'); xmlrpc_server_register_method($xmlrpcserver, 'system/listMethods', 'mnet_system'); xmlrpc_server_register_method($xmlrpcserver, 'system.methodSignature', 'mnet_system'); xmlrpc_server_register_method($xmlrpcserver, 'system/methodSignature', 'mnet_system'); xmlrpc_server_register_method($xmlrpcserver, 'system.methodHelp', 'mnet_system'); xmlrpc_server_register_method($xmlrpcserver, 'system/methodHelp', 'mnet_system'); xmlrpc_server_register_method($xmlrpcserver, 'system.listServices', 'mnet_system'); xmlrpc_server_register_method($xmlrpcserver, 'system/listServices', 'mnet_system'); xmlrpc_server_register_method($xmlrpcserver, 'system.keyswap', 'mnet_keyswap'); xmlrpc_server_register_method($xmlrpcserver, 'system/keyswap', 'mnet_keyswap'); if ($method == 'system.listMethods' || $method == 'system/listMethods' || $method == 'system.methodSignature' || $method == 'system/methodSignature' || $method == 'system.methodHelp' || $method == 'system/methodHelp' || $method == 'system.listServices' || $method == 'system/listServices' || $method == 'system.keyswap' || $method == 'system/keyswap') { $response = xmlrpc_server_call_method($xmlrpcserver, $payload, $MNET_REMOTE_CLIENT, array("encoding" => "utf-8")); $response = mnet_server_prepare_response($response); } else { exit(mnet_server_fault(7018, 'nosuchfunction')); } xmlrpc_server_destroy($xmlrpcserver); echo $response; ////////////////////////////////////// STRICT AUTH } elseif ($callstack[0] == 'auth') { // Break out the callstack into its elements list($base, $plugin, $filename, $methodname) = $callstack; // We refuse to include anything that is not auth.php if ($filename == 'auth.php' && is_enabled_auth($plugin)) { $authclass = 'auth_plugin_' . $plugin; $includefile = '/auth/' . $plugin . '/auth.php'; $response = mnet_server_invoke_method($includefile, $methodname, $method, $payload, $authclass); $response = mnet_server_prepare_response($response); echo $response; } else { // Generate error response - unable to locate function exit(mnet_server_fault(702, 'nosuchfunction')); } ////////////////////////////////////// STRICT ENROL } elseif ($callstack[0] == 'enrol') { // Break out the callstack into its elements list($base, $plugin, $filename, $methodname) = $callstack; if ($filename == 'enrol.php' && is_enabled_enrol($plugin)) { $enrolclass = 'enrolment_plugin_' . $plugin; $includefile = '/enrol/' . $plugin . '/enrol.php'; $response = mnet_server_invoke_method($includefile, $methodname, $method, $payload, $enrolclass); $response = mnet_server_prepare_response($response); echo $response; } else { // Generate error response - unable to locate function exit(mnet_server_fault(703, 'nosuchfunction')); } ////////////////////////////////////// STRICT MOD/* } elseif ($callstack[0] == 'mod' || 'dangerous' == $CFG->mnet_dispatcher_mode) { list($base, $module, $filename, $functionname) = $callstack; ////////////////////////////////////// STRICT MOD/* if ($base == 'mod' && $filename == 'rpclib.php') { $includefile = '/mod/' . $module . '/rpclib.php'; $response = mnet_server_invoke_method($includefile, $functionname, $method, $payload); $response = mnet_server_prepare_response($response); echo $response; ////////////////////////////////////// DANGEROUS } elseif ('dangerous' == $CFG->mnet_dispatcher_mode && $MNET_REMOTE_CLIENT->plaintext_is_ok()) { $functionname = array_pop($callstack); if ($MNET_REMOTE_CLIENT->plaintext_is_ok()) { $filename = clean_param(implode('/', $callstack), PARAM_PATH); if (0 == preg_match("/php\$/", $filename)) { // Filename doesn't end in 'php'; possible attack? // Generate error response - unable to locate function exit(mnet_server_fault(7012, 'nosuchfunction')); } // The call stack holds the path to any include file $includefile = $CFG->dirroot . '/' . $filename; $response = mnet_server_invoke_method($includefile, $functionname, $method, $payload); echo $response; } } else { // Generate error response - unable to locate function exit(mnet_server_fault(7012, 'nosuchfunction')); } } else { // Generate error response - unable to locate function exit(mnet_server_fault(7012, 'nosuchfunction')); } }