pam_fshadow: new option skip-password

* pam_fshadow/pam_fshadow.c (pam_opt): New option skip-password.
(_pam_parse,verify_user_acct,verify_user_pass): Fix log messages.
(verify_user_pass): Skip password verification, if requested.
(pam_sm_authenticate): Don't try to obtain password, if skip-password
is given.
Bugfix: even if password hash is given in passwd and verified successfully,
verify the shadow file too, this time with password set to NULL, so that
the actual account status is taken into account.
* doc/pam-modules.texi: Document skip-password
* doc/pam_fshadow.8in: Likewise.
This commit is contained in:
Sergey Poznyakoff 2022-02-03 18:41:29 +02:00
parent 1af3541706
commit 203524c85d
3 changed files with 66 additions and 18 deletions

View file

@ -106,6 +106,7 @@ Authentication against an alternative shadow file.
* plain mode::
* virtual domain mode::
* disabling password checking::
* summary of pam_fshadow options::
Authentication using regular expressions.
@ -344,6 +345,7 @@ describe the plain mode.
@menu
* plain mode::
* virtual domain mode::
* disabling password checking::
* summary of pam_fshadow options::
@end menu
@ -489,6 +491,21 @@ user name was @samp{smith@@ftp}, then the module will look
for the user name @samp{smith} in files
@file{/etc/auth/ftp/passwd} and @file{/etc/auth/ftp/shadow}.
@node disabling password checking
@section Disabling password checking
You can instruct @command{pam_fshadow} to skip password checking using
the @code{skip-password} option. When given this option,
the module will only check whether the user is listed in the password
and/or shadow files, and whether the user's account in the latter is
active and has not expired. This way @command{pam_fshadow} can
be used as an auxiliary module in the stack, actual authentication being
performed by one of the modules before it.
This option can be used both in plain and in virtual domain mode. The
use of either file (but not both) can be disabled by the
@code{nopasswd} and @code{noshadow} options.
@node summary of pam_fshadow options
@section Summary of pam_fshadow options
@ -530,6 +547,11 @@ name and authentication domain.
selects authentication domain, and group #2 selects user name.
@xref{virtual domain mode, revert-index}.
@opsummary{skip-password}
@item skip-password
Skip password verification. Check only that the user name is listed
in the password and/or shadow files. @xref{disabling password checking}.
@opsummary{sysconfdir}
@item sysconfdir=@var{dir}
Assume @var{dir} as the system configuration directory.

View file

