Server IP : 195.201.23.43 / Your IP : 13.59.203.127 Web Server : Apache System : Linux webserver2.vercom.be 5.4.0-192-generic #212-Ubuntu SMP Fri Jul 5 09:47:39 UTC 2024 x86_64 User : kdecoratie ( 1041) PHP Version : 7.1.33-63+ubuntu20.04.1+deb.sury.org+1 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals, MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /usr/share/webmin/virtual-server/ |
Upload File : |
sub require_acl { return if ($require_acl++); &foreign_require("acl"); } # 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'}); return 1; } # 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'}); return 1; } # 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"); 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'}); return 1; } # 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'}); return 1; } # restart_webmin() # Send a signal to Webmin to re-read its config sub restart_webmin { my $restarted = &getvar('webmin-restarted', undef, 1); &$first_print($text{'setup_webminpid2'}) if (!$restarted); eval { local $main::error_must_die = 1; &reload_miniserv(); }; if ($@) { &$second_print(&text('setup_webmindown2', "$@")) if (!$restarted); } else { &$second_print($text{'setup_done'}) if (!$restarted); } } # restart_webmin_fully() # Send a signal to Webmin to make it fully restart and re-read its config sub restart_webmin_fully { &setvar('webmin-restarted', 1); &$first_print($text{'setup_webminpid'}); eval { local $main::error_must_die = 1; &restart_miniserv(); }; if ($@) { &$second_print(&text('setup_webmindown2', "$@")); } else { &$second_print($text{'setup_done'}); } } # restart_usermin() # Send a signal to Usermin to make it fully restart and re-read it's config sub restart_usermin { &foreign_require("usermin"); &$first_print($text{'setup_userminpid'}); eval { local $main::error_must_die = 1; &usermin::restart_usermin_miniserv(); }; if ($@) { &$second_print(&text('setup_usermindown2', "$@")); } else { &$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($d->{'template'}); local $chroot = &get_domain_jailkit($d); # Work out which module's ACLs to leave alone local %hasmods = map { $_, 1 } @{$wuser->{'modules'}}; %hasmods = ( ) if (!$config{'leave_acl'}); # Work out which domains and features exist local @doms = ( $d, &get_domain_by("parent", $d->{'id'}) ); local %doneid; @doms = grep { !$doneid{$_->{'id'}}++ } @doms; local (%features, $sd, $f); if (!$nofeatures) { foreach $sd (@doms) { foreach $f (@features) { $features{$f}++ if ($sd->{$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'} && !$d->{'provision_dns'} && !$d->{'dns_cloud'}) { # Allow user to manage just their domains push(@mods, "bind8"); local %acl = ( 'noconfig' => 1, 'zones' => join(" ", map { $_->{'dom'} } grep { $_->{'dns'} && !$_->{'provision_dns'} && !$_->{'dns_cloud'} } @doms), 'dir' => &resolve_links($d->{'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, $wuser->{'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 my $mymod = &require_dom_mysql($d); push(@mods, $mymod); 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($d), 'pass' => &mysql_pass($d), 'buser' => $d->{'user'}, 'bpath' => "/" ); &save_module_acl_logged(\%acl, $wuser->{'name'}, $mymod) if (!$hasmods{$mymod}); } else { @mods = grep { !/^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($d), 'pass' => &postgres_pass($d, 1), 'sameunix' => 1, 'backup' => 0, 'restore' => 0 ); &save_module_acl_logged(\%acl, $wuser->{'name'}, "postgresql") if (!$hasmods{'postgresql'}); } else { @mods = grep { $_ ne "postgresql" } @mods; } # Grant access to Apache module if needed if ($features{'web'} && $avail{'web'} && $d->{'edit_phpmode'}) { # 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($d->{'home'}), 'aliasdir' => &resolve_links($d->{'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, $wuser->{'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' => $d->{'user'}, 'dir' => join(" ", @logs) ); &save_module_acl_logged(\%acl, $wuser->{'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' => $d->{'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, $wuser->{'name'}, "spam") if (!$hasmods{'spam'}); } else { @mods = grep { $_ ne "spam" } @mods; } # All users get access to virtualmin at least local $can_create = $d->{'domslimit'} && !$d->{'no_create'} && $d->{'unix'}; push(@mods, $module_name); local %acl = ( 'noconfig' => 1, 'edit' => $d->{'edit_domain'} ? 2 : 0, 'create' => $can_create ? 2 : 0, 'import' => 0, 'stop' => 0, 'local' => 0, 'nodbname' => $d->{'nodbname'}, 'norename' => $d->{'norename'}, 'migrate' => $d->{'migrate'}, 'forceunder' => $d->{'forceunder'}, 'safeunder' => $d->{'safeunder'}, 'ipfollow' => $d->{'ipfollow'}, 'domains' => join(" ", map { $_->{'id'} } @doms), 'admin' => $acls ? $d->{'id'} : undef, ); foreach $f (@opt_features, &list_feature_plugins(), 'virt') { $acl{"feature_$f"} = $d->{"limit_$f"}; } foreach my $ed (@edit_limits) { $acl{'edit_'.$ed} = $d->{'edit_'.$ed}; } $acl{'allowedscripts'} = $d->{'allowedscripts'}; if ($acls) { foreach my $k (keys %$acls) { $acl{$k} = $acls->{$k}; } } &save_module_acl_logged(\%acl, $wuser->{'name'}); %uaccess = %acl; # Set global ACL options local %acl = ( 'feedback' => 0, 'rpc' => 0, 'negative' => 1, 'readonly' => $d->{'demo'}, 'fileunix' => $d->{'user'} ); if ($chroot) { $acl{'root'} = $d->{'home'}; } else { $acl{'root'} = &resolve_links( &substitute_domain_template($tmpl->{'gacl_root'}, $d)); } if ($tmpl->{'gacl_umode'} == 1) { $acl{'uedit_mode'} = 5; $acl{'uedit'} = &substitute_domain_template($tmpl->{'gacl_ugroups'}, $d); } else { $acl{'uedit_mode'} = 2; $acl{'uedit'} = &substitute_domain_template($tmpl->{'gacl_uusers'}, $d); } $acl{'gedit_mode'} = 2; $acl{'gedit'} = &substitute_domain_template($tmpl->{'gacl_groups'}, $d); if (!$d->{'domslimit'}) { $acl{'desc_'.$module_name} = $text{'index_title2'}; } &save_module_acl_logged(\%acl, $wuser->{'name'}, "."); if ($extramods{'file'} && $d->{'unix'}) { # Limit old Java file manager to user's directory, as unix user local %acl = ( 'noconfig' => 1, 'uid' => $d->{'uid'}, 'follow' => 0, 'root' => &resolve_links($d->{'home'}), 'home' => 0, 'goto' => 1 ); &save_module_acl_logged(\%acl, $wuser->{'name'}, "file") if (!$hasmods{'file'}); push(@mods, "file"); } if ($extramods{'filemin'} && $d->{'unix'}) { # Limit new HTML file manager to user's directory, as unix user my $modname = &foreign_check("file-manager") ? "file-manager" : "filemin"; my $homedir; if (@doms == 1) { $homedir = $doms[0]->{'home'}; } else { $homedir = $d->{'home'}; } local %acl = ( 'noconfig' => 1, 'work_as_root' => 0, 'work_as_user', $d->{'user'}, 'allowed_paths' => &resolve_links($homedir), ); &save_module_acl_logged(\%acl, $wuser->{'name'}, $modname) if (!$hasmods{$modname}); push(@mods, $modname); } if ($d->{'unix'}) { if ($extramods{'passwd'} == 1 && !$isextra) { # Can only change domain owners password local %acl = ( 'noconfig' => 1, 'mode' => 1, 'users' => $d->{'user'}, 'repeat' => 1, 'old' => 1, 'expire' => 0, 'others' => 1 ); &save_module_acl_logged(\%acl, $wuser->{'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' => $d->{'group'}, 'notusers' => $d->{'user'}, 'repeat' => 1, 'old' => 0, 'expire' => 0, 'others' => 1 ); &save_module_acl_logged(\%acl, $wuser->{'name'}, "passwd") if (!$hasmods{'passwd'}); push(@mods, "passwd"); } } if ($extramods{'proc'} && $d->{'unix'} && !$chroot) { # Can only manage and see his own processes local %acl = ( 'noconfig' => 1, 'uid' => $d->{'uid'}, 'edit' => 1, 'run' => 1, 'users' => $d->{'user'}, 'only' => ($extramods{'proc'} == 2) ); &save_module_acl_logged(\%acl, $wuser->{'name'}, "proc") if (!$hasmods{'proc'}); push(@mods, "proc"); } if ($extramods{'cron'} && $d->{'unix'} && !$chroot) { # Can only manage his cron jobs local %acl = ( 'noconfig' => 1, 'mode' => 1, 'users' => $d->{'user'}, 'allow' => 0 ); &save_module_acl_logged(\%acl, $wuser->{'name'}, "cron") if (!$hasmods{'cron'}); push(@mods, "cron"); } if ($extramods{'at'} && $d->{'unix'} && !$chroot) { # Can only manage his at jobs local %acl = ( 'noconfig' => 1, 'mode' => 1, 'users' => $d->{'user'}, 'allow' => 0, 'stop' => 0, ); &save_module_acl_logged(\%acl, $wuser->{'name'}, "at") if (!$hasmods{'at'}); push(@mods, "at"); } if ($extramods{'telnet'} && $d->{'unix'}) { # Cannot configure telnet module local %acl = ( 'noconfig' => 1 ); &save_module_acl_logged(\%acl, $wuser->{'name'}, "telnet") if (!$hasmods{'telnet'}); push(@mods, "telnet"); } if ($extramods{'xterm'} && $d->{'unix'}) { # Cannot configure module xterm module, and shell opens as domain user local %acl = ( 'noconfig' => 1, 'user' => $d->{'user'} ); &save_module_acl_logged(\%acl, $wuser->{'name'}, "xterm") if (!$hasmods{'xterm'}); push(@mods, "xterm"); } if ($extramods{'custom'}) { # Cannot edit or create commands local %acl = ( 'noconfig' => 1, 'cmd' => '*', 'edit' => 0 ); &save_module_acl_logged(\%acl, $wuser->{'name'}, "custom") if (!$hasmods{'custom'}); push(@mods, "custom"); } if ($extramods{'shell'} && $d->{'unix'}) { # Can only run commands as server owner local %acl = ( 'noconfig' => 1, 'user' => $d->{'user'}, 'chroot' => $chroot || '/' ); &save_module_acl_logged(\%acl, $wuser->{'name'}, "shell") if (!$hasmods{'shell'}); push(@mods, "shell"); } if ($extramods{'updown'} && $d->{'unix'}) { # Can upload and download to home dir only local %acl = ( 'noconfig' => 1, 'dirs' => $d->{'home'}, 'home' => 0, 'mode' => 3, ); if ($extramods{'updown'} == 2) { # Can only upload $acl{'download'} = 0; $acl{'fetch'} = 0; } &save_module_acl_logged(\%acl, $wuser->{'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_'.$wuser->{'name'}} ||= &resolve_links($d->{'home'}); $udconfig{'ddir_'.$wuser->{'name'}} ||= &resolve_links($d->{'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'} && $d->{'unix'}) { # Can create .htaccess files in home dir, as user local %acl = ( 'noconfig' => 1, 'home' => 0, 'dirs' => $d->{'home'}, 'sync' => 0, 'user' => $d->{'user'} ); &save_module_acl_logged(\%acl, $wuser->{'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($d) ); 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'} = $d->{'gid'}; } &save_module_acl_logged(\%acl, $wuser->{'name'}, "mailboxes") if (!$hasmods{'mailboxes'}); push(@mods, "mailboxes"); } else { @mods = grep { $_ ne "mailboxes" } @mods; } if ($extramods{'logviewer'} && $d->{'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 (&domain_has_website($sd) && !$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); if ($flog && !$done{$flog}++) { push(@extras, $flog." ".&text('webmin_flog', $sd->{'dom'})) } } # Add PHP log my $phplog = &get_domain_php_error_log($d); if ($phplog && !$done{$phplog}++) { push(@extras, $phplog." ".&text('webmin_plog', $sd->{'dom'})); } } if (@extras) { local %acl = ( 'extras' => join("\t", @extras), 'any' => 0, 'noconfig' => 1, 'noedit' => 1, 'syslog' => 0, 'others' => 0 ); &save_module_acl_logged(\%acl, $wuser->{'name'}, "logviewer") if (!$hasmods{'logviewer'}); push(@mods, "logviewer"); @mods = grep { $_ ne "syslog" } @mods; } else { # No logs found! @mods = grep { $_ ne "syslog" && $_ ne "logviewer" } @mods; } } else { @mods = grep { $_ ne "syslog" && $_ ne "logviewer" } @mods; } local @pconfs; if ($extramods{'phpini'} && $d->{'edit_phpmode'}) { # Can edit PHP configuration files foreach my $sd (grep { $_->{'web'} } @doms) { my $mode = &get_domain_php_mode($sd); if ($mode ne "mod_php" && $mode ne "fpm" && $mode ne "none") { # Allow access to .ini files 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'})); } } } } elsif ($mode eq "fpm") { # Allow access to FPM configs for PHP overrides my $conf = &get_php_fpm_config($sd); if ($conf) { my $file = $conf->{'dir'}."/". $sd->{'id'}.".conf"; push(@pconfs, $file."=". &text('webmin_phpini', $sd->{'dom'})); } } } } if (@pconfs) { local %acl = ( 'php_inis' => join("\t", @pconfs), 'noconfig' => 1, 'global' => 0, 'anyfile' => 0, 'user' => $d->{'user'}, 'manual' => 0 ); &save_module_acl_logged(\%acl, $wuser->{'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+/, $d->{'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, $wuser->{'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", $d, \@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], $wuser->{'name'}, $pm->[0]); } } } } if ($extramods{'webminlog'} && $d->{'webmin'}) { # Can view own actions, and those of extra admins. This has to be # done last, to have access to the list of modules. local @users = ( $d->{'user'} ); if ($virtualmin_pro) { push(@users, map { $_->{'name'} } &list_extra_admins($d)); } local %acl = ( 'users' => join(" ", @users), 'mods' => join(" ", @mods), 'rollback' => 0 ); &save_module_acl_logged(\%acl, $wuser->{'name'}, "webminlog") if (!$hasmods{'webminlog'}); push(@mods, "webminlog"); } else { @mods = grep { $_ ne "webminlog" } @mods; } # 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(); &push_all_print(); &set_all_null_print(); foreach my $d (&list_domains()) { if ($d->{'webmin'} && $config{'webmin'} && (!defined($tid) || $d->{'template'} == $tid)) { &modify_webmin($d, $d); } } &pop_all_print(); &release_lock_webmin(); &$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, $homefmt, $increment, $asd, $allopts, $key) = @_; local $compression = $allopts->{'dir'}->{'compression'}; local $destfile = $file.".".&compression_to_suffix($compression); &$first_print($text{'backup_webmin'}); &require_acl(); # Write out .acl files for domain owner and extra admins, if they are in # MySQL or LDAP, and if the .acl files don't already exist 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"; if (!-r $acltemp) { &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(); @files = &expand_glob_to_files($config_directory, @files); local $out = &backquote_command(&make_archive_command( $compression, $config_directory, $temp, @files)." 2>&1"); my $ex = $?; if (!$ex) { ©_write_as_domain_user($d, $temp, $destfile); } &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) = @_; local $srcfile = $file; if (!-r $srcfile) { ($srcfile) = glob("$file.*"); } &$first_print($text{'restore_webmin'}); &require_acl(); &obtain_lock_webmin($_[0]); local $out = &backquote_command( &make_unarchive_command($config_directory, $srcfile)." 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_always_webmin(&domain) # Returns a link to the Webmin Actions Log module sub links_always_webmin { my ($d) = @_; my %miniserv; &get_miniserv_config(\%miniserv); return ( ) if ($miniserv{'log'} eq '0'); # If login name doesn't equal username in search, it's useless, # this is why we will search on currently selected domain name # in description, so it works fine for both master/reseller and # server owner return ( { 'mod' => 'webminlog', 'desc' => $text{'links_webminlog_dom'}, 'page' => "search.cgi?uall=1&desc=".&urlize($d->{'dom'})."&search_sub_title=".&urlize(&domain_in($d)). "&mall=1&tall=1&fall=1&search_title=$text{'links_webminlog_dom'}&no_return=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"); if ($in{'webmin_group'} && $in{'webmin_group'} ne "none") { &require_acl(); local ($group) = grep { $_->{'name'} eq $in{'webmin_group'} } &acl::list_groups(); &indexof($module_name, @{$group->{'members'}}) < 0 || &error($text{'tmpl_ewgroup'}); } $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); } # add_user_module_acl(user, mod) # Add a module to the Webmin ACL for a user sub add_user_module_acl { my ($user, $mod) = @_; my %acl; my $f = &acl_filename(); &lock_file($f); &read_acl(undef, \%acl); &open_lock_tempfile(ACL, ">$f"); foreach $u (keys %acl) { my @mods = @{$acl{$u}}; if ($u eq $user) { @mods = &unique(@mods, $mod); } &print_tempfile(ACL, "$u: ",join(' ', @mods),"\n"); } &close_tempfile(ACL); &unlock_file($f); } # 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;Private