Compare commits

..

10 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
64fe2e6d0d Fix DEV_MODE bypass to work when SESS_REMOTE_UA not set
Co-authored-by: DerLinkman <62480600+DerLinkman@users.noreply.github.com>
2025-12-12 16:36:32 +00:00
copilot-swe-agent[bot]
f01ada9377 Add isset check for DEV_MODE to prevent undefined index
Co-authored-by: DerLinkman <62480600+DerLinkman@users.noreply.github.com>
2025-12-12 16:30:39 +00:00
copilot-swe-agent[bot]
ae6420dc80 Fix DEV_MODE access using $GLOBALS instead of global keyword
Co-authored-by: DerLinkman <62480600+DerLinkman@users.noreply.github.com>
2025-12-12 16:23:16 +00:00
copilot-swe-agent[bot]
a1b0004be9 Add comment explaining User-Agent length validation
Co-authored-by: DerLinkman <62480600+DerLinkman@users.noreply.github.com>
2025-12-12 16:14:09 +00:00
copilot-swe-agent[bot]
88376566f9 Add validation for User-Agent in DEV_MODE bypass
Co-authored-by: DerLinkman <62480600+DerLinkman@users.noreply.github.com>
2025-12-12 16:12:37 +00:00
copilot-swe-agent[bot]
89b4676641 Allow User-Agent changes in DEV_MODE for responsive testing
Co-authored-by: DerLinkman <62480600+DerLinkman@users.noreply.github.com>
2025-12-12 16:11:05 +00:00
copilot-swe-agent[bot]
a710b0e580 Fix line ending issue in functions.inc.php - preserve CRLF
Co-authored-by: DerLinkman <62480600+DerLinkman@users.noreply.github.com>
2025-12-12 15:59:55 +00:00
copilot-swe-agent[bot]
b3e6891802 Fix session handling - explicitly start session after destroy
Co-authored-by: DerLinkman <62480600+DerLinkman@users.noreply.github.com>
2025-12-12 15:53:53 +00:00
copilot-swe-agent[bot]
19225b223c Fix User-Agent validation error after session expiry and regeneration
Co-authored-by: DerLinkman <62480600+DerLinkman@users.noreply.github.com>
2025-12-12 15:51:56 +00:00
copilot-swe-agent[bot]
3e5a58be8f Initial plan 2025-12-12 15:45:29 +00:00
5 changed files with 49 additions and 58 deletions

View File

