diff --git a/acl/edit_unix.cgi b/acl/edit_unix.cgi
index 23fcc66ed..2d0e54e0f 100755
--- a/acl/edit_unix.cgi
+++ b/acl/edit_unix.cgi
@@ -1,7 +1,7 @@
#!/usr/local/bin/perl
# edit_unix.cgi
-# Choose a user whose permissions will be used for logins that don't
-# match any webmin user, but have unix accounts
+# Map Unix users that do not have Webmin accounts to a Webmin user whose
+# access rights and authentication method will be used for their logins.
use strict;
use warnings;
@@ -20,7 +20,7 @@ my %miniserv;
print &ui_form_start("save_unix.cgi", "post");
print &ui_table_start($text{'unix_header'}, undef, 2);
-# Enable Unix auth
+# Configure Unix login mappings
my @unixauth = &get_unixauth(\%miniserv);
my $utable = "";
$utable .= &ui_radio("unix_def", @unixauth ? 0 : 1,
@@ -28,8 +28,13 @@ $utable .= &ui_radio("unix_def", @unixauth ? 0 : 1,
$utable .= &ui_columns_start([ $text{'unix_mode'}, $text{'unix_who'},
$text{'unix_to'} ]);
my $i = 0;
-my @webmins = map { [ $_->{'name'} ] }
- sort { $a->{'name'} cmp $b->{'name'} } &list_users();
+my @webmins = map {
+ my $auth = $_->{'pass'} eq 'x' ? $text{'edit_unix'} :
+ $_->{'pass'} eq 'e' ? $text{'edit_extauth'} :
+ $_->{'pass'} eq '*LK*' || $_->{'pass'} =~ /^!/ ?
+ $text{'edit_lock'} : $text{'unix_webminpass'};
+ [ $_->{'name'}, "$_->{'name'} ($auth)" ]
+ } sort { $a->{'name'} cmp $b->{'name'} } &list_users();
foreach my $ua (@unixauth, [ ], [ ]) {
$utable .= &ui_columns_row([
&ui_select("mode_$i", !defined($ua->[0]) ? 0 :
@@ -47,6 +52,7 @@ foreach my $ua (@unixauth, [ ], [ ]) {
$i++;
}
$utable .= &ui_columns_end();
+$utable .= "
".&ui_note($text{'unix_mapdesc'}, 0)."
\n";
print &ui_table_row($text{'unix_utable'}, $utable);
# Allow users who can sudo to root?
@@ -88,4 +94,3 @@ print &ui_table_end();
print &ui_form_end([ [ undef, $text{'save'} ] ]);
&ui_print_footer("", $text{'index_return'});
-
diff --git a/acl/lang/en b/acl/lang/en
index 738406dba..ee1532cec 100644
--- a/acl/lang/en
+++ b/acl/lang/en
@@ -316,15 +316,17 @@ sync_ecannot=You are not allowed to configure user synchronization.
unix_title=Unix User Authentication
unix_err=Failed to save Unix authentication
-unix_desc=This page allows you to configure Webmin to validate login attempts against the system user list and PAM. This can be useful if you have a large number of existing Unix users who you want to give access to Webmin.
-unix_def=Only allow Webmin users to login
-unix_sel=Allow Unix users listed below to login ..
+unix_desc=This page allows Unix and directory-backed users, as well as PAM-only users, to log in without creating a separate Webmin account for each user. Each login receives the access rights and uses the authentication method of the mapped Webmin user.
+unix_def=Only allow Webmin users to log in
+unix_sel=Allow the users and groups mapped below to log in
unix_mode=Allow
unix_mall=All users
-unix_group=Members of group..
-unix_user=Unix user ..
+unix_group=Members of group
+unix_user=Unix user
unix_who=User or Group
-unix_to=As Webmin user
+unix_to=Webmin user access and authentication
+unix_mapdesc=The selected Webmin user supplies both access rights and the authentication method. To authenticate the original username through PAM, SSSD, or the system password database, select a Webmin user configured for Unix authentication. If the selected user has a Webmin password, every matching login uses that shared password instead.
+unix_webminpass=Webmin password
unix_ecannot=You are not allowed to configure Unix user authentication
unix_epam=Unix authentication is not available as the Authen::PAM Perl module is not installed or not working properly.
unix_all=Allow all Unix users
@@ -345,7 +347,7 @@ unix_pamany=Treat logins that only pass PAM validation as $1
unix_esudo=The $1 command is not installed
unix_esudomod=The Perl module $1 needed for sudo authentication is not installed
unix_header=Unix user authentication settings
-unix_utable=Allowed Unix users
+unix_utable=Login mappings
sessions_title=Current Login Sessions
sessions_id=Session ID
diff --git a/acl/t/run-tests.t b/acl/t/run-tests.t
index 71b115f92..9b0de9a1c 100644
--- a/acl/t/run-tests.t
+++ b/acl/t/run-tests.t
@@ -1179,6 +1179,34 @@ is(group_line({ name => 'empty' }),
# writing /acl/.acl, seeds whatever miniserv-user state the CGI
# needs, runs the CGI as a subprocess, and asserts on what the caller sees.
+# edit_unix.cgi should make the authentication method inherited from each
+# mapped Webmin user visible without changing the submitted username value.
+subtest 'edit_unix.cgi authentication labels' => sub {
+ _reset_fixture();
+ _seed_user_acl('admin', { unix => 1, create => 1, delete => 1 });
+ create_user({ name => 'external-user', pass => 'e', modules => ['acl'] });
+ create_user({ name => 'locked-user', pass => '*LK*', modules => ['acl'] });
+ create_user({ name => 'pam-user', pass => 'x', modules => ['acl'] });
+ create_user({ name => 'webmin-user', pass => 'fixed', modules => ['acl'] });
+
+ my $r = run_cgi('edit_unix.cgi', {});
+ is($r->{status}, 0, 'mapping page renders successfully');
+ like($r->{out},
+ qr/value="pam-user"[^>]*>pam-user \(Unix authentication\)/,
+ 'Unix authentication target is labeled');
+ like($r->{out},
+ qr/value="external-user"[^>]*>external-user \(External authentication program\)/,
+ 'external authentication target is labeled');
+ like($r->{out},
+ qr/value="locked-user"[^>]*>locked-user \(No password accepted\)/,
+ 'locked target is labeled');
+ like($r->{out},
+ qr/value="webmin-user"[^>]*>webmin-user \(Webmin password\)/,
+ 'fixed Webmin password target is labeled');
+ like($r->{out}, qr/every matching login uses that shared password/,
+ 'mapping authentication behavior is explained');
+};
+
# Harness sanity: a CGI with no privileges should reject any action and
# print an HTML error (not a redirect). switch.cgi is the smallest victim.
subtest 'CGI harness smoke' => sub {
diff --git a/miniserv-lib.pl b/miniserv-lib.pl
index b2b5f9f8a..42335b6f7 100644
--- a/miniserv-lib.pl
+++ b/miniserv-lib.pl
@@ -2581,7 +2581,8 @@ elsif ($canmode == 0) {
return ( $canuser, 0, 0, $webminuser );
}
elsif ($canmode == 1) {
- # Attempt Webmin authentication
+ # Attempt Webmin authentication. For a unixauth mapping, this checks
+ # the fixed password of the mapped Webmin user.
my $uinfo = &get_user_details($webminuser, $canuser);
if ($uinfo &&
&password_crypt($pass, $uinfo->{'pass'}) eq $uinfo->{'pass'}) {
@@ -2774,14 +2775,17 @@ return $resp =~ /^OK/i ? 1 : 0;
# Second is 0 if cannot login, 1 if using Webmin pass, 2 if PAM, 3 if password
# file, 4 if external.
# Third is 1 if the user does not exist at all, 0 if he does.
-# Fourth is the Webmin username whose permissions apply, based on unixauth.
+# Fourth is the Webmin username whose permissions apply. For a unixauth
+# mapping, its password setting also selects the authentication method.
# Fifth is a flag indicating if a sudo check is needed.
sub can_user_login
{
local $uinfo = &get_user_details($_[0]);
if (!$uinfo) {
- # See if this user exists in Unix and can be validated by the same
- # method as the unixauth webmin user
+ # See if this user exists in Unix and can use the authentication method
+ # configured for the mapped Webmin user. The original username is passed
+ # to PAM, the password file, or the external authentication program; the
+ # mapped Webmin user supplies the access rights.
local $realuser = $unixauth{$_[0]};
local @uinfo;
local $sudo = 0;
@@ -2821,6 +2825,8 @@ if (!$uinfo) {
return (undef, 0, 1, undef) if (!$realuser);
local $uinfo = &get_user_details($realuser);
return (undef, 0, 1, undef) if (!$uinfo);
+ # The mapped user's password setting selects how the original username
+ # will be authenticated.
local $up = $uinfo->{'pass'};
# Work out possible domain names from the hostname
@@ -2920,17 +2926,17 @@ if (!$uinfo) {
}
if ($up eq 'x') {
- # PAM or passwd file authentication
+ # Authenticate the original username through PAM or the password file
print DEBUG "can_user_login: Validate with PAM\n";
return ( $_[0], $use_pam ? 2 : 3, 0, $realuser, $sudo );
}
elsif ($up eq 'e') {
- # External authentication
+ # Authenticate the original username externally
print DEBUG "can_user_login: Validate externally\n";
return ( $_[0], 4, 0, $realuser, $sudo );
}
else {
- # Fixed Webmin password
+ # Authenticate against the fixed password of the mapped Webmin user
print DEBUG "can_user_login: Validate by Webmin\n";
return ( $_[0], 1, 0, $realuser, $sudo );
}