From adf36a177d8a7be434eb9cf49d7e372b9c2458e2 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Tue, 23 Sep 2025 22:21:08 +0300 Subject: [PATCH 1/9] Fix to make sure SSL_HOST also has port set --- miniserv.pl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/miniserv.pl b/miniserv.pl index 37fe6ad3f..ff4e75afb 100755 --- a/miniserv.pl +++ b/miniserv.pl @@ -2504,6 +2504,8 @@ if (&get_type($full) eq "internal/cgi" && $validated != 4) { $ENV{"SSL_HSTS"} = $config{"ssl_hsts"}; if ($use_ssl) { $ENV{"SSL_HOST"} = $ssl_host; + $ENV{"SSL_HOST"} .= ":".$port + if ($port && $port !~ /^(80|443)$/); $ENV{"SSL_HOST_CERT"} = &ssl_hostname_match($ssl_host, $ssl_cert_hosts); } From 55b5739287f10771e3e6a8238bb8798de1c34ea7 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Tue, 23 Sep 2025 22:35:16 +0300 Subject: [PATCH 2/9] Fix to correctly pick remote host based on connection --- web-lib-funcs.pl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/web-lib-funcs.pl b/web-lib-funcs.pl index ccc765ebf..c7e9f83ea 100755 --- a/web-lib-funcs.pl +++ b/web-lib-funcs.pl @@ -13257,18 +13257,19 @@ my ($mod, $cgi, $def, $forcehost) = @_; # Work out the base URL my $url; +my $proto = lc($ENV{'HTTPS'}) eq 'on' ? 'https' : 'http'; +my $remote_host = $proto eq 'https' ? $ENV{'SSL_HOST'} : $ENV{'HTTP_HOST'}; if (!$def && $gconfig{'webmin_email_url'}) { # From a config option $url = $gconfig{'webmin_email_url'}; } -elsif ($ENV{'HTTP_HOST'}) { +elsif ($remote_host) { # From this HTTP request - my $host = $ENV{'HTTP_HOST'}; + my $host = $remote_host; my $port = $ENV{'SERVER_PORT'} || 80; if ($host =~ s/:(\d+)$//) { $port = $1; } - my $proto = lc($ENV{'HTTPS'}) eq 'on' ? 'https' : 'http'; my $defport = $proto eq 'https' ? 443 : 80; $url = $proto."://".$host.($port == $defport ? "" : ":".$port); $url .= $gconfig{'webprefix'} if ($gconfig{'webprefix'}); From cd489ccefc82771f30ddf437602290581f0ea4bb Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Tue, 23 Sep 2025 23:11:44 +0300 Subject: [PATCH 3/9] Fix not to limit to valid SSL certificate, as self-signed is also valid and safe enough --- forgot.cgi | 3 --- forgot_form.cgi | 3 --- forgot_send.cgi | 3 --- lang/en | 1 - 4 files changed, 10 deletions(-) diff --git a/forgot.cgi b/forgot.cgi index 104ee90f5..f5f5d4bf0 100755 --- a/forgot.cgi +++ b/forgot.cgi @@ -15,9 +15,6 @@ $gconfig{'forgot_pass'} || &error($text{'forgot_ecannot'}); my $timeout = $gconfig{'passreset_timeout'} || 15; $remote_user && &error($text{'forgot_elogin'}); $ENV{'HTTPS'} eq 'ON' || &error($text{'forgot_essl'}); -$ENV{'SSL_HOST_CERT'} == 1 || - &error(&text('forgot_esslhost', - &html_escape($ENV{'SSL_HOST'} || $ENV{'HTTP_HOST'}))); # Check that the random ID is valid $in{'id'} =~ /^[a-f0-9]+$/i || &error($text{'forgot_eid'}); diff --git a/forgot_form.cgi b/forgot_form.cgi index a7757f98a..c8e8b2c23 100755 --- a/forgot_form.cgi +++ b/forgot_form.cgi @@ -14,9 +14,6 @@ $trust_unknown_referers = 1; $gconfig{'forgot_pass'} || &error($text{'forgot_ecannot'}); $remote_user && &error($text{'forgot_elogin'}); $ENV{'HTTPS'} eq 'ON' || &error($text{'forgot_essl'}); -$ENV{'SSL_HOST_CERT'} == 1 || - &error(&text('forgot_esslhost', - &html_escape($ENV{'SSL_HOST'} || $ENV{'HTTP_HOST'}))); &ui_print_header(undef, $text{'forgot_title'}, "", undef, undef, 1, 1); diff --git a/forgot_send.cgi b/forgot_send.cgi index caa2b8b69..7c4e72c37 100755 --- a/forgot_send.cgi +++ b/forgot_send.cgi @@ -13,9 +13,6 @@ $no_acl_check++; $gconfig{'forgot_pass'} || &error($text{'forgot_ecannot'}); $remote_user && &error($text{'forgot_elogin'}); $ENV{'HTTPS'} eq 'ON' || &error($text{'forgot_essl'}); -$ENV{'SSL_HOST_CERT'} == 1 || - &error(&text('forgot_esslhost', - &html_escape($ENV{'SSL_HOST'} || $ENV{'HTTP_HOST'}))); # Lookup the Webmin user &foreign_require("acl"); diff --git a/lang/en b/lang/en index 50959fa2d..130b73f52 100644 --- a/lang/en +++ b/lang/en @@ -184,7 +184,6 @@ forgot_elogin=Forgotten password pages cannot be used when you are already logge forgot_erate=Too many password reset attempts for $1! Please try again later. forgot_eremote=Webmin server on this system is not running or is not configured to allow forgotten password recovery. forgot_essl=Forgotten password recovery can only be used over an SSL connection -forgot_esslhost=Forgotten password recovery cannot be used with invalid SSL hostname $1 pam_header=Login to Webmin pam_mesg=You must respond to the question below to login to Webmin server on $1. From d1ee0a5ed6033b1fe76c907897a1a7fb11b0809d Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Tue, 23 Sep 2025 23:36:09 +0300 Subject: [PATCH 4/9] Fix not to completely forbid password reset without SSL --- forgot.cgi | 3 ++- forgot_form.cgi | 3 ++- forgot_send.cgi | 3 ++- lang/en | 3 ++- usermin/edit_session.cgi | 5 ++++- webmin/edit_session.cgi | 5 ++++- 6 files changed, 16 insertions(+), 6 deletions(-) diff --git a/forgot.cgi b/forgot.cgi index f5f5d4bf0..21cfdd498 100755 --- a/forgot.cgi +++ b/forgot.cgi @@ -14,7 +14,8 @@ $trust_unknown_referers = 1; $gconfig{'forgot_pass'} || &error($text{'forgot_ecannot'}); my $timeout = $gconfig{'passreset_timeout'} || 15; $remote_user && &error($text{'forgot_elogin'}); -$ENV{'HTTPS'} eq 'ON' || &error($text{'forgot_essl'}); +$ENV{'HTTPS'} eq 'ON' || $gconfig{'forgot_pass'} == 2 || + &error($text{'forgot_essl'}); # Check that the random ID is valid $in{'id'} =~ /^[a-f0-9]+$/i || &error($text{'forgot_eid'}); diff --git a/forgot_form.cgi b/forgot_form.cgi index c8e8b2c23..a8ed3d82b 100755 --- a/forgot_form.cgi +++ b/forgot_form.cgi @@ -13,7 +13,8 @@ $trust_unknown_referers = 1; &error_setup($text{'forgot_err'}); $gconfig{'forgot_pass'} || &error($text{'forgot_ecannot'}); $remote_user && &error($text{'forgot_elogin'}); -$ENV{'HTTPS'} eq 'ON' || &error($text{'forgot_essl'}); +$ENV{'HTTPS'} eq 'ON' || $gconfig{'forgot_pass'} == 2 || + &error($text{'forgot_essl'}); &ui_print_header(undef, $text{'forgot_title'}, "", undef, undef, 1, 1); diff --git a/forgot_send.cgi b/forgot_send.cgi index 7c4e72c37..c45be3184 100755 --- a/forgot_send.cgi +++ b/forgot_send.cgi @@ -12,7 +12,8 @@ $no_acl_check++; &error_setup($text{'forgot_err'}); $gconfig{'forgot_pass'} || &error($text{'forgot_ecannot'}); $remote_user && &error($text{'forgot_elogin'}); -$ENV{'HTTPS'} eq 'ON' || &error($text{'forgot_essl'}); +$ENV{'HTTPS'} eq 'ON' || $gconfig{'forgot_pass'} == 2 || + &error($text{'forgot_essl'}); # Lookup the Webmin user &foreign_require("acl"); diff --git a/lang/en b/lang/en index 130b73f52..59a1f7b55 100644 --- a/lang/en +++ b/lang/en @@ -183,7 +183,8 @@ forgot_eunixlock=User user's password is locked! forgot_elogin=Forgotten password pages cannot be used when you are already logged in to Webmin! forgot_erate=Too many password reset attempts for $1! Please try again later. forgot_eremote=Webmin server on this system is not running or is not configured to allow forgotten password recovery. -forgot_essl=Forgotten password recovery can only be used over an SSL connection +forgot_essl=Forgotten password recovery can only be used over an SSL connection unless explicitly allowed +forgot_nossl=Yes, and allow over insecure connection pam_header=Login to Webmin pam_mesg=You must respond to the question below to login to Webmin server on $1. diff --git a/usermin/edit_session.cgi b/usermin/edit_session.cgi index 1269a6e99..798fa5d81 100755 --- a/usermin/edit_session.cgi +++ b/usermin/edit_session.cgi @@ -39,7 +39,10 @@ print &ui_table_row("", # Enable forgotten password recovery print &ui_table_row($text{'session_forgot'}, - &ui_yesno_radio("forgot", $uconfig{'forgot_pass'})); + &ui_radio("forgot", $uconfig{'forgot_pass'}, + [ [ 0, $text{'no'}."
" ], + [ 1, $text{'yes'}."
" ], + [ 2, $text{'forgot_nossl'} ] ])); # Log to syslog eval "use Sys::Syslog qw(:DEFAULT setlogsock)"; diff --git a/webmin/edit_session.cgi b/webmin/edit_session.cgi index bc6645e30..3a54c2490 100755 --- a/webmin/edit_session.cgi +++ b/webmin/edit_session.cgi @@ -42,7 +42,10 @@ print &ui_table_row("", # Enable forgotten password recovery print &ui_table_row($text{'session_forgot'}, - &ui_yesno_radio("forgot", $gconfig{'forgot_pass'})); + &ui_radio("forgot", $gconfig{'forgot_pass'}, + [ [ 0, $text{'no'}."
" ], + [ 1, $text{'yes'}."
" ], + [ 2, $text{'forgot_nossl'} ] ])); # Block bad password requests $gconfig{'passreset_failures'} //= 3; From 4e229d8adbb6742c995dc620e5690eb20b009349 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Wed, 24 Sep 2025 00:15:12 +0300 Subject: [PATCH 5/9] Fix to show clear warning when trying to reset password over insecure connection --- forgot_form.cgi | 3 ++- lang/en | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/forgot_form.cgi b/forgot_form.cgi index a8ed3d82b..118f490d7 100755 --- a/forgot_form.cgi +++ b/forgot_form.cgi @@ -17,7 +17,8 @@ $ENV{'HTTPS'} eq 'ON' || $gconfig{'forgot_pass'} == 2 || &error($text{'forgot_essl'}); &ui_print_header(undef, $text{'forgot_title'}, "", undef, undef, 1, 1); - +print &ui_alert_box(" ⚠ ".$text{'forgot_nossl_warn'}, 'warn') + if ($gconfig{'forgot_pass'} == 2 && $ENV{'HTTPS'} ne 'ON'); print "
\n"; print $text{'forgot_desc'},"

\n"; print &ui_form_start("forgot_send.cgi", "post"); diff --git a/lang/en b/lang/en index 59a1f7b55..4385e8d71 100644 --- a/lang/en +++ b/lang/en @@ -185,6 +185,7 @@ forgot_erate=Too many password reset attempts for $1! Please try again later. forgot_eremote=Webmin server on this system is not running or is not configured to allow forgotten password recovery. forgot_essl=Forgotten password recovery can only be used over an SSL connection unless explicitly allowed forgot_nossl=Yes, and allow over insecure connection +forgot_nossl_warn=Warning: This password reset is being sent over an insecure, not-encrypted connection and is vulnerable to man-in-the-middle (MITM) and header-injection attacks. pam_header=Login to Webmin pam_mesg=You must respond to the question below to login to Webmin server on $1. From e194e2d500d85ed3dfb0d7c9382c487e1a59dd37 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Wed, 24 Sep 2025 14:28:54 +0300 Subject: [PATCH 6/9] Revert "Fix to make sure SSL_HOST also has port set" This reverts commit adf36a177d8a7be434eb9cf49d7e372b9c2458e2. --- miniserv.pl | 2 -- 1 file changed, 2 deletions(-) diff --git a/miniserv.pl b/miniserv.pl index ff4e75afb..37fe6ad3f 100755 --- a/miniserv.pl +++ b/miniserv.pl @@ -2504,8 +2504,6 @@ if (&get_type($full) eq "internal/cgi" && $validated != 4) { $ENV{"SSL_HSTS"} = $config{"ssl_hsts"}; if ($use_ssl) { $ENV{"SSL_HOST"} = $ssl_host; - $ENV{"SSL_HOST"} .= ":".$port - if ($port && $port !~ /^(80|443)$/); $ENV{"SSL_HOST_CERT"} = &ssl_hostname_match($ssl_host, $ssl_cert_hosts); } From e5d6c5627d3063d48e48bce46cf472df54ab0b6f Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Wed, 24 Sep 2025 14:37:18 +0300 Subject: [PATCH 7/9] Add back SSL host cert check but only in SSL mode; show faked host (HTTP_HOST) first for clarity --- forgot.cgi | 4 ++++ forgot_form.cgi | 4 ++++ forgot_send.cgi | 4 ++++ lang/en | 1 + 4 files changed, 13 insertions(+) diff --git a/forgot.cgi b/forgot.cgi index 21cfdd498..98aefbacf 100755 --- a/forgot.cgi +++ b/forgot.cgi @@ -16,6 +16,10 @@ my $timeout = $gconfig{'passreset_timeout'} || 15; $remote_user && &error($text{'forgot_elogin'}); $ENV{'HTTPS'} eq 'ON' || $gconfig{'forgot_pass'} == 2 || &error($text{'forgot_essl'}); +$ENV{'SSL_HOST_CERT'} == 1 || + &error(&text('forgot_esslhost', + &html_escape($ENV{'HTTP_HOST'} || $ENV{'SSL_HOST'}))) + if ($ENV{'HTTPS'} eq 'ON'); # Check that the random ID is valid $in{'id'} =~ /^[a-f0-9]+$/i || &error($text{'forgot_eid'}); diff --git a/forgot_form.cgi b/forgot_form.cgi index 118f490d7..12015ae3c 100755 --- a/forgot_form.cgi +++ b/forgot_form.cgi @@ -15,6 +15,10 @@ $gconfig{'forgot_pass'} || &error($text{'forgot_ecannot'}); $remote_user && &error($text{'forgot_elogin'}); $ENV{'HTTPS'} eq 'ON' || $gconfig{'forgot_pass'} == 2 || &error($text{'forgot_essl'}); +$ENV{'SSL_HOST_CERT'} == 1 || + &error(&text('forgot_esslhost', + &html_escape($ENV{'HTTP_HOST'} || $ENV{'SSL_HOST'}))) + if ($ENV{'HTTPS'} eq 'ON'); &ui_print_header(undef, $text{'forgot_title'}, "", undef, undef, 1, 1); print &ui_alert_box(" ⚠ ".$text{'forgot_nossl_warn'}, 'warn') diff --git a/forgot_send.cgi b/forgot_send.cgi index c45be3184..3db41c484 100755 --- a/forgot_send.cgi +++ b/forgot_send.cgi @@ -14,6 +14,10 @@ $gconfig{'forgot_pass'} || &error($text{'forgot_ecannot'}); $remote_user && &error($text{'forgot_elogin'}); $ENV{'HTTPS'} eq 'ON' || $gconfig{'forgot_pass'} == 2 || &error($text{'forgot_essl'}); +$ENV{'SSL_HOST_CERT'} == 1 || + &error(&text('forgot_esslhost', + &html_escape($ENV{'HTTP_HOST'} || $ENV{'SSL_HOST'}))) + if ($ENV{'HTTPS'} eq 'ON'); # Lookup the Webmin user &foreign_require("acl"); diff --git a/lang/en b/lang/en index 4385e8d71..228458057 100644 --- a/lang/en +++ b/lang/en @@ -185,6 +185,7 @@ forgot_erate=Too many password reset attempts for $1! Please try again later. forgot_eremote=Webmin server on this system is not running or is not configured to allow forgotten password recovery. forgot_essl=Forgotten password recovery can only be used over an SSL connection unless explicitly allowed forgot_nossl=Yes, and allow over insecure connection +forgot_esslhost=Forgotten password recovery cannot be used with invalid SSL hostname $1 forgot_nossl_warn=Warning: This password reset is being sent over an insecure, not-encrypted connection and is vulnerable to man-in-the-middle (MITM) and header-injection attacks. pam_header=Login to Webmin From 3717dfb505e43cf2cac8b0ad9021dfc3bb48f518 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Wed, 24 Sep 2025 15:11:26 +0300 Subject: [PATCH 8/9] Revert "Fix to correctly pick remote host based on connection" This reverts commit 55b5739287f10771e3e6a8238bb8798de1c34ea7. --- web-lib-funcs.pl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/web-lib-funcs.pl b/web-lib-funcs.pl index c7e9f83ea..ccc765ebf 100755 --- a/web-lib-funcs.pl +++ b/web-lib-funcs.pl @@ -13257,19 +13257,18 @@ my ($mod, $cgi, $def, $forcehost) = @_; # Work out the base URL my $url; -my $proto = lc($ENV{'HTTPS'}) eq 'on' ? 'https' : 'http'; -my $remote_host = $proto eq 'https' ? $ENV{'SSL_HOST'} : $ENV{'HTTP_HOST'}; if (!$def && $gconfig{'webmin_email_url'}) { # From a config option $url = $gconfig{'webmin_email_url'}; } -elsif ($remote_host) { +elsif ($ENV{'HTTP_HOST'}) { # From this HTTP request - my $host = $remote_host; + my $host = $ENV{'HTTP_HOST'}; my $port = $ENV{'SERVER_PORT'} || 80; if ($host =~ s/:(\d+)$//) { $port = $1; } + my $proto = lc($ENV{'HTTPS'}) eq 'on' ? 'https' : 'http'; my $defport = $proto eq 'https' ? 443 : 80; $url = $proto."://".$host.($port == $defport ? "" : ":".$port); $url .= $gconfig{'webprefix'} if ($gconfig{'webprefix'}); From 97678653c6eb9c43e07ad6e2dadc4e911285a38b Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Wed, 24 Sep 2025 16:04:19 +0300 Subject: [PATCH 9/9] Fix to prefer SSL_HOST over HTTP_HOST --- web-lib-funcs.pl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/web-lib-funcs.pl b/web-lib-funcs.pl index ccc765ebf..bcc8853ca 100755 --- a/web-lib-funcs.pl +++ b/web-lib-funcs.pl @@ -13261,10 +13261,12 @@ if (!$def && $gconfig{'webmin_email_url'}) { # From a config option $url = $gconfig{'webmin_email_url'}; } -elsif ($ENV{'HTTP_HOST'}) { +elsif ($ENV{'HTTP_HOST'} || $ENV{'SSL_HOST'}) { # From this HTTP request - my $host = $ENV{'HTTP_HOST'}; my $port = $ENV{'SERVER_PORT'} || 80; + my $host = $ENV{'SSL_HOST'} + ? "$ENV{'SSL_HOST'}:$port" + : $ENV{'HTTP_HOST'}; if ($host =~ s/:(\d+)$//) { $port = $1; }