Merge branch 'master' of github.com:webmin/webmin
Some checks are pending
webmin.dev: webmin/webmin / build (push) Waiting to run

This commit is contained in:
Jamie Cameron
2026-05-13 20:56:25 -07:00
8 changed files with 136 additions and 27 deletions

View File

@@ -2363,6 +2363,51 @@ my $out = &backquote_logged(
return (!$?, $out);
}
=head2 split_systemd_exec_commands(command)
Splits a multi-line systemd command field into individual command lines.
=cut
sub split_systemd_exec_commands
{
my ($cmd) = @_;
return ( ) if (!defined($cmd));
$cmd =~ s/\r//g;
my @rv;
foreach my $l (split(/\n/, $cmd)) {
$l =~ s/^\s+//;
$l =~ s/\s+$//;
push(@rv, $l) if ($l =~ /\S/);
}
return @rv;
}
=head2 systemd_shell_exec_command(shell, command)
Returns a systemd command line to run some command via a shell.
=cut
sub systemd_shell_exec_command
{
my ($sh, $cmd) = @_;
$cmd =~ s/'/'\\''/g;
return "$sh -c '$cmd'";
}
=head2 format_systemd_exec_command(shell, command)
Returns a systemd command line, using a shell if redirection is needed.
=cut
sub format_systemd_exec_command
{
my ($sh, $cmd) = @_;
return $cmd =~ /<|>/ ? &systemd_shell_exec_command($sh, $cmd) : $cmd;
}
=head2 create_systemd_service(name, description, start-script, stop-script,
restart-script, [forks], [pidfile])
@@ -2372,20 +2417,22 @@ Create a new systemd service with the given details.
sub create_systemd_service
{
my ($name, $desc, $start, $stop, $restart, $forks, $pidfile, $exits, $opts) = @_;
$start =~ s/\r?\n/ ; /g;
$stop =~ s/\r?\n/ ; /g;
$restart =~ s/\r?\n/ ; /g;
my $sh = &has_command("sh") || "sh";
my $kill = &has_command("kill") || "kill";
if ($start =~ /<|>/) {
$start = "$sh -c '$start'";
my @starts = &split_systemd_exec_commands($start);
my @stops = &split_systemd_exec_commands($stop);
my @restarts = &split_systemd_exec_commands($restart);
my $start_type = ref($opts) ? $opts->{'type'} : undef;
$start_type ||= $forks ? 'forking' : $exits ? 'oneshot' : undef;
my $multi_start_oneshot = @starts > 1 && !$start_type;
if (@starts > 1 && $start_type && $start_type ne 'oneshot') {
@starts = (&systemd_shell_exec_command($sh, join("; ", @starts)));
}
if ($restart =~ /<|>/) {
$restart = "$sh -c '$restart'";
}
if ($stop =~ /<|>/) {
$stop = "$sh -c '$stop'";
else {
@starts = map { &format_systemd_exec_command($sh, $_) } @starts;
}
@stops = map { &format_systemd_exec_command($sh, $_) } @stops;
@restarts = map { &format_systemd_exec_command($sh, $_) } @restarts;
my $cfile = &get_systemd_root($name)."/".$name;
&open_lock_tempfile(CFILE, ">$cfile");
&print_tempfile(CFILE, "[Unit]\n");
@@ -2401,9 +2448,16 @@ if (ref($opts)) {
}
&print_tempfile(CFILE, "\n");
&print_tempfile(CFILE, "[Service]\n");
&print_tempfile(CFILE, "ExecStart=$start\n");
&print_tempfile(CFILE, "ExecStop=$stop\n") if ($stop);
&print_tempfile(CFILE, "ExecReload=$restart\n") if ($restart);
&print_tempfile(CFILE, "Type=oneshot\n") if ($multi_start_oneshot);
foreach my $start (@starts) {
&print_tempfile(CFILE, "ExecStart=$start\n");
}
foreach my $stop (@stops) {
&print_tempfile(CFILE, "ExecStop=$stop\n");
}
foreach my $restart (@restarts) {
&print_tempfile(CFILE, "ExecReload=$restart\n");
}
&print_tempfile(CFILE, "Type=forking\n") if ($forks);
&print_tempfile(CFILE, "Type=oneshot\n",
"RemainAfterExit=yes\n") if ($exits);

View File

@@ -1325,6 +1325,53 @@ $reqline = $request_uri = $page = undef;
$authuser = undef;
$validated = undef;
# Reset all per-request state. Keep-alive lets one child handle many
# requests on one TCP connection; behind a proxy that pools backend
# connections, those requests can be from different clients. Anything
# left set from the prior request would leak across that boundary -
# most dangerously $baseauthuser, which feeds BASE_REMOTE_USER and the
# Webmin ACL layer.
$baseauthuser = undef;
$authpass = undef;
$session_id = undef;
$already_session_id = undef;
$already_authuser = undef;
$miniserv_internal = undef;
$querystring = undef;
$queryargs = undef;
$pathinfo = undef;
$peername = undef;
$uinfo = undef;
$scriptname = undef;
$cgi_pwd = undef;
$full = undef;
$realroot = undef;
$foundroot = undef;
$is_directory = undef;
$nph_script = undef;
$logout = undef;
$failed_user = undef;
$failed_save = undef;
$twofactor_msg = undef;
$timed_out = undef;
$error_handler_recurse = undef;
%cgiheader = ();
@cgiheader = ();
$doneheaders = undef;
$headers = undef;
@stfull = ();
# Reset logout-triggered auth suppression and any partial POST body
# count before handling the next request on this connection.
$deny_authentication = undef;
$clen_read = 0;
# Restore $host/$port to socket defaults; the Host: header overwrite
# below should not persist if the next request omits Host.
local $host = $host;
local $port = $port;
# Scope per-request mutation of %config so it cannot leak to later
# requests on this connection (see $config{'session'} = 0 below).
local $config{'session'} = $config{'session'};
# check address against access list
if (@deny && &ip_match($acptip, $localip, @deny) ||
@allow && !&ip_match($acptip, $localip, @allow)) {
@@ -1743,6 +1790,7 @@ my $trust_ssl = $config{'trust_real_ip'} && !$config{'no_trust_ssl'};
if ($use_ssl && $verified_client ||
$trust_ssl && $header{'x-ssl-client-dn'} &&
$header{'x-ssl-client-verify'} =~ /^success$/i) {
my $u;
if ($use_ssl && $verified_client) {
$peername = Net::SSLeay::X509_NAME_oneline(
Net::SSLeay::X509_get_subject_name(

View File

@@ -2006,11 +2006,12 @@ my $other_field_values = $sc->{'other_field_values'};
my ($ver, $variant) = &get_remote_mysql_variant();
my $plugin = $sc->{'plugin'} || &get_mysql_plugin();
$plugin = $plugin ? "with $plugin" : "";
if ($variant eq "mariadb" && &compare_version_numbers($ver, "10.4") >= 0) {
my $sql = "create user '$user'\@'$host' identified $plugin by ".
"'".&escapestr($pass)."'";
my $auth = $plugin ?
&get_plugin_sql($ver, $variant, $pass, $plugin) :
"identified by '".&escapestr($pass)."'";
my $sql = "create user '$user'\@'$host' $auth";
&execute_sql_logged($master_db, $sql);
&execute_sql_logged($master_db, 'flush privileges');
@@ -2036,6 +2037,7 @@ else {
&execute_sql_logged($master_db, 'flush privileges');
if ($variant eq "mysql" && &compare_version_numbers($ver, "5.7.6") >= 0) {
$plugin = $plugin ? "with $plugin" : "";
&execute_sql_logged($master_db,
"alter user '$user'\@'$host' identified $plugin by ".
"'".&escapestr($pass)."'");

View File

@@ -3,7 +3,7 @@ use warnings;
no warnings 'redefine';
no warnings 'uninitialized';
require './nftables-lib.pl'; ## no critic (Modules::RequireBarewordIncludes)
require 'nftables-lib.pl'; ## no critic
our (%in, %text);
# acl_security_form(&options)

View File

@@ -191,8 +191,8 @@ if ($in{'domain_def'}) {
}
else {
local $old = `domainname`; chop($old);
&system_logged("chypdom -B \"$in{'domain'}\"");
&system_logged("domainname \"$in{'domain'}\" >/dev/null 2>&1");
&system_logged("chypdom -B ".quotemeta($in{'domain'}));
&system_logged("domainname ".quotemeta($in{'domain'})." >/dev/null 2>&1");
if ($in{'boot'}) {
# Create the domain directory
mkdir("/var/yp/$in{'domain'}", 0755);

View File

@@ -143,6 +143,11 @@ for($n=0; defined($in{"old_$n"}); $n++) {
&error(&text('server_edomain', $in{"domain_$n"}));
local $domain = $in{"domain_def_$n"} ? undef : $in{"domain_$n"};
local $old = $in{"old_$n"};
if ($old) {
$old =~ /^[A-Za-z0-9\.\-]+$/ && $old !~ /^\./ &&
-r "$nis_config_dir/$old/.nisupdate.conf" ||
&error(&text('server_edomain', $old));
}
if (!$old && !$domain) {
# No domain before, and none chosen
next;
@@ -150,12 +155,12 @@ for($n=0; defined($in{"old_$n"}); $n++) {
elsif (!$old && $domain) {
# New domain added
mkdir("$nis_config_dir/$domain", 0755);
&system_logged("cp nisupdate.conf ".
"$nis_config_dir/$domain/.nisupdate.conf");
&copy_source_dest("nisupdate.conf",
"$nis_config_dir/$domain/.nisupdate.conf");
}
elsif ($old && !$domain) {
# Domain taken away
&system_logged("rm -rf $nis_config_dir/$old");
&unlink_logged("$nis_config_dir/$old");
next;
}
elsif ($old ne $domain) {

View File

@@ -193,7 +193,7 @@ else {
&open_tempfile(DOM, ">/etc/defaultdomain");
&print_tempfile(DOM, "$in{'domain'}\n");
&close_tempfile(DOM);
&system_logged("domainname \"$in{'domain'}\" >/dev/null 2>&1");
&system_logged("domainname ".quotemeta($in{'domain'})." >/dev/null 2>&1");
if ($in{'boot'}) {
# Create the domain directory
mkdir("/var/yp/$in{'domain'}", 0755);

View File

@@ -3471,13 +3471,13 @@ return &theme_ui_brh() if (defined(&theme_ui_brh));
return "<br data-x-br>\n";
}
# ui_tag_start(tag, [attrs], [no-new-line])
# ui_tag_start(tag, [attrs])
# Function to create an opening HTML tag with optional attributes.
# Attributes are passed as a hash reference and its values are quote escaped.
sub ui_tag_start
{
return theme_ui_tag_start(@_) if (defined(&theme_ui_tag_start));
my ($tag, $attrs, $nnl) = @_;
my ($tag, $attrs) = @_;
# Ensure every tag gets a proper marker class
$attrs ||= {};
@@ -3505,7 +3505,7 @@ if ($attrs && ref($attrs) eq 'HASH') {
}
# Close the opening tag
$rv .= $nnl ? ">" : ">\n";
$rv .= ">";
# Handle special case for <html> tag
$rv = "<!DOCTYPE html>\n$rv" if ($tag eq 'html');
@@ -3539,7 +3539,7 @@ sub ui_tag
{
return theme_ui_tag(@_) if (defined(&theme_ui_tag));
my ($tag, $content, $attrs) = @_;
my $rv = ui_tag_start($tag, $attrs, !defined($content));
my $rv = ui_tag_start($tag, $attrs);
$rv .= ui_tag_content($content) if (defined($content));
my %void_tags = map { $_ => 1 }
qw(