Compare commits

...

9 Commits

Author SHA1 Message Date
Michael Kaufmann
e10f77e219 fix missing id parameter after security question when deleting a ssl-certificate
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2022-12-02 16:17:59 +01:00
Michael Kaufmann
83ff0f5b00 fix api-key creation
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2022-12-02 13:42:26 +01:00
Michael Kaufmann
0562d248b5 use same error message for invalid user and disabled password reset to not give away if a user exists
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2022-12-02 13:24:27 +01:00
Michael Kaufmann
3fda5be5a3 set version to 0.10.38.3 for upcoming security release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2022-12-02 09:32:29 +01:00
Michael Kaufmann
4d454a3903 secure api-key generation, dns-record as well as ssl-certificate deletion, logo uploading, frame-inclusion and user/email enumeration via 'forgot password'
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2022-12-02 09:22:08 +01:00
Michael Kaufmann
0e703a4199 adjust validate-test for 'sql username too long' for mariadb
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2022-11-24 10:30:01 +01:00
Michael Kaufmann
47c71f6035 adjust mysql-usernamelength values for mariadb according to docs
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2022-11-24 10:03:23 +01:00
Michael Kaufmann
5923cbb1d5 set version to 0.10.38.2 for security release
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2022-11-05 12:41:50 +01:00
Michael Kaufmann
3f10a4aded fix still possible html injection
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
2022-11-05 12:37:53 +01:00
13 changed files with 314 additions and 244 deletions

View File

@@ -71,6 +71,8 @@ if ($action == 'delete') {
} }
} }
} elseif ($action == 'add') { } elseif ($action == 'add') {
if (isset($_POST['send']) && $_POST['send'] == 'send') {
$ins_stmt = Database::prepare(" $ins_stmt = Database::prepare("
INSERT INTO `" . TABLE_API_KEYS . "` SET INSERT INTO `" . TABLE_API_KEYS . "` SET
`apikey` = :key, `secret` = :secret, `adminid` = :aid, `customerid` = :cid, `valid_until` = '-1', `allowed_from` = '' `apikey` = :key, `secret` = :secret, `adminid` = :aid, `customerid` = :cid, `valid_until` = '-1', `allowed_from` = ''
@@ -90,8 +92,18 @@ if ($action == 'delete') {
'cid' => $cid 'cid' => $cid
)); ));
$success_message = $lng['apikeys']['apikey_added']; $success_message = $lng['apikeys']['apikey_added'];
} else {
\Froxlor\UI\HTML::askYesNo('api_reallyadd', $filename, array(
'page' => $page,
'action' => $action
), $id);
}
} elseif ($action == 'jqEditApiKey') { } elseif ($action == 'jqEditApiKey') {
$keyid = isset($_POST['id']) ? (int) $_POST['id'] : 0; $keyid = isset($_POST['id']) ? (int) $_POST['id'] : 0;
if (empty($keyid)) {
echo json_encode(false);
exit;
}
$allowed_from = isset($_POST['allowed_from']) ? $_POST['allowed_from'] : ""; $allowed_from = isset($_POST['allowed_from']) ? $_POST['allowed_from'] : "";
$valid_until = isset($_POST['valid_until']) ? (int) $_POST['valid_until'] : -1; $valid_until = isset($_POST['valid_until']) ? (int) $_POST['valid_until'] : -1;

View File

@@ -58,6 +58,7 @@ if ($action == 'add_record' && ! empty($_POST)) {
// remove entry // remove entry
$entry_id = isset($_GET['id']) ? (int) $_GET['id'] : 0; $entry_id = isset($_GET['id']) ? (int) $_GET['id'] : 0;
if ($entry_id > 0) { if ($entry_id > 0) {
if (isset($_POST['send']) && $_POST['send'] == 'send') {
try { try {
DomainZones::getLocal($userinfo, array( DomainZones::getLocal($userinfo, array(
'entry_id' => $entry_id, 'entry_id' => $entry_id,
@@ -68,6 +69,13 @@ if ($action == 'add_record' && ! empty($_POST)) {
} catch (Exception $e) { } catch (Exception $e) {
$errors = str_replace("\n", "<br>", $e->getMessage()); $errors = str_replace("\n", "<br>", $e->getMessage());
} }
} else {
\Froxlor\UI\HTML::askYesNo('dnsentry_reallydelete', $filename, array(
'page' => $page,
'action' => $action,
'id' => $id
), $id);
}
} }
} }

View File

@@ -353,12 +353,7 @@ if ($action == '2fa_entercode') {
$message = sprintf($lng['error']['login_blocked'], Settings::Get('login.deactivatetime')); $message = sprintf($lng['error']['login_blocked'], Settings::Get('login.deactivatetime'));
break; break;
case 4: case 4:
$cmail = isset($_GET['customermail']) ? $_GET['customermail'] : 'unknown'; $message = $lng['error']['errorsendingmailpub'];
if (!Validate::validateEmail($cmail)) {
$message = str_replace('%s', 'invalid.address', $lng['error']['errorsendingmail']);
} else {
$message = str_replace('%s', $cmail, $lng['error']['errorsendingmail']);
}
break; break;
case 5: case 5:
$message = $lng['error']['user_banned']; $message = $lng['error']['user_banned'];
@@ -430,6 +425,19 @@ if ($action == 'forgotpwd') {
} }
} }
$no_action = false;
if ($adminchecked) {
if (Settings::Get('panel.allow_preset_admin') != '1') {
$message = $lng['pwdreminder']['notallowed'];
unset($adminchecked);
}
} else {
if (Settings::Get('panel.allow_preset') != '1') {
$message = $lng['pwdreminder']['notallowed'];
}
}
if (empty($message)) {
if ($result_stmt !== null) { if ($result_stmt !== null) {
$user = $result_stmt->fetch(PDO::FETCH_ASSOC); $user = $result_stmt->fetch(PDO::FETCH_ASSOC);
@@ -571,18 +579,8 @@ if ($action == 'forgotpwd') {
unset($user); unset($user);
} }
} else { } else {
$message = $lng['login']['usernotfound'];
}
}
if ($adminchecked) {
if (Settings::Get('panel.allow_preset_admin') != '1') {
$message = $lng['pwdreminder']['notallowed']; $message = $lng['pwdreminder']['notallowed'];
unset($adminchecked);
} }
} else {
if (Settings::Get('panel.allow_preset') != '1') {
$message = $lng['pwdreminder']['notallowed'];
} }
} }

