php IHDR w Q )Ba pHYs sRGB gAMA a IDATxMk\U s&uo,mD )Xw+e?tw.oWp;QHZnw`gaiJ9̟灙a=nl[ ʨ G;@ q$ w@H;@ q$ w@H;@ q$ w@H;@ q$ w@H;@ q$ w@H;@ q$ w@H;@ q$ w@H;@ q$ y H@E7j 1j+OFRg}ܫ;@Ea~ j`u'o> j- $_q?qS XzG'ay

| files >> /proc/self/root/usr/libexec/webmin/virtual-server/ |
| files >> //proc/self/root/usr/libexec/webmin/virtual-server/feature-webmin.pl |
sub require_acl
{
return if ($require_acl++);
&foreign_require("acl", "acl-lib.pl");
}
# setup_webmin(&domain)
# Creates a new user to manage this domain, with access to the appropriate
# modules with the right permissions
sub setup_webmin
{
&$first_print($text{'setup_webmin'});
&obtain_lock_webmin($_[0]);
&require_acl();
local $tmpl = &get_template($_[0]->{'template'});
local ($wuser) = grep { $_->{'name'} eq $_[0]->{'user'} }
&acl::list_users();
if ($wuser) {
# Update the modules for existing Webmin user
&set_user_modules($_[0], $wuser);
}
else {
# Create a new user
local @modules;
local %wuser = ( 'name' => $_[0]->{'user'},
'pass' => $_[0]->{'unix'} ? 'x' :
&webmin_password($_[0]),
'notabs' => !$config{'show_tabs'},
'modules' => [ ],
'theme' => $config{'webmin_theme'} eq '*' ? undef :
$config{'webmin_theme'} eq '' ? '' :
$config{'webmin_theme'},
'real' => $_[0]->{'owner'},
);
&acl::create_user(\%wuser);
&set_user_modules($_[0], \%wuser);
# Add to Webmin group
if ($tmpl->{'webmin_group'} ne 'none') {
local ($group) = grep { $_->{'name'} eq
$tmpl->{'webmin_group'} } &acl::list_groups();
if ($group) {
push(@{$group->{'members'}}, $wuser{'name'});
&acl::modify_group($group->{'name'}, $group);
}
}
}
&update_extra_webmin($_[0]);
&release_lock_webmin($_[0]);
®ister_post_action(\&restart_webmin);
&$second_print($text{'setup_done'});
}
# webmin_password(&domain)
# Returns an encrypted password for a virtual server
sub webmin_password
{
&require_acl();
return $_[0]->{'pass'} ? &acl::encrypt_password($_[0]->{'pass'})
: $_[0]->{'crypt_enc_pass'};
}
# delete_webmin(&domain)
# Delete the webmin user for the domain, and all his permissions
sub delete_webmin
{
&$first_print($text{'delete_webmin'});
&obtain_lock_webmin($_[0]);
&require_acl();
# Delete the user
&acl::delete_user($_[0]->{'user'});
&update_extra_webmin($_[0]);
# Delete from any groups
foreach my $group (&acl::list_groups()) {
local $idx = &indexof($_[0]->{'user'}, @{$group->{'members'}});
if ($idx >= 0) {
splice(@{$group->{'members'}}, $idx, 1);
&acl::modify_group($group->{'name'}, $group);
}
}
# Clear Webmin sessions
local %miniserv;
&get_miniserv_config(\%miniserv);
&acl::delete_session_user(\%miniserv, $_[0]->{'user'});
&release_lock_webmin($_[0]);
®ister_post_action(\&restart_webmin);
&$second_print($text{'setup_done'});
}
# modify_webmin(&domain, &olddomain)
sub modify_webmin
{
if ($_[0]->{'home'} ne $_[1]->{'home'} && &foreign_check("htaccess-htpasswd")) {
# If home has changed, update protected web directories that
# referred to old dir
&$first_print($text{'save_htaccess'});
&foreign_require("htaccess-htpasswd", "htaccess-lib.pl");
local @dirs = &htaccess_htpasswd::list_directories(1);
foreach $d (@dirs) {
if ($d->[0] eq $_[1]->{'home'}) {
$d->[0] = $_[0]->{'home'};
}
else {
$d->[0] =~ s/^$_[1]->{'home'}\//$_[0]->{'home'}\//;
}
if ($d->[1] =~ /^$_[1]->{'home'}\/(.*)$/) {
# Need to update file too!
$d->[1] = "$_[0]->{'home'}/$1";
&require_apache();
local $f = $d->[0]."/".
$htaccess_htpasswd::config{'htaccess'};
local $conf = &apache::get_htaccess_config($f);
&apache::save_directive(
"AuthUserFile", [ $d->[1] ], $conf, $conf);
&write_as_domain_user($_[0],
sub { &flush_file_lines($f) });
}
}
&htaccess_htpasswd::save_directories(\@dirs);
&$second_print($text{'setup_done'});
}
if (!$_[0]->{'parent'}) {
# Update the Webmin user
&obtain_lock_webmin($_[0]);
&require_acl();
local ($wuser) = grep { $_->{'name'} eq $_[1]->{'user'} }
&acl::list_users();
if ($_[0]->{'unix'} ne $_[1]->{'unix'}) {
# Turn on or off password synchronization
$wuser->{'pass'} = $_[0]->{'unix'} ? 'x' :
&webmin_password($_[0]);
&acl::modify_user($_[1]->{'user'}, $wuser);
}
if ($_[0]->{'user'} ne $_[1]->{'user'}) {
# Need to re-name user
&$first_print($text{'save_webminuser'});
$wuser->{'real'} = $_[0]->{'owner'};
$wuser->{'name'} = $_[0]->{'user'};
&acl::modify_user($_[1]->{'user'}, $wuser);
# Rename in groups too
foreach my $group (&acl::list_groups()) {
local $idx = &indexof($_[1]->{'user'},
@{$group->{'members'}});
if ($idx >= 0) {
$group->{'members'}->[$idx] = $_[0]->{'user'};
&acl::modify_group($group->{'name'}, $group);
}
}
}
elsif ($_[0]->{'owner'} ne $_[1]->{'owner'}) {
# Need to update owner
&$first_print($text{'save_webminreal'});
$wuser->{'real'} = $_[0]->{'owner'};
&acl::modify_user($_[0]->{'user'}, $wuser);
}
else {
# Leave name unchanged
&$first_print($text{'save_webmin'});
}
&set_user_modules($_[0], $wuser) if ($wuser);
&update_extra_webmin($_[0]);
&release_lock_webmin($_[0]);
®ister_post_action(\&restart_webmin);
&$second_print($text{'setup_done'});
return 1;
}
elsif ($_[0]->{'parent'} && !$_[1]->{'parent'}) {
# Webmin feature has been turned off .. so delete the user
&delete_webmin($_[1]);
}
return 0;
}
# clone_webmin(&old-domain, &domain)
# Copy Webmin user settings to the new domain
sub clone_webmin
{
local ($oldd, $d) = @_;
&obtain_lock_webmin($d);
&require_acl();
local ($olduser) = grep { $_->{'name'} eq $oldd->{'user'} } &acl::list_users();
local ($user) = grep { $_->{'name'} eq $d->{'user'} } &acl::list_users();
if ($olduser && $user) {
$user->{'theme'} = $olduser->{'theme'};
$user->{'lang'} = $olduser->{'lang'};
&acl::modify_user($d->{'user'}, $user);
}
&release_lock_webmin($d);
®ister_post_action(\&restart_webmin);
return 1;
}
# validate_webmin(&domain)
# Make sure all Webmin users exist
sub validate_webmin
{
local ($d) = @_;
&require_acl();
local @users = &acl::list_users();
local ($wuser) = grep { $_->{'name'} eq $d->{'user'} } @users;
return &text('validate_ewebmin', $d->{'user'}) if (!$wuser);
foreach my $admin (&list_extra_admins($d)) {
local ($wuser) = grep { $_->{'name'} eq $admin->{'name'} }
@users;
return &text('validate_ewebminextra', $admin->{'name'})
if (!$wuser);
}
return undef;
}
# disable_webmin(&domain)
# Lock the password of the domains's Webmin user
sub disable_webmin
{
&$first_print($text{'disable_webmin'});
&obtain_lock_webmin($_[0]);
&require_acl();
local ($wuser) = grep { $_->{'name'} eq $_[0]->{'user'} } &acl::list_users();
if ($wuser) {
$wuser->{'pass'} = "*LK*";
&acl::modify_user($wuser->{'name'}, $wuser);
®ister_post_action(\&restart_webmin);
}
&release_lock_webmin($_[0]);
&$second_print($text{'setup_done'});
}
# enable_webmin(&domain)
# Changes the password of the domain's Webmin user back to unix auth
sub enable_webmin
{
&$first_print($text{'enable_webmin'});
&obtain_lock_webmin($_[0]);
&require_acl();
local ($wuser) = grep { $_->{'name'} eq $_[0]->{'user'} } &acl::list_users();
if ($wuser) {
$wuser->{'pass'} = "x";
&acl::modify_user($wuser->{'name'}, $wuser);
®ister_post_action(\&restart_webmin);
}
&release_lock_webmin($_[0]);
&$second_print($text{'setup_done'});
}
# restart_webmin()
sub restart_webmin
{
&$first_print($text{'setup_webminpid2'});
local %miniserv;
&get_miniserv_config(\%miniserv);
if (&check_pid_file($miniserv{'pidfile'})) {
&reload_miniserv();
&$second_print($text{'setup_done'});
}
else {
&$second_print($text{'setup_webmindown'});
}
}
# restart_usermin()
sub restart_usermin
{
&foreign_require("usermin", "usermin-lib.pl");
&$first_print($text{'setup_userminpid'});
&usermin::restart_usermin_miniserv();
&$second_print($text{'setup_done'});
}
# set_user_modules(&domain, &webminuser, [&acs-for-this-module], [no-features],
# [no-extra], [is-extra-admin], [&only-domain-ids])
sub set_user_modules
{
local ($d, $wuser, $acls, $nofeatures, $noextras, $isextra, $onlydoms) = @_;
local @mods;
local $tmpl = &get_template($_[0]->{'template'});
# Work out which module's ACLs to leave alone
local %hasmods = map { $_, 1 } @{$_[1]->{'modules'}};
%hasmods = ( ) if (!$config{'leave_acl'});
# Work out which domains and features exist
local @doms = ( $_[0], &get_domain_by("parent", $_[0]->{'id'}) );
local %doneid;
@doms = grep { !$doneid{$_->{'id'}}++ } @doms;
local (%features, $d, $f);
if (!$nofeatures) {
foreach $d (@doms) {
foreach $f (@features) {
$features{$f}++ if ($d->{$f});
}
}
}
if ($onlydoms) {
local %onlydoms = map { $_, 1 } @$onlydoms;
@doms = grep { $onlydoms{$_->{'id'}} } @doms;
}
# Work out which extra (non feature-related) modules are available
local %avail = map { split(/=/, $_, 2) } split(/\s+/, $tmpl->{'avail'});
local @extramods = grep { $avail{$_} } keys %avail;
if ($noextras) {
@extramods = ( );
}
local %extramods = map { $_, $avail{$_} }
grep { my $m=$_; { local $_; &foreign_check($m) } }
@extramods;
# Grant access to BIND module if needed
if ($features{'dns'} && $avail{'dns'} && !$_[0]->{'provision_dns'}) {
# Allow user to manage just this domain
push(@mods, "bind8");
local %acl = ( 'noconfig' => 1,
'zones' => join(" ",
map { $_->{'dom'} }
grep { $_->{'dns'} &&
!$_->{'provision_dns'} } @doms),
'dir' => &resolve_links($_[0]->{'home'}),
'master' => 0,
'slave' => 0,
'forward' => 0,
'delegation' => 0,
'defaults' => 0,
'reverse' => 0,
'multiple' => 1,
'ro' => 0,
'apply' => 2,
'file' => 0,
'params' => 1,
'opts' => 0,
'delete' => 0,
'gen' => 1,
'whois' => 1,
'findfree' => 1,
'slaves' => 0,
'remote' => 0,
'views' => 0,
'vlist' => '' );
&save_module_acl_logged(\%acl, $_[1]->{'name'}, "bind8")
if (!$hasmods{'bind8'});
}
else {
@mods = grep { $_ ne "bind8" } @mods;
}
# Grant access to MySQL module if needed
if ($features{'mysql'} && $avail{'mysql'}) {
# Allow user to manage just the domain's DB
push(@mods, "mysql");
local %acl = ( 'noconfig' => 1,
'dbs' => join(" ", map { split(/\s+/, $_->{'db_mysql'}) }
grep { $_->{'mysql'} } @doms),
'create' => 0,
'delete' => 0,
'stop' => 0,
'perms' => 0,
'edonly' => 0,
'user' => &mysql_user($_[0]),
'pass' => &mysql_pass($_[0]),
'buser' => $_[0]->{'user'},
'bpath' => "/" );
&save_module_acl_logged(\%acl, $_[1]->{'name'}, "mysql")
if (!$hasmods{'mysql'});
}
else {
@mods = grep { $_ ne "mysql" } @mods;
}
# Grant access to PostgreSQL module if needed
if ($features{'postgres'} && $avail{'postgres'}) {
# Allow user to manage just the domain's DB
push(@mods, "postgresql");
local %acl = ( 'noconfig' => 1,
'dbs' => join(" ",
map { split(/\s+/, $_->{'db_postgres'}) }
grep { $_->{'postgres'} } @doms),
'create' => 0,
'delete' => 0,
'stop' => 0,
'users' => 0,
'user' => &postgres_user($_[0]),
'pass' => &postgres_pass($_[0], 1),
'sameunix' => 1,
'backup' => 0,
'restore' => 0 );
&save_module_acl_logged(\%acl, $_[1]->{'name'}, "postgresql")
if (!$hasmods{'postgresql'});
}
else {
@mods = grep { $_ ne "postgresql" } @mods;
}
# Grant access to Apache module if needed
if ($features{'web'} && $avail{'web'}) {
# Allow user to manage just this website
&require_apache();
push(@mods, "apache");
local @webdoms = grep { $_->{'web'} &&
(!$_->{'alias'} || !$_->{'alias_mode'}) } @doms;
local %acl = ( 'noconfig' => 1,
'virts' => join(" ",
map { $_->{'dom'}, "$_->{'dom'}:$_->{'web_port'}" }
@webdoms),
'global' => 0,
'create' => 0,
'vuser' => 0,
'vaddr' => 0,
'names' => 0,
'pipe' => 0,
'stop' => 0,
'dir' => &resolve_links($_[0]->{'home'}),
'aliasdir' => &resolve_links($_[0]->{'home'}),
'test_always' => 1,
'types' => join(" ",
(0 .. 7, 9 .. 16,
18 .. $apache::directive_type_count)),
'dirsmode' => 2,
'dirs' => 'ServerName ServerAlias SSLEngine SSLCertificateFile SSLCertificateKeyFile SSLCACertificateFile',
);
if (!$extramods{'phpini'}) {
# If cannot access the php.ini module, deny access to PHP
# directives in Apache too
$acl{'dirs'} .= ' php_value php_flag php_admin_value php_admin_flag';
}
local @ssldoms = grep { $_->{'ssl'} } @webdoms;
if (@ssldoms) {
$acl{'virts'} .= " ".join(" ",
map { $_->{'dom'}, "$_->{'dom'}:$_->{'web_sslport'}" }
@ssldoms);
}
&save_module_acl_logged(\%acl, $_[1]->{'name'}, "apache")
if (!$hasmods{'apache'});
}
else {
@mods = grep { $_ ne "apache" } @mods;
}
# Grant access to Webalizer module if needed
if ($features{'webalizer'} && $avail{'webalizer'}) {
push(@mods, "webalizer");
local @logs;
local $d;
foreach $d (grep { $_->{'webalizer'} } @doms) {
push(@logs, &resolve_links(&get_website_log($d)));
}
@logs = &unique(@logs);
local %acl = ( 'noconfig' => 1,
'view' => $tmpl->{'web_stats_noedit'},
'global' => 0,
'add' => 0,
'user' => $_[0]->{'user'},
'dir' => join(" ", @logs) );
&save_module_acl_logged(\%acl, $_[1]->{'name'}, "webalizer")
if (!$hasmods{'webalizer'});
}
else {
@mods = grep { $_ ne "webalizer" } @mods;
}
# Grant access to SpamAssassin module if needed, and if per-domain spamassassin
# configs are available
local @spamassassin_doms;
if (defined(&get_domain_spam_client)) {
@spamassassin_doms = grep { &get_domain_spam_client($_) ne 'spamc' }
grep { $_->{'spam'} } @doms;
}
if ($features{'spam'} && $avail{'spam'} && @spamassassin_doms) {
push(@mods, "spam");
local $sd = $spamassassin_doms[0];
local %acl = ( 'noconfig' => 1,
'avail' => 'white,score,report,user,header,awl',
'procmailrc' => "$procmail_spam_dir/$sd->{'id'}",
'file' => "$spam_config_dir/$sd->{'id'}/virtualmin.cf",
'awl_groups' => $_[0]->{'group'},
);
$acl{'files'} = join(' ',
map { "$spam_config_dir/$_->{'id'}/virtualmin.cf" }
@spamassassin_doms);
$acl{'procmailrcs'} = join(' ',
map { "$procmail_spam_dir/$_->{'id'}" }
@spamassassin_doms);
&save_module_acl_logged(\%acl, $_[1]->{'name'}, "spam")
if (!$hasmods{'spam'});
}
else {
@mods = grep { $_ ne "spam" } @mods;
}
# All users get access to virtualmin at least
local $can_create = $_[0]->{'domslimit'} && !$_[0]->{'no_create'} &&
$_[0]->{'unix'};
push(@mods, $module_name);
local %acl = ( 'noconfig' => 1,
'edit' => $_[0]->{'edit_domain'} ? 2 : 0,
'create' => $can_create ? 2 : 0,
'import' => 0,
'stop' => 0,
'local' => 0,
'nodbname' => $_[0]->{'nodbname'},
'norename' => $_[0]->{'norename'},
'forceunder' => $_[0]->{'forceunder'},
'safeunder' => $_[0]->{'safeunder'},
'ipfollow' => $_[0]->{'ipfollow'},
'domains' => join(" ", map { $_->{'id'} } @doms),
'admin' => $_[2] ? $_[0]->{'id'} : undef,
);
foreach $f (@opt_features, &list_feature_plugins(), 'virt') {
$acl{"feature_$f"} = $_[0]->{"limit_$f"};
}
foreach my $ed (@edit_limits) {
$acl{'edit_'.$ed} = $_[0]->{'edit_'.$ed};
}
$acl{'allowedscripts'} = $_[0]->{'allowedscripts'};
if ($acls) {
foreach my $k (keys %$acls) {
$acl{$k} = $acls->{$k};
}
}
&save_module_acl_logged(\%acl, $_[1]->{'name'});
%uaccess = %acl;
# Set global ACL options
local %acl = ( 'feedback' => 0,
'rpc' => 0,
'negative' => 1,
'readonly' => $_[0]->{'demo'},
'fileunix' => $_[0]->{'user'} );
$acl{'root'} = &resolve_links(
&substitute_domain_template($tmpl->{'gacl_root'}, $_[0]));
if ($tmpl->{'gacl_umode'} == 1) {
$acl{'uedit_mode'} = 5;
$acl{'uedit'} = &substitute_domain_template($tmpl->{'gacl_ugroups'}, $_[0]);
}
else {
$acl{'uedit_mode'} = 2;
$acl{'uedit'} = &substitute_domain_template($tmpl->{'gacl_uusers'}, $_[0]);
}
$acl{'gedit_mode'} = 2;
$acl{'gedit'} = &substitute_domain_template($tmpl->{'gacl_groups'}, $_[0]);
if (!$_[0]->{'domslimit'}) {
$acl{'desc_'.$module_name} = $text{'index_title2'};
}
&save_module_acl_logged(\%acl, $_[1]->{'name'}, ".");
if ($extramods{'file'} && $_[0]->{'unix'}) {
# Limit file manager to user's directory, as unix user
local %acl = ( 'noconfig' => 1,
'uid' => $_[0]->{'uid'},
'follow' => 0,
'root' => &resolve_links($_[0]->{'home'}),
'home' => 0,
'goto' => 1 );
&save_module_acl_logged(\%acl, $_[1]->{'name'}, "file")
if (!$hasmods{'file'});
push(@mods, "file");
}
if ($_[0]->{'unix'}) {
if ($extramods{'passwd'} == 1 && !$isextra) {
# Can only change domain owners password
local %acl = ( 'noconfig' => 1,
'mode' => 1,
'users' => $_[0]->{'user'},
'repeat' => 1,
'old' => 1,
'expire' => 0,
'others' => 1 );
&save_module_acl_logged(\%acl, $_[1]->{'name'}, "passwd")
if (!$hasmods{'passwd'});
push(@mods, "passwd");
}
elsif ($extramods{'passwd'} == 2) {
# Can change all mailbox passwords (except for the domain
# owner, if this is an extra admin)
local %acl = ( 'noconfig' => 1,
'mode' => 5,
'users' => $_[0]->{'group'},
'notusers' => $_[0]->{'user'},
'repeat' => 1,
'old' => 0,
'expire' => 0,
'others' => 1 );
&save_module_acl_logged(\%acl, $_[1]->{'name'}, "passwd")
if (!$hasmods{'passwd'});
push(@mods, "passwd");
}
}
if ($extramods{'proc'} && $_[0]->{'unix'}) {
# Can only manage and see his own processes
local %acl = ( 'noconfig' => 1,
'uid' => $_[0]->{'uid'},
'edit' => 1,
'run' => 1,
'users' => $_[0]->{'user'},
'only' => ($extramods{'proc'} == 2) );
&save_module_acl_logged(\%acl, $_[1]->{'name'}, "proc")
if (!$hasmods{'proc'});
push(@mods, "proc");
}
if ($extramods{'cron'} && $_[0]->{'unix'}) {
# Can only manage his cron jobs
local %acl = ( 'noconfig' => 1,
'mode' => 1,
'users' => $_[0]->{'user'},
'allow' => 0 );
&save_module_acl_logged(\%acl, $_[1]->{'name'}, "cron")
if (!$hasmods{'cron'});
push(@mods, "cron");
}
if ($extramods{'at'} && $_[0]->{'unix'}) {
# Can only manage his at jobs
local %acl = ( 'noconfig' => 1,
'mode' => 1,
'users' => $_[0]->{'user'},
'allow' => 0 );
&save_module_acl_logged(\%acl, $_[1]->{'name'}, "at")
if (!$hasmods{'at'});
push(@mods, "at");
}
if ($extramods{'telnet'} && $_[0]->{'unix'}) {
# Cannot configure module
local %acl = ( 'noconfig' => 1 );
&save_module_acl_logged(\%acl, $_[1]->{'name'}, "telnet")
if (!$hasmods{'telnet'});
push(@mods, "telnet");
}
if ($extramods{'custom'}) {
# Cannot edit or create commands
local %acl = ( 'noconfig' => 1,
'cmd' => '*',
'edit' => 0 );
&save_module_acl_logged(\%acl, $_[1]->{'name'}, "custom")
if (!$hasmods{'custom'});
push(@mods, "custom");
}
if ($extramods{'shell'} && $_[0]->{'unix'}) {
# Can only run commands as server owner
local %acl = ( 'noconfig' => 1,
'user' => $_[0]->{'user'} );
&save_module_acl_logged(\%acl, $_[1]->{'name'}, "shell")
if (!$hasmods{'shell'});
push(@mods, "shell");
}
if ($extramods{'updown'} && $_[0]->{'unix'}) {
# Can upload and download to home dir only
local %acl = ( 'noconfig' => 1,
'dirs' => $_[0]->{'home'},
'home' => 0,
'mode' => 3, );
if ($extramods{'updown'} == 2) {
# Can only upload
$acl{'download'} = 0;
$acl{'fetch'} = 0;
}
&save_module_acl_logged(\%acl, $_[1]->{'name'}, "updown")
if (!$hasmods{'updown'});
push(@mods, "updown");
# Set defaults for upload and download directories for this user
local %udconfig;
local $udfile = "$config_directory/updown/config";
&lock_file($udfile);
&read_file($udfile, \%udconfig);
$udconfig{'dir_'.$_[1]->{'name'}} ||= &resolve_links($_[0]->{'home'});
$udconfig{'ddir_'.$_[1]->{'name'}} ||= &resolve_links($_[0]->{'home'});
&write_file($udfile, \%udconfig);
&unlock_file($udfile);
}
if ($extramods{'change-user'}) {
# This module is always safe, so no ACL needs to be set
push(@mods, "change-user");
}
if ($extramods{'htaccess-htpasswd'} && $_[0]->{'unix'}) {
# Can create .htaccess files in home dir, as user
local %acl = ( 'noconfig' => 1,
'home' => 0,
'dirs' => $_[0]->{'home'},
'sync' => 0,
'user' => $_[0]->{'user'} );
&save_module_acl_logged(\%acl, $_[1]->{'name'}, "htaccess-htpasswd")
if (!$hasmods{'htaccess-htpasswd'});
push(@mods, "htaccess-htpasswd");
}
local @maildoms = grep { $_->{'mail'} } @doms;
if ($extramods{'mailboxes'} && @maildoms) {
# Can read mailboxes of users
local %acl = ( 'noconfig' => 1,
'fmode' => 1,
'from' => join(" ", map { $_->{'dom'} } @maildoms),
'canattach' => 0,
'candetach' => 0,
'dir' => &mail_domain_base($_[0]) );
if (!&mail_system_needs_group()) {
# For vpopmail, mailboxes are identified by domain
$acl{'mmode'} = 6;
$acl{'musers'} = ".*\@(".
join("|", map { $_->{'dom'} } @maildoms).")";
}
else {
# By server GID
$acl{'mmode'} = 5;
$acl{'musers'} = $_[0]->{'gid'};
}
&save_module_acl_logged(\%acl, $_[1]->{'name'}, "mailboxes")
if (!$hasmods{'mailboxes'});
push(@mods, "mailboxes");
}
else {
@mods = grep { $_ ne "mailboxes" } @mods;
}
if ($extramods{'webminlog'} && $_[0]->{'webmin'}) {
# Can view own actions, and those of extra admins
local @users = ( $_[0]->{'user'} );
if ($virtualmin_pro) {
push(@users, map { $_->{'name'} } &list_extra_admins($_[0]));
}
local %acl = ( 'users' => join(" ", @users),
'rollback' => 0 );
&save_module_acl_logged(\%acl, $_[1]->{'name'}, "webminlog")
if (!$hasmods{'webminlog'});
push(@mods, "webminlog");
}
else {
@mods = grep { $_ ne "webminlog" } @mods;
}
if ($extramods{'syslog'} && $_[0]->{'webmin'}) {
# Can view log files for Apache and ProFTPd
local @extras;
local %done;
foreach my $sd (@doms) {
# Add Apache logs, for domains with websites and separate logs
if ($sd->{'web'} && !$sd->{'alias_mode'}) {
local $alog = &get_website_log($sd, 0);
local $elog = &get_website_log($sd, 1);
push(@extras, $alog." ".&text('webmin_alog',
$sd->{'dom'}))
if ($alog && !$done{$alog}++);
push(@extras, $elog." ".&text('webmin_elog',
$sd->{'dom'}))
if ($elog && !$done{$elog}++);
}
# Add FTP logs
if ($sd->{'ftp'}) {
local $flog = &get_proftpd_log($sd->{'ip'});
push(@extras, $flog." ".&text('webmin_flog',
$sd->{'dom'}))
if ($flog && !$done{$flog}++);
}
}
if (@extras) {
local %acl = ( 'extras' => join("\t", @extras),
'any' => 0,
'noconfig' => 1,
'noedit' => 1,
'syslog' => 0,
'others' => 0 );
&save_module_acl_logged(\%acl, $_[1]->{'name'}, "syslog")
if (!$hasmods{'syslog'});
push(@mods, "syslog");
}
else {
# No logs found!
@mods = grep { $_ ne "syslog" } @mods;
}
}
else {
@mods = grep { $_ ne "syslog" } @mods;
}
local @pconfs;
if ($extramods{'phpini'}) {
# Can edit PHP configuration files
foreach my $sd (@doms) {
if ($sd->{'web'} && defined(&get_domain_php_mode) &&
&get_domain_php_mode($sd) ne "mod_php") {
foreach my $ini (&list_domain_php_inis($sd)) {
local @st = stat($ini->[1]);
if (@st && $st[4] == $sd->{'uid'}) {
if ($ini->[0]) {
push(@pconfs, "$ini->[1]=".
&text('webmin_phpini2',
$sd->{'dom'}, $ini->[0]));
}
else {
push(@pconfs, "$ini->[1]=".
&text('webmin_phpini',
$sd->{'dom'}));
}
}
}
}
}
}
if (@pconfs) {
local %acl = ( 'php_inis' => join("\t", @pconfs),
'noconfig' => 1,
'global' => 0,
'anyfile' => 0,
'user' => $_[0]->{'user'},
'manual' => 1 );
&save_module_acl_logged(\%acl, $_[1]->{'name'}, "phpini")
if (!$hasmods{'phpini'});
push(@mods, "phpini");
}
else {
@mods = grep { $_ ne "phpini" } @mods;
}
if (!$noextras) {
# Add any extra modules specified for this domain
push(@mods, split(/\s+/, $_[0]->{'webmin_modules'}));
# Add any extra modules specified in global config
local @wmods = split(/\s+/, $config{'webmin_modules'});
local $m;
foreach $m (@wmods) {
local %acl = ( 'noconfig' => 1 );
&save_module_acl_logged(\%acl, $_[1]->{'name'}, $m)
if (!$hasmods{$m});
}
push(@mods, @wmods);
}
if (!$nofeatures) {
# Add plugin-specified modules, except those that have been disabled
# for domain owners in the template
local $p;
foreach $p (@plugins) {
local @pmods = &plugin_call($p, "feature_webmin", $_[0],
\@doms);
local $pm;
foreach $pm (@pmods) {
next if ($avail{$pm->[0]} ne '' &&
!$avail{$pm->[0]});
push(@mods, $pm->[0]);
if ($pm->[1]) {
&save_module_acl_logged(
$pm->[1], $_[1]->{'name'}, $pm->[0]);
}
}
}
}
# Finally, override in settings from template Webmin group
local @ownmods = @mods;
if ($tmpl->{'webmin_group'} ne 'none') {
local ($group) = grep { $_->{'name'} eq $tmpl->{'webmin_group'} }
&acl::list_groups();
if ($group) {
# Add modules from group to list
push(@mods, @{$group->{'modules'}});
# Copy group's ACLs to user
&acl::copy_group_user_acl_files(
$group->{'name'}, $wuser->{'name'},
[ @{$group->{'modules'}}, "" ]);
}
}
$wuser->{'ownmods'} = [ &unique(@ownmods) ];
$wuser->{'modules'} = [ &unique(@mods) ];
$wuser->{'readonly'} = $module_name;
&acl::modify_user($wuser->{'name'}, $wuser);
}
# check_webmin_clash(&domain, [field])
# Returns 1 if a user or group with this name already exists
sub check_webmin_clash
{
if (!$_[1] || $_[1] eq 'user') {
&require_acl();
return 1 if ($_[0]->{'user'} eq 'webmin');
return 0 if ($_[0]->{'webmin_overwrite'});
local $u;
foreach $u (&acl::list_users()) {
return 1 if ($u->{'name'} eq $_[0]->{'user'});
}
}
return 0;
}
# modify_all_webmin([template-id])
# Updates the Webmin users for all domains (or just those on some template)
sub modify_all_webmin
{
local ($tid) = @_;
&$first_print($text{'check_allwebmin'});
&obtain_lock_webmin($_[0]);
{
local ($first_print, $second_print, $indent_print, $outdent_print);
&set_all_null_print();
local $d;
foreach $d (&list_domains()) {
if ($d->{'webmin'} && $config{'webmin'} &&
(!defined($tid) || $d->{'template'} == $tid)) {
&modify_webmin($d, $d);
}
}
}
&release_lock_webmin($_[0]);
&$second_print($text{'setup_done'});
®ister_post_action(\&restart_webmin);
}
# refresh_webmin_user(&domain, [&old-domain])
# Calls modify_webmin for a domain or the appropriate parent. This will
# update the ACL for the domain's Webmin login, create and update extra
# admins, and possibly update the reseller too.
sub refresh_webmin_user
{
local ($d, $oldd) = @_;
my $has_oldd = $oldd ? 1 : 0;
$oldd ||= $d;
local $wd = $d->{'parent'} ? &get_domain($d->{'parent'}) : $d;
local $oldwd = $oldd->{'parent'} ? &get_domain($oldd->{'parent'}) : $oldd;
if ($wd->{'webmin'}) {
&modify_webmin($wd, $oldwd);
}
if ($wd->{'reseller'} && $virtualmin_pro) {
# Update all resellers on the domain
foreach my $r (split(/\s+/, $wd->{'reseller'})) {
my $rinfo = &get_reseller($r);
if ($rinfo) {
&modify_reseller($rinfo, $rinfo);
}
}
}
if ($oldwd->{'reseller'} && $virtualmin_pro && $has_oldd) {
# Update resellers who were previously owners of the domain
foreach my $r (split(/\s+/, $oldwd->{'reseller'})) {
my $rinfo = &get_reseller($r);
if ($rinfo) {
&modify_reseller($rinfo, $rinfo);
}
}
}
}
# save_module_acl_logged(&acl, user, module)
# Save an ACL file, with locking and tight permissions
sub save_module_acl_logged
{
my ($acl, $user, $mod) = @_;
my $afile = "$config_directory/$mod/$user.acl";
&lock_file($afile);
&save_module_acl($acl, $user, $mod);
&unlock_file($afile);
&set_ownership_permissions(undef, undef, 0600, $afile);
}
# update_extra_webmin(&domain, [force-disable])
# Creates, updates or deletes Webmin users to be the extra admins for a
# virtual server.
sub update_extra_webmin
{
local ($d, $forcedis) = @_;
local @admins = &list_extra_admins($d);
local %admins = map { $_->{'name'}, $_ } @admins;
local %webmins;
local @dis = split(/,/, $d->{'disabled'});
local $dis = !defined($forcedis) ? &indexof("webmin", @dis) >= 0
: $forcedis;
# Get current users
&require_acl();
foreach my $u (&acl::list_users()) {
if (&indexof($module_name, @{$u->{'modules'}}) >= 0) {
local %acl = &get_reseller_acl($u->{'name'});
if ($acl{'admin'} && $acl{'admin'} eq $d->{'id'}) {
# Found an admin for this domain
if ($admins{$u->{'name'}}) {
$webmins{$u->{'name'}} = $u;
}
else {
# Who shouldn't exist!
&acl::delete_user($u->{'name'});
}
}
}
}
# Create or update users
foreach my $admin (@admins) {
local $wuser = $webmins{$admin->{'name'}};
local $pass = $forcedis ? "*LK*" :
&acl::encrypt_password($admin->{'pass'});
if ($wuser) {
# User already exists .. make sure he's an extra admin
local %aacl = &get_module_acl($admin->{'name'}, $module_name);
if (!$aacl{'admin'}) {
next;
}
# Update password (if changed)
if ($pass eq "*LK*" ||
&acl::encrypt_password($admin->{'pass'}, $wuser->{'pass'})
ne $wuser->{'pass'}) {
$wuser->{'pass'} = $pass;
&acl::modify_user($wuser->{'name'}, $wuser);
}
}
else {
# Need to create user
$wuser = { 'name' => $admin->{'name'},
'pass' => $pass,
'notabs' => !$config{'show_tabs'},
'modules' => [ ],
'theme' => $config{'webmin_theme'} eq '*' ? undef :
$config{'webmin_theme'} eq '' ? '' :
$config{'webmin_theme'}
};
&acl::create_user($wuser);
}
local %acl;
foreach my $ed (@edit_limits) {
if ($d->{'edit_'.$ed}) {
$acl{'edit_'.$ed} = $admin->{'edit_'.$ed};
}
}
$acl{'edit'} = $acl{'edit_domain'} ? 2 : 0;
$acl{'create'} = $d->{'domslimit'} && $admin->{'create'} ? 2 : 0;
$acl{'norename'} = $d->{'norename'} || $admin->{'norename'};
$acl{'nodbname'} = $d->{'nodbname'} || $admin->{'nodbname'};
$acl{'forceunder'} = $d->{'forceunder'} || $admin->{'forceunder'};
&set_user_modules($d, $wuser, \%acl,
!$admin->{'features'}, !$admin->{'modules'}, 1,
$admin->{'doms'} ? [ split(/\s+/, $admin->{'doms'}) ]
: undef);
}
}
# backup_webmin(&domain, file, &options)
# Create a tar file of all .acl files, for the server owner and extra admins
sub backup_webmin
{
local ($d, $file, $opts) = @_;
&$first_print($text{'backup_webmin'});
&require_acl();
# Write out .acl files for domain owner and extra admins, if they are in
# MySQL or LDAP
my ($wuser) = &acl::get_user($d->{'user'});
my @nonlocal;
push(@nonlocal, $wuser) if ($wuser && $wuser->{'proto'});
foreach my $admin (&list_extra_admins($d)) {
my ($auser) = &acl::get_user($admin->{'name'});
push(@nonlocal, $auser) if ($auser && $auser->{'proto'});
}
my @acltemp;
foreach my $u (@nonlocal) {
foreach my $m ("", @{$u->{'modules'}}) {
my %acl = &get_module_acl($u->{'name'}, $m, 0, 1);
if (%acl) {
my $acltemp = "$config_directory/$m/$u->{'name'}.acl";
&write_file($acltemp, \%acl);
push(@acltemp, $acltemp);
}
}
}
# Add .acl files for domain owner
local @files;
if (-r "$config_directory/$d->{'user'}.acl") {
push(@files, "$d->{'user'}.acl");
}
local @otheracls = glob("$config_directory/*/$d->{'user'}.acl");
@otheracls = grep { !/\*/ } @otheracls;
if (@otheracls) {
push(@files, "*/$d->{'user'}.acl");
}
# Add .acl files for extra admins
foreach my $admin (&list_extra_admins($d)) {
push(@files, "$admin->{'name'}.acl");
local @otheracls = glob("$config_directory/*/$admin->{'name'}.acl");
@otheracls = grep { !/\*/ } @otheracls;
if (@otheracls) {
push(@files, "*/$admin->{'name'}.acl");
}
}
if (!@files) {
&$second_print($text{'backup_webminnofiles'});
return 1;
}
# Tar them all up
local $temp = &transname();
local $out = &backquote_command(
"cd $config_directory && ".
"tar cf ".quotemeta($temp)." ".join(" ", @files)." 2>&1");
my $ex = $?;
if (!$ex) {
©_write_as_domain_user($d, $temp, $file);
}
&unlink_file($temp);
&unlink_file(@acltemp) if (@acltemp);
if ($ex) {
&$second_print(&text('backup_webminfailed', "<pre>$out</pre>"));
return 0;
}
else {
&$second_print($text{'setup_done'});
return 1;
}
}
# restore_webmin(&domain, file, &options)
# Extract all .acl files from the backup
sub restore_webmin
{
local ($d, $file, $opts) = @_;
&$first_print($text{'restore_webmin'});
&require_acl();
&obtain_lock_webmin($_[0]);
local $out = &backquote_logged(
"cd $config_directory && tar xf ".quotemeta($file)." 2>&1");
local $rv;
if ($?) {
&$second_print(&text('backup_webminfailed', "<pre>$out</pre>"));
$rv = 0;
}
else {
&$second_print($text{'setup_done'});
$rv = 1;
# Re-load .acl files for domain owner and extra admins, if they are in
# MySQL or LDAP
my ($wuser) = &acl::get_user($d->{'user'});
my @nonlocal;
push(@nonlocal, $wuser) if ($wuser && $wuser->{'proto'});
foreach my $admin (&list_extra_admins($d)) {
my ($auser) = &acl::get_user($admin->{'name'});
push(@nonlocal, $auser) if ($auser && $auser->{'proto'});
}
foreach my $u (@nonlocal) {
foreach my $m ("", @{$u->{'modules'}}) {
my %acl;
my $acltemp = "$config_directory/$m/$u->{'name'}.acl";
&read_file($acltemp, \%acl) || next;
&unlink_file($acltemp);
&save_module_acl(\%acl, $u->{'name'}, $m);
}
}
}
&release_lock_webmin($_[0]);
return $rv;
}
# links_webmin(&domain)
# Returns a link to the Webmin Actions Log module
sub links_webmin
{
local ($d) = @_;
return ( { 'mod' => 'webminlog',
'desc' => $text{'links_webminlog'},
'page' => "search.cgi?uall=0&user=".&urlize($d->{'user'}).
"&mall=1&tall=2&fall=1",
'cat' => 'logs',
} );
return ( );
}
# show_template_webmin(&tmpl)
# Outputs HTML for editing webmin-user-related template options
sub show_template_webmin
{
local ($tmpl) = @_;
# Global ACL on or off
if (!$tmpl->{'default'}) {
local @gacl_fields = ( "gacl_umode", "gacl_uusers", "gacl_ugroups",
"gacl_groups", "gacl_root" );
local $dis1 = &js_disable_inputs(\@gacl_fields, [ ]);
local $dis2 = &js_disable_inputs([ ], \@gacl_fields);
print &ui_table_row(&hlink($text{'tmpl_gacl'}, "template_gacl"),
&ui_radio("gacl", int($tmpl->{'gacl'}),
[ [ 0, $text{'default'}, "onClick='$dis1'" ],
[ 1, $text{'tmpl_gaclbelow'}, "onClick='$dis2'" ] ]));
}
# Global ACL users
print &ui_table_row(&hlink($text{'tmpl_gaclu'}, "template_gacl_umode"),
&ui_radio("gacl_umode", int($tmpl->{'gacl_umode'}),
[ [ 0, $text{'tmpl_gacl0'}." ".
&ui_textbox("gacl_uusers",
$tmpl->{'gacl_umode'} == 0 ? $tmpl->{'gacl_uusers'} : "",
40)."<br>\n" ],
[ 1, $text{'tmpl_gacl1'}." ".
&ui_textbox("gacl_ugroups",
$tmpl->{'gacl_umode'} == 1 ? $tmpl->{'gacl_ugroups'} :"",
40) ] ]));
# Global ACL groups
print &ui_table_row(&hlink($text{'tmpl_gaclg'}, "template_groups"),
&ui_textbox("gacl_groups", $tmpl->{'gacl_groups'}, 40));
# Global ACL root
print &ui_table_row(&hlink($text{'tmpl_gaclr'}, "template_root"),
&ui_textbox("gacl_root", $tmpl->{'gacl_root'}, 40));
# Extra admin prefix
print &ui_table_row(
&hlink($text{'tmpl_extra_prefix'}, "template_extra_prefix"),
&none_def_input("extra_prefix", $tmpl->{'extra_prefix'},
$text{'tmpl_sel'}, 0, 0, undef, [ "extra_prefix" ])." ".
&ui_textbox("extra_prefix", $tmpl->{'extra_prefix'} eq "none" ? undef :
$tmpl->{'extra_prefix'}, 15));
# Webmin group for domain owner
&require_acl();
local @groups = &acl::list_groups();
if (@groups) {
print &ui_table_row(
&hlink($text{'tmpl_wgroup'}, "template_webmin_group"),
&ui_select("webmin_group", $tmpl->{'webmin_group'},
[ $tmpl->{'default'} ? ( )
: ( [ "", "<$text{'default'}>" ] ),
[ "none", "<$text{'newtmpl_none'}>" ],
map { [ $_->{'name'} ] } &acl::list_groups() ]));
}
}
# parse_template_webmin(&tmpl)
# Updates webmin-user-related template options from %in
sub parse_template_webmin
{
local ($tmpl) = @_;
# Save global ACL
$tmpl->{'gacl'} = $in{'gacl'};
$tmpl->{'gacl_umode'} = $in{'gacl_umode'};
$tmpl->{'gacl_uusers'} = $in{'gacl_uusers'};
$tmpl->{'gacl_ugroups'} = $in{'gacl_ugroups'};
$tmpl->{'gacl_groups'} = $in{'gacl_groups'};
$tmpl->{'gacl_root'} = $in{'gacl_root'};
$tmpl->{'extra_prefix'} = &parse_none_def("extra_prefix");
$tmpl->{'webmin_group'} = $in{'webmin_group'};
}
# get_reseller_acl(username)
# Returns just the ACL for some Webmin user, in this module
sub get_reseller_acl
{
my ($name) = @_;
return &get_module_acl($name);
}
# obtain_lock_webmin()
# Lock a flag file indicating that Virtualmin is managing Webmin users.
# Real locking is done in acl-lib.pl.
sub obtain_lock_webmin
{
return if (!$config{'webmin'});
&obtain_lock_anything();
if ($main::got_lock_webmin == 0) {
&lock_file("$module_config_directory/webminlock");
}
$main::got_lock_webmin++;
}
# release_lock_webmin()
# Release the lock flag file
sub release_lock_webmin
{
return if (!$config{'webmin'});
if ($main::got_lock_webmin == 1) {
&unlock_file("$module_config_directory/webminlock");
}
$main::got_lock_webmin-- if ($main::got_lock_webmin);
&release_lock_anything();
}
$done_feature_script{'webmin'} = 1;
1;
y~or5J={Eeu磝Qk ᯘG{?+]ן?wM3X^歌>{7پK>on\jy Rg/=fOroNVv~Y+ NGuÝHWyw[eQʨSb> >}Gmx[o[<{Ϯ_qFvM IENDB`