diff options
Diffstat (limited to 'src/usr/local/www/xmlrpc.php')
-rwxr-xr-x | src/usr/local/www/xmlrpc.php | 592 |
1 files changed, 592 insertions, 0 deletions
diff --git a/src/usr/local/www/xmlrpc.php b/src/usr/local/www/xmlrpc.php new file mode 100755 index 0000000..5fd022a --- /dev/null +++ b/src/usr/local/www/xmlrpc.php @@ -0,0 +1,592 @@ +<?php +/* + xmlrpc.php + Copyright (C) 2013-2015 Electric Sheep Fencing, LP + Copyright (C) 2009, 2010 Scott Ullrich + Copyright (C) 2005 Colin Smith + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +##|+PRIV +##|*IDENT=page-xmlrpclibrary +##|*NAME=XMLRPC Library page +##|*DESCR=Allow access to the 'XMLRPC Library' page. +##|*MATCH=xmlrpc.php* +##|-PRIV + +require("config.inc"); +require("functions.inc"); +require_once("filter.inc"); +require("ipsec.inc"); +require("vpn.inc"); +require("shaper.inc"); +require("xmlrpc_server.inc"); +require("xmlrpc.inc"); + +function xmlrpc_loop_detect() { + global $config; + + /* grab sync to ip if enabled */ + if ($config['hasync']) { + $synchronizetoip = $config['hasync']['synchronizetoip']; + } + if ($synchronizetoip) { + if ($synchronizetoip == $_SERVER['REMOTE_ADDR']) { + return true; + } + } + + return false; +} + +$xmlrpc_g = array( + "return" => array( + "true" => new XML_RPC_Response(new XML_RPC_Value(true, $XML_RPC_Boolean)), + "false" => new XML_RPC_Response(new XML_RPC_Value(false, $XML_RPC_Boolean)), + "authfail" => new XML_RPC_Response(new XML_RPC_Value(gettext("Authentication failed"), $XML_RPC_String)) + ) +); + +/* + * pfSense XMLRPC errors + * $XML_RPC_erruser + 1 = Auth failure + */ +$XML_RPC_erruser = 200; + +/* EXPOSED FUNCTIONS */ +$exec_php_doc = gettext("XMLRPC wrapper for eval(). This method must be called with two parameters: a string containing the local system\'s password followed by the PHP code to evaluate."); +$exec_php_sig = array( + array( + $XML_RPC_Boolean, // First signature element is return value. + $XML_RPC_String, // password + $XML_RPC_String, // shell code to exec + ) +); + +function xmlrpc_authfail() { + log_auth("webConfigurator authentication error for 'admin' from {$_SERVER['REMOTE_ADDR']}"); +} + +function exec_php_xmlrpc($raw_params) { + global $config, $xmlrpc_g; + + $params = xmlrpc_params_to_php($raw_params); + if (!xmlrpc_auth($params)) { + xmlrpc_authfail(); + return $xmlrpc_g['return']['authfail']; + } + $exec_php = $params[0]; + eval($exec_php); + if ($toreturn) { + $response = XML_RPC_encode($toreturn); + return new XML_RPC_Response($response); + } else { + return $xmlrpc_g['return']['true']; + } +} + +/*****************************/ +$exec_shell_doc = gettext("XMLRPC wrapper for mwexec(). This method must be called with two parameters: a string containing the local system\'s password followed by an shell command to execute."); +$exec_shell_sig = array( + array( + $XML_RPC_Boolean, // First signature element is return value. + $XML_RPC_String, // password + $XML_RPC_String, // shell code to exec + ) +); + +function exec_shell_xmlrpc($raw_params) { + global $config, $xmlrpc_g; + + $params = xmlrpc_params_to_php($raw_params); + if (!xmlrpc_auth($params)) { + xmlrpc_authfail(); + return $xmlrpc_g['return']['authfail']; + } + $shell_cmd = $params[0]; + mwexec($shell_cmd); + + return $xmlrpc_g['return']['true']; +} + +/*****************************/ +$backup_config_section_doc = gettext("XMLRPC wrapper for backup_config_section. This method must be called with two parameters: a string containing the local system\'s password followed by an array containing the keys to be backed up."); +$backup_config_section_sig = array( + array( + $XML_RPC_Struct, // First signature element is return value. + $XML_RPC_String, + $XML_RPC_Array + ) +); + +function backup_config_section_xmlrpc($raw_params) { + global $config, $xmlrpc_g; + + if (xmlrpc_loop_detect()) { + log_error("Disallowing CARP sync loop"); + return; + } + + $params = xmlrpc_params_to_php($raw_params); + if (!xmlrpc_auth($params)) { + xmlrpc_authfail(); + return $xmlrpc_g['return']['authfail']; + } + $val = array_intersect_key($config, array_flip($params[0])); + + return new XML_RPC_Response(XML_RPC_encode($val)); +} + +/*****************************/ +$restore_config_section_doc = gettext("XMLRPC wrapper for restore_config_section. This method must be called with two parameters: a string containing the local system\'s password and an array to merge into the system\'s config. This function returns true upon completion."); +$restore_config_section_sig = array( + array( + $XML_RPC_Boolean, + $XML_RPC_String, + $XML_RPC_Struct + ) +); + +function restore_config_section_xmlrpc($raw_params) { + global $config, $xmlrpc_g; + + $old_config = $config; + + if (xmlrpc_loop_detect()) { + log_error("Disallowing CARP sync loop"); + return; + } + + $params = xmlrpc_params_to_php($raw_params); + if (!xmlrpc_auth($params)) { + xmlrpc_authfail(); + return $xmlrpc_g['return']['authfail']; + } + + /* + * Make sure it doesn't end up with both dnsmasq and unbound enabled + * simultaneously in secondary + * */ + if (isset($params[0]['unbound']['enable']) && isset($config['dnsmasq']['enable'])) { + unset($config['dnsmasq']['enable']); + services_dnsmasq_configure(); + } else if (isset($params[0]['dnsmasq']['enable']) && isset($config['unbound']['enable'])) { + unset($config['unbound']['enable']); + services_unbound_configure(); + } + + // Some sections should just be copied and not merged or we end + // up unable to sync the deletion of the last item in a section + $sync_full = array('dnsmasq', 'unbound', 'ipsec', 'aliases', 'wol', 'load_balancer', 'openvpn', 'cert', 'ca', 'crl', 'schedules', 'filter', 'nat', 'dhcpd', 'dhcpv6'); + $sync_full_done = array(); + foreach ($sync_full as $syncfull) { + if (isset($params[0][$syncfull])) { + $config[$syncfull] = $params[0][$syncfull]; + unset($params[0][$syncfull]); + $sync_full_done[] = $syncfull; + } + } + + $vipbackup = array(); + $oldvips = array(); + if (isset($params[0]['virtualip'])) { + if (is_array($config['virtualip']['vip'])) { + foreach ($config['virtualip']['vip'] as $vipindex => $vip) { + if ($vip['mode'] == "carp") { + $oldvips["{$vip['interface']}_vip{$vip['vhid']}"]['content'] = "{$vip['password']}{$vip['advskew']}{$vip['subnet']}{$vip['subnet_bits']}{$vip['advbase']}"; + $oldvips["{$vip['interface']}_vip{$vip['vhid']}"]['interface'] = $vip['interface']; + $oldvips["{$vip['interface']}_vip{$vip['vhid']}"]['subnet'] = $vip['subnet']; + } else if ($vip['mode'] == "ipalias" && (substr($vip['interface'], 0, 4) == '_vip' || strpos($vip['interface'], "lo0"))) { + $oldvips[$vip['subnet']]['content'] = "{$vip['interface']}{$vip['subnet']}{$vip['subnet_bits']}"; + $oldvips[$vip['subnet']]['interface'] = $vip['interface']; + $oldvips[$vip['subnet']]['subnet'] = $vip['subnet']; + } else if (($vip['mode'] == "ipalias" || $vip['mode'] == 'proxyarp') && !(substr($vip['interface'], 0, 4) == '_vip') || strpos($vip['interface'], "lo0")) { + $vipbackup[] = $vip; + } + } + } + } + + // For vip section, first keep items sent from the master + $config = array_merge_recursive_unique($config, $params[0]); + + /* Then add ipalias and proxyarp types already defined on the backup */ + if (is_array($vipbackup) && !empty($vipbackup)) { + if (!is_array($config['virtualip'])) { + $config['virtualip'] = array(); + } + if (!is_array($config['virtualip']['vip'])) { + $config['virtualip']['vip'] = array(); + } + foreach ($vipbackup as $vip) { + array_unshift($config['virtualip']['vip'], $vip); + } + } + + /* Log what happened */ + $mergedkeys = implode(",", array_merge(array_keys($params[0]), $sync_full_done)); + write_config(sprintf(gettext("Merged in config (%s sections) from XMLRPC client."), $mergedkeys)); + + /* + * The real work on handling the vips specially + * This is a copy of intefaces_vips_configure with addition of not reloading existing/not changed carps + */ + if (isset($params[0]['virtualip']) && is_array($config['virtualip']) && is_array($config['virtualip']['vip'])) { + $carp_setuped = false; + $anyproxyarp = false; + foreach ($config['virtualip']['vip'] as $vip) { + if ($vip['mode'] == "carp" && isset($oldvips["{$vip['interface']}_vip{$vip['vhid']}"])) { + if ($oldvips["{$vip['interface']}_vip{$vip['vhid']}"]['content'] == "{$vip['password']}{$vip['advskew']}{$vip['subnet']}{$vip['subnet_bits']}{$vip['advbase']}") { + if (does_vip_exist($vip)) { + unset($oldvips["{$vip['interface']}_vip{$vip['vhid']}"]); + continue; // Skip reconfiguring this vips since nothing has changed. + } + } + } else if ($vip['mode'] == "ipalias" && strstr($vip['interface'], "_vip") && isset($oldvips[$vip['subnet']])) { + if ($oldvips[$vip['subnet']]['content'] == "{$vip['interface']}{$vip['subnet']}{$vip['subnet_bits']}") { + if (does_vip_exist($vip)) { + unset($oldvips[$vip['subnet']]); + continue; // Skip reconfiguring this vips since nothing has changed. + } + } + unset($oldvips[$vip['subnet']]); + } + + switch ($vip['mode']) { + case "proxyarp": + $anyproxyarp = true; + break; + case "ipalias": + interface_ipalias_configure($vip); + break; + case "carp": + if ($carp_setuped == false) { + $carp_setuped = true; + } + interface_carp_configure($vip); + break; + } + } + /* Cleanup remaining old carps */ + foreach ($oldvips as $oldvipar) { + $oldvipif = get_real_interface($oldvipar['interface']); + if (!empty($oldvipif)) { + if (is_ipaddrv6($oldvipar['subnet'])) { + mwexec("/sbin/ifconfig " . escapeshellarg($oldvipif) . " inet6 " . escapeshellarg($oldvipar['subnet']) . " delete"); + } else { + pfSense_interface_deladdress($oldvipif, $oldvipar['subnet']); + } + } + } + if ($carp_setuped == true) { + interfaces_sync_setup(); + } + if ($anyproxyarp == true) { + interface_proxyarp_configure(); + } + } + + if (isset($old_config['ipsec']['enable']) !== isset($config['ipsec']['enable'])) { + vpn_ipsec_configure(); + } + + unset($old_config); + + return $xmlrpc_g['return']['true']; +} + +/*****************************/ +$merge_config_section_doc = gettext("XMLRPC wrapper for merging package sections. This method must be called with two parameters: a string containing the local system\'s password and an array to merge into the system\'s config. This function returns true upon completion."); +$merge_config_section_sig = array( + array( + $XML_RPC_Boolean, + $XML_RPC_String, + $XML_RPC_Struct + ) +); + +function merge_installedpackages_section_xmlrpc($raw_params) { + global $config, $xmlrpc_g; + + if (xmlrpc_loop_detect()) { + log_error("Disallowing CARP sync loop"); + return; + } + + $params = xmlrpc_params_to_php($raw_params); + if (!xmlrpc_auth($params)) { + xmlrpc_authfail(); + return $xmlrpc_g['return']['authfail']; + } + $config['installedpackages'] = array_merge($config['installedpackages'], $params[0]); + $mergedkeys = implode(",", array_keys($params[0])); + write_config(sprintf(gettext("Merged in config (%s sections) from XMLRPC client."), $mergedkeys)); + + return $xmlrpc_g['return']['true']; +} + +/*****************************/ +$merge_config_section_doc = gettext("XMLRPC wrapper for merge_config_section. This method must be called with two parameters: a string containing the local system\'s password and an array to merge into the system\'s config. This function returns true upon completion."); +$merge_config_section_sig = array( + array( + $XML_RPC_Boolean, + $XML_RPC_String, + $XML_RPC_Struct + ) +); + +function merge_config_section_xmlrpc($raw_params) { + global $config, $xmlrpc_g; + + if (xmlrpc_loop_detect()) { + log_error("Disallowing CARP sync loop"); + return; + } + + $params = xmlrpc_params_to_php($raw_params); + if (!xmlrpc_auth($params)) { + xmlrpc_authfail(); + return $xmlrpc_g['return']['authfail']; + } + $config_new = array_overlay($config, $params[0]); + $config = $config_new; + $mergedkeys = implode(",", array_keys($params[0])); + write_config(sprintf(gettext("Merged in config (%s sections) from XMLRPC client."), $mergedkeys)); + return $xmlrpc_g['return']['true']; +} + +/*****************************/ +$filter_configure_doc = gettext("Basic XMLRPC wrapper for filter_configure. This method must be called with one parameter: a string containing the local system\'s password. This function returns true upon completion."); +$filter_configure_sig = array( + array( + $XML_RPC_Boolean, + $XML_RPC_String + ) +); + +function filter_configure_xmlrpc($raw_params) { + global $xmlrpc_g, $config; + + $params = xmlrpc_params_to_php($raw_params); + if (!xmlrpc_auth($params)) { + xmlrpc_authfail(); + return $xmlrpc_g['return']['authfail']; + } + filter_configure(); + system_routing_configure(); + setup_gateways_monitor(); + relayd_configure(); + require_once("openvpn.inc"); + openvpn_resync_all(); + if (isset($config['dnsmasq']['enable'])) { + services_dnsmasq_configure(); + } elseif (isset($config['unbound']['enable'])) { + services_unbound_configure(); + } else { + # Both calls above run services_dhcpd_configure(), then we just + # need to call it when they are not called to avoid restarting dhcpd + # twice, as described on ticket #3797 + services_dhcpd_configure(); + } + local_sync_accounts(); + + return $xmlrpc_g['return']['true']; +} + +/*****************************/ +$carp_configure_doc = gettext("Basic XMLRPC wrapper for configuring CARP interfaces."); +$carp_configure_sig = array( + array( + $XML_RPC_Boolean, + $XML_RPC_String + ) +); + +function interfaces_carp_configure_xmlrpc($raw_params) { + global $xmlrpc_g; + + if (xmlrpc_loop_detect()) { + log_error("Disallowing CARP sync loop"); + return; + } + + $params = xmlrpc_params_to_php($raw_params); + if (!xmlrpc_auth($params)) { + xmlrpc_authfail(); + return $xmlrpc_g['return']['authfail']; + } + interfaces_vips_configure(); + + return $xmlrpc_g['return']['true']; +} + +/*****************************/ +$check_firmware_version_doc = gettext("Basic XMLRPC wrapper for check_firmware_version. This function will return the output of check_firmware_version upon completion."); + +$check_firmware_version_sig = array( + array( + $XML_RPC_String, + $XML_RPC_String + ) +); + +function check_firmware_version_xmlrpc($raw_params) { + global $xmlrpc_g, $XML_RPC_String; + + $params = xmlrpc_params_to_php($raw_params); + if (!xmlrpc_auth($params)) { + xmlrpc_authfail(); + return $xmlrpc_g['return']['authfail']; + } + return new XML_RPC_Response(new XML_RPC_Value(check_firmware_version(false), $XML_RPC_String)); +} + +/*****************************/ +$pfsense_firmware_version_doc = gettext("Basic XMLRPC wrapper for check_firmware_version. This function will return the output of check_firmware_version upon completion."); + +$pfsense_firmware_version_sig = array ( + array ( + $XML_RPC_Struct, + $XML_RPC_String + ) +); + +function pfsense_firmware_version_xmlrpc($raw_params) { + global $xmlrpc_g; + + $params = xmlrpc_params_to_php($raw_params); + if (!xmlrpc_auth($params)) { + xmlrpc_authfail(); + return $xmlrpc_g['return']['authfail']; + } + return new XML_RPC_Response(XML_RPC_encode(host_firmware_version())); +} + +/*****************************/ +$reboot_doc = gettext("Basic XMLRPC wrapper for rc.reboot."); +$reboot_sig = array(array($XML_RPC_Boolean, $XML_RPC_String)); +function reboot_xmlrpc($raw_params) { + global $xmlrpc_g; + + $params = xmlrpc_params_to_php($raw_params); + if (!xmlrpc_auth($params)) { + xmlrpc_authfail(); + return $xmlrpc_g['return']['authfail']; + } + mwexec_bg("/etc/rc.reboot"); + + return $xmlrpc_g['return']['true']; +} + +/*****************************/ +$get_notices_sig = array( + array( + $XML_RPC_Array, + $XML_RPC_String + ), + array( + $XML_RPC_Array + ) +); + +function get_notices_xmlrpc($raw_params) { + global $g, $xmlrpc_g; + + $params = xmlrpc_params_to_php($raw_params); + if (!xmlrpc_auth($params)) { + xmlrpc_authfail(); + return $xmlrpc_g['return']['authfail']; + } + if (!function_exists("get_notices")) { + require("notices.inc"); + } + if (!$params) { + $toreturn = get_notices(); + } else { + $toreturn = get_notices($params); + } + $response = new XML_RPC_Response(XML_RPC_encode($toreturn)); + + return $response; +} + +$xmlrpclockkey = lock('xmlrpc', LOCK_EX); + +/*****************************/ +$server = new XML_RPC_Server( + array( + 'pfsense.exec_shell' => array('function' => 'exec_shell_xmlrpc', + 'signature' => $exec_shell_sig, + 'docstring' => $exec_shell_doc), + 'pfsense.exec_php' => array('function' => 'exec_php_xmlrpc', + 'signature' => $exec_php_sig, + 'docstring' => $exec_php_doc), + 'pfsense.filter_configure' => array('function' => 'filter_configure_xmlrpc', + 'signature' => $filter_configure_sig, + 'docstring' => $filter_configure_doc), + 'pfsense.interfaces_carp_configure' => array('function' => 'interfaces_carp_configure_xmlrpc', + 'docstring' => $carp_configure_sig), + 'pfsense.backup_config_section' => array('function' => 'backup_config_section_xmlrpc', + 'signature' => $backup_config_section_sig, + 'docstring' => $backup_config_section_doc), + 'pfsense.restore_config_section' => array('function' => 'restore_config_section_xmlrpc', + 'signature' => $restore_config_section_sig, + 'docstring' => $restore_config_section_doc), + 'pfsense.merge_config_section' => array('function' => 'merge_config_section_xmlrpc', + 'signature' => $merge_config_section_sig, + 'docstring' => $merge_config_section_doc), + 'pfsense.merge_installedpackages_section_xmlrpc' => array('function' => 'merge_installedpackages_section_xmlrpc', + 'signature' => $merge_config_section_sig, + 'docstring' => $merge_config_section_doc), + 'pfsense.check_firmware_version' => array('function' => 'check_firmware_version_xmlrpc', + 'signature' => $check_firmware_version_sig, + 'docstring' => $check_firmware_version_doc), + 'pfsense.host_firmware_version' => array('function' => 'pfsense_firmware_version_xmlrpc', + 'signature' => $pfsense_firmware_version_sig, + 'docstring' => $host_firmware_version_doc), + 'pfsense.reboot' => array('function' => 'reboot_xmlrpc', + 'signature' => $reboot_sig, + 'docstring' => $reboot_doc), + 'pfsense.get_notices' => array('function' => 'get_notices_xmlrpc', + 'signature' => $get_notices_sig) + ) +); + +unlock($xmlrpclockkey); + +function array_overlay($a1, $a2) { + foreach ($a1 as $k => $v) { + if (!array_key_exists($k, $a2)) { + continue; + } + if (is_array($v) && is_array($a2[$k])) { + $a1[$k] = array_overlay($v, $a2[$k]); + } else { + $a1[$k] = $a2[$k]; + } + } + return $a1; +} + +?> |