diff --git a/admin_traffic.php b/admin_traffic.php index 2060fe18..352bdc74 100644 --- a/admin_traffic.php +++ b/admin_traffic.php @@ -37,6 +37,9 @@ if ($page == 'overview' || $page == 'customers') { try { $context = Traffic::getCustomerStats($userinfo, $range); } catch (Exception $e) { + if ($e->getCode() === 405) { + Response::dynamicError(lng('traffic.nocustomers')); + } Response::dynamicError($e->getMessage()); } diff --git a/dns_editor.php b/dns_editor.php index 84b62e34..112e5195 100644 --- a/dns_editor.php +++ b/dns_editor.php @@ -125,7 +125,7 @@ UI::view('user/dns-editor.html.twig', [ 'listing' => Listing::format($collection, $dns_list_data, 'dns_list', ['domain_id' => $domain_id]), 'actions_links' => [ [ - 'class' => 'btn-secondary', + 'class' => 'btn-outline-secondary', 'href' => $linker->getLink([ 'section' => 'domains', 'page' => 'domains', @@ -136,7 +136,7 @@ UI::view('user/dns-editor.html.twig', [ 'icon' => 'fa-solid fa-pen' ], [ - 'class' => 'btn-secondary', + 'class' => 'btn-outline-primary', 'href' => $linker->getLink(['section' => 'domains', 'page' => 'domains']), 'label' => lng('menue.domains.domains'), 'icon' => 'fa-solid fa-globe' diff --git a/index.php b/index.php index 4f4fa6ac..902d9cbb 100644 --- a/index.php +++ b/index.php @@ -120,7 +120,6 @@ if ($action == '2fa_entercode') { ]); exit(); } elseif ($action == 'login') { - $languages = Language::getLanguages(); if (isset($_POST['send']) && $_POST['send'] == 'send') { $loginname = Validate::validate($_POST['loginname'], 'loginname'); $password = Validate::validate($_POST['password'], 'password'); @@ -358,7 +357,11 @@ if ($action == '2fa_entercode') { break; case 4: $cmail = isset($_GET['customermail']) ? $_GET['customermail'] : 'unknown'; - $message = str_replace('%s', $cmail, lng('error.errorsendingmail')); + if (!Validate::validateEmail($cmail)) { + $message = lng('error.errorsendingmail', ['invalid.address']); + } else { + $message = lng('error.errorsendingmail', [$cmail]); + } break; case 5: $message = lng('error.user_banned'); diff --git a/install/froxlor.sql.php b/install/froxlor.sql.php index 140dcf45..8ec52008 100644 --- a/install/froxlor.sql.php +++ b/install/froxlor.sql.php @@ -42,7 +42,7 @@ CREATE TABLE `ftp_users` ( `username` varchar(255) NOT NULL, `uid` int(5) NOT NULL default '0', `gid` int(5) NOT NULL default '0', - `password` varchar(128) NOT NULL, + `password` varchar(255) NOT NULL, `homedir` varchar(255) NOT NULL default '', `shell` varchar(255) NOT NULL default '/bin/false', `login_enabled` enum('N','Y') NOT NULL default 'N', @@ -65,8 +65,8 @@ CREATE TABLE `mail_users` ( `id` int(11) NOT NULL auto_increment, `email` varchar(255) NOT NULL default '', `username` varchar(255) NOT NULL default '', - `password` varchar(128) NOT NULL default '', - `password_enc` varchar(128) NOT NULL default '', + `password` varchar(255) NOT NULL default '', + `password_enc` varchar(255) NOT NULL default '', `uid` int(11) NOT NULL default '0', `gid` int(11) NOT NULL default '0', `homedir` varchar(255) NOT NULL default '', diff --git a/install/updates/froxlor/update_2.x.inc.php b/install/updates/froxlor/update_2.x.inc.php index 61844be3..405aabfd 100644 --- a/install/updates/froxlor/update_2.x.inc.php +++ b/install/updates/froxlor/update_2.x.inc.php @@ -68,6 +68,11 @@ if (Froxlor::isFroxlorVersion('0.10.38')) { Database::query($sql); // new customer allowed_mysqlserver field Database::query("ALTER TABLE `" . TABLE_PANEL_CUSTOMERS . "` ADD `allowed_mysqlserver` varchar(500) NOT NULL default '[0]';"); + // ftp_users adjustments + Database::query("ALTER TABLE `" . TABLE_FTP_USERS . "` CHANGE `password` varchar(255) NOT NULL default '';"); + // mail_users adjustments + Database::query("ALTER TABLE `" . TABLE_MAIL_USERS . "` CHANGE `password` varchar(255) NOT NULL default '';"); + Database::query("ALTER TABLE `" . TABLE_MAIL_USERS . "` CHANGE `password_enc` varchar(255) NOT NULL default '';"); Update::lastStepStatus(0); Update::showUpdateStep("Checking for multiple mysql-servers to allow acccess to customers for existing databases"); diff --git a/lib/Froxlor/Api/ApiCommand.php b/lib/Froxlor/Api/ApiCommand.php index ca530273..35130a4d 100644 --- a/lib/Froxlor/Api/ApiCommand.php +++ b/lib/Froxlor/Api/ApiCommand.php @@ -470,7 +470,7 @@ abstract class ApiCommand extends ApiParameter * * @param string $customer_hide_option * optional, when called as customer, some options might be hidden due to the - * panel.customer_hide_options ettings + * panel.customer_hide_options settings * * @return array * @throws Exception diff --git a/lib/Froxlor/Api/Commands/Admins.php b/lib/Froxlor/Api/Commands/Admins.php index 8c2b516e..a2ed4b76 100644 --- a/lib/Froxlor/Api/Commands/Admins.php +++ b/lib/Froxlor/Api/Commands/Admins.php @@ -245,7 +245,7 @@ class Admins extends ApiCommand implements ResourceEntity $ipaddress = $this->getParam('ipaddress', true, -1); // validation - $name = Validate::validate($name, 'name', '', '', [], true); + $name = Validate::validate($name, 'name', Validate::REGEX_DESC_TEXT, '', [], true); $idna_convert = new IdnaWrapper(); $email = $idna_convert->encode(Validate::validate($email, 'email', '', '', [], true)); $def_language = Validate::validate($def_language, 'default language', '', '', [], true); @@ -581,7 +581,7 @@ class Admins extends ApiCommand implements ResourceEntity } // validation - $name = Validate::validate($name, 'name', '', '', [], true); + $name = Validate::validate($name, 'name', Validate::REGEX_DESC_TEXT, '', [], true); $idna_convert = new IdnaWrapper(); $email = $idna_convert->encode(Validate::validate($email, 'email', '', '', [], true)); $def_language = Validate::validate($def_language, 'default language', '', '', [], true); diff --git a/lib/Froxlor/Api/Commands/Customers.php b/lib/Froxlor/Api/Commands/Customers.php index 82789b24..23160048 100644 --- a/lib/Froxlor/Api/Commands/Customers.php +++ b/lib/Froxlor/Api/Commands/Customers.php @@ -361,12 +361,12 @@ class Customers extends ApiCommand implements ResourceEntity } // validation - $name = Validate::validate($name, 'name', '', '', [], true); - $firstname = Validate::validate($firstname, 'first name', '', '', [], true); - $company = Validate::validate($company, 'company', '', '', [], true); - $street = Validate::validate($street, 'street', '', '', [], true); + $name = Validate::validate($name, 'name', Validate::REGEX_DESC_TEXT, '', [], true); + $firstname = Validate::validate($firstname, 'first name', Validate::REGEX_DESC_TEXT, '', [], true); + $company = Validate::validate($company, 'company', Validate::REGEX_DESC_TEXT, '', [], true); + $street = Validate::validate($street, 'street', Validate::REGEX_DESC_TEXT, '', [], true); $zipcode = Validate::validate($zipcode, 'zipcode', '/^[0-9 \-A-Z]*$/', '', [], true); - $city = Validate::validate($city, 'city', '', '', [], true); + $city = Validate::validate($city, 'city', Validate::REGEX_DESC_TEXT, '', [], true); $phone = Validate::validate($phone, 'phone', '/^[0-9\- \+\(\)\/]*$/', '', [], true); $fax = Validate::validate($fax, 'fax', '/^[0-9\- \+\(\)\/]*$/', '', [], true); $idna_convert = new IdnaWrapper(); @@ -1080,12 +1080,12 @@ class Customers extends ApiCommand implements ResourceEntity // validation if ($this->isAdmin()) { $idna_convert = new IdnaWrapper(); - $name = Validate::validate($name, 'name', '', '', [], true); - $firstname = Validate::validate($firstname, 'first name', '', '', [], true); - $company = Validate::validate($company, 'company', '', '', [], true); - $street = Validate::validate($street, 'street', '', '', [], true); + $name = Validate::validate($name, 'name', Validate::REGEX_DESC_TEXT, '', [], true); + $firstname = Validate::validate($firstname, 'first name', Validate::REGEX_DESC_TEXT, '', [], true); + $company = Validate::validate($company, 'company', Validate::REGEX_DESC_TEXT, '', [], true); + $street = Validate::validate($street, 'street', Validate::REGEX_DESC_TEXT, '', [], true); $zipcode = Validate::validate($zipcode, 'zipcode', '/^[0-9 \-A-Z]*$/', '', [], true); - $city = Validate::validate($city, 'city', '', '', [], true); + $city = Validate::validate($city, 'city', Validate::REGEX_DESC_TEXT, '', [], true); $phone = Validate::validate($phone, 'phone', '/^[0-9\- \+\(\)\/]*$/', '', [], true); $fax = Validate::validate($fax, 'fax', '/^[0-9\- \+\(\)\/]*$/', '', [], true); $email = $idna_convert->encode(Validate::validate($email, 'email', '', '', [], true)); diff --git a/lib/Froxlor/Api/Commands/MysqlServer.php b/lib/Froxlor/Api/Commands/MysqlServer.php index 6ae319d1..42405825 100644 --- a/lib/Froxlor/Api/Commands/MysqlServer.php +++ b/lib/Froxlor/Api/Commands/MysqlServer.php @@ -131,7 +131,7 @@ class MysqlServer extends ApiCommand implements ResourceEntity require Froxlor::getInstallDir() . "/lib/userdata.inc.php"; // le format - if (isset($sql['root_user']) && isset($sql['root_password']) &&!is_array($sql_root)) { + if (isset($sql['root_user']) && isset($sql['root_password']) && !is_array($sql_root)) { $sql_root = array( 0 => array( 'caption' => 'Default', @@ -377,7 +377,7 @@ class MysqlServer extends ApiCommand implements ResourceEntity $mysql_ca = $this->getParam('mysql_ca', true, $result['ssl']['caFile'] ?? ''); $mysql_verifycert = $this->getBoolParam('mysql_verifycert', true, $result['ssl']['verifyServerCertificate'] ?? 0); $privileged_user = $this->getParam('privileged_user', true, $result['user']); - $privileged_password = $this->getParam('privileged_password', true, $result['password']); + $privileged_password = $this->getParam('privileged_password', true, ''); $description = $this->getParam('description', true, $result['caption']); $allow_all_customers = $this->getParam('allow_all_customers', true, 0); $test_connection = $this->getParam('test_connection', true, 1); @@ -397,6 +397,11 @@ class MysqlServer extends ApiCommand implements ResourceEntity $privileged_password = Validate::validate($privileged_password, 'password', '', '', [], true); $description = Validate::validate(trim($description), 'description', Validate::REGEX_DESC_TEXT, '', [], true); + // keep old password? + if (empty($privileged_password)) { + $privileged_password = $result['password']; + } + if ($mysql_host != $result['host']) { // check whether the server is in use by any customer $result_ms = $this->databasesOnServer(true, $dbserver); diff --git a/lib/Froxlor/Cron/Traffic/TrafficCron.php b/lib/Froxlor/Cron/Traffic/TrafficCron.php index 9d8a872e..cb79996b 100644 --- a/lib/Froxlor/Cron/Traffic/TrafficCron.php +++ b/lib/Froxlor/Cron/Traffic/TrafficCron.php @@ -78,7 +78,7 @@ class TrafficCron extends FroxlorCron // Fork failed return 1; } - } else { + } else if (!defined('CRON_NOFORK_FLAG')) { if (extension_loaded('pcntl')) { $msg = "PHP compiled with pcntl but pcntl_fork function is not available."; } else { diff --git a/lib/Froxlor/System/Crypt.php b/lib/Froxlor/System/Crypt.php index ebe00c86..f3122f53 100644 --- a/lib/Froxlor/System/Crypt.php +++ b/lib/Froxlor/System/Crypt.php @@ -35,8 +35,13 @@ class Crypt /** * Generates a random password + * + * @param int $length optional, will be read from settings if not given + * @param bool $isSalt optional, default false, do not include special characters + * + * @return string */ - public static function generatePassword(int $length = 0) + public static function generatePassword(int $length = 0, bool $isSalt = false) { $alpha_lower = 'abcdefghijklmnopqrstuvwxyz'; $alpha_upper = strtoupper($alpha_lower); @@ -57,7 +62,7 @@ class Crypt $pw .= mb_substr(self::specialShuffle($numeric), 0, $n); } - if (Settings::Get('panel.password_special_char_required')) { + if (Settings::Get('panel.password_special_char_required') && !$isSalt) { $pw .= mb_substr(self::specialShuffle($special), 0, $n); } @@ -208,23 +213,22 @@ class Crypt * Password to be encrypted * @param bool $htpasswd * optional whether to generate a SHA1 password for directory protection - * @param bool $openssl - * optional generates $htpasswd like strings but for proftpd - * - * @return string encrypted password) - * - * 0 - default crypt (depends on system configuration) - * 1 - MD5 $1$ - * 2 - BLOWFISH $2y$07$ - * 3 - SHA-256 $5$ (default) - * 4 - SHA-512 $6$ + * @param bool $ftpd + * optional generates sha256 password strings for proftpd/pureftpd * + * @return string encrypted password */ - public static function makeCryptPassword($password, $htpasswd = false, $openssl = false) + public static function makeCryptPassword(string $password, bool $htpasswd = false, bool $ftpd = false) { - if ($htpasswd || $openssl) { - return '{SHA' . ($openssl ? '1' : '') . '}' . base64_encode(sha1($password, true)); + if ($htpasswd || $ftpd) { + if ($ftpd) { + // sha256 compatible for proftpd and pure-ftpd + return crypt($password, '$5$' . self::generatePassword(16, true) . '$'); + } + // sha1 hash for dir-protection + return '{SHA}' . base64_encode(sha1($password, true)); } + // crypt using the specified crypt-algorithm or system default $algo = Settings::Get('system.passwordcryptfunc') !== null ? Settings::Get('system.passwordcryptfunc') : PASSWORD_DEFAULT; return password_hash($password, $algo); } diff --git a/lib/configfiles/bionic.xml b/lib/configfiles/bionic.xml index acfd215d..db899f82 100644 --- a/lib/configfiles/bionic.xml +++ b/lib/configfiles/bionic.xml @@ -4333,6 +4333,26 @@ TLSVerifyClient off + + " +openssl dhparam -out /etc/ssl/private/pure-ftpd-dhparams.pem 3072 +chmod 0600 /etc/ssl/private/pure-ftpd.pem /etc/ssl/private/pure-ftpd-dhparams.pem +]]> + + + + + + + + + + + bin/froxlor-cli froxlor:cron --run-task 99]]> diff --git a/lib/configfiles/bookworm.xml b/lib/configfiles/bookworm.xml index 54b20ecc..a99fd877 100644 --- a/lib/configfiles/bookworm.xml +++ b/lib/configfiles/bookworm.xml @@ -2972,6 +2972,26 @@ TLSVerifyClient off + + " +openssl dhparam -out /etc/ssl/private/pure-ftpd-dhparams.pem 3072 +chmod 0600 /etc/ssl/private/pure-ftpd.pem /etc/ssl/private/pure-ftpd-dhparams.pem +]]> + + + + + + + + + + + bin/froxlor-cli froxlor:cron --run-task 99]]> diff --git a/lib/configfiles/bullseye.xml b/lib/configfiles/bullseye.xml index ba4b885a..64433043 100644 --- a/lib/configfiles/bullseye.xml +++ b/lib/configfiles/bullseye.xml @@ -4544,6 +4544,26 @@ TLSVerifyClient off + + " +openssl dhparam -out /etc/ssl/private/pure-ftpd-dhparams.pem 3072 +chmod 0600 /etc/ssl/private/pure-ftpd.pem /etc/ssl/private/pure-ftpd-dhparams.pem +]]> + + + + + + + + + + + bin/froxlor-cli froxlor:cron --run-task 99]]> diff --git a/lib/configfiles/buster.xml b/lib/configfiles/buster.xml index 3b6d50ca..ab76b0b0 100644 --- a/lib/configfiles/buster.xml +++ b/lib/configfiles/buster.xml @@ -4535,6 +4535,26 @@ TLSVerifyClient off + + " +openssl dhparam -out /etc/ssl/private/pure-ftpd-dhparams.pem 3072 +chmod 0600 /etc/ssl/private/pure-ftpd.pem /etc/ssl/private/pure-ftpd-dhparams.pem +]]> + + + + + + + + + + + bin/froxlor-cli froxlor:cron --run-task 99]]> diff --git a/lib/configfiles/focal.xml b/lib/configfiles/focal.xml index 68a9da90..6831c6ef 100644 --- a/lib/configfiles/focal.xml +++ b/lib/configfiles/focal.xml @@ -3750,6 +3750,26 @@ TLSVerifyClient off + + " +openssl dhparam -out /etc/ssl/private/pure-ftpd-dhparams.pem 3072 +chmod 0600 /etc/ssl/private/pure-ftpd.pem /etc/ssl/private/pure-ftpd-dhparams.pem +]]> + + + + + + + + + + + bin/froxlor-cli froxlor:cron --run-task 99]]> diff --git a/lib/configfiles/gentoo.xml b/lib/configfiles/gentoo.xml index 2e4c4069..ddf594a3 100644 --- a/lib/configfiles/gentoo.xml +++ b/lib/configfiles/gentoo.xml @@ -3525,6 +3525,19 @@ UseReverseDNS off > /etc/portage/package.use/froxlor]]> + + " +openssl dhparam -out /etc/ssl/private/pure-ftpd-dhparams.pem 3072 +chmod 0600 /etc/ssl/private/pure-ftpd.pem /etc/ssl/private/pure-ftpd-dhparams.pem +]]> + + + + + + @@ -3563,7 +3576,7 @@ MYSQLSocket /var/run/mysqld/mysqld.sock MYSQLUser MYSQLPassword MYSQLDatabase -MYSQLCrypt Crypt +MYSQLCrypt any MYSQLGetPW SELECT password FROM ftp_users WHERE username="\L" AND login_enabled="y" MYSQLGetUID SELECT uid FROM ftp_users WHERE username="\L" AND login_enabled="y" MYSQLGetGID SELECT gid FROM ftp_users WHERE username="\L" AND login_enabled="y" @@ -3910,6 +3923,7 @@ aliases: files + bin/froxlor-cli froxlor:cron --run-task 99]]> diff --git a/lib/configfiles/jammy.xml b/lib/configfiles/jammy.xml index a6ea5e30..5311eefe 100644 --- a/lib/configfiles/jammy.xml +++ b/lib/configfiles/jammy.xml @@ -3750,6 +3750,26 @@ TLSVerifyClient off + + " +openssl dhparam -out /etc/ssl/private/pure-ftpd-dhparams.pem 3072 +chmod 0600 /etc/ssl/private/pure-ftpd.pem /etc/ssl/private/pure-ftpd-dhparams.pem +]]> + + + + + + + + + + + bin/froxlor-cli froxlor:cron --run-task 99]]> diff --git a/lng/de.lng.php b/lng/de.lng.php index 272f4a54..ac447272 100644 --- a/lng/de.lng.php +++ b/lng/de.lng.php @@ -2117,6 +2117,7 @@ Vielen Dank, Ihr Administrator', 'http' => 'HTTP', 'ftp' => 'FTP', 'mail' => 'Mail', + 'nocustomers' => 'Es wird mindestens ein Kunde benötigt um die Traffic Statistiken anzuzeigen.', ], 'translator' => '', 'update' => [ diff --git a/lng/en.lng.php b/lng/en.lng.php index 7d440122..18436845 100644 --- a/lng/en.lng.php +++ b/lng/en.lng.php @@ -2243,6 +2243,7 @@ Yours sincerely, your administrator', 'http' => 'HTTP', 'ftp' => 'FTP', 'mail' => 'Mail', + 'nocustomers' => 'You need at least one customer to view the traffic reports.', ], 'translator' => '', 'update' => [ diff --git a/ssl_editor.php b/ssl_editor.php index 21871190..d4b7bab3 100644 --- a/ssl_editor.php +++ b/ssl_editor.php @@ -97,7 +97,7 @@ if ($action == '' || $action == 'view') { 'editid' => $id, 'actions_links' => [ [ - 'class' => 'btn-secondary', + 'class' => 'btn-outline-secondary', 'href' => $linker->getLink([ 'section' => 'domains', 'page' => 'domains', diff --git a/templates/Froxlor/src/scss/components/_search.scss b/templates/Froxlor/src/scss/components/_search.scss index b16ba530..520891d0 100644 --- a/templates/Froxlor/src/scss/components/_search.scss +++ b/templates/Froxlor/src/scss/components/_search.scss @@ -20,7 +20,7 @@ width: 70vh; max-height: 50vh; - background: #fff; + background: $search-bg; border: $border-color solid 1px; border-radius: 0 0 $border-radius $border-radius; diff --git a/templates/Froxlor/src/scss/variables/_dark.scss b/templates/Froxlor/src/scss/variables/_dark.scss index f0765b25..de2fd249 100644 --- a/templates/Froxlor/src/scss/variables/_dark.scss +++ b/templates/Froxlor/src/scss/variables/_dark.scss @@ -20,6 +20,8 @@ $nav-link-color: $body-color; // List groups $list-group-bg: $gray-800; $list-group-color: $body-color; +$list-group-hover-bg: $gray-700; +$list-group-action-color: $body-color; // Navbar $navbar-bg: $gray-800; @@ -51,3 +53,9 @@ $modal-content-bg: $gray-800; $input-bg: $gray-900; $input-border-color: $black; $input-group-addon-bg: $gray-800; + +// Progress bar +$progress-bg: $gray-900; + +// Search +$search-bg: $gray-800; diff --git a/templates/Froxlor/src/scss/variables/_main.scss b/templates/Froxlor/src/scss/variables/_main.scss index 0bd42f27..667acfe0 100644 --- a/templates/Froxlor/src/scss/variables/_main.scss +++ b/templates/Froxlor/src/scss/variables/_main.scss @@ -80,3 +80,6 @@ $card-border-width: 0; $heading-bg: $navbar-bg; $heading-color: $body-color; $heading-border-color: #dee2e6; + +// Search +$search-bg: $navbar-bg; diff --git a/templates/Froxlor/user/dns-editor.html.twig b/templates/Froxlor/user/dns-editor.html.twig index ec0df31a..dea6e69b 100644 --- a/templates/Froxlor/user/dns-editor.html.twig +++ b/templates/Froxlor/user/dns-editor.html.twig @@ -25,7 +25,7 @@ {% if zonefile is not empty %}
- +
{% endif %}