Compare commits

...

69 Commits
2.510 ... 2.520

Author SHA1 Message Date
Ilia Ross
8957333dca Fix not to quotemeta hardcoded flag #2159 2025-10-05 01:17:28 +03:00
Ilia Ross
1caf80bbc1 Update changelog 2025-10-04 17:15:46 +03:00
Ilia Ross
18c12c7f8b Fix to correctly fetch "PPTP VPN Client" version as --help returns different output
https://github.com/webmin/webmin/issues/2567
2025-10-04 12:35:26 +03:00
Ilia Ross
78a3ab924d Fix to display error message correctly in PPTP VPN Client 2025-10-04 12:29:03 +03:00
Ilia Ross
652f2db774 Fix tooltips 2025-10-04 12:17:28 +03:00
Jamie Cameron
8b58d7cce3 Merge branch 'master' of github.com:webmin/webmin 2025-10-03 20:04:19 -07:00
Jamie Cameron
52f87286a4 Add tooltips for white and black lists 2025-10-03 19:43:46 -07:00
Ilia Ross
28c7939636 Fix to check if helper command is running as root 2025-10-04 01:12:05 +03:00
Ilia Ross
face8319f2 Fix to safely kill only targeted scripts 2025-10-04 00:45:21 +03:00
Jamie Cameron
7a651027bf New version bump 2025-10-02 20:15:30 -07:00
Jamie Cameron
bbf317803a Merge pull request #2565 from webmin/dev/check-https-redirect-later
Fix to redirect to HTTPS when we get the host from the browser URL
2025-10-02 19:25:35 -07:00
Ilia Ross
d694649872 Fix to safely get user hostname from URL 2025-10-03 03:19:54 +03:00
Ilia Ross
1091434ff4 Add more days to view log since #2564 2025-10-02 16:24:41 +03:00
Ilia Ross
991795c296 Fix to redirect to HTTPS when we get the host from the browser URL 2025-10-02 15:52:14 +03:00
Ilia Ross
8932f3bd6a Fix to truly move option tag rather than recreating
*Note: It keep existing styles and using index from "add()" will also put moved options to the top of the select and make them clearly visible
2025-10-02 13:36:22 +03:00
Ilia Ross
b3ec083c7b Update changelog 2025-10-01 23:45:22 +03:00
Ilia Ross
da18dea780 Update changelog release date to today (night) 2025-10-01 20:19:58 +03:00
Ilia Ross
35f8061049 Fix to kill Webmin subprocesses during RC stop on FreeBSD and other systems 2025-10-01 20:12:54 +03:00
Ilia Ross
e6e79a7eb5 Fix to replace vmstat with /proc sampler for same output with much lower overhead 2025-10-01 18:35:20 +03:00
Ilia Ross
a4d1280ef7 Fix copy-paste artifact 2025-10-01 14:34:40 +03:00
Ilia Ross
cc2cc62717 Fix to query specific fields in FreeBSD memory stats collection
*Note: Replace `sysctl -a` with targeted queries for only the 5 needed values (hw.physmem, hw.pagesize, vm.stats.vm.v_*_count) instead of dumping thousands of kernel params. This reduces `get_memory_info()` overhead from 25% CPU to ~5% CPU when called by real-time monitoring every 1-3 seconds.
2025-10-01 14:33:22 +03:00
Ilia Ross
c59591e3de Update changelog 2025-10-01 01:15:54 +03:00
Ilia Ross
fc4e2751dc Fix broken redirect when there is no way to get FQDN
*Note: "$host = &get_socket_name(SOCK, $ipv6fhs{$s});" won't return FQDN if it can't be resolved (or if it isn't in /etc/hosts), breaking some redirects, most likely proxied ones that aren't using HTTPS in its config.
2025-10-01 01:03:45 +03:00
Ilia Ross
4dc64f5028 Fix recommended package name (again) 2025-09-30 23:51:27 +03:00
Ilia Ross
01d2323496 Fix to mention IO::Socket::INET6 too 2025-09-30 22:28:08 +03:00
Ilia Ross
4c8b0fe008 Update changelog for 2.520 2025-09-30 17:26:46 +03:00
Jamie Cameron
e44ec464eb Merge branch 'master' of github.com:webmin/webmin 2025-09-29 13:35:49 -07:00
Jamie Cameron
98c54fe3fb Include config files by default
https://github.com/webmin/webmin/issues/2562
2025-09-29 13:35:43 -07:00
Ilia Ross
86968bfc31 Fix support for other Raspberry Pi sensors #2545 2025-09-29 18:23:10 +03:00
Ilia Ross
956ad7ed0f Dev: Trigger rebuilt for testing purposes for all modules
[no-commit-check]
2025-09-28 02:05:04 +03:00
Ilia Ross
a0d99e0a31 Dev: Trigger rebuilt for testing purposes 2025-09-28 02:02:11 +03:00
Ilia Ross
f27b1415be Fix to remove extra space
*Note: Made to trigger a re-built for testing purposes
2025-09-28 01:27:54 +03:00
Ilia Ross
1f4b467ea8 Fix to never mess around with headers; no headers check log 2025-09-28 01:20:29 +03:00
Ilia Ross
95423c7425 Fix not to loose prefix 2025-09-27 20:09:52 +03:00
Ilia Ross
89d23c5aa8 Fix for tar builds have no release but consider edition
*Note: Release for tar files should exist, however edition that can be passed like .gpl or .pro should exist
2025-09-27 20:06:57 +03:00
Ilia Ross
be3fcb89b0 Fix to remove release number from tar builds as never applicable 2025-09-27 18:28:52 +03:00
Ilia Ross
25fa7c589d Fix to add prefix for tar builds too 2025-09-27 03:50:54 +03:00
Ilia Ross
c86c45b10a Fix to keep release number too in tar builds 2025-09-27 03:00:44 +03:00
Ilia Ross
f10540bd54 Fix to add a flag to copy tar build too 2025-09-27 02:08:45 +03:00
Jamie Cameron
388f51843f Merge pull request #2558 from webmin/dev/remove-bottleneck-of-shelling-out
Fix to remove significant bottleneck of shelling out
2025-09-25 20:28:21 -07:00
Ilia Ross
7a723719da Fix to recommend Sys::Syslog module #2557 2025-09-25 15:33:52 +03:00
Ilia Ross
a027ad5dd6 Fix variable names to avoid ambiguity
* Note: Discussed here:
https://github.com/webmin/webmin/pull/2553#issuecomment-3328436525
2025-09-25 15:23:06 +03:00
Ilia Ross
d99a24b045 Fix to remove significant bottleneck of shelling out 2025-09-25 14:48:14 +03:00
Jamie Cameron
f08ad4eb19 Merge branch 'master' of github.com:webmin/webmin 2025-09-24 20:32:47 -07:00
Jamie Cameron
df97b4a419 Gracefully handle monitor types that don't exist
https://forum.virtualmin.com/t/finally-upgraded-and-a-problem/135204
2025-09-24 20:32:40 -07:00
Jamie Cameron
1ef0914610 Merge pull request #2553 from webmin/dev/further-fixes-to-ssl-host
Add final fixes to address ongoing issue with checking remote host
2025-09-24 20:24:28 -07:00
Ilia Ross
97678653c6 Fix to prefer SSL_HOST over HTTP_HOST 2025-09-24 16:04:19 +03:00
Ilia Ross
3717dfb505 Revert "Fix to correctly pick remote host based on connection"
This reverts commit 55b5739287.
2025-09-24 15:11:26 +03:00
Ilia Ross
e5d6c5627d Add back SSL host cert check but only in SSL mode; show faked host (HTTP_HOST) first for clarity 2025-09-24 14:37:18 +03:00
Ilia Ross
e194e2d500 Revert "Fix to make sure SSL_HOST also has port set"
This reverts commit adf36a177d.
2025-09-24 14:28:54 +03:00
Jamie Cameron
d5a22a592f Cache reads of the same cert file 2025-09-23 17:10:55 -07:00
Jamie Cameron
890a4ffd3f Valid http host header against cert name, as it may be different from the SSL hostname 2025-09-23 17:05:32 -07:00
Ilia Ross
4e229d8adb Fix to show clear warning when trying to reset password over insecure connection 2025-09-24 00:15:12 +03:00
Ilia Ross
d1ee0a5ed6 Fix not to completely forbid password reset without SSL 2025-09-23 23:36:09 +03:00
Ilia Ross
cd489ccefc Fix not to limit to valid SSL certificate, as self-signed is also valid and safe enough 2025-09-23 23:11:44 +03:00
Ilia Ross
55b5739287 Fix to correctly pick remote host based on connection 2025-09-23 22:35:16 +03:00
Ilia Ross
adf36a177d Fix to make sure SSL_HOST also has port set 2025-09-23 22:21:08 +03:00
Ilia Ross
9393162b0e Add a complete overhaul of var_dump, which is now portable
*Note: Can now be easily used in "miniserv.pl" or anywhere else. Supports multiple passed params.
2025-09-23 18:58:08 +03:00
Jamie Cameron
287fb3cb81 Only allow forgotten password reset when in SSL mode and hostname is valid 2025-09-22 21:42:26 -07:00
Jamie Cameron
61b2603e06 Add check for same hostname 2025-09-22 21:14:07 -07:00
Jamie Cameron
eb02824bfc Detect if SSL hostname is valid for the cert being used, and pass it to an environment variable 2025-09-22 21:13:46 -07:00
Ilia Ross
6191a222ae Revert "Fix to make sure the mail URL uses a well-known host name"
This reverts commit e88a77d32a.
2025-09-23 02:22:17 +03:00
Ilia Ross
2ac82016aa Revert "Perfect previous code"
This reverts commit 8f987d21a9.
2025-09-23 02:22:06 +03:00
Ilia Ross
8f987d21a9 Perfect previous code 2025-09-21 22:54:25 +03:00
Ilia Ross
e88a77d32a Fix to make sure the mail URL uses a well-known host name 2025-09-21 21:47:12 +03:00
Ilia Ross
5231b31ddd Add classes for text colors 2025-09-20 21:22:35 +03:00
Ilia Ross
5c5d5fe699 Fix printing bottom button row
https://forum.virtualmin.com/t/clarifying-how-to-create-custom-links/135115?u=ilia
2025-09-17 22:07:57 +03:00
Ilia Ross
34fba22799 Revert "Dev: Testing build script issues for separate modules"
This reverts commit a708e5f6e9.
2025-09-17 04:30:23 +03:00
Ilia Ross
a708e5f6e9 Dev: Testing build script issues for separate modules 2025-09-17 01:01:17 +03:00
33 changed files with 560 additions and 139 deletions

View File

@@ -1,5 +1,25 @@
## Changelog
#### 2.520 (October 4, 2025)
* Fix to make sure the mail URL uses a well-known host name [security]
* Fix support for other Raspberry Pi sensors [#2545](https://github.com/webmin/webmin/issues/2545)
* Fix the printing of the bottom button row in the form column table
* Fix to recommend Perl `Sys::Syslog` module [#2557](https://github.com/webmin/webmin/issues/2557)
* Fix to avoid using short hostname in HTTPS redirects when an FQDN is available
* Fix to use _/proc_ sampler instead of `vmstat` for the same output with much lower overhead
* Fix to query specific fields in FreeBSD memory stats collection, cutting CPU use by 80%
* Fix to kill Webmin subprocesses during RC stop on FreeBSD and other systems
* Fix to correctly fetch command version in `PPTP VPN Client` module [#2567](https://github.com/webmin/webmin/issues/2567)
* Add a complete overhaul of `var_dump` subroutine, which is now fully portable
* Update the Authentic theme to the latest version with various fixes:
- Fix the text color when reading email in the Read User Mail module [webmin#2555](https://github.com/webmin/webmin/issues/2555)
- Fix to ensure the selected color palette is correctly stored when changed manually [webmin#2552](https://github.com/webmin/webmin/issues/2552)
- Fix a bug when the Webmin version label was missing when copying to clipboard system information from the dashboard
- Fix DNS query spike from network stats collection on FreeBSD [webmin#2556](https://github.com/webmin/webmin/issues/2556)
- Fix to display the appropriate icon for proxy mode on new Bunny DNS
- Fix spinner color in toast messages for dark palette
- Fix other bugs and add various small improvements
#### 2.510 (September 16, 2025)
* Fix to ensure DNSSEC re-signing period is less than 30 days in the BIND DNS module
* Fix to treat 201 as a valid response code in the internal download function

View File

@@ -15,6 +15,8 @@ if ($in{'new'}) {
$backup = { 'emode' => 0,
'email' => $gconfig{'webmin_email_to'},
'sched' => 1,
'configfile' => 1,
'nofiles' => 0,
'mins' => 0,
'hours' => 0,
'days' => '*',

View File

@@ -9,6 +9,13 @@ use Getopt::Long qw(:config permute pass_through);
use Term::ANSIColor qw(:constants);
use Pod::Usage;
# Check if root
if ($> != 0) {
die BRIGHT_RED, "Error: ", RESET, BRIGHT_YELLOW,"webmin", RESET,
" command must be run as root\n";
exit 1;
}
my $a0 = $ARGV[0];
sub main {

View File

@@ -647,5 +647,4 @@ else {
}
}
1;

View File

@@ -50,7 +50,7 @@ if ($action ne '-b' && $action ne '-k') {
$types .= " ".join(' ', @extra) ;
}
}
my $args = quotemeta($action)." ".$types." ".quotemeta($recursive);
my $args = quotemeta($action)." ".$types." ".$recursive;
$args =~ s/\s+/ /g;
$args = &trim($args);
foreach my $file (@files) {

View File

@@ -14,6 +14,12 @@ $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' || $gconfig{'forgot_pass'} == 2 ||
&error($text{'forgot_essl'});
$ENV{'SSL_CN_CERT'} == 1 ||
&error(&text('forgot_esslhost',
&html_escape($ENV{'HTTP_HOST'} || $ENV{'SSL_CN'})))
if ($ENV{'HTTPS'} eq 'ON');
# Check that the random ID is valid
$in{'id'} =~ /^[a-f0-9]+$/i || &error($text{'forgot_eid'});
@@ -23,6 +29,12 @@ my $linkfile = $main::forgot_password_link_dir."/".$in{'id'};
time() - $link{'time'} > 60*$timeout &&
&error(&text('forgot_etime', $timeout));
# Check that the hostname in the original email matches the current hostname
my ($basehost) = &parse_http_url(&get_webmin_email_url());
if ($basehost ne $link{'host'}) {
&error($text{'forgot_ehost'});
}
# Get the Webmin user
&foreign_require("acl");
my ($wuser) = grep { $_->{'name'} eq $link{'user'} } &acl::list_users();

View File

@@ -13,9 +13,16 @@ $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' || $gconfig{'forgot_pass'} == 2 ||
&error($text{'forgot_essl'});
$ENV{'SSL_CN_CERT'} == 1 ||
&error(&text('forgot_esslhost',
&html_escape($ENV{'HTTP_HOST'} || $ENV{'SSL_CN'})))
if ($ENV{'HTTPS'} eq 'ON');
&ui_print_header(undef, $text{'forgot_title'}, "", undef, undef, 1, 1);
print &ui_alert_box("<b> ⚠ ".$text{'forgot_nossl_warn'}, 'warn')
if ($gconfig{'forgot_pass'} == 2 && $ENV{'HTTPS'} ne 'ON');
print "<center>\n";
print $text{'forgot_desc'},"<p>\n";
print &ui_form_start("forgot_send.cgi", "post");

View File

@@ -12,6 +12,12 @@ $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' || $gconfig{'forgot_pass'} == 2 ||
&error($text{'forgot_essl'});
$ENV{'SSL_CN_CERT'} == 1 ||
&error(&text('forgot_esslhost',
&html_escape($ENV{'HTTP_HOST'} || $ENV{'SSL_CN'})))
if ($ENV{'HTTPS'} eq 'ON');
# Lookup the Webmin user
&foreign_require("acl");
@@ -103,8 +109,11 @@ sleep($maxtries);
$wuser->{'pass'} eq '*LK*' && &error($text{'forgot_elock'});
# Generate a random ID and tracking file for this password reset
my $baseurl = &get_webmin_email_url();
my ($basehost) = &parse_http_url($baseurl);
my %link = ( 'id' => &acl::generate_random_id(),
'remote' => $ENV{'REMOTE_ADDR'},
'host' => $basehost,
'time' => $now,
'user' => $wuser->{'name'},
'uuser' => $uuser ? $uuser->{'user'} : undef,
@@ -114,7 +123,6 @@ my $linkfile = $main::forgot_password_link_dir."/".$link{'id'};
&lock_file($linkfile);
&write_file($linkfile, \%link);
&unlock_file($linkfile);
my $baseurl = &get_webmin_email_url();
my $url = $baseurl.'/forgot.cgi?id='.&urlize($link{'id'});
my $username = $muser ? $muser->{'user'} :
$uuser ? $uuser->{'user'} : $wuser->{'name'};

View File

@@ -891,3 +891,15 @@ body > .mode > b[data-mode="server-manager"] > a > .ff-cloudmin {
field-sizing: content !important;
min-width: 40px !important;
}
.text-danger {
color: #bc0303;
}
.text-success {
color: #3c763d;
}
.text-warning {
color: #b58900;
}
.text-info {
color: #108eda;
}

View File

@@ -159,6 +159,7 @@ forgot_erandom=Failed to generate random ID!
forgot_eid=Missing or invalid-looking reset ID!
forgot_eid2=Reset ID is not valid!
forgot_etime=Password reset email is more than $1 minutes old.
forgot_ehost=Hostname mismatch in reset email
forgot_newpass=New password for user $1
forgot_newpass2=New password again
forgot_passok=Change Password
@@ -182,6 +183,10 @@ 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 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
pam_mesg=You must respond to the question below to login to Webmin server on $1.

View File

@@ -21,6 +21,7 @@ journal_since0=Latest available
journal_since1=Real-time follow
journal_since2=Current boot
journal_since2-1=Last boot
journal_since30=30 days ago
journal_since3=7 days ago
journal_since4=24 hours ago
journal_since5=8 hours ago

View File

@@ -36,6 +36,7 @@ return [
{ "--follow" => $text{'journal_since1'} },
{ "--boot" => $text{'journal_since2'} },
{ "--boot -1" => $text{'journal_since2-1'} },
{ "--since '30 days ago'" => $text{'journal_since30'} },
{ "--since '7 days ago'" => $text{'journal_since3'} },
{ "--since '24 hours ago'" => $text{'journal_since4'} },
{ "--since '8 hours ago'" => $text{'journal_since5'} },

View File

@@ -109,7 +109,7 @@ if ($product eq "webmin") {
$size = int(`du -sk $tmp_dir`);
@deps = ( "perl", "libnet-ssleay-perl", "openssl", "libauthen-pam-perl", "libpam-runtime", "libio-pty-perl", "unzip", "shared-mime-info", "tar", "libdigest-sha-perl", "libdigest-md5-perl", "gzip" );
$deps = join(", ", @deps);
@recommends = ( "libdatetime-perl", "libdatetime-timezone-perl", "libdatetime-locale-perl", "libtime-piece-perl", "libencode-detect-perl", "libtime-hires-perl", "libsocket6-perl", "html2text", "qrencode", "libdbi-perl", "libdbd-mysql-perl", "libjson-xs-perl" );
@recommends = ( "libdatetime-perl", "libdatetime-timezone-perl", "libdatetime-locale-perl", "libtime-piece-perl", "libencode-detect-perl", "libtime-hires-perl", "libsocket6-perl", "html2text", "qrencode", "libdbi-perl", "libdbd-mysql-perl", "libjson-xs-perl", "libsys-syslog-perl" );
$recommends = join(", ", @recommends);
open(CONTROL, ">$control_file");
print CONTROL <<EOF;

View File

@@ -34,7 +34,7 @@ my $allow_overwrite = 0;
my ($force_theme, $no_prefix, $set_prefix,
$obsolete_wbm, $vendor, $url, $force_usermin, $final_mod, $sign, $keyname,
$epoch, $dir, $ver, @exclude,
$epoch, $dir, $ver, @exclude, $copy_tar,
$rpmdepends, $norpmdepends, $rpmrecommends, $norpmrecommends,
$no_requires, $no_recommends, $no_suggests, $no_conflicts, $no_provides,
@@ -136,6 +136,9 @@ while(@ARGV) {
elsif ($a eq "--mod-list") {
$mod_list = shift(@ARGV);
}
elsif ($a eq "--copy-tar") {
$copy_tar = 1;
}
elsif ($a =~ /^\-\-/) {
print STDERR "Unknown option $a\n";
exit(1);
@@ -556,6 +559,15 @@ fi
EOF
close($SPEC);
# Put the tar in /tmp too if tar package is requested
my $tar_release_file;
if ($copy_tar) {
my $tar_release = $release;
$tar_release =~ s/^[0-9]+//;
$tar_release_file = "$prefix$mod-$ver$tar_release.tar.gz";
system("cp $rpm_source_dir/$mod.tar.gz /tmp/$tar_release_file");
}
# Build the actual RPM
my $cmd = -x "/usr/bin/rpmbuild" ? "/usr/bin/rpmbuild" : "/bin/rpm";
system("$cmd -ba $spec_dir/$prefix$mod.spec") && exit;
@@ -570,10 +582,20 @@ if ($sign) {
if ($target_dir =~ /:/) {
# scp to dest
system("scp $rpm_dir/$prefix$mod-$ver-$release.noarch.rpm $target_dir/$prefix$mod-$ver-$release.noarch.rpm");
# scp the tar too if requested
if ($copy_tar) {
system("scp /tmp/$tar_release_file $target_dir/$tar_release_file");
unlink("/tmp/$tar_release_file");
}
}
elsif ($rpm_dir ne $target_dir) {
# Just copy
system("/bin/cp $rpm_dir/$prefix$mod-$ver-$release.noarch.rpm $target_dir/$prefix$mod-$ver-$release.noarch.rpm");
# Copy the tar too if requested
if ($copy_tar) {
system("/bin/cp /tmp/$tar_release_file $target_dir/$tar_release_file");
unlink("/tmp/$tar_release_file");
}
}
# read_file(file, &assoc, [&order], [lowercase])

View File

@@ -88,7 +88,7 @@ Release: $rel
Provides: %{name}-%{version} perl(WebminCore)
Requires(pre): /usr/bin/perl
Requires: /bin/sh /usr/bin/perl perl(lib) perl(open) perl(Net::SSLeay) perl(Time::Local) perl(Data::Dumper) perl(File::Path) perl(File::Basename) perl(Digest::SHA) perl(Digest::MD5) openssl unzip tar gzip
Recommends: perl(DateTime) perl(DateTime::TimeZone) perl(DateTime::Locale) perl(Time::Piece) perl(Encode::Detect) perl(Time::HiRes) perl(Socket6) html2text shared-mime-info perl-File-Basename perl-File-Path perl-JSON-XS qrencode perl(DBI) perl(DBD::mysql)
Recommends: perl(DateTime) perl(DateTime::TimeZone) perl(DateTime::Locale) perl(Time::Piece) perl(Encode::Detect) perl(Time::HiRes) perl(Socket6) perl(Sys::Syslog) html2text shared-mime-info perl-File-Basename perl-File-Path perl-JSON-XS qrencode perl(DBI) perl(DBD::mysql)
AutoReq: 0
License: BSD-3-Clause
Group: System/Tools

View File

@@ -941,7 +941,9 @@ while(1) {
(ord($byte) & 0x80))) {
($ssl_con,
$ssl_certfile,
$ssl_keyfile) =
$ssl_keyfile,
$ssl_cn,
$ssl_alts) =
&ssl_connection_for_ip(
SOCK, $ipv6fhs{$s});
print DEBUG "ssl_con returned ".
@@ -1379,26 +1381,48 @@ $method = $page = $request_uri = undef;
print DEBUG "handle_request reqline=$reqline\n";
alarm(0);
if (!$use_ssl && $config{'ssl'} && $config{'ssl_enforce'}) {
# This is an http request when https must be enforced
local $urlhost = $config{'musthost'} || $host;
$urlhost = "[".$urlhost."]" if (&check_ip6address($urlhost));
local $wantport = $port;
if ($wantport == 80 &&
&indexof(443, @listening_on_ports) >= 0) {
# Connection was to port 80, but since we are also
# accepting on port 443, redirect to that
$wantport = 443;
# This is an HTTP request when HTTPS should be enforced
my $musthost = $config{'musthost'};
my $hostheader;
if (!$musthost) {
# Read host HTTP header because we want one earlier
alarm(10);
local $SIG{'ALRM'} = sub { die "timeout" };
while (defined(my $line = &read_line())) {
$line =~ s/\r|\n//g;
last if $line eq '';
if ($line =~ /^host:\s*(.*)$/i) {
$hostheader = "https://$1";
last;
}
}
alarm(0);
}
local $url = $wantport == 443
? "https://$urlhost/"
: "https://$urlhost:$wantport/";
# Host header must already contain full URL
my $url = $hostheader;
if (!$url) {
# No host header
local $urlhost = $musthost || $host;
$urlhost = "[".$urlhost."]" if (&check_ip6address($urlhost));
local $wantport = $port;
if ($wantport == 80 &&
&indexof(443, @listening_on_ports) >= 0) {
# Connection was to port 80, but since we are also
# accepting on port 443, redirect to that
$wantport = 443;
}
$url = $wantport == 443
? "https://$urlhost/"
: "https://$urlhost:$wantport/";
}
# Enforce HTTPS
&write_data("HTTP/1.0 302 Moved Temporarily\r\n");
&write_data("Date: $datestr\r\n");
&write_data("Server: @{[&server_info()]}\r\n");
&write_data("Location: $url\r\n");
&write_keep_alive(0);
&write_data("\r\n");
&log_error("Redirecting HTTP request to HTTPS for $acptip");
&log_error("Redirecting HTTP request to $url");
&log_request($loghost, $authuser, $reqline, 302, 0);
return 0;
}
@@ -2500,6 +2524,11 @@ if (&get_type($full) eq "internal/cgi" && $validated != 4) {
$ENV{"MINISERV_CONFIG"} = $config_file;
$ENV{"HTTPS"} = $use_ssl ? "ON" : "";
$ENV{"SSL_HSTS"} = $config{"ssl_hsts"};
if ($use_ssl) {
$ENV{"SSL_CN"} = $ssl_cn;
$ENV{"SSL_CN_CERT"} =
&ssl_hostname_match($header{'host'}, $ssl_alts);
}
$ENV{"MINISERV_PID"} = $miniserv_main_pid;
if ($use_ssl) {
$ENV{"MINISERV_CERTFILE"} = $ssl_certfile;
@@ -4776,11 +4805,18 @@ if ($config{'ssl_honorcipherorder'}) {
eval 'Net::SSLeay::CTX_set_options($ssl_ctx,
&Net::SSLeay::OP_NO_RENEGOTIATION)';
# Get the hostnames each cert is valid for
my $info = &cert_names($certfile);
my @hosts;
push(@hosts, $info->{'cn'}) if ($info->{'cn'});
push(@hosts, @{$info->{'alt'}}) if ($info->{'alt'});
return { 'keyfile' => $keyfile,
'keytime' => $kst[9],
'certfile' => $certfile,
'certtime' => $cst[9],
'extracas' => $extracas,
'hosts' => \@hosts,
'ctx' => $ssl_ctx };
}
@@ -4816,8 +4852,9 @@ alarm(0);
return undef if (!$ok);
# Check for a per-hostname SSL context and use that instead
my $h;
if (defined(&Net::SSLeay::get_servername)) {
my $h = Net::SSLeay::get_servername($ssl_con);
$h = Net::SSLeay::get_servername($ssl_con);
if ($h) {
my $c = $ssl_contexts{$h} ||
$h =~ /^[^\.]+\.(.*)$/ && $ssl_contexts{"*.$1"};
@@ -4826,7 +4863,8 @@ if (defined(&Net::SSLeay::get_servername)) {
}
}
}
return ($ssl_con, $ssl_ctx->{'certfile'}, $ssl_ctx->{'keyfile'});
return ($ssl_con, $ssl_ctx->{'certfile'}, $ssl_ctx->{'keyfile'}, $h,
$ssl_ctx->{'hosts'});
}
# parse_websockets_config()
@@ -4873,8 +4911,7 @@ print DEBUG "in reload_config_file\n";
&build_config_mappings();
&read_webmin_crons();
&precache_files();
&setup_ssl_contexts()
if ($use_ssl);
&setup_ssl_contexts() if ($use_ssl);
&parse_websockets_config();
if ($config{'session'}) {
dbmclose(%sessiondb);
@@ -5507,6 +5544,9 @@ foreach my $pe (split(/\t+/, $config{'expires_paths'})) {
# Reset cache of sudo checks
undef(%sudocache);
# Reset cache of cert files
undef(%cert_names_cache);
}
# is_group_member(&uinfo, groupname)
@@ -6995,3 +7035,75 @@ if (!$sig) {
}
return $sig;
}
=head2 cert_names($file)
Extract Common Name and Subject Alternative Names from an X.509 certificate
file. Supports both PEM and DER certificates. Returns undef if file cannot be
read or parsed. Cache results for speed.
=cut
sub cert_names
{
my ($file) = @_;
return $cert_names_cache{$file} if ($cert_names_cache{$file});
return undef if (!$file || !-r $file);
my %rv;
my $cert;
# Try PEM first
my $bio = Net::SSLeay::BIO_new_file($file, 'r');
if ($bio) {
$cert = Net::SSLeay::PEM_read_bio_X509($bio);
Net::SSLeay::BIO_free($bio);
}
# Try DER if PEM failed
if (!$cert) {
my $bio = Net::SSLeay::BIO_new_file($file, 'rb');
if ($bio) {
$cert = Net::SSLeay::d2i_X509_bio($bio);
Net::SSLeay::BIO_free($bio);
}
}
# Certificate not found
return undef if !$cert;
# Subject
my $subject = Net::SSLeay::X509_get_subject_name($cert);
if ($subject) {
# commonName
my $cn = Net::SSLeay::X509_NAME_get_text_by_NID($subject, 13);
$rv{cn} = $cn if defined $cn && $cn ne '' && $cn ne '-1';
}
# subjectAltName
my @alts = Net::SSLeay::X509_get_subjectAltNames($cert);
if (@alts) {
my @dns;
while (my ($type, $val) = splice(@alts, 0, 2)) {
push @dns, $val if $type == 2;
}
$rv{alt} = \@dns if @dns;
}
Net::SSLeay::X509_free($cert);
$cert_names_cache{$file} = \%rv;
return \%rv;
}
# ssl_hostname_match(hostname, &hosts-list)
# Does a hostname match a list of hostnames for an SSL cert?
sub ssl_hostname_match
{
my ($h, $hosts) = @_;
$h =~ s/:\d+$//;
foreach my $p (@$hosts) {
return 1 if (lc($p) eq lc($h));
return 1 if ($p =~ /^\*\.(\S+)$/ &&
(lc($h) eq lc($1) || $h =~ /^([^\.]+)\.\Q$1\E$/i));
return 2 if ($p eq "*");
}
return 0;
}

View File

@@ -22,7 +22,7 @@ if (!&has_command($config{'pptp'})) {
}
elsif (!$vers) {
# The PPP daemon is not installed
print "<p>",&text('index_epppd', "<tt>pppd</tt>"),"<p>\n";
print "<p>",&text('index_eppp', "<tt>pppd</tt>"),"<p>\n";
}
else {
# Show icons

View File

@@ -258,7 +258,7 @@ return 0;
# get_pppd_version(&out)
sub get_pppd_version
{
local $out = `pppd --help 2>&1`;
local $out = `pppd --version 2>&1`;
${$_[0]} = $out;
return $out =~ /version\s+(\S+)/i ? $1 : undef;
}

View File

@@ -101,7 +101,16 @@ ioctl($ttyfh, 536900705, 0);
sub get_memory_info
{
my $sysctl = {};
my $sysctl_output = &backquote_command("/sbin/sysctl -a 2>/dev/null");
# Get only the specific values we need not all with -a
my @needed = qw(
hw.physmem
hw.pagesize
vm.stats.vm.v_inactive_count
vm.stats.vm.v_cache_count
vm.stats.vm.v_free_count
);
my $sysctl_output = &backquote_command("/sbin/sysctl " .
join(" ", @needed) . " 2>/dev/null");
return ( ) if ($?);
foreach my $line (split(/\n/, $sysctl_output)) {
if ($line =~ m/^([^:]+):\s+(.+)\s*$/s) {

View File

@@ -700,14 +700,14 @@ if (!@fans && @cpu && @fans_all &&
}
# Fall back logic for CPU temperature and fans spread over multiple
# devices like Raspberry Pi #2517 and #2539
# devices like Raspberry Pi #2517 and #2539 #2545
if (@cpu || !@fans) {
# - Look for least two ISA voltage rails anywhere
# - See a CPU temp under cpu_thermal
# - Optionally grab a fan RPM under pwmfan-isa-*
# - Optionally grab a fan RPM under *fan-isa-*
my $can_fallback =
(!@cpu && (grep { /^\s*cpu_thermal/i } @sensors)) ||
(@cpu && !@fans && (grep { /^\s*pwmfan-isa-/i } @sensors));
(@cpu && !@fans && (grep { /fan-isa-\d+/i } @sensors));
return (\@cpu, \@fans) if (!$can_fallback);
my ($chip, $bus); # isa|pci|platform|virtual
my $isa_volt;
@@ -735,7 +735,7 @@ if (@cpu || !@fans) {
}
# Fan RPM
if (defined $chip && $chip =~ /^pwmfan/i &&
if (defined $chip && $chip =~ /fan$/i &&
/\b(?:cpu[_ ]?fan(?:\s*\d+)?|fan\d+)\s*:\s*(\d+)\s*rpm\b/i) {
my $rpm = $1 + 0;
$fan_rpm = $rpm if (!$fan_rpm || $rpm > $fan_rpm);
@@ -754,31 +754,65 @@ return (\@cpu, \@fans);
}
# get_cpu_io_usage()
# Returns a list containing CPU user, kernel, idle, io and VM time, and IO
# blocks in and out
# Returns a list of "us", "sy", "id", "wa", "st", "bi", "bo" that match `vmstat`
# output by using /proc with much lower overhead
sub get_cpu_io_usage
{
my ($nodelay) = @_;
my $interval;
$interval = " 1 2"
if (!$nodelay);
my ($out, @lines, @w);
if (&has_command("vmstat")) {
$out = &backquote_command("vmstat$interval 2>/dev/null");
@lines = split(/\r?\n/, $out);
@w = split(/\s+/, $lines[$#lines]);
shift(@w) if ($w[0] eq '');
if ($w[8] =~ /^\d+$/ && $w[9] =~ /^\d+$/) {
return ( @w[12..16], $w[8], $w[9] );
}
} elsif (&has_command("dstat")) {
$out = &backquote_command("dstat 1 1 2>/dev/null");
@lines = split(/\r?\n/, $out);
@w = split(/[\s|]+/, $lines[$#lines]);
shift(@w) if ($w[0] eq '');
return( @w[0..4], @w[6..7]);
}
return undef;
# Read CPU counters from /proc/stat, the first "cpu" line
my $read_cpu = sub {
open(my $fh, '<', '/proc/stat') or return;
my $line = <$fh>;
close($fh);
return unless defined $line && $line =~ /^cpu\s+/;
my @v = split /\s+/, $line; shift @v;
push @v, (0) x (8-@v) if @v < 8;
return @v[0..7];
};
# Read pgpgin/pgpgout from /proc/vmstat (in KB). Note, that all modern kernels,
# /proc/vmstat pgpgin/pgpgout are KiB (scale=1); not as *pages*, so no need to
# convert deltas
my $read_io = sub {
open(my $fh, '<', '/proc/vmstat') or return (undef, undef);
my ($in, $out);
while (my $l = <$fh>) {
$in = $1 if !defined $in && $l =~ /^pgpgin\s+(\d+)/;
$out = $1 if !defined $out && $l =~ /^pgpgout\s+(\d+)/;
last if defined $in && defined $out;
}
close($fh);
return ($in, $out); # KB on kernels 2.6.18+; no conversion needed
};
# Sample A
my @cb = $read_cpu->() or return;
my ($pgin_b, $pgout_b) = $read_io->();
# Sleep half a second
select(undef, undef, undef, 0.5);
# Sample B
my @ca = $read_cpu->() or return;
my ($pgin_a, $pgout_a) = $read_io->();
# CPU percentages as in vmstat
my @d = map { $ca[$_] - $cb[$_] } 0..7;
for (@d) { $_ = 0 if $_ < 0 }
my $tot = 0; $tot += $_ for @d[0..7];
return if !$tot;
# Calculate percentages
my $us = int( (($d[0] + $d[1]) / $tot) * 100 ); # user+nice
my $sy = int( (($d[2] + $d[5] + $d[6]) / $tot) * 100 ); # system+irq+softirq
my $id = int( ( $d[3] / $tot ) * 100 ); # idle
my $wa = int( ( $d[4] / $tot ) * 100 ); # iowait
my $st = int( ( $d[7] / $tot ) * 100 ); # steal
# Calculate bi/bo in KiB/s over half a second
my ($bi, $bo) = (0, 0);
$bi = int( (($pgin_a // 0) - ($pgin_b // 0)) / 0.5 );
$bo = int( (($pgout_a // 0) - ($pgout_b // 0)) / 0.5 );
return ($us, $sy, $id, $wa, $st, $bi, $bo);
}
# has_disk_stats()

View File

@@ -108,7 +108,7 @@ if ($out =~ /Primary\s+memory\s+available:\s+([0-9\.]+)\s+g/i) {
$rv[0] = $1 * 1024 * 1024;
}
else {
my $out = &backquote_command("sysctl -a hw.physmem 2>/dev/null");
my $out = &backquote_command("sysctl hw.physmem 2>/dev/null");
if ($out =~ /:\s*(\d+)/) {
$rv[0] = $1 / 1024;
}
@@ -129,7 +129,7 @@ if ($usage > $rv[0]) {
$rv[1] = $rv[0] - $usage;
# Get swap usage
$out = &backquote_command("sysctl -a vm.swapusage 2>/dev/null");
$out = &backquote_command("sysctl vm.swapusage 2>/dev/null");
if ($out =~ /total\s*=\s*([0-9\.]+)([KMGT]).*free\s*=\s*([0-9\.]+)([KMGT])/) {
$rv[2] = $1*($2 eq "K" ? 1 :
$2 eq "M" ? 1024 :
@@ -155,7 +155,7 @@ my $out = &backquote_command("uptime 2>&1");
my @rv = $out =~ /average(s)?:\s+([0-9\.]+),?\s+([0-9\.]+),?\s+([0-9\.]+)/i ?
( $2, $3, $4 ) : ( undef, undef, undef );
$out = &backquote_command("sysctl -a machdep.cpu.brand_string");
$out = &backquote_command("sysctl machdep.cpu.brand_string");
if ($out =~ /:\s*(\S.*)/) {
$rv[4] = $1;
if ($rv[4] =~ s/\s*\@\s*([0-9\.]+)(GHz|MHz)//i) {
@@ -169,7 +169,7 @@ if (!$?) {
$rv[5] = $out;
}
else {
$out = &backquote_command("sysctl -a machdep.cpu.vendor");
$out = &backquote_command("sysctl machdep.cpu.vendor");
if ($out =~ /:\s*(\S.*)/) {
$rv[5] = $1;
}
@@ -180,7 +180,7 @@ if ($out =~ /:\s*(\d+)/) {
$rv[6] = $1 * 1024;
}
$out = &backquote_command("sysctl -a machdep.cpu.core_count");
$out = &backquote_command("sysctl machdep.cpu.core_count");
if ($out =~ /:\s*(\d+)/) {
$rv[7] = $1;
}

View File

@@ -616,6 +616,19 @@ else {
print STOP "else\n";
print STOP " echo Stopping Webmin server in $wadir\n";
print STOP "fi\n";
print STOP "targets=\"stats.pl shellserver.pl\"\n";
print STOP "collect_pids() {\n";
print STOP " for s in \$targets; do\n";
print STOP " ps axww | grep \"$wadir/\" | grep \"/\$s\" | grep -v grep\n";
print STOP " done | awk '{print \$1}' | sort -u\n";
print STOP "}\n";
print STOP "pids=\$(collect_pids)\n";
print STOP "[ -n \"\$pids\" ] && kill \$pids 2>/dev/null || true\n";
print STOP "if [ \"\$1\" = \"--kill\" ]; then\n";
print STOP " sleep 1\n";
print STOP " pids=\$(collect_pids)\n";
print STOP " [ -n \"\$pids\" ] && kill -KILL \$pids 2>/dev/null || true\n";
print STOP "fi\n";
print STOP "pidfile=\`grep \"^pidfile=\" $config_directory/miniserv.conf | sed -e 's/pidfile=//g'\`\n";
print STOP "pid=\`cat \$pidfile 2>/dev/null\`\n";
print STOP "if [ \"\$pid\" != \"\" ]; then\n";
@@ -971,7 +984,7 @@ if (!$ENV{'nostart'}) {
print " - DateTime, DateTime::Locale, DateTime::TimeZone, Data::Dumper,\n";
print " - Digest::MD5, Digest::SHA, Encode::Detect, File::Basename,\n";
print " - File::Path, Net::SSLeay, Time::HiRes, Time::Local, Time::Piece,\n";
print " - JSON::XS, lib, open\n";
print " - Socket6, Sys::Syslog, JSON::XS, lib, open\n";
print " Packages:\n";
print " - openssl - Cryptography library with TLS implementation\n";
print " - shared-mime-info - Shared MIME information database\n";

View File

@@ -695,6 +695,19 @@ echo " echo Force stopping Webmin server in $wadir" >>$config_dir/.stop-init
echo "else" >>$config_dir/.stop-init
echo " echo Stopping Webmin server in $wadir" >>$config_dir/.stop-init
echo "fi" >>$config_dir/.stop-init
echo "targets=\"stats.pl shellserver.pl\"" >>$config_dir/.stop-init
echo "collect_pids() {" >>$config_dir/.stop-init
echo " for s in \$targets; do" >>$config_dir/.stop-init
echo " ps axww | grep \"$wadir/\" | grep \"/\$s\" | grep -v grep" >>$config_dir/.stop-init
echo " done | awk '{print \$1}' | sort -u" >>$config_dir/.stop-init
echo "}" >>$config_dir/.stop-init
echo "pids=\$(collect_pids)" >>$config_dir/.stop-init
echo "[ -n \"\$pids\" ] && kill \$pids 2>/dev/null || true" >>$config_dir/.stop-init
echo "if [ \"\$1\" = \"--kill\" ]; then" >>$config_dir/.stop-init
echo " sleep 1" >>$config_dir/.stop-init
echo " pids=\$(collect_pids)" >>$config_dir/.stop-init
echo " [ -n \"\$pids\" ] && kill -KILL \$pids 2>/dev/null || true" >>$config_dir/.stop-init
echo "fi" >>$config_dir/.stop-init
echo "pidfile=\`grep \"^pidfile=\" $config_dir/miniserv.conf | sed -e 's/pidfile=//g'\`" >>$config_dir/.stop-init
echo "pid=\`cat \$pidfile 2>/dev/null\`" >>$config_dir/.stop-init
echo "if [ \"\$pid\" != \"\" ]; then" >>$config_dir/.stop-init
@@ -1063,7 +1076,7 @@ if [ "$nostart" = "" ]; then
echo " - DateTime, DateTime::Locale, DateTime::TimeZone, Data::Dumper,"
echo " - Digest::MD5, Digest::SHA, Encode::Detect, File::Basename,"
echo " - File::Path, Net::SSLeay, Time::HiRes, Time::Local, Time::Piece,"
echo " - JSON::XS, lib, open"
echo " - Socket6, Sys::Syslog, JSON::XS, lib, open"
echo " Packages:"
echo " - openssl - Cryptography library with TLS implementation"
echo " - shared-mime-info - Shared MIME information database"

View File

@@ -28,13 +28,13 @@ print &ui_table_start(undef, undef, 2);
# Addresses to always whitelist
@from = &find("whitelist_from", $conf);
print &ui_table_row($text{'white_from'},
print &ui_table_row(&hlink($text{'white_from'}, 'white_from'),
&edit_textbox("whitelist_from",
[ map { @{$_->{'words'}} } @from ], 60, 10));
# Exceptions to whitelist
@un = &find("unwhitelist_from", $conf);
print &ui_table_row($text{'white_unfrom'},
print &ui_table_row(&hlink($text{'white_unfrom'}, 'white_unfrom'),
&edit_textbox("unwhitelist_from",
[ map { @{$_->{'words'}} } @un ], 60, 5));
@@ -54,7 +54,7 @@ if ($config{'show_global'}) {
# Whitelist by received header
@rcvd = &find("whitelist_from_rcvd", $conf);
print &ui_table_row($text{'white_rcvd2'},
print &ui_table_row(&hlink($text{'white_rcvd2'}, 'white_rcvd2'),
&edit_table("whitelist_from_rcvd",
[ $text{'white_addr'}, $text{'white_rcvdhost'} ],
[ map { $_->{'words'} } @rcvd ], [ 40, 30 ], undef, 3));

13
spam/help/white_from.html Normal file
View File

@@ -0,0 +1,13 @@
<header>Senders to never classify as spam</header>
The <tt>whitelist_from</tt> option is used to whitelist sender addresses which
send mail that is often tagged (incorrectly) as spam. <p>
Use of this setting is not recommended, since it blindly trusts the message,
which is routinely and easily forged by spammers and phish senders. <p>
The recommended solution is to instead use whitelist_auth or other authenticated
whitelisting methods, or <tt>whitelist_from_rcvd</tt>. <p>
<footer>

View File

@@ -0,0 +1,8 @@
<header>Sender to never classify as spam</header>
The <tt>whitelist_from_rcvd</tt> option is used to supplement the allowed
sender addresses with a check against the received headers. The first parameter
is the address to whitelist, and the second is a string to match the relay's
rDNS. <p>
<footer>

View File

@@ -0,0 +1,7 @@
<header>Exceptions for senders to never classify as spam</header>
The <tt>unwhitelist_from</tt> option is used to override a sender to never
classify as spam, for example to be more specific or to block an address that is
allowed globally. <p>
<footer>

View File

@@ -197,9 +197,14 @@ foreach $r (&expand_remotes($serv)) {
# Just include and use the local monitor library
do "${t}-monitor.pl" if (!$done_monitor{$t}++);
my $func = "get_${t}_status";
$rv = &$func($serv,
if (defined(&$func)) {
$rv = &$func($serv,
$serv->{'clone'} ? $serv->{'clone'} : $t,
$fromcgi);
}
else {
$rv = { 'up' => -1 };
}
}
alarm(0);
};

View File

@@ -673,7 +673,7 @@ $rv .= &ui_columns_table($heads, $width, $data, $types, $nosort, $title,
$emptymsg);
# Add the bottom links unless excluded
if ($selectall && $selectall != 2) {
if ($selectall != 2) {
$rv .= $links;
}
@@ -1108,9 +1108,8 @@ if (dir == 1 && opts_idx >= 0) {
for(var i=0; i<opts.options.length; i++) {
var o = opts.options[i];
if (o.selected) {
vals.options[vals.options.length] =
new Option(o.text, o.value);
opts.remove(i);
o.selected = false;
vals.add(o, 0);
i--;
}
}
@@ -1120,9 +1119,8 @@ else if (dir == 0 && vals_idx >= 0) {
for(var i=0; i<vals.options.length; i++) {
var o = vals.options[i];
if (o.selected) {
opts.options[opts.options.length] =
new Option(o.text, o.value);
vals.remove(i);
o.selected = false;
opts.add(o, 0);
i--;
}
}

View File

@@ -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'}."<br>" ],
[ 1, $text{'yes'}."<br>" ],
[ 2, $text{'forgot_nossl'} ] ]));
# Log to syslog
eval "use Sys::Syslog qw(:DEFAULT setlogsock)";

View File

@@ -1 +1 @@
2.510
2.520

View File

@@ -7477,84 +7477,189 @@ if ($gconfig{'logfiles'} && !&get_module_variable('$no_log_file_changes')) {
}
}
=head2 var_dump(objref, [filename|no-html], [pid-to-filename])
=head2 var_dump(@data)
Prints to UI or dumps to a file content of array/hash
ref or variable. For internal debug use only.
Dump any Perl values to a log file and, when running in a
Webmin web context, also print them to the UI.
Examples to print output to UI:
var_dump(\@array_ref, 0); # No HTML, standard output
var_dump(\%hash_ref); # HTML output (newlines and spaces replaced with <br> and &nbsp;)
=item * Call with one or more arguments.
Examples to dump content to a file (under Webmin tempdir):
var_dump(\@array_ref, 'array_ref_name', 'add-process-pid-to-filename');
var_dump(\%hash_ref, 'hash_ref_name');
Use a reference for composites to avoid list flattening.
var_dump(\@array);
var_dump(\%hash);
var_dump($hashref);
var_dump($scalar);
var_dump($scalar, \%hash, \@array);
=item * Log file
Prints by appending to the "/var/webmin/webmin.dump" file.
=item * Header
Each entry starts with:
===== @ Tue Sep 21 12:00:00.123 2025 pid 1234 =====
Millisecond precision is used when Time::HiRes is available.
=item * UI (web context)
If the dump is also printed to the browser, it is HTML-escaped with preserved
formatting. A single header is sent once per process if a header hasn't already
been sent.
Long output is collapsed after the first 5 lines, with the rest shown inside an
expandable/collapsible accordion toggle.
=item * Return
Returns 0 on success, or an "Error: ..." string. If Data::Dumper is missing,
the error is written to the log and shown in the UI on the web.
=back
=cut
sub var_dump
{
my ($objref, $filename, $pidtofilename) = @_;
my $pid;
$pid = "-" . $$ if ($pidtofilename);
my $filename_ = "$pid";
my @args = @_;
if ($filename && $filename_) {
$filename =~ tr/A-Za-z0-9\_\-//cd;
$filename = "$filename--";
# Output target
my $dir = $main::var_dir || $main::var_directory || '/var/webmin';
my $file = "$dir/webmin.dump";
# Create directory if missing
mkdir $dir, 0750 if (!-d $dir);
# Timestamp with milliseconds if possible
my ($sec, $usec);
{
eval 'use Time::HiRes qw(gettimeofday)';
if ($@) { $sec = time; $usec = 0; }
else { ($sec, $usec) = Time::HiRes::gettimeofday(); }
}
my $ts = scalar localtime($sec);
my $ms = int($usec / 1000);
$ts =~ s/(\d{2}:\d{2}:\d{2})/$1 . sprintf(".%03d", $ms)/e;
# Open log file ealier to log open errors too
my $fh;
my $opened = open($fh, '>>', $file);
if ($opened) {
eval { flock($fh, 2); };
print {$fh} "===== @ $ts pid $$ =====\n";
}
state $check_header;
my $check_headers = sub {
if (!$check_header++ && !$main::done_webmin_header) {
print "Content-type: text/html\n\n";
}
};
# Load Data::Dumper dynamically
my $dumper_err;
eval 'use Data::Dumper';
if (!$@) {
$Data::Dumper::Indent = 1;
$Data::Dumper::Terse = 1;
$Data::Dumper::Deepcopy = 1;
$Data::Dumper::Sortkeys = 1;
if ($@) {
$dumper_err = "Error: The Data::Dumper Perl module is not available ".
"on your system";
print {$fh} "$dumper_err\n" if $opened;
}
# Write file
if ($filename) {
my ($seconds, $microseconds);
eval 'use Time::Piece';
if (!$@) {
eval 'use Time::HiRes';
if (!$@) {
($seconds, $microseconds) = Time::HiRes::gettimeofday;
$microseconds = "$seconds$microseconds-";
# Build dump or error text
my $dump_txt = $dumper_err || '';
if (!$dumper_err) {
local $Data::Dumper::Indent = 2;
local $Data::Dumper::Terse = 1;
local $Data::Dumper::Sortkeys = 1;
local $Data::Dumper::Quotekeys = 0;
local $Data::Dumper::Useqq = 0;
for (my $i = 0; $i < @args; $i++) {
$dump_txt .= "----- param #".($i+1)." -----\n";
$dump_txt .= Data::Dumper::Dumper($args[$i]);
}
}
# Write to file or show error if can't open
$dump_txt = "Error: open($file) failed: $!\n" . ($dump_txt // '') if (!$opened);
if ($opened) {
print {$fh} $dump_txt, "\n\n";
eval { flock($fh, 8); };
close $fh or return "Error: close($file): $!";
}
# Optionally print to UI (web scripts only) only if header already sent
if ($main::done_webmin_header && $main::webmin_script_type eq 'web') {
# Limit long dumps by wrapping parameter chunks between accordion lines
my $out = $dump_txt;
# Add header
$out = "===== @ $ts pid $$ =====\n" . $out;
# Split into chunks
my @chunks;
{
my $param_re = qr/^-{3,}\s*param\s*#\d+\s*-{3,}\s*$/i;
my @lines = split /\n/, $out, -1;
my ($cur_header, @cur_lines);
my $push_chunk = sub {
push @chunks, {
header => $cur_header,
lines => [ @cur_lines ] }
if defined($cur_header) || @cur_lines;
($cur_header, @cur_lines) = (undef);
};
for my $ln (@lines) {
if ($ln =~ $param_re) {
$push_chunk->();
$cur_header = $ln;
}
else {
push @cur_lines, $ln;
}
}
write_file_contents(tempname("${microseconds}${filename}${filename_}"), Dumper($objref));
}
# Print on screen
else {
my $dumped_data = Dumper($objref);
# If print to UI, escape HTML and
# replace new lines and spaces
if ($main::webmin_script_type eq 'web') {
$dumped_data = &html_escape($dumped_data);
$dumped_data =~ s/\n/<br>/g;
$dumped_data =~ s/\s/&nbsp;/g;
$push_chunk->();
}
# Render each chunk and wrap only param chunks that have more than the
# limit lines
my $limit = 5;
my $esc = sub {
my ($s) = @_;
$s =~ s/&/&amp;/g;
$s =~ s/</&lt;/g;
$s =~ s/>/&gt;/g;
$s =~ s/"/&quot;/g;
$s =~ s/'/&apos;/g;
$s =~ s/ /&nbsp;/g;
return $s;
};
my @rendered;
my $first = 1;
for my $c (@chunks) {
my $piece = '';
$piece .= "<br>\n" unless $first;
$first = 0;
my $hdr = defined $c->{header} ? $esc->($c->{header}) : undef;
my @elines = map { $esc->($_) } @{$c->{lines}};
$piece .= "$hdr<br>\n" if defined $hdr;
if (defined($hdr) && @elines > $limit) {
# Param chunk with wrapping
my $head = join '<br>', @elines[0 .. $limit-1];
my $tail = join '<br>', @elines[$limit .. $#elines];
$piece .= $head
. "<br>\n"
. "<details class=\"inline\">"
. "<summary>&nbsp;&nbsp;...</summary>\n"
. $tail . "\n</details>\n";
}
&$check_headers();
$objref && print $dumped_data;
}
}
else {
my $dumpererr = "Error: The Data::Dumper Perl module is not available on your system";
# Write file
if ($filename) {
write_file_contents(tempname("${filename_}_error"), $dumpererr);
}
# Print on screen
else {
&$check_headers();
$objref && print Dumper($dumpererr);
else {
# Non-param chunk or short param with no wrapping
$piece .= join '<br>', @elines;
}
push @rendered, $piece;
}
$out = join('', @rendered);
$out .= "<br>\n";
print "<tt>$out</tt>";
}
# Return result
return $dumper_err || 0;
}
=head2 webmin_debug_log(type, message)
@@ -13150,10 +13255,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_CN'}) {
# From this HTTP request
my $host = $ENV{'HTTP_HOST'};
my $port = $ENV{'SERVER_PORT'} || 80;
my $host = $ENV{'SSL_CN'}
? "$ENV{'SSL_CN'}:$port"
: $ENV{'HTTP_HOST'};
if ($host =~ s/:(\d+)$//) {
$port = $1;
}

View File

@@ -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'}."<br>" ],
[ 1, $text{'yes'}."<br>" ],
[ 2, $text{'forgot_nossl'} ] ]));
# Block bad password requests
$gconfig{'passreset_failures'} //= 3;