@@ -117,77 +117,51 @@ while ($row = $sth->fetchrow_arrayref()) {
if ($enc1 eq "TLS") { $enc1 = "--tls1"; } elsif ($enc1 eq "SSL") { $enc1 = "--ssl1"; } else { undef $enc1; }
my $template = $run_dir . '/imapsync.XXXXXXX';
my $passfile1 = File::Temp->new(TEMPLATE => $template);
my $passfile2 = File::Temp->new(TEMPLATE => $template);
binmode( $passfile1, ":utf8" );
print $passfile1 "$password1\n";
print $passfile2 trim($master_pass) . "\n";
my @custom_params_a = qqw($custom_params);
my $custom_params_ref = \@custom_params_a;
# Helper function to run imapsync with a specific encoding for password1
my $run_imapsync = sub {
my ($encoding) = @_;
my $passfile1 = File::Temp->new(TEMPLATE => $template);
if ($encoding eq 'utf8') {
binmode( $passfile1, ":utf8" );
} elsif ($encoding eq 'latin1') {
binmode( $passfile1, ":encoding(iso-8859-1)" );
}
print $passfile1 "$password1\n";
$passfile1->flush();
my $generated_cmds = [ "/usr/local/bin/imapsync",
"--tmpdir", "/tmp",
"--nofoldersizes",
"--addheader",
($timeout1 le "0" ? () : ('--timeout1', $timeout1)),
($timeout2 le "0" ? () : ('--timeout2', $timeout2)),
($exclude eq "" ? () : ("--exclude", $exclude)),
($subfolder2 eq "" ? () : ('--subfolder2', $subfolder2)),
($maxage eq "0" ? () : ('--maxage', $maxage)),
($maxbytespersecond eq "0" ? () : ('--maxbytespersecond', $maxbytespersecond)),
($delete2duplicates ne "1" ? () : ('--delete2duplicates')),
($subscribeall ne "1" ? () : ('--subscribeall')),
($delete1 ne "1" ? () : ('--delete')),
($delete2 ne "1" ? () : ('--delete2')),
($automap ne "1" ? () : ('--automap')),
($skipcrossduplicates ne "1" ? () : ('--skipcrossduplicates')),
(!defined($enc1) ? () : ($enc1)),
"--host1", $host1,
"--user1", $user1,
"--passfile1", $passfile1->filename,
"--port1", $port1,
"--host2", "localhost",
"--user2", $user2 . '*' . trim($master_user),
"--passfile2", $passfile2->filename,
($dry eq "1" ? ('--dry') : ()),
'--no-modulesversion',
'--noreleasecheck'];
my $stdout;
run [@$generated_cmds, @$custom_params_ref], '&>', \$stdout;
return $stdout;
};
my $generated_cmds = [ "/usr/local/bin/imapsync",
"--tmpdir", "/tmp",
"--nofoldersizes",
"--addheader",
($timeout1 le "0" ? () : ('--timeout1', $timeout1)),
($timeout2 le "0" ? () : ('--timeout2', $timeout2)),
($exclude eq "" ? () : ("--exclude", $exclude)),
($subfolder2 eq "" ? () : ('--subfolder2', $subfolder2)),
($maxage eq "0" ? () : ('--maxage', $maxage)),
($maxbytespersecond eq "0" ? () : ('--maxbytespersecond', $maxbytespersecond)),
($delete2duplicates ne "1" ? () : ('--delete2duplicates')),
($subscribeall ne "1" ? () : ('--subscribeall')),
($delete1 ne "1" ? () : ('--delete')),
($delete2 ne "1" ? () : ('--delete2')),
($automap ne "1" ? () : ('--automap')),
($skipcrossduplicates ne "1" ? () : ('--skipcrossduplicates')),
(!defined($enc1) ? () : ($enc1)),
"--host1", $host1,
"--user1", $user1,
"--passfile1", $passfile1->filename,
"--port1", $port1,
"--host2", "localhost",
"--user2", $user2 . '*' . trim($master_user),
"--passfile2", $passfile2->filename,
($dry eq "1" ? ('--dry') : ()),
'--no-modulesversion',
'--noreleasecheck'];
try {
$is_running = $dbh->prepare("UPDATE imapsync SET is_running = 1, success = NULL, exit_status = NULL WHERE id = ?");
$is_running->bind_param( 1, ${id} );
$is_running->execute();
# First attempt with UTF-8 encoding (default behavior)
my $stdout = $run_imapsync->('utf8');
# Check if authentication failed
my $auth_failed = ($stdout =~ /\b(LOGIN failed|authentication failed|AUTHENTICATIONFAILED)\b/i);
# If authentication failed with UTF-8, retry with Latin-1 encoding for legacy passwords
if ($auth_failed) {
$stdout = $run_imapsync->('latin1');
}
run [@$generated_cmds, @$custom_params_ref], '&>', \my $stdout;
# check exit code and status
($exit_code, $exit_status) = ($stdout =~ m/Exiting\swith\sreturn\svalue\s(\d+)\s\(([^:)]+)/);

View File

@@ -3397,6 +3397,8 @@ function set_user_loggedin_session($user) {
session_regenerate_id(true);
$_SESSION['mailcow_cc_username'] = $user;
$_SESSION['mailcow_cc_role'] = 'user';
// Update User-Agent after session regeneration to prevent validation errors
$_SESSION['SESS_REMOTE_UA'] = $_SERVER['HTTP_USER_AGENT'];
$sogo_sso_pass = file_get_contents("/etc/sogo-sso/sogo-sso.pass");
$_SESSION['sogo-sso-user-allowed'][] = $user;
$_SESSION['sogo-sso-pass'] = $sogo_sso_pass;

View File

@@ -43,6 +43,9 @@ if (!isset($_SESSION['SESS_REMOTE_UA'])) {
if (isset($_SESSION['LAST_ACTIVITY']) && (time() - $_SESSION['LAST_ACTIVITY'] > $SESSION_LIFETIME)) {
session_unset();
session_destroy();
session_start();
// After destroying session, we need to reset the User-Agent for the new session
$_SESSION['SESS_REMOTE_UA'] = $_SERVER['HTTP_USER_AGENT'];
}
$_SESSION['LAST_ACTIVITY'] = time();
@@ -134,6 +137,12 @@ function session_check() {
return true;
}
if (!isset($_SESSION['SESS_REMOTE_UA']) || ($_SESSION['SESS_REMOTE_UA'] != $_SERVER['HTTP_USER_AGENT'])) {
// In development mode, allow User-Agent changes (e.g., for responsive testing in dev tools)
// Validate UA is not empty and has reasonable length (most UAs are under 200 chars, 500 is safe upper limit)
if (isset($GLOBALS['DEV_MODE']) && $GLOBALS['DEV_MODE'] && !empty($_SERVER['HTTP_USER_AGENT']) && strlen($_SERVER['HTTP_USER_AGENT']) < 500) {
$_SESSION['SESS_REMOTE_UA'] = $_SERVER['HTTP_USER_AGENT'];
return true;
}
$_SESSION['return'][] = array(
'type' => 'warning',
'msg' => 'session_ua'

View File

@@ -50,6 +50,8 @@ if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) {
session_regenerate_id(true);
$_SESSION['mailcow_cc_username'] = $login_user;
$_SESSION['mailcow_cc_role'] = "admin";
// Update User-Agent after session regeneration to prevent validation errors
$_SESSION['SESS_REMOTE_UA'] = $_SERVER['HTTP_USER_AGENT'];
header("Location: /admin/dashboard");
die();
}

View File

@@ -7,6 +7,8 @@ if (!empty($_GET['sso_token'])) {
session_regenerate_id(true);
$_SESSION['mailcow_cc_username'] = $username;
$_SESSION['mailcow_cc_role'] = 'domainadmin';
// Update User-Agent after session regeneration to prevent validation errors
$_SESSION['SESS_REMOTE_UA'] = $_SERVER['HTTP_USER_AGENT'];
header('Location: /domainadmin/mailbox');
}
}
@@ -61,6 +63,8 @@ if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) {
session_regenerate_id(true);
$_SESSION['mailcow_cc_username'] = $login_user;
$_SESSION['mailcow_cc_role'] = "domainadmin";
// Update User-Agent after session regeneration to prevent validation errors
$_SESSION['SESS_REMOTE_UA'] = $_SERVER['HTTP_USER_AGENT'];
header("Location: /domainadmin/mailbox");
die();
}