mirror of
https://github.com/mailcow/mailcow-dockerized.git
synced 2026-07-28 10:29:37 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e245ac04d9 | |||
| fea38c8e1b | |||
| cc9af65852 | |||
| 92cc8bec90 | |||
| 145745329e | |||
| 8e72d22c56 | |||
| ddd76d99cd |
@@ -14,7 +14,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Mark/Close Stale Issues and Pull Requests 🗑️
|
||||
uses: actions/stale@v11.0.0
|
||||
uses: actions/stale@v10.4.0
|
||||
with:
|
||||
repo-token: ${{ secrets.STALE_ACTION_PAT }}
|
||||
days-before-stale: 60
|
||||
|
||||
@@ -42,6 +42,11 @@ http {
|
||||
https https;
|
||||
}
|
||||
|
||||
map $server_port $sogo_auth_internal {
|
||||
65510 "1";
|
||||
default "";
|
||||
}
|
||||
|
||||
{% if HTTP_REDIRECT %}
|
||||
# HTTP to HTTPS redirect
|
||||
server {
|
||||
|
||||
@@ -116,6 +116,8 @@ location ~ \.php$ {
|
||||
include /etc/nginx/fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||
# trusted internal-auth marker; empty for external clients (see nginx.conf map)
|
||||
fastcgi_param SOGO_AUTH_INTERNAL $sogo_auth_internal;
|
||||
fastcgi_read_timeout 3600;
|
||||
fastcgi_send_timeout 3600;
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ $raw_data = mb_convert_encoding($raw_data_content, 'HTML-ENTITIES', "UTF-8");
|
||||
$raw_size = (int)$_FILES['message']['size'];
|
||||
|
||||
$qid = $meta['qid'] ?? 'unknown';
|
||||
$subject = $meta['subject'] ?? '';
|
||||
$subject = iconv_mime_decode($meta['subject'] ?? '');
|
||||
$score = $meta['score'] ?? 0;
|
||||
$rcpts = $meta['rcpt'] ?? array();
|
||||
$user = $meta['user'] ?? 'unknown';
|
||||
|
||||
@@ -50,7 +50,7 @@ $qid = $meta['qid'] ?? 'unknown';
|
||||
$rcpts = $meta['rcpt'] ?? array();
|
||||
$sender = $meta['from'] ?? '';
|
||||
$ip = $meta['ip'] ?? 'unknown';
|
||||
$subject = $meta['subject'] ?? '';
|
||||
$subject = iconv_mime_decode($meta['subject'] ?? '');
|
||||
$messageid= $meta['message_id'] ?? '';
|
||||
$priority = 0;
|
||||
|
||||
|
||||
@@ -429,14 +429,25 @@ function domain_admin_sso($_action, $_data) {
|
||||
|
||||
switch ($_action) {
|
||||
case 'check':
|
||||
$token = $_data;
|
||||
$token = preg_replace('/[^a-zA-Z0-9-]/', '', $_data);
|
||||
|
||||
$stmt = $pdo->prepare("SELECT `t1`.`username` FROM `da_sso` AS `t1` JOIN `admin` AS `t2` ON `t1`.`username` = `t2`.`username` WHERE `t1`.`token` = :token AND `t1`.`created` > DATE_SUB(NOW(), INTERVAL '30' SECOND) AND `t2`.`active` = 1 AND `t2`.`superadmin` = 0;");
|
||||
$stmt->execute(array(
|
||||
':token' => preg_replace('/[^a-zA-Z0-9-]/', '', $token)
|
||||
':token' => $token
|
||||
));
|
||||
$return = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
return empty($return['username']) ? false : $return['username'];
|
||||
if (empty($return['username'])) {
|
||||
return false;
|
||||
}
|
||||
// single-use: consume the token; if a concurrent request already used it, deny
|
||||
$del = $pdo->prepare("DELETE FROM `da_sso` WHERE `token` = :token");
|
||||
$del->execute(array(
|
||||
':token' => $token
|
||||
));
|
||||
if ($del->rowCount() < 1) {
|
||||
return false;
|
||||
}
|
||||
return $return['username'];
|
||||
case 'issue':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
$_SESSION['return'][] = array(
|
||||
|
||||
@@ -5647,8 +5647,11 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
$stmt->execute(array(
|
||||
':username' => $username
|
||||
));
|
||||
$stmt = $pdo->prepare("DELETE FROM `sogo_acl` WHERE `c_object` LIKE '%/" . $username . "/%' OR `c_uid` = :username");
|
||||
// bind LIKE pattern + escape LIKE wildcards so the value matches literally (no SQLi, no over-match)
|
||||
$c_object_like = '%/' . addcslashes($username, '\\%_') . '/%';
|
||||
$stmt = $pdo->prepare("DELETE FROM `sogo_acl` WHERE `c_object` LIKE :c_object_like OR `c_uid` = :username");
|
||||
$stmt->execute(array(
|
||||
':c_object_like' => $c_object_like,
|
||||
':username' => $username
|
||||
));
|
||||
$stmt = $pdo->prepare("DELETE FROM `sogo_store` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
|
||||
@@ -6044,8 +6047,11 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
$stmt->execute(array(
|
||||
':username' => $username
|
||||
));
|
||||
$stmt = $pdo->prepare("DELETE FROM `sogo_acl` WHERE `c_object` LIKE '%/" . str_replace('%', '\%', $username) . "/%' OR `c_uid` = :username");
|
||||
// bind LIKE pattern + escape LIKE wildcards so the value matches literally (no SQLi, no over-match)
|
||||
$c_object_like = '%/' . addcslashes($username, '\\%_') . '/%';
|
||||
$stmt = $pdo->prepare("DELETE FROM `sogo_acl` WHERE `c_object` LIKE :c_object_like OR `c_uid` = :username");
|
||||
$stmt->execute(array(
|
||||
':c_object_like' => $c_object_like,
|
||||
':username' => $username
|
||||
));
|
||||
$stmt = $pdo->prepare("DELETE FROM `sogo_store` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
|
||||
@@ -6201,8 +6207,11 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
$stmt->execute(array(
|
||||
':username' => $name
|
||||
));
|
||||
$stmt = $pdo->prepare("DELETE FROM `sogo_acl` WHERE `c_object` LIKE '%/" . $name . "/%' OR `c_uid` = :username");
|
||||
// bind LIKE pattern + escape LIKE wildcards so the value matches literally (no SQLi, no over-match)
|
||||
$c_object_like = '%/' . addcslashes($name, '\\%_') . '/%';
|
||||
$stmt = $pdo->prepare("DELETE FROM `sogo_acl` WHERE `c_object` LIKE :c_object_like OR `c_uid` = :username");
|
||||
$stmt->execute(array(
|
||||
':c_object_like' => $c_object_like,
|
||||
':username' => $name
|
||||
));
|
||||
$stmt = $pdo->prepare("DELETE FROM `sogo_store` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
|
||||
|
||||
@@ -22,7 +22,7 @@ function quarantine($_action, $_data = null) {
|
||||
return false;
|
||||
}
|
||||
$stmt = $pdo->prepare('SELECT `id` FROM `quarantine` LEFT OUTER JOIN `user_acl` ON `user_acl`.`username` = `rcpt`
|
||||
WHERE `qhash` = :hash
|
||||
WHERE `qhash` = :hash
|
||||
AND user_acl.quarantine = 1
|
||||
AND rcpt IN (SELECT username FROM mailbox)');
|
||||
$stmt->execute(array(':hash' => $hash));
|
||||
@@ -65,7 +65,7 @@ function quarantine($_action, $_data = null) {
|
||||
return false;
|
||||
}
|
||||
$stmt = $pdo->prepare('SELECT `id` FROM `quarantine` LEFT OUTER JOIN `user_acl` ON `user_acl`.`username` = `rcpt`
|
||||
WHERE `qhash` = :hash
|
||||
WHERE `qhash` = :hash
|
||||
AND `user_acl`.`quarantine` = 1
|
||||
AND `username` IN (SELECT `username` FROM `mailbox`)');
|
||||
$stmt->execute(array(':hash' => $hash));
|
||||
@@ -170,6 +170,8 @@ function quarantine($_action, $_data = null) {
|
||||
}
|
||||
elseif ($release_format == 'raw') {
|
||||
$detail_row['msg'] = preg_replace('/^X-Spam-Flag: (.*)/m', 'X-Pre-Release-Spam-Flag: $1', $detail_row['msg']);
|
||||
// dot-stuffing per RFC5321 4.5.2: escape leading dots
|
||||
$detail_row['msg'] = preg_replace('~^\.~m', '..', $detail_row['msg']);
|
||||
$postfix_talk = array(
|
||||
array('220', 'HELO quarantine' . chr(10)),
|
||||
array('250', 'MAIL FROM: ' . $sender . chr(10)),
|
||||
@@ -180,7 +182,7 @@ function quarantine($_action, $_data = null) {
|
||||
array('221', '')
|
||||
);
|
||||
// Thanks to https://stackoverflow.com/questions/6632399/given-an-email-as-raw-text-how-can-i-send-it-using-php
|
||||
$smtp_connection = fsockopen($postfix, 590, $errno, $errstr, 1);
|
||||
$smtp_connection = fsockopen($postfix, 590, $errno, $errstr, 1);
|
||||
if (!$smtp_connection) {
|
||||
logger(array('return' => array(
|
||||
array(
|
||||
@@ -192,7 +194,7 @@ function quarantine($_action, $_data = null) {
|
||||
return false;
|
||||
}
|
||||
for ($i=0; $i < count($postfix_talk); $i++) {
|
||||
$smtp_resource = fgets($smtp_connection, 256);
|
||||
$smtp_resource = fgets($smtp_connection, 256);
|
||||
if (substr($smtp_resource, 0, 3) !== $postfix_talk[$i][0]) {
|
||||
$ret = substr($smtp_resource, 0, 3);
|
||||
$ret = (empty($ret)) ? '-' : $ret;
|
||||
@@ -465,17 +467,19 @@ function quarantine($_action, $_data = null) {
|
||||
}
|
||||
elseif ($release_format == 'raw') {
|
||||
$row['msg'] = preg_replace('/^X-Spam-Flag: (.*)/m', 'X-Pre-Release-Spam-Flag: $1', $row['msg']);
|
||||
// dot-stuffing per RFC5321 4.5.2: escape leading dots
|
||||
$row['msg'] = preg_replace('~^\.~m', '..', $row['msg']);
|
||||
$postfix_talk = array(
|
||||
array('220', 'HELO quarantine' . chr(10)),
|
||||
array('250', 'MAIL FROM: ' . $sender . chr(10)),
|
||||
array('250', 'RCPT TO: ' . $row['rcpt'] . chr(10)),
|
||||
array('250', 'DATA' . chr(10)),
|
||||
array('354', str_replace("\n.", '', $row['msg']) . chr(10) . '.' . chr(10)),
|
||||
array('354', $row['msg'] . chr(10) . '.' . chr(10)),
|
||||
array('250', 'QUIT' . chr(10)),
|
||||
array('221', '')
|
||||
);
|
||||
// Thanks to https://stackoverflow.com/questions/6632399/given-an-email-as-raw-text-how-can-i-send-it-using-php
|
||||
$smtp_connection = fsockopen($postfix, 590, $errno, $errstr, 1);
|
||||
$smtp_connection = fsockopen($postfix, 590, $errno, $errstr, 1);
|
||||
if (!$smtp_connection) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'warning',
|
||||
@@ -485,7 +489,7 @@ function quarantine($_action, $_data = null) {
|
||||
return false;
|
||||
}
|
||||
for ($i=0; $i < count($postfix_talk); $i++) {
|
||||
$smtp_resource = fgets($smtp_connection, 256);
|
||||
$smtp_resource = fgets($smtp_connection, 256);
|
||||
if (substr($smtp_resource, 0, 3) !== $postfix_talk[$i][0]) {
|
||||
$ret = substr($smtp_resource, 0, 3);
|
||||
$ret = (empty($ret)) ? '-' : $ret;
|
||||
@@ -833,7 +837,7 @@ function quarantine($_action, $_data = null) {
|
||||
)));
|
||||
return false;
|
||||
}
|
||||
$stmt = $pdo->prepare('SELECT * FROM `quarantine` WHERE `qhash` = :hash');
|
||||
$stmt = $pdo->prepare('SELECT * FROM `quarantine` WHERE `qhash` = :hash');
|
||||
$stmt->execute(array(':hash' => $hash));
|
||||
return $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
break;
|
||||
|
||||
@@ -1089,6 +1089,8 @@ jQuery(function($){
|
||||
return str;
|
||||
}).join('<br>\n');
|
||||
item.subject = escapeHtml(item.subject);
|
||||
if (item.sender_mime != null) item.sender_mime = escapeHtml(item.sender_mime);
|
||||
if (item['message-id'] != null) item['message-id'] = escapeHtml(item['message-id']);
|
||||
var scan_time = item.time_real.toFixed(3);
|
||||
if (item.time_virtual) {
|
||||
scan_time += ' / ' + item.time_virtual.toFixed(3);
|
||||
@@ -1212,6 +1214,11 @@ jQuery(function($){
|
||||
});
|
||||
} else if (table == 'rllog') {
|
||||
$.each(data, function (i, item) {
|
||||
if (item.header_from != null) item.header_from = escapeHtml(item.header_from);
|
||||
if (item.header_subject != null) item.header_subject = escapeHtml(item.header_subject);
|
||||
if (item.from != null) item.from = escapeHtml(item.from);
|
||||
if (item.rcpt != null) item.rcpt = escapeHtml(item.rcpt);
|
||||
if (item.message_id != null) item.message_id = escapeHtml(item.message_id);
|
||||
if (item.user == null) {
|
||||
item.user = "none";
|
||||
}
|
||||
|
||||
@@ -996,6 +996,7 @@ jQuery(function($){
|
||||
'style="min-width:2em;width:' + item.percent_in_use + '%">' + item.percent_in_use + '%' + '</div></div>'
|
||||
};
|
||||
item.username = escapeHtml(item.username);
|
||||
item.name = escapeHtml(item.name);
|
||||
|
||||
if (Array.isArray(item.tags)){
|
||||
var tags = '';
|
||||
|
||||
+11
-2
@@ -8,8 +8,12 @@ $ALLOW_ADMIN_EMAIL_LOGIN = (preg_match(
|
||||
$session_var_user_allowed = 'sogo-sso-user-allowed';
|
||||
$session_var_pass = 'sogo-sso-pass';
|
||||
|
||||
// only the internal nginx auth_request loopback (127.0.0.1:65510) sets this;
|
||||
// external clients can never supply it (bare fastcgi param, not an HTTP header)
|
||||
$is_internal_auth = (($_SERVER['SOGO_AUTH_INTERNAL'] ?? '') === '1');
|
||||
|
||||
// validate credentials for basic auth requests
|
||||
if (isset($_SERVER['PHP_AUTH_USER'])) {
|
||||
if ($is_internal_auth && isset($_SERVER['PHP_AUTH_USER'])) {
|
||||
// load prerequisites only when required
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
|
||||
|
||||
@@ -50,6 +54,11 @@ elseif (isset($_GET['login'])) {
|
||||
(($_SESSION['acl']['login_as'] == "1" && $ALLOW_ADMIN_EMAIL_LOGIN !== 0) || ($is_dual === false && $login == $_SESSION['mailcow_cc_username']))) {
|
||||
if (filter_var($login, FILTER_VALIDATE_EMAIL)) {
|
||||
if (user_get_alias_details($login) !== false) {
|
||||
// enforce tenant boundary
|
||||
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $login)) {
|
||||
header("Location: /");
|
||||
exit;
|
||||
}
|
||||
// Block SOGo access if pending actions (2FA setup, password update)
|
||||
if (!empty($_SESSION['pending_tfa_setup']) || !empty($_SESSION['pending_pw_update'])) {
|
||||
header("Location: /");
|
||||
@@ -80,7 +89,7 @@ elseif (isset($_GET['login'])) {
|
||||
exit;
|
||||
}
|
||||
// only check for admin-login on sogo GUI requests
|
||||
elseif (isset($_SERVER['HTTP_X_ORIGINAL_URI']) && strcasecmp(substr($_SERVER['HTTP_X_ORIGINAL_URI'], 0, 9), "/SOGo/so/") === 0) {
|
||||
elseif ($is_internal_auth && isset($_SERVER['HTTP_X_ORIGINAL_URI']) && strcasecmp(substr($_SERVER['HTTP_X_ORIGINAL_URI'], 0, 9), "/SOGo/so/") === 0) {
|
||||
// this is an nginx auth_request call, we check for existing sogo-sso session variables
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/vars.inc.php';
|
||||
if (file_exists($_SERVER['DOCUMENT_ROOT'] . '/inc/vars.local.inc.php')) {
|
||||
|
||||
Reference in New Issue
Block a user