View File

@@ -723,7 +723,7 @@ opcache.validate_timestamps'),
('panel', 'logo_image_login', ''), ('panel', 'logo_image_login', ''),
('panel', 'logo_overridetheme', '0'), ('panel', 'logo_overridetheme', '0'),
('panel', 'logo_overridecustom', '0'), ('panel', 'logo_overridecustom', '0'),
('panel', 'version', '0.10.38.1'), ('panel', 'version', '0.10.38.3'),
('panel', 'db_version', '202112310'); ('panel', 'db_version', '202112310');

View File

@@ -1011,3 +1011,13 @@ if (\Froxlor\Froxlor::isFroxlorVersion('0.10.38')) {
showUpdateStep("Updating from 0.10.38 to 0.10.38.1", false); showUpdateStep("Updating from 0.10.38 to 0.10.38.1", false);
\Froxlor\Froxlor::updateToVersion('0.10.38.1'); \Froxlor\Froxlor::updateToVersion('0.10.38.1');
} }
if (\Froxlor\Froxlor::isFroxlorVersion('0.10.38.1')) {
showUpdateStep("Updating from 0.10.38.1 to 0.10.38.2", false);
\Froxlor\Froxlor::updateToVersion('0.10.38.2');
}
if (\Froxlor\Froxlor::isFroxlorVersion('0.10.38.2')) {
showUpdateStep("Updating from 0.10.38.2 to 0.10.38.3", false);
\Froxlor\Froxlor::updateToVersion('0.10.38.3');
}

View File

