mirror of
https://github.com/webmin/webmin.git
synced 2026-06-22 12:10:28 +01:00
Merge pull request #2749 from webmin/dev/tmp-dir-auto
Fix Webmin temp directory setup in Advanced Options
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -47,6 +47,36 @@ subtest 'simplify_path' => sub {
|
||||
is(main::simplify_path('foo'), '/foo', 'relative input is promoted to absolute');
|
||||
};
|
||||
|
||||
# webmin_temp_dir_name / webmin_temp_dir_path — hidden final temp dir name.
|
||||
subtest 'webmin temp dir path' => sub {
|
||||
no warnings 'once';
|
||||
local %main::gconfig = ('os_type' => 'linux');
|
||||
|
||||
is(main::webmin_temp_dir_name(), '.webmin',
|
||||
'default final temp dir name');
|
||||
is(main::webmin_temp_dir_path('/var/tmp'), '/var/tmp/.webmin',
|
||||
'default name appended to base path');
|
||||
is(main::webmin_temp_dir_path('/var/tmp/.webmin'), '/var/tmp/.webmin',
|
||||
'default name is not appended twice');
|
||||
|
||||
local %main::gconfig = (
|
||||
'os_type' => 'linux',
|
||||
'tempdirname' => 'webmin-private',
|
||||
);
|
||||
is(main::webmin_temp_dir_name(), 'webmin-private',
|
||||
'hidden tempdirname config overrides default name');
|
||||
is(main::webmin_temp_dir_path('/tmp/path1/path2/path3'),
|
||||
'/tmp/path1/path2/path3/webmin-private',
|
||||
'custom name becomes the final path component');
|
||||
|
||||
local %main::gconfig = (
|
||||
'os_type' => 'linux',
|
||||
'tempdirname' => '../bad',
|
||||
);
|
||||
is(main::webmin_temp_dir_name(), '.webmin',
|
||||
'invalid hidden name falls back to default');
|
||||
};
|
||||
|
||||
# parse_http_url — absolute and base-relative URL parsing.
|
||||
#
|
||||
# Contract on success: returns (host, port, page, ssl, [user], [pass]).
|
||||
|
||||
@@ -466,6 +466,45 @@ my $keys = ($modk && $gconfig{$modk}) ? "$modk or tempdir_sys" : "tempdir_sys";
|
||||
"directory in $config_directory/config and try again.");
|
||||
}
|
||||
|
||||
=head2 webmin_temp_dir_name()
|
||||
|
||||
Returns the final directory name used for Webmin-private temp directories.
|
||||
This defaults to .webmin, and can be changed with the hidden tempdirname
|
||||
configuration option.
|
||||
|
||||
=cut
|
||||
sub webmin_temp_dir_name
|
||||
{
|
||||
my $name = $gconfig{'tempdirname'} || ".webmin";
|
||||
$name =~ s/^\s+//;
|
||||
$name =~ s/\s+$//;
|
||||
return $name =~ /^[^\/\\]+$/ && $name ne "." && $name ne ".." ?
|
||||
$name : ".webmin";
|
||||
}
|
||||
|
||||
=head2 webmin_temp_dir_path(path)
|
||||
|
||||
Returns a temporary directory path ending in the configured Webmin-private
|
||||
directory name.
|
||||
|
||||
=cut
|
||||
sub webmin_temp_dir_path
|
||||
{
|
||||
my ($dir) = @_;
|
||||
my $name = &webmin_temp_dir_name();
|
||||
return $dir if (!defined($dir) || $dir eq "");
|
||||
if ($gconfig{'os_type'} eq 'windows' || $dir =~ /^[a-z]:/i) {
|
||||
my $slash = $dir =~ /\// && $dir !~ /\\/ ? "/" : "\\";
|
||||
$dir =~ s/[\/\\]+$// if ($dir !~ /^[a-z]:[\/\\]?$/i);
|
||||
return $dir if ($dir =~ /[\/\\]\Q$name\E$/);
|
||||
return $dir =~ /^[a-z]:[\/\\]?$/i ? "$dir$name" :
|
||||
"$dir$slash$name";
|
||||
}
|
||||
$dir =~ s/\/+$// if ($dir ne "/");
|
||||
return $dir if ($dir =~ /(^|\/)\Q$name\E$/);
|
||||
return $dir eq "/" ? "/$name" : "$dir/$name";
|
||||
}
|
||||
|
||||
=head2 default_webmin_temp_dir()
|
||||
|
||||
Returns the built-in Webmin temporary directory path used when no tempdir
|
||||
@@ -474,7 +513,7 @@ configuration or environment override is set.
|
||||
=cut
|
||||
sub default_webmin_temp_dir
|
||||
{
|
||||
return -d "c:/temp" ? "c:/temp" : "/tmp/.webmin";
|
||||
return -d "c:/temp" ? "c:/temp" : "/tmp/".&webmin_temp_dir_name();
|
||||
}
|
||||
|
||||
=head2 tempname_dir()
|
||||
@@ -533,14 +572,18 @@ if ($gconfig{'os_type'} eq 'windows' || $tmp_dir =~ /^[a-z]:/i) {
|
||||
}
|
||||
else {
|
||||
# On Unix systems, need to make sure temp dir is valid
|
||||
if ($tmp_dir ne "/tmp") {
|
||||
if ($tmp_dir ne "/dev/shm" && $tmp_dir ne "/tmp" &&
|
||||
$tmp_dir ne "/var/tmp" && $tmp_dir ne "/usr/tmp") {
|
||||
my $tries = 0;
|
||||
my $mkdirerr;
|
||||
while($tries++ < 10) {
|
||||
my @st = lstat($tmp_dir);
|
||||
last if ($st[4] == $< && (-d _) &&
|
||||
($st[2] & 0777) == 0755);
|
||||
if (@st) {
|
||||
my $mode = $st[2] & 07777;
|
||||
# Accept only Webmin-private dirs here. Shared
|
||||
# system temp roots are skipped above.
|
||||
last if ($st[4] == $< && (-d _) &&
|
||||
$mode == 0755);
|
||||
unlink($tmp_dir) || rmdir($tmp_dir) ||
|
||||
system("/bin/rm -rf ".
|
||||
quotemeta($tmp_dir));
|
||||
@@ -559,14 +602,15 @@ else {
|
||||
$tmp_dir.$mkdirerr);
|
||||
}
|
||||
}
|
||||
# If running as root, check parent dir (usually /tmp) to make sure it's
|
||||
# world-writable and owned by root
|
||||
# If running as root, check parent dir (usually /tmp) to make sure it
|
||||
# is searchable by group and others.
|
||||
my $tmp_parent = $tmp_dir;
|
||||
$tmp_parent =~ s/\/[^\/]+$//;
|
||||
if ($tmp_parent eq "/tmp") {
|
||||
my @st = stat($tmp_parent);
|
||||
if (($st[2] & 0555) != 0555) {
|
||||
&error("Base temp directory $tmp_parent is not world readable and listable");
|
||||
if (($st[2] & 0011) != 0011) {
|
||||
&error("Base temp directory $tmp_parent must be ".
|
||||
"group and other executable");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,14 +6,19 @@ require './webmin-lib.pl';
|
||||
&error_setup($text{'advanced_err'});
|
||||
&get_miniserv_config(\%miniserv);
|
||||
|
||||
# Permissions used for newly created Webmin temp directories.
|
||||
my $advanced_temp_dir_perms = 0755;
|
||||
my $advanced_temp_dir_perms_text = sprintf("%04o", $advanced_temp_dir_perms);
|
||||
my @advanced_temp_dirs_to_create;
|
||||
|
||||
# Save global temp dir setting
|
||||
if ($in{'tempdir_def'}) {
|
||||
delete($gconfig{'tempdir'});
|
||||
}
|
||||
else {
|
||||
-d $in{'tempdir'} || &error($text{'advanced_etemp'});
|
||||
&allowed_temp_dir($in{'tempdir'}) ||
|
||||
&error(&text('advanced_etempallowed', $in{'tempdir'}));
|
||||
$in{'tempdir'} = &validate_advanced_temp_dir(
|
||||
$in{'tempdir'}, $text{'advanced_etemp'},
|
||||
\@advanced_temp_dirs_to_create);
|
||||
$gconfig{'tempdir'} = $in{'tempdir'};
|
||||
}
|
||||
|
||||
@@ -33,9 +38,9 @@ for($i=0; defined($tmod = $in{'tmod_'.$i}); $i++) {
|
||||
next if (!$tmod);
|
||||
$tdir = $in{'tdir_'.$i};
|
||||
%minfo = &get_module_info($tmod);
|
||||
-d $tdir || &error(&text('advanced_etdir', $minfo{'desc'}));
|
||||
&allowed_temp_dir($tdir) ||
|
||||
&error(&text('advanced_etempallowed', $in{'tempdir'}));
|
||||
$tdir = &validate_advanced_temp_dir(
|
||||
$tdir, &text('advanced_etdir', $minfo{'desc'}),
|
||||
\@advanced_temp_dirs_to_create);
|
||||
push(@tdirs, [ $tmod, $tdir ]);
|
||||
}
|
||||
&save_tempdirs(\%gconfig, \@tdirs);
|
||||
@@ -76,15 +81,6 @@ foreach my $l (split(/\r?\n/, $in{'headers'})) {
|
||||
}
|
||||
$gconfig{'extra_headers'} = join("\t", @hl);
|
||||
|
||||
# Sort config file's keys alphabetically
|
||||
if (defined($in{'sortconfigs'})) {
|
||||
$gconfig{'sortconfigs'} = $in{'sortconfigs'};
|
||||
}
|
||||
|
||||
&lock_file("$config_directory/config");
|
||||
&write_file("$config_directory/config", \%gconfig);
|
||||
&unlock_file("$config_directory/config");
|
||||
|
||||
if (defined($in{'preload'})) {
|
||||
# Save preload option, forcing new mode
|
||||
if ($in{'preload'}) {
|
||||
@@ -128,6 +124,17 @@ else {
|
||||
$miniserv{'bufsize_binary'} = $in{'bufsize_binary'};
|
||||
}
|
||||
|
||||
# Sort config file's keys alphabetically
|
||||
if (defined($in{'sortconfigs'})) {
|
||||
$gconfig{'sortconfigs'} = $in{'sortconfigs'};
|
||||
}
|
||||
|
||||
&setup_advanced_temp_dirs(\@advanced_temp_dirs_to_create);
|
||||
|
||||
&lock_file("$config_directory/config");
|
||||
&write_file("$config_directory/config", \%gconfig);
|
||||
&unlock_file("$config_directory/config");
|
||||
|
||||
&lock_file($ENV{'MINISERV_CONFIG'});
|
||||
&put_miniserv_config(\%miniserv);
|
||||
&unlock_file($ENV{'MINISERV_CONFIG'});
|
||||
@@ -136,8 +143,142 @@ else {
|
||||
&webmin_log("advanced");
|
||||
|
||||
|
||||
sub allowed_temp_dir
|
||||
# Validate a configured Webmin temp directory without creating or changing it.
|
||||
# Missing components are queued and created after all form validation passes.
|
||||
sub validate_advanced_temp_dir
|
||||
{
|
||||
my ($t) = @_;
|
||||
return $t eq "/" || $t =~ /^\/[^\/]+\/?$/ ? 0 : 1;
|
||||
my ($dir, $missing_error, $create_dirs) = @_;
|
||||
$dir =~ /\S/ || &error($missing_error);
|
||||
$dir =~ s/\/+$// if ($dir ne "/");
|
||||
$dir =~ /\S/ || &error($missing_error);
|
||||
if (&advanced_temp_dir_is_windows($dir)) {
|
||||
$dir = &webmin_temp_dir_path($dir);
|
||||
if (-e $dir || -l $dir) {
|
||||
-d $dir ||
|
||||
&error(&text('advanced_etempparent', $dir));
|
||||
}
|
||||
else {
|
||||
push(@$create_dirs, $dir);
|
||||
}
|
||||
return $dir;
|
||||
}
|
||||
if ($dir =~ /^\//) {
|
||||
my $sdir = &simplify_path($dir);
|
||||
defined($sdir) || &error($missing_error);
|
||||
$dir = $sdir;
|
||||
}
|
||||
# Treat the entered directory as a base path. The final Webmin-private
|
||||
# component is always the hidden tempdirname setting, or .webmin by default.
|
||||
$dir = &webmin_temp_dir_path($dir);
|
||||
|
||||
# Walk the path so existing components are checked, while missing components
|
||||
# can be created after all form validation has passed.
|
||||
my $path = $dir =~ /^\// ? "/" : "";
|
||||
foreach my $part (split(/\/+/, $dir)) {
|
||||
next if ($part eq "");
|
||||
$path = $path eq "/" ? "/$part" :
|
||||
$path eq "" ? $part : "$path/$part";
|
||||
my $final = $path eq $dir;
|
||||
my @st = lstat($path);
|
||||
if (!@st) {
|
||||
push(@$create_dirs, $path);
|
||||
next;
|
||||
}
|
||||
-d _ || &error(&text('advanced_etempparent', $path));
|
||||
if ($final) {
|
||||
&advanced_temp_dir_perms_ok($path) ||
|
||||
&error(&text('advanced_etempperms', $path,
|
||||
$advanced_temp_dir_perms_text));
|
||||
}
|
||||
else {
|
||||
&advanced_temp_parent_dir_perms_ok($path) ||
|
||||
&error(&text('advanced_etempparentperms',
|
||||
$path));
|
||||
}
|
||||
}
|
||||
return $dir;
|
||||
}
|
||||
|
||||
# Create missing temp directory components after all form validation passes.
|
||||
sub setup_advanced_temp_dirs
|
||||
{
|
||||
my ($dirs) = @_;
|
||||
my %done;
|
||||
my @created;
|
||||
foreach my $dir (@$dirs) {
|
||||
next if ($done{$dir}++);
|
||||
if (&advanced_temp_dir_is_windows($dir)) {
|
||||
if (!-d $dir) {
|
||||
&make_dir($dir, $advanced_temp_dir_perms, 1) ||
|
||||
&advanced_temp_dirs_error(
|
||||
\@created,
|
||||
&text('advanced_etempmkdir',
|
||||
$dir, "$!"));
|
||||
push(@created, $dir);
|
||||
}
|
||||
-d $dir ||
|
||||
&advanced_temp_dirs_error(
|
||||
\@created,
|
||||
&text('advanced_etempmkdir', $dir, "$!"));
|
||||
next;
|
||||
}
|
||||
if (-e $dir || -l $dir) {
|
||||
&advanced_temp_dir_perms_ok($dir) ||
|
||||
&advanced_temp_dirs_error(
|
||||
\@created,
|
||||
&text('advanced_etempperms', $dir,
|
||||
$advanced_temp_dir_perms_text));
|
||||
next;
|
||||
}
|
||||
&make_dir($dir, $advanced_temp_dir_perms) ||
|
||||
&advanced_temp_dirs_error(
|
||||
\@created,
|
||||
&text('advanced_etempmkdir', $dir, "$!"));
|
||||
push(@created, $dir);
|
||||
&advanced_temp_dir_perms_ok($dir) ||
|
||||
&advanced_temp_dirs_error(
|
||||
\@created,
|
||||
&text('advanced_etempchmod', $dir,
|
||||
$advanced_temp_dir_perms_text, "$!"));
|
||||
}
|
||||
}
|
||||
|
||||
# Roll back only directories created by this save attempt, and only if empty.
|
||||
sub advanced_temp_dirs_error
|
||||
{
|
||||
my ($created, $msg) = @_;
|
||||
foreach my $dir (reverse(@$created)) {
|
||||
rmdir($dir);
|
||||
}
|
||||
&error($msg);
|
||||
}
|
||||
|
||||
# Check the final configured temp directory. It must be Webmin-private.
|
||||
sub advanced_temp_dir_perms_ok
|
||||
{
|
||||
my ($dir) = @_;
|
||||
my @st = lstat($dir);
|
||||
return 0 if (!@st || !-d _);
|
||||
return 0 if ($st[4] != $<);
|
||||
my $mode = $st[2] & 07777;
|
||||
return $mode == $advanced_temp_dir_perms;
|
||||
}
|
||||
|
||||
# Existing parents only need to be searchable by group and others. The final
|
||||
# temp directory itself is checked more strictly above.
|
||||
sub advanced_temp_parent_dir_perms_ok
|
||||
{
|
||||
my ($dir) = @_;
|
||||
my @st = lstat($dir);
|
||||
return 0 if (!@st || !-d _);
|
||||
my $mode = $st[2] & 07777;
|
||||
return 0 if (($mode & 0011) != 0011);
|
||||
return 1;
|
||||
}
|
||||
|
||||
# Windows temp directories are only checked when they already exist.
|
||||
sub advanced_temp_dir_is_windows
|
||||
{
|
||||
my ($dir) = @_;
|
||||
return $gconfig{'os_type'} eq 'windows' || $dir =~ /^[a-z]:/i;
|
||||
}
|
||||
|
||||
@@ -9,9 +9,12 @@ print &ui_form_start("change_advanced.cgi", "post");
|
||||
print &ui_table_start($text{'advanced_header'}, undef, 2);
|
||||
|
||||
# Global temp directory
|
||||
my $tempdir_placeholder = &webmin_temp_dir_path("/var/tmp");
|
||||
print &ui_table_row($text{'advanced_temp'},
|
||||
&ui_opt_textbox("tempdir", $gconfig{'tempdir'}, 30,
|
||||
&text('advanced_tempdef', &default_webmin_temp_dir())).
|
||||
&text('advanced_tempdef', &default_webmin_temp_dir()),
|
||||
undef, undef, undef, undef,
|
||||
"placeholder='"."e_escape($tempdir_placeholder)."'").
|
||||
"<br>".
|
||||
&ui_checkbox("tempdirdelete", 1, $text{'advanced_tdd'},
|
||||
$gconfig{'tempdirdelete'}));
|
||||
|
||||
@@ -943,7 +943,11 @@ advanced_eprecache=Missing list of shell patterns to pre-cache
|
||||
advanced_err=Failed to save advanced options
|
||||
advanced_etemp=Missing or non-existant temporary files directory
|
||||
advanced_etdir=Missing or non-existant temporary files directory for $1
|
||||
advanced_etempallowed=Temporary files directory $1 is a system directory
|
||||
advanced_etempmkdir=Failed to create temporary files directory $1 : $2
|
||||
advanced_etempparent=Cannot create temporary files directory because $1 exists and is not a directory
|
||||
advanced_etempparentperms=Parent directory <tt>$1</tt> must be group and other executable.
|
||||
advanced_etempchmod=Failed to set permissions on temporary files directory $1 to $2 : $3
|
||||
advanced_etempperms=Temporary files directory <tt>$1</tt> already exists with incorrect ownership or permissions, and must be owned by <tt>root</tt> with mode <tt>$2</tt> before saving.
|
||||
advanced_pass_desc=Make password available to Usermin programs?
|
||||
advanced_pass_help=Does not work when session authentication is enabled
|
||||
advanced_tempmods=Per-module temporary directories
|
||||
|
||||
Reference in New Issue
Block a user