Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6cd061d74c | ||
|
|
53b7420dc9 | ||
|
|
aa85c648a3 | ||
|
|
35e228ff09 | ||
|
|
62236da496 | ||
|
|
e1cc896b6c | ||
|
|
36595baa65 | ||
|
|
ec3fd1d105 | ||
|
|
e39dcfbfe2 | ||
|
|
ef6254b307 | ||
|
|
44bf211ab5 | ||
|
|
b0e920104f | ||
|
|
299e201142 | ||
|
|
46982ad2dc | ||
|
|
c0e07fd659 | ||
|
|
5c11eecbd7 | ||
|
|
9689afc759 | ||
|
|
d76f4108e5 | ||
|
|
9c4d619840 | ||
|
|
7774e7606d | ||
|
|
2ed0cad27b | ||
|
|
686c2ae534 | ||
|
|
faf3abe800 | ||
|
|
220b493a1b | ||
|
|
e8d67f9711 | ||
|
|
83e932b068 | ||
|
|
84d1be538e | ||
|
|
c97cdb1c0e | ||
|
|
ffefe85fb4 | ||
|
|
27341ca490 | ||
|
|
822bb2bd4d | ||
|
|
836b6f2fdb | ||
|
|
f297058461 | ||
|
|
0f4d8d76ae | ||
|
|
12884c91a6 |
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@@ -1,3 +1,4 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: d00p
|
||||
custom: ['https://paypal.me/Froxlor']
|
||||
|
||||
14
SECURITY.md
Normal file
14
SECURITY.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Our main and active version is currently 0.10.x. It will receive maintenance and security updates periodically. The older version 0.9.x will not receive any kind of updates. Please update to [0.10.x](https://github.com/Froxlor/Froxlor/wiki/Updating-Froxlor)
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 0.10.x | :white_check_mark: |
|
||||
| 0.9.x | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you think you have found a vulnerability in froxlor, please send an email to [team@froxlor.org](mailto:team@froxlor.org) with as many information as possible. Also, please give us appropriate time to fix the issue and build update-packages before publishing anything into the wild.
|
||||
@@ -289,6 +289,7 @@ if ($page == '' || $page == 'overview') {
|
||||
$result['customernumber'] = null;
|
||||
$result['custom_notes'] = null;
|
||||
$result['custom_notes_show'] = null;
|
||||
$result['api_allowed'] = null;
|
||||
$hosting_plans = null;
|
||||
$admin_select_cnt = null;
|
||||
$admin_select = null;
|
||||
|
||||
8
api.php
8
api.php
@@ -23,7 +23,7 @@ if (empty($request)) {
|
||||
}
|
||||
|
||||
// decode json request
|
||||
$decoded_request = json_decode(stripslashes($request), true);
|
||||
$decoded_request = json_decode($request, true);
|
||||
|
||||
// is it valid?
|
||||
if (is_null($decoded_request)) {
|
||||
@@ -32,6 +32,7 @@ if (is_null($decoded_request)) {
|
||||
|
||||
// validate content
|
||||
try {
|
||||
$decoded_request = stripcslashes_deep($decoded_request);
|
||||
$request = \Froxlor\Api\FroxlorRPC::validateRequest($decoded_request);
|
||||
// now actually do it
|
||||
$cls = "\\Froxlor\\Api\\Commands\\" . $request['command']['class'];
|
||||
@@ -72,3 +73,8 @@ function json_response($status, $status_message = '', $data = null)
|
||||
echo $json_response;
|
||||
exit();
|
||||
}
|
||||
|
||||
function stripcslashes_deep($value)
|
||||
{
|
||||
return is_array($value) ? array_map('stripcslashes_deep', $value) : stripcslashes($value);
|
||||
}
|
||||
|
||||
@@ -695,7 +695,7 @@ opcache.interned_strings_buffer'),
|
||||
('panel', 'password_special_char', '!?<>§$%+#=@'),
|
||||
('panel', 'customer_hide_options', ''),
|
||||
('panel', 'is_configured', '0'),
|
||||
('panel', 'version', '0.10.3'),
|
||||
('panel', 'version', '0.10.4'),
|
||||
('panel', 'db_version', '201910200');
|
||||
|
||||
|
||||
|
||||
@@ -406,9 +406,9 @@ if (\Froxlor\Froxlor::isDatabaseVersion('201910110')) {
|
||||
// select all domains with an ssl IP connected and specialsettings content to include these in the ssl-vhost
|
||||
// to maintain former behavior
|
||||
$sel_stmt = Database::prepare("
|
||||
SELECT d.id FROM `". TABLE_PANEL_DOMAINS . "` d
|
||||
LEFT JOIN `". TABLE_DOMAINTOIP . "` d2i ON d2i.id_domain = d.id
|
||||
LEFT JOIN `". TABLE_PANEL_IPSANDPORTS."` i ON i.id = d2i.id_ipandports
|
||||
SELECT d.id FROM `" . TABLE_PANEL_DOMAINS . "` d
|
||||
LEFT JOIN `" . TABLE_DOMAINTOIP . "` d2i ON d2i.id_domain = d.id
|
||||
LEFT JOIN `" . TABLE_PANEL_IPSANDPORTS . "` i ON i.id = d2i.id_ipandports
|
||||
WHERE d.specialsettings <> '' AND i.ssl = '1'
|
||||
");
|
||||
Database::pexecute($sel_stmt);
|
||||
@@ -447,3 +447,8 @@ if (\Froxlor\Froxlor::isFroxlorVersion('0.10.2')) {
|
||||
showUpdateStep("Updating from 0.10.2 to 0.10.3", false);
|
||||
\Froxlor\Froxlor::updateToVersion('0.10.3');
|
||||
}
|
||||
|
||||
if (\Froxlor\Froxlor::isFroxlorVersion('0.10.3')) {
|
||||
showUpdateStep("Updating from 0.10.3 to 0.10.4", false);
|
||||
\Froxlor\Froxlor::updateToVersion('0.10.4');
|
||||
}
|
||||
|
||||
@@ -56,7 +56,9 @@ class Ftps extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\ResourceEntit
|
||||
throw new \Exception("You cannot access this resource", 405);
|
||||
}
|
||||
|
||||
if ($this->getUserDetail('ftps_used') < $this->getUserDetail('ftps') || $this->getUserDetail('ftps') == '-1') {
|
||||
$is_defaultuser = $this->getBoolParam('is_defaultuser', true, 0);
|
||||
|
||||
if (($this->getUserDetail('ftps_used') < $this->getUserDetail('ftps') || $this->getUserDetail('ftps') == '-1') || $this->isAdmin() && $is_defaultuser == 1) {
|
||||
|
||||
// required paramters
|
||||
$path = $this->getParam('path');
|
||||
@@ -71,7 +73,6 @@ class Ftps extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\ResourceEntit
|
||||
$ftpdomain = $this->getParam('ftp_domain', true, '');
|
||||
|
||||
$additional_members = $this->getParam('additional_members', true, array());
|
||||
$is_defaultuser = $this->getBoolParam('is_defaultuser', true, 0);
|
||||
|
||||
// validation
|
||||
$password = \Froxlor\Validate\Validate::validate($password, 'password', '', '', array(), true);
|
||||
@@ -105,7 +106,7 @@ class Ftps extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\ResourceEntit
|
||||
$sendinfomail = 0;
|
||||
}
|
||||
|
||||
if (Settings::Get('customer.ftpatdomain') == '1' && !$is_defaultuser) {
|
||||
if (Settings::Get('customer.ftpatdomain') == '1' && ! $is_defaultuser) {
|
||||
if ($ftpusername == '') {
|
||||
\Froxlor\UI\Response::standard_error(array(
|
||||
'stringisempty',
|
||||
@@ -541,6 +542,9 @@ class Ftps extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\ResourceEntit
|
||||
"username" => $customer_data['loginname']
|
||||
);
|
||||
Database::pexecute($stmt, $params, true, true);
|
||||
} else {
|
||||
// do not allow removing default ftp-account
|
||||
\Froxlor\UI\Response::standard_error('ftp_cantdeletemainaccount', '', true);
|
||||
}
|
||||
|
||||
// remove all quotatallies
|
||||
|
||||
@@ -135,7 +135,7 @@ class IpsAndPorts extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\Resour
|
||||
{
|
||||
if ($this->isAdmin() && $this->getUserDetail('change_serversettings')) {
|
||||
|
||||
$ip = \Froxlor\Validate\Validate::validate_ip2($this->getParam('ip'), false, 'invalidip', false, false, false, true);
|
||||
$ip = \Froxlor\Validate\Validate::validate_ip2($this->getParam('ip'), false, 'invalidip', false, false, false, false, true);
|
||||
$port = \Froxlor\Validate\Validate::validate($this->getParam('port', true, 80), 'port', '/^(([1-9])|([1-9][0-9])|([1-9][0-9][0-9])|([1-9][0-9][0-9][0-9])|([1-5][0-9][0-9][0-9][0-9])|(6[0-4][0-9][0-9][0-9])|(65[0-4][0-9][0-9])|(655[0-2][0-9])|(6553[0-5]))$/Di', array(
|
||||
'stringisempty',
|
||||
'myport'
|
||||
@@ -332,7 +332,7 @@ class IpsAndPorts extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\Resour
|
||||
'id' => $id
|
||||
));
|
||||
|
||||
$ip = \Froxlor\Validate\Validate::validate_ip2($this->getParam('ip', true, $result['ip']), false, 'invalidip', false, false, false, true);
|
||||
$ip = \Froxlor\Validate\Validate::validate_ip2($this->getParam('ip', true, $result['ip']), false, 'invalidip', false, false, false, false, true);
|
||||
$port = \Froxlor\Validate\Validate::validate($this->getParam('port', true, $result['port']), 'port', '/^(([1-9])|([1-9][0-9])|([1-9][0-9][0-9])|([1-9][0-9][0-9][0-9])|([1-5][0-9][0-9][0-9][0-9])|(6[0-4][0-9][0-9][0-9])|(65[0-4][0-9][0-9])|(655[0-2][0-9])|(6553[0-5]))$/Di', array(
|
||||
'stringisempty',
|
||||
'myport'
|
||||
|
||||
@@ -45,19 +45,8 @@ class AcmeSh extends \Froxlor\Cron\FroxlorCron
|
||||
|
||||
public static $no_inserttask = false;
|
||||
|
||||
public static function run($internal = false)
|
||||
private static function needRenew()
|
||||
{
|
||||
if (! defined('CRON_IS_FORCED') && ! defined('CRON_DEBUG_FLAG') && $internal == false) {
|
||||
// FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_WARNING, "Let's Encrypt cronjob is combined with regeneration of webserver configuration files.\nFor debugging purposes you can use the --debug switch and/or the --force switch to run the cron manually.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
self::checkInstall();
|
||||
|
||||
self::$apiserver = 'https://acme-v0' . \Froxlor\Settings::Get('system.leapiversion') . '.api.letsencrypt.org/directory';
|
||||
|
||||
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Requesting/renewing Let's Encrypt certificates");
|
||||
|
||||
$certificates_stmt = Database::query("
|
||||
SELECT
|
||||
domssl.`id`,
|
||||
@@ -94,6 +83,46 @@ class AcmeSh extends \Froxlor\Cron\FroxlorCron
|
||||
OR domssl.`expirationdate` IS NULL
|
||||
)
|
||||
");
|
||||
$customer_ssl = $certificates_stmt->fetchAll(\PDO::FETCH_ASSOC);
|
||||
|
||||
$froxlor_ssl = array();
|
||||
if (Settings::Get('system.le_froxlor_enabled') == '1') {
|
||||
$froxlor_ssl_settings_stmt = Database::prepare("
|
||||
SELECT * FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "`
|
||||
WHERE `domainid` = '0' AND
|
||||
(`expirationdate` < DATE_ADD(NOW(), INTERVAL 30 DAY) OR `expirationdate` IS NULL)
|
||||
");
|
||||
$froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt);
|
||||
}
|
||||
|
||||
if (count($customer_ssl) > 0 || count($froxlor_ssl) > 0) {
|
||||
return array(
|
||||
'customer_ssl' => $customer_ssl,
|
||||
'froxlor_ssl' => $froxlor_ssl
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function run($internal = false)
|
||||
{
|
||||
if (! defined('CRON_IS_FORCED') && ! defined('CRON_DEBUG_FLAG') && $internal == false) {
|
||||
// Let's Encrypt cronjob is combined with regeneration of webserver configuration files.
|
||||
// For debugging purposes you can use the --debug switch and the --force switch to run the cron manually.
|
||||
// check whether we MIGHT need to run although there is no task to regenerate config-files
|
||||
$needRenew = self::needRenew();
|
||||
if ($needRenew) {
|
||||
// insert task to generate certificates and vhost-configs
|
||||
\Froxlor\System\Cronjob::inserttask(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
self::checkInstall();
|
||||
|
||||
self::$apiserver = 'https://acme-v0' . \Froxlor\Settings::Get('system.leapiversion') . '.api.letsencrypt.org/directory';
|
||||
|
||||
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Requesting/renewing Let's Encrypt certificates");
|
||||
|
||||
$aliasdomains_stmt = Database::prepare("
|
||||
SELECT
|
||||
@@ -127,6 +156,8 @@ class AcmeSh extends \Froxlor\Cron\FroxlorCron
|
||||
// flag for re-generation of vhost files
|
||||
$changedetected = 0;
|
||||
|
||||
$needRenew = self::needRenew();
|
||||
|
||||
// first - generate LE for system-vhost if enabled
|
||||
if (Settings::Get('system.le_froxlor_enabled') == '1') {
|
||||
|
||||
@@ -147,12 +178,7 @@ class AcmeSh extends \Froxlor\Cron\FroxlorCron
|
||||
'id' => null
|
||||
);
|
||||
|
||||
$froxlor_ssl_settings_stmt = Database::prepare("
|
||||
SELECT * FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "`
|
||||
WHERE `domainid` = '0' AND
|
||||
(`expirationdate` < DATE_ADD(NOW(), INTERVAL 30 DAY) OR `expirationdate` IS NULL)
|
||||
");
|
||||
$froxlor_ssl = Database::pexecute_first($froxlor_ssl_settings_stmt);
|
||||
$froxlor_ssl = $needRenew['froxlor_ssl'];
|
||||
|
||||
$cert_mode = 'issue';
|
||||
if ($froxlor_ssl) {
|
||||
@@ -210,7 +236,7 @@ class AcmeSh extends \Froxlor\Cron\FroxlorCron
|
||||
}
|
||||
|
||||
// customer domains
|
||||
$certrows = $certificates_stmt->fetchAll(\PDO::FETCH_ASSOC);
|
||||
$certrows = $needRenew['customer_ssl'];
|
||||
$cert_mode = 'issue';
|
||||
foreach ($certrows as $certrow) {
|
||||
|
||||
@@ -390,6 +416,6 @@ class AcmeSh extends \Froxlor\Cron\FroxlorCron
|
||||
$acmesh_result = \Froxlor\FileDir::safe_exec(self::$acmesh . " --upgrade");
|
||||
// check for activated cron (which is installed automatically) but we don't need it
|
||||
$acmesh_result2 = \Froxlor\FileDir::safe_exec(self::$acmesh . " --uninstall-cronjob");
|
||||
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Checking for LetsEncrypt client upgrades before renewing certificates:\n" . implode("\n", $acmesh_result)."\n".implode("\n", $acmesh_result2));
|
||||
FroxlorLogger::getInstanceOf()->logAction(FroxlorLogger::CRON_ACTION, LOG_INFO, "Checking for LetsEncrypt client upgrades before renewing certificates:\n" . implode("\n", $acmesh_result) . "\n" . implode("\n", $acmesh_result2));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,7 +285,7 @@ class Nginx extends HttpConfigBase
|
||||
|
||||
if (! $is_redirect) {
|
||||
$this->nginx_data[$vhost_filename] .= "\tlocation ~ \.php {\n";
|
||||
$this->nginx_data[$vhost_filename] .= "\t\tfastcgi_split_path_info ^(.+\.php)(/.+)\$;\n";
|
||||
$this->nginx_data[$vhost_filename] .= "\t\tfastcgi_split_path_info ^(.+?\.php)(/.*)$;\n";
|
||||
$this->nginx_data[$vhost_filename] .= "\t\tinclude " . Settings::Get('nginx.fastcgiparams') . ";\n";
|
||||
$this->nginx_data[$vhost_filename] .= "\t\tfastcgi_param SCRIPT_FILENAME \$request_filename;\n";
|
||||
$this->nginx_data[$vhost_filename] .= "\t\tfastcgi_param PATH_INFO \$fastcgi_path_info;\n";
|
||||
@@ -955,7 +955,7 @@ class Nginx extends HttpConfigBase
|
||||
$phpopts .= "\t" . '}' . "\n\n";
|
||||
|
||||
$phpopts .= "\tlocation @php {\n";
|
||||
$phpopts .= "\t\tfastcgi_split_path_info ^(.+\.php)(/.+)\$;\n";
|
||||
$phpopts .= "\t\tfastcgi_split_path_info ^(.+?\.php)(/.*)$;\n";
|
||||
$phpopts .= "\t\tinclude " . Settings::Get('nginx.fastcgiparams') . ";\n";
|
||||
$phpopts .= "\t\tfastcgi_param SCRIPT_FILENAME \$request_filename;\n";
|
||||
$phpopts .= "\t\tfastcgi_param PATH_INFO \$fastcgi_path_info;\n";
|
||||
|
||||
@@ -37,7 +37,7 @@ class NginxFcgi extends Nginx
|
||||
$php_options_text .= "\t" . 'location @php {' . "\n";
|
||||
$php_options_text .= "\t\t" . 'try_files $1 =404;' . "\n\n";
|
||||
$php_options_text .= "\t\t" . 'include ' . Settings::Get('nginx.fastcgiparams') . ";\n";
|
||||
$php_options_text .= "\t\t" . 'fastcgi_split_path_info ^(.+\.php)(/.+)\$;' . "\n";
|
||||
$php_options_text .= "\t\t" . 'fastcgi_split_path_info ^(.+?\.php)(/.*)$;' . "\n";
|
||||
$php_options_text .= "\t\t" . 'fastcgi_param SCRIPT_FILENAME $request_filename;' . "\n";
|
||||
$php_options_text .= "\t\t" . 'fastcgi_param PATH_INFO $2;' . "\n";
|
||||
if ($domain['ssl'] == '1' && $ssl_vhost) {
|
||||
|
||||
@@ -7,7 +7,7 @@ final class Froxlor
|
||||
{
|
||||
|
||||
// Main version variable
|
||||
const VERSION = '0.10.3';
|
||||
const VERSION = '0.10.4';
|
||||
|
||||
// Database version (YYYYMMDDC where C is a daily counter)
|
||||
const DBVERSION = '201910200';
|
||||
|
||||
@@ -252,6 +252,28 @@ class Store
|
||||
|
||||
public static function storeSettingMysqlAccessHost($fieldname, $fielddata, $newfieldvalue)
|
||||
{
|
||||
$ips = $newfieldvalue;
|
||||
// Convert cidr to netmask for mysql, if needed be
|
||||
if (strpos($ips, ',') !== false) {
|
||||
$ips = explode(',', $ips);
|
||||
}
|
||||
if (is_array($ips) && count($ips) > 0) {
|
||||
$newfieldvalue = [];
|
||||
foreach ($ips as $ip) {
|
||||
$org_ip = $ip;
|
||||
$ip_cidr = explode("/", $ip);
|
||||
if (count($ip_cidr) === 2) {
|
||||
$ip = $ip_cidr[0];
|
||||
if (strlen($ip_cidr[1]) <= 2) {
|
||||
$ip_cidr[1] = \Froxlor\Validate\Validate::cidr2NetmaskAddr($org_ip);
|
||||
}
|
||||
$newfieldvalue[] = $ip . '/' . $ip_cidr[1];
|
||||
} else {
|
||||
$newfieldvalue[] = $org_ip;
|
||||
}
|
||||
}
|
||||
$newfieldvalue = implode(',', $newfieldvalue);
|
||||
}
|
||||
$returnvalue = self::storeSettingField($fieldname, $fielddata, $newfieldvalue);
|
||||
|
||||
if ($returnvalue !== false && is_array($fielddata) && isset($fielddata['settinggroup']) && $fielddata['settinggroup'] == 'system' && isset($fielddata['varname']) && $fielddata['varname'] == 'mysql_access_host') {
|
||||
|
||||
@@ -77,8 +77,7 @@ class Check
|
||||
$mysql_access_host_array = array_map('trim', explode(',', $newfieldvalue));
|
||||
|
||||
foreach ($mysql_access_host_array as $host_entry) {
|
||||
|
||||
if (Validate::validate_ip2($host_entry, true, 'invalidip', true, true) == false && Validate::validateDomain($host_entry) == false && Validate::validateLocalHostname($host_entry) == false && $host_entry != '%') {
|
||||
if (Validate::validate_ip2($host_entry, true, 'invalidip', true, true, true, true, false) == false && Validate::validateDomain($host_entry) == false && Validate::validateLocalHostname($host_entry) == false && $host_entry != '%') {
|
||||
return array(
|
||||
self::FORMFIELDS_PLAUSIBILITY_CHECK_ERROR,
|
||||
'invalidmysqlhost',
|
||||
|
||||
@@ -62,6 +62,38 @@ class Validate
|
||||
\Froxlor\UI\Response::standard_error($lng, $fieldname, $throw_exception);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts CIDR to a netmask address
|
||||
*
|
||||
* @thx to https://stackoverflow.com/a/5711080/3020926
|
||||
* @param string $cidr
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function cidr2NetmaskAddr($cidr)
|
||||
{
|
||||
$ta = substr($cidr, strpos($cidr, '/') + 1) * 1;
|
||||
$netmask = str_split(str_pad(str_pad('', $ta, '1'), 32, '0'), 8);
|
||||
|
||||
foreach ($netmask as &$element) {
|
||||
$element = bindec($element);
|
||||
}
|
||||
|
||||
return implode('.', $netmask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an $address (IP) is IPv6
|
||||
*
|
||||
* @param string $address
|
||||
*
|
||||
* @return string|bool ip address on success, false on failure
|
||||
*/
|
||||
public static function is_ipv6($address)
|
||||
{
|
||||
return filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether it is a valid ip
|
||||
*
|
||||
@@ -77,17 +109,35 @@ class Validate
|
||||
* whether to allow private network addresses
|
||||
* @param bool $allow_cidr
|
||||
* whether to allow CIDR values e.g. 10.10.10.10/16
|
||||
* @param bool $cidr_as_netmask
|
||||
* whether to format CIDR nodation to netmask notation
|
||||
* @param bool $throw_exception
|
||||
* whether to throw an exception on failure
|
||||
*
|
||||
* @return string|bool ip address on success, false on failure
|
||||
*/
|
||||
public static function validate_ip2($ip, $return_bool = false, $lng = 'invalidip', $allow_localhost = false, $allow_priv = false, $allow_cidr = false, $throw_exception = false)
|
||||
public static function validate_ip2($ip, $return_bool = false, $lng = 'invalidip', $allow_localhost = false, $allow_priv = false, $allow_cidr = false, $cidr_as_netmask = false, $throw_exception = false)
|
||||
{
|
||||
$cidr = "";
|
||||
if ($allow_cidr) {
|
||||
$org_ip = $ip;
|
||||
$ip_cidr = explode("/", $ip);
|
||||
if (count($ip_cidr) == 2) {
|
||||
if (count($ip_cidr) === 2) {
|
||||
if (strlen($ip_cidr[1]) <= 2 && in_array((int) $ip_cidr[1], array_values(range(1, 32)), true) === false) {
|
||||
\Froxlor\UI\Response::standard_error($lng, $ip, $throw_exception);
|
||||
}
|
||||
if ($cidr_as_netmask && self::is_ipv6($ip_cidr[0])) {
|
||||
// MySQL does not handle CIDR of IPv6 addresses, return error
|
||||
if ($return_bool) {
|
||||
return false;
|
||||
} else {
|
||||
\Froxlor\UI\Response::standard_error($lng, $ip, $throw_exception);
|
||||
}
|
||||
}
|
||||
$ip = $ip_cidr[0];
|
||||
if ($cidr_as_netmask && strlen($ip_cidr[1]) <= 2) {
|
||||
$ip_cidr[1] = self::cidr2NetmaskAddr($org_ip);
|
||||
}
|
||||
$cidr = "/" . $ip_cidr[1];
|
||||
} else {
|
||||
$ip = $org_ip;
|
||||
|
||||
@@ -569,7 +569,7 @@ $lng['serversettings']['apacheconf_htpasswddir']['description'] = 'Where should
|
||||
|
||||
$lng['error']['formtokencompromised'] = 'The request seems to be compromised. For security reasons you were logged out.';
|
||||
$lng['serversettings']['mysql_access_host']['title'] = 'MySQL-Access-Hosts';
|
||||
$lng['serversettings']['mysql_access_host']['description'] = 'A comma separated list of hosts from which users should be allowed to connect to the MySQL-Server.';
|
||||
$lng['serversettings']['mysql_access_host']['description'] = 'A comma separated list of hosts from which users should be allowed to connect to the MySQL-Server. To allow a subnet the netmask or cidr syntax is valid.';
|
||||
|
||||
// ADDED IN 1.2.18-svn1
|
||||
|
||||
|
||||
@@ -564,7 +564,7 @@ $lng['serversettings']['apacheconf_htpasswddir']['description'] = 'Wo sollen die
|
||||
|
||||
$lng['error']['formtokencompromised'] = 'Das Formular scheint manipuliert worden zu sein. Aus Sicherheitsgründen wurden Sie ausgelogged.';
|
||||
$lng['serversettings']['mysql_access_host']['title'] = 'MySQL-Access-Hosts';
|
||||
$lng['serversettings']['mysql_access_host']['description'] = 'Eine durch Komma getrennte Liste mit den Hostnamen aller Hostnames/IP-Adressen, von denen sich die Benutzer einloggen dürfen.';
|
||||
$lng['serversettings']['mysql_access_host']['description'] = 'Eine durch Komma getrennte Liste mit den Hostnamen aller Hostnames/IP-Adressen, von denen sich die Benutzer einloggen dürfen. Um ein Subnetz zu erlauben ist die Netzmaske oder CIDR Syntax erlaubt.';
|
||||
|
||||
// ADDED IN 1.2.18-svn1
|
||||
|
||||
|
||||
@@ -189,7 +189,6 @@ class CertificatesTest extends TestCase
|
||||
));
|
||||
|
||||
// export
|
||||
openssl_csr_export($csr, $csrout);
|
||||
openssl_x509_export($sscert, $certout);
|
||||
openssl_pkey_export($privkey, $pkeyout, null);
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ use Froxlor\Database\Database;
|
||||
use Froxlor\Api\Commands\Admins;
|
||||
use Froxlor\Api\Commands\Customers;
|
||||
use Froxlor\Api\Commands\SubDomains;
|
||||
use Froxlor\Api\Commands\Ftps;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -61,7 +62,9 @@ class CustomersTest extends TestCase
|
||||
$this->assertEquals('secret', $result['custom_notes']);
|
||||
|
||||
// validate that the std-subdomain has been added
|
||||
$json_result = SubDomains::getLocal($admin_userdata, array('id' => $result['standardsubdomain']))->get();
|
||||
$json_result = SubDomains::getLocal($admin_userdata, array(
|
||||
'id' => $result['standardsubdomain']
|
||||
))->get();
|
||||
$result = json_decode($json_result, true)['data'];
|
||||
$this->assertEquals('test1.dev.froxlor.org', $result['domain']);
|
||||
}
|
||||
@@ -555,4 +558,49 @@ class CustomersTest extends TestCase
|
||||
$this->expectExceptionMessage('Loginname contains too many characters. Only ' . (\Froxlor\Database\Database::getSqlUsernameLength() - strlen(Settings::Get('customer.mysqlprefix'))) . ' characters are allowed.');
|
||||
Customers::getLocal($admin_userdata, $data)->add();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @depends testAdminCustomersAddAutoLoginname
|
||||
*/
|
||||
public function testResellerCustomersAddNoFtpValidateDefaultUserExists()
|
||||
{
|
||||
global $admin_userdata;
|
||||
// get reseller
|
||||
$json_result = Admins::getLocal($admin_userdata, array(
|
||||
'loginname' => 'reseller'
|
||||
))->get();
|
||||
$reseller_userdata = json_decode($json_result, true)['data'];
|
||||
$reseller_userdata['adminsession'] = 1;
|
||||
|
||||
// set available ftp resources to 0 to validate that when the customer
|
||||
// is added the default ftp user for the customer is created too regardless of
|
||||
// available resource of the reseller/admin
|
||||
$reseller_userdata['ftps'] = 0;
|
||||
|
||||
// add new customer
|
||||
$data = [
|
||||
'new_loginname' => 'testftpx',
|
||||
'email' => 'testftp@froxlor.org',
|
||||
'firstname' => 'Test',
|
||||
'name' => 'Ftpman',
|
||||
'customernumber' => 1339,
|
||||
'new_customer_password' => 'h0lYmo1y'
|
||||
];
|
||||
Customers::getLocal($reseller_userdata, $data)->add();
|
||||
|
||||
// get FTP user
|
||||
$json_result = Ftps::getLocal($reseller_userdata, [
|
||||
'username' => 'testftpx'
|
||||
])->get();
|
||||
$ftp_data = json_decode($json_result, true)['data'];
|
||||
$this->assertEquals("testftpx", $ftp_data['username']);
|
||||
|
||||
// now get rid of the customer again
|
||||
$json_result = Customers::getLocal($reseller_userdata, array(
|
||||
'loginname' => 'testftpx'
|
||||
))->delete();
|
||||
$result = json_decode($json_result, true)['data'];
|
||||
$this->assertEquals('testftpx', $result['loginname']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ class ValidateTest extends TestCase
|
||||
|
||||
public function testValidateIp()
|
||||
{
|
||||
$result = Validate::validate_ip2("12.34.56.78", false, 'invalidip', false, false, false, true);
|
||||
$result = Validate::validate_ip2("12.34.56.78", false, 'invalidip', false, false, false, false, true);
|
||||
$this->assertEquals("12.34.56.78", $result);
|
||||
}
|
||||
|
||||
@@ -58,12 +58,12 @@ class ValidateTest extends TestCase
|
||||
{
|
||||
$this->expectException("Exception");
|
||||
$this->expectExceptionCode(400);
|
||||
Validate::validate_ip2("10.0.0.1", false, 'invalidip', false, false, false, true);
|
||||
Validate::validate_ip2("10.0.0.1", false, 'invalidip', false, false, false, false, true);
|
||||
}
|
||||
|
||||
public function testValidateIpPrivNotAllowedBool()
|
||||
{
|
||||
$result = Validate::validate_ip2("10.0.0.1", true, 'invalidip', false, false, false, true);
|
||||
$result = Validate::validate_ip2("10.0.0.1", true, 'invalidip', false, false, false, false, true);
|
||||
$this->assertFalse($result);
|
||||
}
|
||||
|
||||
@@ -71,32 +71,63 @@ class ValidateTest extends TestCase
|
||||
{
|
||||
$this->expectException("Exception");
|
||||
$this->expectExceptionCode(400);
|
||||
Validate::validate_ip2("12.34.56.78/24", false, 'invalidip', false, false, false, true);
|
||||
Validate::validate_ip2("12.34.56.78/24", false, 'invalidip', false, false, false, false, true);
|
||||
}
|
||||
|
||||
public function testValidateIpCidrNotAllowedBool()
|
||||
{
|
||||
$result = Validate::validate_ip2("12.34.56.78/24", true, 'invalidip', false, false, false, true);
|
||||
$result = Validate::validate_ip2("12.34.56.78/24", true, 'invalidip', false, false, false, false, true);
|
||||
$this->assertFalse($result);
|
||||
}
|
||||
|
||||
public function testValidateIpCidr()
|
||||
{
|
||||
$result = Validate::validate_ip2("12.34.56.78/24", false, 'invalidip', false, false, true, true);
|
||||
$result = Validate::validate_ip2("12.34.56.78/24", false, 'invalidip', false, false, true, false, true);
|
||||
$this->assertEquals("12.34.56.78/24", $result);
|
||||
}
|
||||
|
||||
public function testValidateIpv6Disallowed()
|
||||
{
|
||||
$this->expectException("Exception");
|
||||
$this->expectExceptionCode(400);
|
||||
Validate::validate_ip2("2620:0:2d0:200::7/32", false, 'invalidip', false, false, true, true, true);
|
||||
}
|
||||
|
||||
public function testValidateIpLocalhostAllowed()
|
||||
{
|
||||
$result = Validate::validate_ip2("127.0.0.1/32", false, 'invalidip', true, false, true, true);
|
||||
$result = Validate::validate_ip2("127.0.0.1/32", false, 'invalidip', true, false, true, false, true);
|
||||
$this->assertEquals("127.0.0.1/32", $result);
|
||||
}
|
||||
|
||||
public function testValidateCidrNoationToNetmaskNotationIPv4()
|
||||
{
|
||||
$result = Validate::validate_ip2("1.1.1.1/4", false, 'invalidip', true, false, true, true, true);
|
||||
$this->assertEquals("1.1.1.1/240.0.0.0", $result);
|
||||
$result = Validate::validate_ip2("8.8.8.8/18", false, 'invalidip', true, false, true, true, true);
|
||||
$this->assertEquals("8.8.8.8/255.255.192.0", $result);
|
||||
$result = Validate::validate_ip2("8.8.8.8/1", false, 'invalidip', true, false, true, true, true);
|
||||
$this->assertEquals("8.8.8.8/128.0.0.0", $result);
|
||||
}
|
||||
|
||||
public function testValidateIPv6()
|
||||
{
|
||||
$result = Validate::is_ipv6('1.1.1.1/4');
|
||||
$this->assertFalse($result);
|
||||
$result = Validate::is_ipv6('1.1.1.1');
|
||||
$this->assertFalse($result);
|
||||
$result = Validate::is_ipv6('::ffff:10.20.30.40');
|
||||
$this->assertEquals('::ffff:10.20.30.40', $result);
|
||||
$result = Validate::is_ipv6('2620:0:2d0:200::7/32');
|
||||
$this->assertFalse($result);
|
||||
$result = Validate::is_ipv6('2620:0:2d0:200::7');
|
||||
$this->assertEquals('2620:0:2d0:200::7', $result);
|
||||
}
|
||||
|
||||
public function testValidateIpLocalhostAllowedWrongIp()
|
||||
{
|
||||
$this->expectException("Exception");
|
||||
$this->expectExceptionCode(400);
|
||||
Validate::validate_ip2("127.0.0.2", false, 'invalidip', true, false, false, true);
|
||||
Validate::validate_ip2("127.0.0.2", false, 'invalidip', true, false, false, false, true);
|
||||
}
|
||||
|
||||
public function testValidateUrl()
|
||||
|
||||
@@ -277,4 +277,21 @@ class FtpsTest extends TestCase
|
||||
$result = json_decode($json_result, true)['data'];
|
||||
$this->assertEquals('test1ftp2', $result['username']);
|
||||
}
|
||||
|
||||
public function testCustomerFtpsDeleteDefaultUser()
|
||||
{
|
||||
global $admin_userdata;
|
||||
|
||||
// get customer
|
||||
$json_result = Customers::getLocal($admin_userdata, array(
|
||||
'loginname' => 'test1'
|
||||
))->get();
|
||||
$customer_userdata = json_decode($json_result, true)['data'];
|
||||
$data = [
|
||||
'username' => 'test1'
|
||||
];
|
||||
$this->expectExceptionCode(400);
|
||||
$this->expectExceptionMessage('You cannot delete your main FTP account');
|
||||
Ftps::getLocal($customer_userdata, $data)->delete();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,4 +124,63 @@ class FroxlorRpcTest extends TestCase
|
||||
$this->assertEquals('listFunctions', $result['command']['method']);
|
||||
$this->assertNull($result['params']);
|
||||
}
|
||||
|
||||
public function testApiPhpEscaping()
|
||||
{
|
||||
$key = $this->generateKey();
|
||||
$request = array(
|
||||
'body' => [
|
||||
'command' => 'Froxlor.listFunctions',
|
||||
'params' => $key
|
||||
]
|
||||
);
|
||||
$json_request = json_encode($request);
|
||||
$decoded_request = json_decode($json_request, true);
|
||||
$decoded_request = $this->stripcslashes_deep($decoded_request);
|
||||
$this->assertEquals($key['key'], $decoded_request['body']['params']['key']);
|
||||
$this->assertEquals($key['cert'], $decoded_request['body']['params']['cert']);
|
||||
}
|
||||
|
||||
private function stripcslashes_deep($value)
|
||||
{
|
||||
return is_array($value) ? array_map([$this, 'stripcslashes_deep'], $value) : stripcslashes($value);
|
||||
}
|
||||
|
||||
private function generateKey()
|
||||
{
|
||||
$dn = array(
|
||||
"countryName" => "DE",
|
||||
"stateOrProvinceName" => "Hessen",
|
||||
"localityName" => "Frankfurt",
|
||||
"organizationName" => "Froxlor",
|
||||
"organizationalUnitName" => "Testing",
|
||||
"commonName" => "test2.local",
|
||||
"emailAddress" => "team@froxlor.org"
|
||||
);
|
||||
|
||||
// generate key pair
|
||||
$privkey = openssl_pkey_new(array(
|
||||
"private_key_bits" => 2048,
|
||||
"private_key_type" => OPENSSL_KEYTYPE_RSA
|
||||
));
|
||||
|
||||
// generate csr
|
||||
$csr = openssl_csr_new($dn, $privkey, array(
|
||||
'digest_alg' => 'sha256'
|
||||
));
|
||||
|
||||
// generate self-signed certificate
|
||||
$sscert = openssl_csr_sign($csr, null, $privkey, 365, array(
|
||||
'digest_alg' => 'sha256'
|
||||
));
|
||||
|
||||
// export
|
||||
openssl_x509_export($sscert, $certout);
|
||||
openssl_pkey_export($privkey, $pkeyout, null);
|
||||
|
||||
return array(
|
||||
'cert' => $certout,
|
||||
'key' => $pkeyout
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user