@@ -190,11 +190,17 @@ class Database
*/ */
public static function getSqlUsernameLength() public static function getSqlUsernameLength()
{ {
// MariaDB supports up to 80 characters but only 64 for databases and as we use the loginname also for
// database names, we set the limit to 64 here
if (strpos(strtolower(Database::getAttribute(\PDO::ATTR_SERVER_VERSION)), "mariadb") !== false) {
$mysql_max = 64;
} else {
// MySQL user names can be up to 32 characters long (16 characters before MySQL 5.7.8). // MySQL user names can be up to 32 characters long (16 characters before MySQL 5.7.8).
$mysql_max = 32; $mysql_max = 32;
if (version_compare(Database::getAttribute(\PDO::ATTR_SERVER_VERSION), '5.7.8', '<')) { if (version_compare(Database::getAttribute(\PDO::ATTR_SERVER_VERSION), '5.7.8', '<')) {
$mysql_max = 16; $mysql_max = 16;
} }
}
return $mysql_max; return $mysql_max;
} }

View File

@@ -7,7 +7,7 @@ final class Froxlor
{ {
// Main version variable // Main version variable
const VERSION = '0.10.38.1'; const VERSION = '0.10.38.3';
// Database version (YYYYMMDDC where C is a daily counter) // Database version (YYYYMMDDC where C is a daily counter)
const DBVERSION = '202112310'; const DBVERSION = '202112310';

View File

@@ -1,4 +1,5 @@
<?php <?php
namespace Froxlor\Settings; namespace Froxlor\Settings;
use Froxlor\Database\Database; use Froxlor\Database\Database;
@@ -391,8 +392,18 @@ class Store
} }
// Make sure mime-type matches an image // Make sure mime-type matches an image
if (!in_array(mime_content_type($_FILES[$fieldname]['tmp_name']), ['image/jpeg','image/jpg','image/png','image/gif'])) { if (function_exists('finfo_open')) {
throw new \Exception("Uploaded file not a valid image"); $finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimetype = finfo_file($finfo, $_FILES[$fieldname]['tmp_name']);
finfo_close($finfo);
} else {
$mimetype = mime_content_type($_FILES[$fieldname]['tmp_name']);
}
if (empty($mimetype)) {
$mimetype = 'application/octet-stream';
}
if (!in_array($mimetype, ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'])) {
throw new \Exception("Uploaded file is not a valid image");
} }
// Determine file extension // Determine file extension
@@ -400,6 +411,15 @@ class Store
$file_extension = strtolower(array_pop($spl)); $file_extension = strtolower(array_pop($spl));
unset($spl); unset($spl);
if (!in_array($file_extension, [
'jpeg',
'jpg',
'png',
'gif'
])) {
throw new Exception("Invalid file-extension, use one of: jpeg, jpg, png, gif");
}
// Move file // Move file
if (!move_uploaded_file($_FILES[$fieldname]['tmp_name'], $path . $fielddata['image_name'] . '.' . $file_extension)) { if (!move_uploaded_file($_FILES[$fieldname]['tmp_name'], $path . $fielddata['image_name'] . '.' . $file_extension)) {
throw new \Exception("Unable to save image to img folder"); throw new \Exception("Unable to save image to img folder");

View File

@@ -59,7 +59,7 @@ header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', time()));
// Inline-JS is no longer allowed and used // Inline-JS is no longer allowed and used
// See: http://people.mozilla.org/~bsterne/content-security-policy/index.html // See: http://people.mozilla.org/~bsterne/content-security-policy/index.html
// New stuff see: https://www.owasp.org/index.php/List_of_useful_HTTP_headers and https://www.owasp.org/index.php/Content_Security_Policy // New stuff see: https://www.owasp.org/index.php/List_of_useful_HTTP_headers and https://www.owasp.org/index.php/Content_Security_Policy
$csp_content = "default-src 'self'; script-src 'self'; connect-src 'self'; img-src 'self' data:; style-src 'self';"; $csp_content = "default-src 'self'; script-src 'self'; connect-src 'self'; img-src 'self' data:; style-src 'self'; object-src 'self'; frame-src 'self'; frame-ancestors 'self';";
header("Content-Security-Policy: " . $csp_content); header("Content-Security-Policy: " . $csp_content);
header("X-Content-Security-Policy: " . $csp_content); header("X-Content-Security-Policy: " . $csp_content);
header("X-WebKit-CSP: " . $csp_content); header("X-WebKit-CSP: " . $csp_content);

View File

@@ -673,6 +673,7 @@ $lng['admin']['message'] = 'Write a Message';
$lng['admin']['text'] = 'Message'; $lng['admin']['text'] = 'Message';
$lng['menu']['message'] = 'Messages'; $lng['menu']['message'] = 'Messages';
$lng['error']['errorsendingmail'] = 'The message to "%s" failed'; $lng['error']['errorsendingmail'] = 'The message to "%s" failed';
$lng['error']['errorsendingmailpub'] = 'The message to the given email-address failed';
$lng['error']['cannotreaddir'] = 'Unable to read directory "%s"'; $lng['error']['cannotreaddir'] = 'Unable to read directory "%s"';
$lng['message']['success'] = 'Successfully sent message to %s recipients'; $lng['message']['success'] = 'Successfully sent message to %s recipients';
$lng['message']['norecipients'] = 'No e-mail has been sent because there are no recipients in the database'; $lng['message']['norecipients'] = 'No e-mail has been sent because there are no recipients in the database';
@@ -760,7 +761,7 @@ $lng['pwdreminder']['success'] = 'Password reset successfully requested. Please
// ADDED IN 1.2.19-svn18 // ADDED IN 1.2.19-svn18
$lng['serversettings']['allow_password_reset']['title'] = 'Allow password reset by customers'; $lng['serversettings']['allow_password_reset']['title'] = 'Allow password reset by customers';
$lng['pwdreminder']['notallowed'] = 'Password reset is disabled'; $lng['pwdreminder']['notallowed'] = 'Unknown user or password reset is disabled';
// ADDED IN 1.2.19-svn21 // ADDED IN 1.2.19-svn21
@@ -2139,4 +2140,7 @@ $lng['error']['pathmustberelative'] = 'The user does not have the permission to
$lng['serversettings']['acmeshpath']['title'] = 'Path to acme.sh'; $lng['serversettings']['acmeshpath']['title'] = 'Path to acme.sh';
$lng['serversettings']['acmeshpath']['description'] = 'Set this to where acme.sh is installed to, including the acme.sh script<br>Default is <b>/root/.acme.sh/acme.sh</b>'; $lng['serversettings']['acmeshpath']['description'] = 'Set this to where acme.sh is installed to, including the acme.sh script<br>Default is <b>/root/.acme.sh/acme.sh</b>';
$lng['question']['api_reallydelete'] = 'Do you really want to delete the api-key #%d?'; $lng['question']['api_reallydelete'] = 'Do you really want to delete the api-key?';
$lng['question']['api_reallyadd'] = 'Do you really want to create a new api-key?';
$lng['question']['dnsentry_reallydelete'] = 'Do you really want to delete the dns entry?';
$lng['question']['certificate_reallydelete'] = 'Do you really want to delete the certificate?';

View File

@@ -666,6 +666,7 @@ $lng['admin']['message'] = 'Rundmail senden';
$lng['admin']['text'] = 'Nachricht'; $lng['admin']['text'] = 'Nachricht';
$lng['menu']['message'] = 'Nachrichten'; $lng['menu']['message'] = 'Nachrichten';
$lng['error']['errorsendingmail'] = 'Das Versenden der Nachricht an "%s" schlug fehl.'; $lng['error']['errorsendingmail'] = 'Das Versenden der Nachricht an "%s" schlug fehl.';
$lng['error']['errorsendingmailpub'] = 'Das Versenden der Nachricht an die angegebene E-Mail Adresse schlug fehl.';
$lng['error']['cannotreaddir'] = 'Der Ordner "%s" kann nicht gelesen werden'; $lng['error']['cannotreaddir'] = 'Der Ordner "%s" kann nicht gelesen werden';
$lng['message']['success'] = 'Nachricht erfolgreich an "%s" Empfänger gesendet'; $lng['message']['success'] = 'Nachricht erfolgreich an "%s" Empfänger gesendet';
$lng['message']['norecipients'] = 'Es wurde keine E-Mail versendet, da sich keine Empfänger in der Datenbank befinden'; $lng['message']['norecipients'] = 'Es wurde keine E-Mail versendet, da sich keine Empfänger in der Datenbank befinden';
@@ -753,7 +754,7 @@ $lng['pwdreminder']['success'] = 'Das Zurücksetzen des Passworts wurde erfolgre
// ADDED IN 1.2.19-svn18 // ADDED IN 1.2.19-svn18
$lng['serversettings']['allow_password_reset']['title'] = 'Erlaube das Zurücksetzen des Kundenpassworts.'; $lng['serversettings']['allow_password_reset']['title'] = 'Erlaube das Zurücksetzen des Kundenpassworts.';
$lng['pwdreminder']['notallowed'] = 'Das Zurücksetzen des Passworts ist deaktiviert.'; $lng['pwdreminder']['notallowed'] = 'Unbekannter Benutzer oder Zurücksetzen des Passworts ist deaktiviert.';
// ADDED IN 1.2.19-svn21 // ADDED IN 1.2.19-svn21
@@ -1785,4 +1786,7 @@ $lng['error']['pathmustberelative'] = 'Der Benutzer hat nicht die benötigten Be
$lng['serversettings']['acmeshpath']['title'] = 'Pfad zu acme.sh'; $lng['serversettings']['acmeshpath']['title'] = 'Pfad zu acme.sh';
$lng['serversettings']['acmeshpath']['description'] = 'Installationspfad zu acme.sh, inklusive acme.sh Script<br>Standard ist <b>/root/.acme.sh/acme.sh</b>'; $lng['serversettings']['acmeshpath']['description'] = 'Installationspfad zu acme.sh, inklusive acme.sh Script<br>Standard ist <b>/root/.acme.sh/acme.sh</b>';
$lng['question']['api_reallydelete'] = 'Api-Key #%d wirklich löschen?'; $lng['question']['api_reallydelete'] = 'Api-Key wirklich löschen?';
$lng['question']['api_reallyadd'] = 'Einen neuen Api-Key erstellen?';
$lng['question']['dnsentry_reallydelete'] = 'Zonen-Eintrag wirklich löschen?';
$lng['question']['certificate_reallydelete'] = 'Zertifikat wirklich löschen?';

View File

@@ -29,8 +29,9 @@ $success_message = "";
// do the delete and then just show a success-message and the certificates list again // do the delete and then just show a success-message and the certificates list again
if ($action == 'delete') { if ($action == 'delete') {
$id = isset($_GET['id']) ? (int) $_GET['id'] : 0; $id = isset($_POST['id']) ? (int) $_POST['id'] : (isset($_GET['id']) ? (int) $_GET['id'] : 0);
if ($id > 0) { if ($id > 0) {
if (isset($_POST['send']) && $_POST['send'] == 'send') {
try { try {
$json_result = Certificates::getLocal($userinfo, array( $json_result = Certificates::getLocal($userinfo, array(
'id' => $id 'id' => $id
@@ -39,6 +40,13 @@ if ($action == 'delete') {
} catch (Exception $e) { } catch (Exception $e) {
\Froxlor\UI\Response::dynamic_error($e->getMessage()); \Froxlor\UI\Response::dynamic_error($e->getMessage());
} }
} else {
\Froxlor\UI\HTML::askYesNo('certificate_reallydelete', $filename, array(
'page' => $page,
'action' => $action,
'id' => $id
), $id);
}
} }
} }

View File

@@ -198,7 +198,7 @@ class ValidateTest extends TestCase
$result = Validate::validateUsername('web123sql2', true, $mysql_max); $result = Validate::validateUsername('web123sql2', true, $mysql_max);
$this->assertTrue($result); $this->assertTrue($result);
// too long // too long
$result = Validate::validateUsername('myperfectsuperduperwebuser123sql2', true, $mysql_max); $result = Validate::validateUsername('myperfectsuperduperwebuserwhosnameisenormouslylongandprettyandshouldinnowaybeaccepted123sql2', true, $mysql_max);
$this->assertFalse($result); $this->assertFalse($result);
// not unix-conform // not unix-conform
$result = Validate::validateUsername('web123-sql2', true, $mysql_max); $result = Validate::validateUsername('web123-sql2', true, $mysql_max);