@ -1,5 +1,5 @@
.\" This file is part of PAM-Modules -*- nroff -*-
.\" Copyright (C) 2001-2021 Sergey Poznyakoff
.\" Copyright (C) 2001-2022 Sergey Poznyakoff
.\"
.\" PAM-Modules is free software; you can redistribute it and/or modify
.\" it under the terms of the GNU General Public License as published by
@ -14,7 +14,7 @@
.\" You should have received a copy of the GNU General Public License
.\" along with PAM-Modules. If not, see <http://www.gnu.org/licenses/>.
.so config.so
.TH PAM_FSHADOW 8 "December 22, 2017" "PAM-MODULES" "Pam-Modules User Reference"
.TH PAM_FSHADOW 8 "February 3, 2022" "PAM-MODULES" "Pam-Modules User Reference"
.SH NAME
pam_fshadow \- use alternative passwd and/or shadow files
.SH SYNOPSIS
@ -30,6 +30,7 @@ pam_fshadow \- use alternative passwd and/or shadow files
[\fBnoshadow\fR]\
[\fBregex=\fIEXPR\fR]\
[\fBrevert\-index\fR]\
[\fBskip\-password\fR]\
[\fBsysconfdir=\fIDIR\fR]\
[\fBuse_authtok\fR]\
[\fBusername\-index=\fIN\fR]\
@ -101,6 +102,12 @@ regex=(.*)@(.*)
This regular expression will match user names like \fBsmith@domain\fR.
.TP
.B skip\-password
Disable password verification. With this flag, the module only checks
whether the user is listed in the password and shadow files and
whether the user's account has not expired. Use of either file
can be disabled using \fBnopasswd\fR or \fBnoshadow\fR (but not both).
.TP
\fBusername\-index=\fIN\fR
Use \fIN\fRth parenthesized group of the regular expression as the
user name. Default is 1.
@ -214,7 +221,7 @@ Sergey Poznyakoff <gray@gnu.org>
.SH "BUG REPORTS"
Report bugs to <bug\-pam\-modules@gnu.org.ua>.
.SH COPYRIGHT
Copyright \(co 2001-2014 Sergey Poznyakoff
Copyright \(co 2001-2022 Sergey Poznyakoff
.br
.na
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

View file

@ -1,5 +1,5 @@
/* This file is part of pam-modules.
* Copyright (C) 2001-2021 Sergey Poznyakoff
* Copyright (C) 2001-2022 Sergey Poznyakoff
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@ -101,6 +101,7 @@ fgetpwent(FILE *fp)
#define CNTL_SHADOW 0x0040
#define CNTL_REGEX 0x0080
#define CNTL_REVERT_INDEX 0x0100
#define CNTL_SKIP_PASSWORD 0x0200
static regex_t rexp;
static char *sysconfdir = SYSCONFDIR;
@ -140,6 +141,8 @@ struct pam_opt pam_opt[] = {
{ .value = CNTL_REVERT_INDEX } },
{ PAM_OPTSTR(username-index), pam_opt_long, &username_index },
{ PAM_OPTSTR(domain-index), pam_opt_long, &domain_index },
{ PAM_OPTSTR(skip-password), pam_opt_bool, &cntl_flags,
{ .value = CNTL_SKIP_PASSWORD } },
{ NULL }
};
@ -160,7 +163,7 @@ _pam_parse(pam_handle_t *pamh, int argc, const char **argv)
if ((cntl_flags & (CNTL_PASSWD|CNTL_SHADOW)) == 0) {
_pam_log(LOG_CRIT,
"either passwd or shadow must be true");
"at least one of passwd or shadow must be set");
return PAM_AUTHINFO_UNAVAIL;
}
if (username_index <= 0) {
@ -340,8 +343,13 @@ verify_user_acct(const char *confdir, const char *username, char **pwd)
username, filename);
retval = PAM_USER_UNKNOWN;
} else {
if (pw->pw_passwd && strlen(pw->pw_passwd) > 1)
if (pw->pw_passwd && strlen(pw->pw_passwd) > 1) {
if (debug_level && !(cntl_flags & CNTL_SKIP_PASSWORD)) {
_pam_debug("user %s: password hash obtained from %s",
username, filename);
}
*pwd = strdup(pw->pw_passwd);
}
retval = PAM_SUCCESS;
}
} else {
@ -364,12 +372,13 @@ verify_user_pass(const char *confdir, const char *username,
int retval = PAM_AUTH_ERR;
char *shadow = mkfilename(confdir, "shadow");
if (debug_level == 100)
if (debug_level == 100 && password)
_pam_debug("Verifying user `%s' with password `%s' in `%s'",
username, password, shadow);
else if (debug_level >= 10)
_pam_debug("Verifying user `%s' in `%s'",
username, password, shadow);
_pam_debug("Verifying user `%s' in `%s'%s",
username, shadow,
password ? "" : " (password verification turned off)");
fp = fopen(shadow, "r");
if (!fp) {
@ -407,6 +416,9 @@ verify_user_pass(const char *confdir, const char *username,
/* Account has expired */
retval = PAM_ACCT_EXPIRED;
#endif
else if (password == NULL)
/* No password verification is required */
retval = PAM_SUCCESS;
else if (strcmp(sp->sp_pwdp, crypt(password, sp->sp_pwdp)) == 0)
retval = PAM_SUCCESS;
else
@ -532,21 +544,28 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags,
return retval;
/* Get the password */
if (_pam_get_password(pamh, &password, "Password:"))
return PAM_SERVICE_ERR;
if (cntl_flags & CNTL_SKIP_PASSWORD)
password = NULL;
else if ((retval = _pam_get_password(pamh, &password, "Password:")) != PAM_SUCCESS)
return retval;
if (cntl_flags & CNTL_PASSWD)
retval = verify_user_acct(confdir, username, &pwstr);
else
retval = PAM_SUCCESS;
if (retval == PAM_SUCCESS) {
if (pwstr) {
if (strcmp(pwstr, crypt(password, pwstr)) == 0)
retval = PAM_SUCCESS;
else
retval = PAM_AUTH_ERR;
free(pwstr);
} else if (cntl_flags & CNTL_SHADOW)
if (password) {
if (pwstr) {
if (strcmp(pwstr, crypt(password, pwstr)) == 0) {
password = NULL;
retval = PAM_SUCCESS;
} else
retval = PAM_AUTH_ERR;
free(pwstr);
}
}
if (retval == PAM_SUCCESS && (cntl_flags & CNTL_SHADOW))
retval = verify_user_pass(confdir, username, password);
}