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 ); }