summaryrefslogtreecommitdiff
path: root/Linux-PAM/modules/pam_unix
diff options
context:
space:
mode:
Diffstat (limited to 'Linux-PAM/modules/pam_unix')
-rw-r--r--Linux-PAM/modules/pam_unix/Makefile.am26
-rw-r--r--Linux-PAM/modules/pam_unix/README17
-rw-r--r--Linux-PAM/modules/pam_unix/bigcrypt.c26
-rw-r--r--Linux-PAM/modules/pam_unix/pam_unix.8172
-rw-r--r--Linux-PAM/modules/pam_unix/pam_unix.8.xml37
-rw-r--r--Linux-PAM/modules/pam_unix/pam_unix_acct.c224
-rw-r--r--Linux-PAM/modules/pam_unix/pam_unix_auth.c2
-rw-r--r--Linux-PAM/modules/pam_unix/pam_unix_passwd.c705
-rw-r--r--Linux-PAM/modules/pam_unix/pam_unix_sess.c6
-rw-r--r--Linux-PAM/modules/pam_unix/passverify.c1129
-rw-r--r--Linux-PAM/modules/pam_unix/passverify.h124
-rw-r--r--Linux-PAM/modules/pam_unix/support.c280
-rw-r--r--Linux-PAM/modules/pam_unix/support.h19
-rw-r--r--Linux-PAM/modules/pam_unix/unix_chkpwd.898
-rw-r--r--Linux-PAM/modules/pam_unix/unix_chkpwd.8.xml67
-rw-r--r--Linux-PAM/modules/pam_unix/unix_chkpwd.c467
-rw-r--r--Linux-PAM/modules/pam_unix/unix_update.837
-rw-r--r--Linux-PAM/modules/pam_unix/unix_update.8.xml67
-rw-r--r--Linux-PAM/modules/pam_unix/unix_update.c187
19 files changed, 2120 insertions, 1570 deletions
diff --git a/Linux-PAM/modules/pam_unix/Makefile.am b/Linux-PAM/modules/pam_unix/Makefile.am
index f9d77f4c..4d2c58b8 100644
--- a/Linux-PAM/modules/pam_unix/Makefile.am
+++ b/Linux-PAM/modules/pam_unix/Makefile.am
@@ -7,8 +7,8 @@ CLEANFILES = *~
EXTRA_DIST = README md5.c md5_crypt.c lckpwdf.-c $(MANS) CHANGELOG \
tst-pam_unix $(XMLS)
-man_MANS = pam_unix.8 unix_chkpwd.8
-XMLS = README.xml pam_unix.8.xml
+man_MANS = pam_unix.8 unix_chkpwd.8 unix_update.8
+XMLS = README.xml pam_unix.8.xml unix_chkpwd.8.xml unix_update.8.xml
TESTS = tst-pam_unix
@@ -16,7 +16,8 @@ securelibdir = $(SECUREDIR)
secureconfdir = $(SCONFIGDIR)
AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
- -DCHKPWD_HELPER=\"$(sbindir)/unix_chkpwd\"
+ -DCHKPWD_HELPER=\"$(sbindir)/unix_chkpwd\" \
+ -DUPDATE_HELPER=\"$(sbindir)/unix_update\"
if HAVE_LIBSELINUX
AM_CFLAGS += -D"WITH_SELINUX"
@@ -34,24 +35,31 @@ pam_unix_la_LIBADD = @LIBCRACK@ @LIBNSL@ -L$(top_builddir)/libpam -lpam \
securelib_LTLIBRARIES = pam_unix.la
-noinst_HEADERS = md5.h support.h yppasswd.h bigcrypt.h
+noinst_HEADERS = md5.h support.h yppasswd.h bigcrypt.h passverify.h
-sbin_PROGRAMS = unix_chkpwd
+sbin_PROGRAMS = unix_chkpwd unix_update
noinst_PROGRAMS = bigcrypt
pam_unix_la_SOURCES = bigcrypt.c pam_unix_acct.c \
pam_unix_auth.c pam_unix_passwd.c pam_unix_sess.c support.c \
- yppasswd_xdr.c md5_good.c md5_broken.c
+ passverify.c yppasswd_xdr.c md5_good.c md5_broken.c
bigcrypt_SOURCES = bigcrypt.c bigcrypt_main.c
bigcrypt_CFLAGS = $(AM_CFLAGS)
bigcrypt_LDADD = @LIBCRYPT@
-unix_chkpwd_SOURCES = unix_chkpwd.c md5_good.c md5_broken.c bigcrypt.c
-unix_chkpwd_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@
+unix_chkpwd_SOURCES = unix_chkpwd.c md5_good.c md5_broken.c bigcrypt.c \
+ passverify.c
+unix_chkpwd_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@ -DHELPER_COMPILE=\"unix_chkpwd\"
unix_chkpwd_LDFLAGS = @PIE_LDFLAGS@
-unix_chkpwd_LDADD = -L$(top_builddir)/libpam -lpam @LIBCRYPT@ @LIBSELINUX@
+unix_chkpwd_LDADD = @LIBCRYPT@ @LIBSELINUX@
+
+unix_update_SOURCES = unix_update.c md5_good.c md5_broken.c bigcrypt.c \
+ passverify.c
+unix_update_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@ -DHELPER_COMPILE=\"unix_update\"
+unix_update_LDFLAGS = @PIE_LDFLAGS@
+unix_update_LDADD = @LIBCRYPT@ @LIBSELINUX@
if ENABLE_REGENERATE_MAN
noinst_DATA = README
diff --git a/Linux-PAM/modules/pam_unix/README b/Linux-PAM/modules/pam_unix/README
index 3421eb31..fb2efab4 100644
--- a/Linux-PAM/modules/pam_unix/README
+++ b/Linux-PAM/modules/pam_unix/README
@@ -114,6 +114,23 @@ bigcrypt
When a user changes their password next, encrypt it with the DEC C2
algorithm.
+sha256
+
+ When a user changes their password next, encrypt it with the SHA256
+ algorithm. If the SHA256 algorithm is not known to the libcrypt, fall back
+ to MD5.
+
+sha512
+
+ When a user changes their password next, encrypt it with the SHA512
+ algorithm. If the SHA512 algorithm is not known to the libcrypt, fall back
+ to MD5.
+
+rounds=n
+
+ Set the optional number of rounds of the SHA256 and SHA512 password hashing
+ algorithms to n.
+
broken_shadow
Ignore errors reading shadow inforation for users in the account management
diff --git a/Linux-PAM/modules/pam_unix/bigcrypt.c b/Linux-PAM/modules/pam_unix/bigcrypt.c
index d825bc71..9cd55384 100644
--- a/Linux-PAM/modules/pam_unix/bigcrypt.c
+++ b/Linux-PAM/modules/pam_unix/bigcrypt.c
@@ -51,7 +51,9 @@
char *bigcrypt(const char *key, const char *salt)
{
char *dec_c2_cryptbuf;
-
+#ifdef HAVE_CRYPT_R
+ struct crypt_data *cdata;
+#endif
unsigned long int keylen, n_seg, j;
char *cipher_ptr, *plaintext_ptr, *tmp_ptr, *salt_ptr;
char keybuf[KEYBUF_SIZE + 1];
@@ -63,6 +65,14 @@ char *bigcrypt(const char *key, const char *salt)
if (!dec_c2_cryptbuf) {
return NULL;
}
+#ifdef HAVE_CRYPT_R
+ cdata = malloc(sizeof(*cdata));
+ if(!cdata) {
+ free(dec_c2_cryptbuf);
+ return NULL;
+ }
+ cdata->initialized = 0;
+#endif
memset(keybuf, 0, KEYBUF_SIZE + 1);
memset(dec_c2_cryptbuf, 0, CBUF_SIZE);
@@ -92,8 +102,11 @@ char *bigcrypt(const char *key, const char *salt)
plaintext_ptr = keybuf;
/* do the first block with supplied salt */
+#ifdef HAVE_CRYPT_R
+ tmp_ptr = crypt_r(plaintext_ptr, salt, cdata); /* libc crypt_r() */
+#else
tmp_ptr = crypt(plaintext_ptr, salt); /* libc crypt() */
-
+#endif
/* and place in the static area */
strncpy(cipher_ptr, tmp_ptr, 13);
cipher_ptr += ESEGMENT_SIZE + SALT_SIZE;
@@ -110,7 +123,11 @@ char *bigcrypt(const char *key, const char *salt)
if (n_seg > 1) {
for (j = 2; j <= n_seg; j++) {
+#ifdef HAVE_CRYPT_R
+ tmp_ptr = crypt_r(plaintext_ptr, salt_ptr, cdata);
+#else
tmp_ptr = crypt(plaintext_ptr, salt_ptr);
+#endif
/* skip the salt for seg!=0 */
strncpy(cipher_ptr, (tmp_ptr + SALT_SIZE), ESEGMENT_SIZE);
@@ -122,7 +139,10 @@ char *bigcrypt(const char *key, const char *salt)
}
D(("key=|%s|, salt=|%s|\nbuf=|%s|\n", key, salt, dec_c2_cryptbuf));
- /* this is the <NUL> terminated encrypted password */
+#ifdef HAVE_CRYPT_R
+ free(cdata);
+#endif
+ /* this is the <NUL> terminated encrypted password */
return dec_c2_cryptbuf;
}
diff --git a/Linux-PAM/modules/pam_unix/pam_unix.8 b/Linux-PAM/modules/pam_unix/pam_unix.8
index e57de221..d826a4a9 100644
--- a/Linux-PAM/modules/pam_unix/pam_unix.8
+++ b/Linux-PAM/modules/pam_unix/pam_unix.8
@@ -1,133 +1,179 @@
.\" Title: pam_unix
.\" Author:
-.\" Generator: DocBook XSL Stylesheets v1.70.1 <http://docbook.sf.net/>
-.\" Date: 09/20/2006
-.\" Manual: Linux\-PAM Manual
-.\" Source: Linux\-PAM Manual
+.\" Generator: DocBook XSL Stylesheets v1.73.1 <http://docbook.sf.net/>
+.\" Date: 01/28/2008
+.\" Manual: Linux-PAM Manual
+.\" Source: Linux-PAM Manual
.\"
-.TH "PAM_UNIX" "8" "09/20/2006" "Linux\-PAM Manual" "Linux\-PAM Manual"
+.TH "PAM_UNIX" "8" "01/28/2008" "Linux-PAM Manual" "Linux\-PAM Manual"
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.SH "NAME"
-pam_unix \- Module for traditional password authentication
+pam_unix - Module for traditional password authentication
.SH "SYNOPSIS"
.HP 12
-\fBpam_unix.so\fR [...]
+\fBpam_unix\.so\fR [\.\.\.]
.SH "DESCRIPTION"
.PP
-This is the standard Unix authentication module. It uses standard calls from the system's libraries to retrieve and set account information as well as authentication. Usually this is obtained from the /etc/passwd and the /etc/shadow file as well if shadow is enabled.
+This is the standard Unix authentication module\. It uses standard calls from the system\'s libraries to retrieve and set account information as well as authentication\. Usually this is obtained from the /etc/passwd and the /etc/shadow file as well if shadow is enabled\.
.PP
-The account component performs the task of establishing the status of the user's account and password based on the following
+The account component performs the task of establishing the status of the user\'s account and password based on the following
\fIshadow\fR
-elements: expire, last_change, max_change, min_change, warn_change. In the case of the latter, it may offer advice to the user on changing their password or, through the
+elements: expire, last_change, max_change, min_change, warn_change\. In the case of the latter, it may offer advice to the user on changing their password or, through the
\fBPAM_AUTHTOKEN_REQD\fR
-return, delay giving service to the user until they have established a new password. The entries listed above are documented in the
+return, delay giving service to the user until they have established a new password\. The entries listed above are documented in the
\fBshadow\fR(5)
-manual page. Should the user's record not contain one or more of these entries, the corresponding
+manual page\. Should the user\'s record not contain one or more of these entries, the corresponding
\fIshadow\fR
-check is not performed.
+check is not performed\.
.PP
-The authentication component performs the task of checking the users credentials (password). The default action of this module is to not permit the user access to a service if their official password is blank.
+The authentication component performs the task of checking the users credentials (password)\. The default action of this module is to not permit the user access to a service if their official password is blank\.
.PP
A helper binary,
-\fBunix_chkpwd\fR(8), is provided to check the user's password when it is stored in a read protected database. This binary is very simple and will only check the password of the user invoking it. It is called transparently on behalf of the user by the authenticating component of this module. In this way it is possible for applications like
+\fBunix_chkpwd\fR(8), is provided to check the user\'s password when it is stored in a read protected database\. This binary is very simple and will only check the password of the user invoking it\. It is called transparently on behalf of the user by the authenticating component of this module\. In this way it is possible for applications like
\fBxlock\fR(1)
-to work without being setuid\-root. The module, by default, will temporarily turn off SIGCHLD handling for the duration of execution of the helper binary. This is generally the right thing to do, as many applications are not prepared to handle this signal from a child they didn't know was
-\fBfork()\fRd. The
+to work without being setuid\-root\. The module, by default, will temporarily turn off SIGCHLD handling for the duration of execution of the helper binary\. This is generally the right thing to do, as many applications are not prepared to handle this signal from a child they didn\'t know was
+\fBfork()\fRd\. The
\fBnoreap\fR
-module argument can be used to suppress this temporary shielding and may be needed for use with certain applications.
+module argument can be used to suppress this temporary shielding and may be needed for use with certain applications\.
.PP
-The password component of this module performs the task of updating the user's password.
+The password component of this module performs the task of updating the user\'s password\.
.PP
-The session component of this module logs when a user logins or leave the system.
+The session component of this module logs when a user logins or leave the system\.
.PP
-Remaining arguments, supported by others functions of this module, are silently ignored. Other arguments are logged as errors through
-\fBsyslog\fR(3).
+Remaining arguments, supported by others functions of this module, are silently ignored\. Other arguments are logged as errors through
+\fBsyslog\fR(3)\.
.SH "OPTIONS"
-.TP 3n
+.PP
\fBdebug\fR
+.RS 4
Turns on debugging via
-\fBsyslog\fR(3).
-.TP 3n
+\fBsyslog\fR(3)\.
+.RE
+.PP
\fBaudit\fR
-A little more extreme than debug.
-.TP 3n
+.RS 4
+A little more extreme than debug\.
+.RE
+.PP
\fBnullok\fR
-The default action of this module is to not permit the user access to a service if their official password is blank. The
+.RS 4
+The default action of this module is to not permit the user access to a service if their official password is blank\. The
\fBnullok\fR
-argument overrides this default.
-.TP 3n
+argument overrides this default\.
+.RE
+.PP
\fBtry_first_pass\fR
-Before prompting the user for their password, the module first tries the previous stacked module's password in case that satisfies this module as well.
-.TP 3n
+.RS 4
+Before prompting the user for their password, the module first tries the previous stacked module\'s password in case that satisfies this module as well\.
+.RE
+.PP
\fBuse_first_pass\fR
+.RS 4
The argument
\fBuse_first_pass\fR
-forces the module to use a previous stacked modules password and will never prompt the user \- if no password is available or the password is not appropriate, the user will be denied access.
-.TP 3n
+forces the module to use a previous stacked modules password and will never prompt the user \- if no password is available or the password is not appropriate, the user will be denied access\.
+.RE
+.PP
\fBnodelay\fR
-This argument can be used to discourage the authentication component from requesting a delay should the authentication as a whole fail. The default action is for the module to request a delay\-on\-failure of the order of two second.
-.TP 3n
+.RS 4
+This argument can be used to discourage the authentication component from requesting a delay should the authentication as a whole fail\. The default action is for the module to request a delay\-on\-failure of the order of two second\.
+.RE
+.PP
\fBuse_authtok\fR
+.RS 4
When password changing enforce the module to set the new password to the one provided by a previously stacked
\fBpassword\fR
module (this is used in the example of the stacking of the
\fBpam_cracklib\fR
-module documented above).
-.TP 3n
+module documented above)\.
+.RE
+.PP
\fBnot_set_pass\fR
-This argument is used to inform the module that it is not to pay attention to/make available the old or new passwords from/to other (stacked) password modules.
-.TP 3n
+.RS 4
+This argument is used to inform the module that it is not to pay attention to/make available the old or new passwords from/to other (stacked) password modules\.
+.RE
+.PP
\fBnis\fR
-NIS RPC is used for setting new passwords.
-.TP 3n
+.RS 4
+NIS RPC is used for setting new passwords\.
+.RE
+.PP
\fBremember=\fR\fB\fIn\fR\fR
+.RS 4
The last
\fIn\fR
passwords for each user are saved in
\fI/etc/security/opasswd\fR
-in order to force password change history and keep the user from alternating between the same password too frequently.
-.TP 3n
+in order to force password change history and keep the user from alternating between the same password too frequently\.
+.RE
+.PP
\fBshadow\fR
-Try to maintain a shadow based system.
-.TP 3n
+.RS 4
+Try to maintain a shadow based system\.
+.RE
+.PP
\fBmd5\fR
-When a user changes their password next, encrypt it with the MD5 algorithm.
-.TP 3n
+.RS 4
+When a user changes their password next, encrypt it with the MD5 algorithm\.
+.RE
+.PP
\fBbigcrypt\fR
-When a user changes their password next, encrypt it with the DEC C2 algorithm.
-.TP 3n
+.RS 4
+When a user changes their password next, encrypt it with the DEC C2 algorithm\.
+.RE
+.PP
+\fBsha256\fR
+.RS 4
+When a user changes their password next, encrypt it with the SHA256 algorithm\. If the SHA256 algorithm is not known to the libcrypt, fall back to MD5\.
+.RE
+.PP
+\fBsha512\fR
+.RS 4
+When a user changes their password next, encrypt it with the SHA512 algorithm\. If the SHA512 algorithm is not known to the libcrypt, fall back to MD5\.
+.RE
+.PP
+\fBrounds=\fR\fB\fIn\fR\fR
+.RS 4
+Set the optional number of rounds of the SHA256 and SHA512 password hashing algorithms to
+\fIn\fR\.
+.RE
+.PP
\fBbroken_shadow\fR
-Ignore errors reading shadow inforation for users in the account management module.
+.RS 4
+Ignore errors reading shadow inforation for users in the account management module\.
+.RE
.PP
Invalid arguments are logged with
-\fBsyslog\fR(3).
+\fBsyslog\fR(3)\.
.SH "MODULE SERVICES PROVIDED"
.PP
-All service are supported.
+All service are supported\.
.SH "RETURN VALUES"
-.TP 3n
+.PP
PAM_IGNORE
-Ignore this module.
+.RS 4
+Ignore this module\.
+.RE
.SH "EXAMPLES"
.PP
An example usage for
-\fI/etc/pam.d/login\fR
+\fI/etc/pam\.d/login\fR
would be:
.sp
-.RS 3n
+.RS 4
.nf
# Authenticate the user
-auth required pam_unix.so
+auth required pam_unix\.so
# Ensure users account and password are still active
-account required pam_unix.so
+account required pam_unix\.so
# Change the users password, but at first check the strength
# with pam_cracklib(8)
-password required pam_cracklib.so retry=3 minlen=6 difok=3
-password required pam_unix.so use_authtok nullok md5
-session required pam_unix.so
+password required pam_cracklib\.so retry=3 minlen=6 difok=3
+password required pam_unix\.so use_authtok nullok md5
+session required pam_unix\.so
.fi
.RE
@@ -140,4 +186,4 @@ session required pam_unix.so
\fBpam\fR(8)
.SH "AUTHOR"
.PP
-pam_unix was written by various people.
+pam_unix was written by various people\.
diff --git a/Linux-PAM/modules/pam_unix/pam_unix.8.xml b/Linux-PAM/modules/pam_unix/pam_unix.8.xml
index 41757977..290cb2b9 100644
--- a/Linux-PAM/modules/pam_unix/pam_unix.8.xml
+++ b/Linux-PAM/modules/pam_unix/pam_unix.8.xml
@@ -260,6 +260,43 @@
</varlistentry>
<varlistentry>
<term>
+ <option>sha256</option>
+ </term>
+ <listitem>
+ <para>
+ When a user changes their password next,
+ encrypt it with the SHA256 algorithm. If the
+ SHA256 algorithm is not known to the libcrypt,
+ fall back to MD5.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>sha512</option>
+ </term>
+ <listitem>
+ <para>
+ When a user changes their password next,
+ encrypt it with the SHA512 algorithm. If the
+ SHA512 algorithm is not known to the libcrypt,
+ fall back to MD5.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>rounds=<replaceable>n</replaceable></option>
+ </term>
+ <listitem>
+ <para>
+ Set the optional number of rounds of the SHA256 and SHA512
+ password hashing algorithms to <replaceable>n</replaceable>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
<option>broken_shadow</option>
</term>
<listitem>
diff --git a/Linux-PAM/modules/pam_unix/pam_unix_acct.c b/Linux-PAM/modules/pam_unix/pam_unix_acct.c
index cb2550df..c09bc175 100644
--- a/Linux-PAM/modules/pam_unix/pam_unix_acct.c
+++ b/Linux-PAM/modules/pam_unix/pam_unix_acct.c
@@ -47,10 +47,6 @@
#include <time.h> /* for time() */
#include <errno.h>
#include <sys/wait.h>
-#ifdef WITH_SELINUX
-#include <selinux/selinux.h>
-#define SELINUX_ENABLED is_selinux_enabled()>0
-#endif
#include <security/_pam_macros.h>
@@ -63,12 +59,10 @@
#include <security/pam_modutil.h>
#include "support.h"
+#include "passverify.h"
-#ifdef WITH_SELINUX
-
-struct spwd spwd;
-
-struct spwd *_unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user)
+int _unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl,
+ const char *user, int *daysleft)
{
int retval=0, child, fds[2];
void (*sighandler)(int) = NULL;
@@ -78,7 +72,7 @@ struct spwd *_unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl, cons
if (pipe(fds) != 0) {
D(("could not make pipe"));
pam_syslog(pamh, LOG_ERR, "Could not make pipe: %m");
- return NULL;
+ return PAM_AUTH_ERR;
}
D(("called."));
@@ -117,7 +111,7 @@ struct spwd *_unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl, cons
}
}
- if (SELINUX_ENABLED && geteuid() == 0) {
+ if (geteuid() == 0) {
/* must set the real uid to 0 so the helper will not error
out if pam is called from setuid binary (su, sudo...) */
setuid(0);
@@ -126,7 +120,7 @@ struct spwd *_unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl, cons
/* exec binary helper */
args[0] = x_strdup(CHKPWD_HELPER);
args[1] = x_strdup(user);
- args[2] = x_strdup("verify");
+ args[2] = x_strdup("chkexpiry");
execve(CHKPWD_HELPER, args, envp);
@@ -134,11 +128,12 @@ struct spwd *_unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl, cons
/* should not get here: exit with error */
close (fds[1]);
D(("helper binary is not available"));
+ printf("-1\n");
exit(PAM_AUTHINFO_UNAVAIL);
} else {
close(fds[1]);
if (child > 0) {
- char buf[1024];
+ char buf[32];
int rc=0;
rc=waitpid(child, &retval, 0); /* wait for helper to complete */
if (rc<0) {
@@ -146,22 +141,16 @@ struct spwd *_unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl, cons
retval = PAM_AUTH_ERR;
} else {
retval = WEXITSTATUS(retval);
- if (retval != PAM_AUTHINFO_UNAVAIL) {
- rc = pam_modutil_read(fds[0], buf, sizeof(buf) - 1);
- if(rc > 0) {
+ rc = pam_modutil_read(fds[0], buf, sizeof(buf) - 1);
+ if(rc > 0) {
buf[rc] = '\0';
- if (sscanf(buf,"%ld:%ld:%ld:%ld:%ld:%ld",
- &spwd.sp_lstchg, /* last password change */
- &spwd.sp_min, /* days until change allowed. */
- &spwd.sp_max, /* days before change required */
- &spwd.sp_warn, /* days warning for expiration */
- &spwd.sp_inact, /* days before account inactive */
- &spwd.sp_expire) /* date when account expires */ != 6 ) retval = PAM_AUTH_ERR;
+ if (sscanf(buf,"%d", daysleft) != 1 )
+ retval = PAM_AUTH_ERR;
}
- else {
- pam_syslog(pamh, LOG_ERR, " ERROR %d: %m", rc); retval = PAM_AUTH_ERR;
+ else {
+ pam_syslog(pamh, LOG_ERR, "read unix_chkpwd output error %d: %m", rc);
+ retval = PAM_AUTH_ERR;
}
- }
}
} else {
pam_syslog(pamh, LOG_ERR, "Fork failed: %m");
@@ -174,15 +163,9 @@ struct spwd *_unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl, cons
(void) signal(SIGCHLD, sighandler); /* restore old signal handler */
}
D(("Returning %d",retval));
- if (retval != PAM_SUCCESS) {
- return NULL;
- }
- return &spwd;
+ return retval;
}
-#endif
-
-
/*
* PAM framework looks for this entry-point to pass control to the
* account management module.
@@ -195,14 +178,13 @@ PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t * pamh, int flags,
const void *void_uname;
const char *uname;
int retval, daysleft;
- time_t curdays;
struct spwd *spent;
struct passwd *pwent;
char buf[256];
D(("called."));
- ctrl = _set_ctrl(pamh, flags, NULL, argc, argv);
+ ctrl = _set_ctrl(pamh, flags, NULL, NULL, argc, argv);
retval = pam_get_item(pamh, PAM_USER, &void_uname);
uname = void_uname;
@@ -214,134 +196,90 @@ PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t * pamh, int flags,
return PAM_USER_UNKNOWN;
}
- pwent = pam_modutil_getpwnam(pamh, uname);
- if (!pwent) {
+ retval = get_account_info(pamh, uname, &pwent, &spent);
+ if (retval == PAM_USER_UNKNOWN) {
pam_syslog(pamh, LOG_ALERT,
"could not identify user (from getpwnam(%s))",
uname);
- return PAM_USER_UNKNOWN;
+ return retval;
}
- if (!strcmp( pwent->pw_passwd, "*NP*" )) { /* NIS+ */
- uid_t save_euid, save_uid;
-
- save_euid = geteuid();
- save_uid = getuid();
- if (save_uid == pwent->pw_uid)
- setreuid( save_euid, save_uid );
- else {
- setreuid( 0, -1 );
- if (setreuid( -1, pwent->pw_uid ) == -1) {
- setreuid( -1, 0 );
- setreuid( 0, -1 );
- if(setreuid( -1, pwent->pw_uid ) == -1)
- return PAM_CRED_INSUFFICIENT;
- }
- }
- spent = pam_modutil_getspnam (pamh, uname);
- if (save_uid == pwent->pw_uid)
- setreuid( save_uid, save_euid );
- else {
- if (setreuid( -1, 0 ) == -1)
- setreuid( save_uid, -1 );
- setreuid( -1, save_euid );
- }
-
- } else if (_unix_shadowed (pwent))
- spent = pam_modutil_getspnam (pamh, uname);
- else
+ if (retval == PAM_SUCCESS && spent == NULL)
return PAM_SUCCESS;
-#ifdef WITH_SELINUX
- if (!spent && SELINUX_ENABLED )
- spent = _unix_run_verify_binary(pamh, ctrl, uname);
-#endif
-
- if (!spent)
+ if (retval == PAM_UNIX_RUN_HELPER) {
+ retval = _unix_run_verify_binary(pamh, ctrl, uname, &daysleft);
+ if (retval == PAM_AUTHINFO_UNAVAIL &&
+ on(UNIX_BROKEN_SHADOW, ctrl))
+ return PAM_SUCCESS;
+ } else if (retval != PAM_SUCCESS) {
if (on(UNIX_BROKEN_SHADOW,ctrl))
return PAM_SUCCESS;
+ else
+ return retval;
+ } else
+ retval = check_shadow_expiry(pamh, spent, &daysleft);
- if (!spent)
- return PAM_AUTHINFO_UNAVAIL; /* Couldn't get username from shadow */
-
- curdays = time(NULL) / (60 * 60 * 24);
- D(("today is %d, last change %d", curdays, spent->sp_lstchg));
- if ((curdays > spent->sp_expire) && (spent->sp_expire != -1)) {
+ switch (retval) {
+ case PAM_ACCT_EXPIRED:
pam_syslog(pamh, LOG_NOTICE,
- "account %s has expired (account expired)",
- uname);
+ "account %s has expired (account expired)",
+ uname);
_make_remark(pamh, ctrl, PAM_ERROR_MSG,
- _("Your account has expired; please contact your system administrator"));
- D(("account expired"));
- return PAM_ACCT_EXPIRED;
- }
- if (spent->sp_lstchg == 0) {
- pam_syslog(pamh, LOG_NOTICE,
- "expired password for user %s (root enforced)",
- uname);
- _make_remark(pamh, ctrl, PAM_ERROR_MSG,
- _("You are required to change your password immediately (root enforced)"));
- D(("need a new password"));
- return PAM_NEW_AUTHTOK_REQD;
- }
- if (curdays < spent->sp_lstchg) {
- pam_syslog(pamh, LOG_DEBUG,
- "account %s has password changed in future",
- uname);
- return PAM_SUCCESS;
- }
- if ((curdays - spent->sp_lstchg > spent->sp_max)
- && (curdays - spent->sp_lstchg > spent->sp_inact)
- && (curdays - spent->sp_lstchg > spent->sp_max + spent->sp_inact)
- && (spent->sp_max != -1) && (spent->sp_inact != -1)) {
+ _("Your account has expired; please contact your system administrator"));
+ break;
+ case PAM_NEW_AUTHTOK_REQD:
+ if (daysleft == 0) {
+ pam_syslog(pamh, LOG_NOTICE,
+ "expired password for user %s (root enforced)",
+ uname);
+ _make_remark(pamh, ctrl, PAM_ERROR_MSG,
+ _("You are required to change your password immediately (root enforced)"));
+ } else {
+ pam_syslog(pamh, LOG_DEBUG,
+ "expired password for user %s (password aged)",
+ uname);
+ _make_remark(pamh, ctrl, PAM_ERROR_MSG,
+ _("You are required to change your password immediately (password aged)"));
+ }
+ break;
+ case PAM_AUTHTOK_EXPIRED:
pam_syslog(pamh, LOG_NOTICE,
- "account %s has expired (failed to change password)",
- uname);
- _make_remark(pamh, ctrl, PAM_ERROR_MSG,
- _("Your account has expired; please contact your system administrator"));
- D(("account expired 2"));
- return PAM_ACCT_EXPIRED;
- }
- if ((curdays - spent->sp_lstchg > spent->sp_max) && (spent->sp_max != -1)) {
- pam_syslog(pamh, LOG_DEBUG,
- "expired password for user %s (password aged)",
- uname);
+ "account %s has expired (failed to change password)",
+ uname);
_make_remark(pamh, ctrl, PAM_ERROR_MSG,
- _("You are required to change your password immediately (password aged)"));
- D(("need a new password 2"));
- return PAM_NEW_AUTHTOK_REQD;
- }
- if ((curdays - spent->sp_lstchg > spent->sp_max - spent->sp_warn)
- && (spent->sp_max != -1) && (spent->sp_warn != -1)) {
- daysleft = (spent->sp_lstchg + spent->sp_max) - curdays;
- pam_syslog(pamh, LOG_DEBUG,
- "password for user %s will expire in %d days",
- uname, daysleft);
-#ifdef HAVE_DNGETTEXT
- snprintf (buf, sizeof (buf),
- dngettext(PACKAGE,
- "Warning: your password will expire in %d day",
- "Warning: your password will expire in %d days",
- daysleft),
- daysleft);
+ _("Your account has expired; please contact your system administrator"));
+ break;
+ case PAM_SUCCESS:
+ if (daysleft >= 0) {
+ pam_syslog(pamh, LOG_DEBUG,
+ "password for user %s will expire in %d days",
+ uname, daysleft);
+#if defined HAVE_DNGETTEXT && defined ENABLE_NLS
+ snprintf (buf, sizeof (buf),
+ dngettext(PACKAGE,
+ "Warning: your password will expire in %d day",
+ "Warning: your password will expire in %d days",
+ daysleft),
+ daysleft);
#else
- if (daysleft == 1)
- snprintf(buf, sizeof (buf),
- _("Warning: your password will expire in %d day"),
- daysleft);
- else
- snprintf(buf, sizeof (buf),
- /* TRANSLATORS: only used if dngettext is not support
-ed */
- _("Warning: your password will expire in %d days"),
- daysleft);
+ if (daysleft == 1)
+ snprintf(buf, sizeof (buf),
+ _("Warning: your password will expire in %d day"),
+ daysleft);
+ else
+ snprintf(buf, sizeof (buf),
+ /* TRANSLATORS: only used if dngettext is not supported */
+ _("Warning: your password will expire in %d days"),
+ daysleft);
#endif
- _make_remark(pamh, ctrl, PAM_TEXT_INFO, buf);
+ _make_remark(pamh, ctrl, PAM_TEXT_INFO, buf);
+ }
}
D(("all done"));
- return PAM_SUCCESS;
+ return retval;
}
diff --git a/Linux-PAM/modules/pam_unix/pam_unix_auth.c b/Linux-PAM/modules/pam_unix/pam_unix_auth.c
index 3004bee8..dfedd608 100644
--- a/Linux-PAM/modules/pam_unix/pam_unix_auth.c
+++ b/Linux-PAM/modules/pam_unix/pam_unix_auth.c
@@ -111,7 +111,7 @@ PAM_EXTERN int pam_sm_authenticate(pam_handle_t * pamh, int flags
D(("called."));
- ctrl = _set_ctrl(pamh, flags, NULL, argc, argv);
+ ctrl = _set_ctrl(pamh, flags, NULL, NULL, argc, argv);
/* Get a few bytes so we can pass our return value to
pam_sm_setcred(). */
diff --git a/Linux-PAM/modules/pam_unix/pam_unix_passwd.c b/Linux-PAM/modules/pam_unix/pam_unix_passwd.c
index c8ee5492..432f687f 100644
--- a/Linux-PAM/modules/pam_unix/pam_unix_passwd.c
+++ b/Linux-PAM/modules/pam_unix/pam_unix_passwd.c
@@ -2,6 +2,7 @@
* Main coding by Elliot Lee <sopwith@redhat.com>, Red Hat Software.
* Copyright (C) 1996.
* Copyright (c) Jan Rêkorajski, 1999.
+ * Copyright (c) Red Hat, Inc., 2007, 2008.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -63,7 +64,6 @@
#ifdef WITH_SELINUX
static int selinux_enabled=-1;
#include <selinux/selinux.h>
-static security_context_t prev_context=NULL;
#define SELINUX_ENABLED (selinux_enabled!=-1 ? selinux_enabled : (selinux_enabled=is_selinux_enabled()>0))
#endif
@@ -84,6 +84,7 @@ static security_context_t prev_context=NULL;
#include "yppasswd.h"
#include "md5.h"
#include "support.h"
+#include "passverify.h"
#include "bigcrypt.h"
#if !((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 1))
@@ -92,15 +93,6 @@ extern int getrpcport(const char *host, unsigned long prognum,
#endif /* GNU libc 2.1 */
/*
- * PAM framework looks for these entry-points to pass control to the
- * password changing module.
- */
-
-#if defined(USE_LCKPWDF) && !defined(HAVE_LCKPWDF)
-# include "./lckpwdf.-c"
-#endif
-
-/*
How it works:
Gets in username (has to be done) from the calling program
Does authentication of user (only if we are not running as root)
@@ -108,82 +100,15 @@ extern int getrpcport(const char *host, unsigned long prognum,
Sets it.
*/
-/* passwd/salt conversion macros */
-
-#define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
-#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
-
/* data tokens */
#define _UNIX_OLD_AUTHTOK "-UN*X-OLD-PASS"
#define _UNIX_NEW_AUTHTOK "-UN*X-NEW-PASS"
#define MAX_PASSWD_TRIES 3
-#define PW_TMPFILE "/etc/npasswd"
-#define SH_TMPFILE "/etc/nshadow"
#ifndef CRACKLIB_DICTS
#define CRACKLIB_DICTS NULL
#endif
-#define OPW_TMPFILE "/etc/security/nopasswd"
-#define OLD_PASSWORDS_FILE "/etc/security/opasswd"
-
-/*
- * i64c - convert an integer to a radix 64 character
- */
-static int i64c(int i)
-{
- if (i < 0)
- return ('.');
- else if (i > 63)
- return ('z');
- if (i == 0)
- return ('.');
- if (i == 1)
- return ('/');
- if (i >= 2 && i <= 11)
- return ('0' - 2 + i);
- if (i >= 12 && i <= 37)
- return ('A' - 12 + i);
- if (i >= 38 && i <= 63)
- return ('a' - 38 + i);
- return ('\0');
-}
-
-static char *crypt_md5_wrapper(const char *pass_new)
-{
- /*
- * Code lifted from Marek Michalkiewicz's shadow suite. (CG)
- * removed use of static variables (AGM)
- */
-
- struct timeval tv;
- MD5_CTX ctx;
- unsigned char result[16];
- char *cp = (char *) result;
- unsigned char tmp[16];
- int i;
- char *x = NULL;
-
- GoodMD5Init(&ctx);
- gettimeofday(&tv, (struct timezone *) 0);
- GoodMD5Update(&ctx, (void *) &tv, sizeof tv);
- i = getpid();
- GoodMD5Update(&ctx, (void *) &i, sizeof i);
- i = clock();
- GoodMD5Update(&ctx, (void *) &i, sizeof i);
- GoodMD5Update(&ctx, result, sizeof result);
- GoodMD5Final(tmp, &ctx);
- strcpy(cp, "$1$"); /* magic for the MD5 */
- cp += strlen(cp);
- for (i = 0; i < 8; i++)
- *cp++ = i64c(tmp[i] & 077);
- *cp = '\0';
-
- /* no longer need cleartext */
- x = Goodcrypt_md5(pass_new, (const char *) result);
-
- return x;
-}
static char *getNISserver(pam_handle_t *pamh)
{
@@ -217,7 +142,8 @@ static char *getNISserver(pam_handle_t *pamh)
#ifdef WITH_SELINUX
-static int _unix_run_shadow_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user, const char *fromwhat, const char *towhat)
+static int _unix_run_update_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user,
+ const char *fromwhat, const char *towhat, int remember)
{
int retval, child, fds[2];
void (*sighandler)(int) = NULL;
@@ -247,7 +173,8 @@ static int _unix_run_shadow_binary(pam_handle_t *pamh, unsigned int ctrl, const
size_t i=0;
struct rlimit rlim;
static char *envp[] = { NULL };
- char *args[] = { NULL, NULL, NULL, NULL };
+ char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL };
+ char buffer[16];
/* XXX - should really tidy up PAM here too */
@@ -270,11 +197,18 @@ static int _unix_run_shadow_binary(pam_handle_t *pamh, unsigned int ctrl, const
}
/* exec binary helper */
- args[0] = x_strdup(CHKPWD_HELPER);
+ args[0] = x_strdup(UPDATE_HELPER);
args[1] = x_strdup(user);
- args[2] = x_strdup("shadow");
+ args[2] = x_strdup("update");
+ if (on(UNIX_SHADOW, ctrl))
+ args[3] = x_strdup("1");
+ else
+ args[3] = x_strdup("0");
- execve(CHKPWD_HELPER, args, envp);
+ snprintf(buffer, sizeof(buffer), "%d", remember);
+ args[4] = x_strdup(buffer);
+
+ execve(UPDATE_HELPER, args, envp);
/* should not get here: exit with error */
D(("helper binary is not available"));
@@ -297,7 +231,7 @@ static int _unix_run_shadow_binary(pam_handle_t *pamh, unsigned int ctrl, const
close(fds[1]);
rc=waitpid(child, &retval, 0); /* wait for helper to complete */
if (rc<0) {
- pam_syslog(pamh, LOG_ERR, "unix_chkpwd waitpid returned %d: %m", rc);
+ pam_syslog(pamh, LOG_ERR, "unix_update waitpid failed: %m");
retval = PAM_AUTH_ERR;
} else {
retval = WEXITSTATUS(retval);
@@ -354,393 +288,6 @@ static int check_old_password(const char *forwho, const char *newpass)
return retval;
}
-static int save_old_password(pam_handle_t *pamh,
- const char *forwho, const char *oldpass,
- int howmany)
-{
- static char buf[16384];
- static char nbuf[16384];
- char *s_luser, *s_uid, *s_npas, *s_pas, *pass;
- int npas;
- FILE *pwfile, *opwfile;
- int err = 0;
- int oldmask;
- int found = 0;
- struct passwd *pwd = NULL;
- struct stat st;
-
- if (howmany < 0) {
- return PAM_SUCCESS;
- }
-
- if (oldpass == NULL) {
- return PAM_SUCCESS;
- }
-
- oldmask = umask(077);
-
-#ifdef WITH_SELINUX
- if (SELINUX_ENABLED) {
- security_context_t passwd_context=NULL;
- if (getfilecon("/etc/passwd",&passwd_context)<0) {
- return PAM_AUTHTOK_ERR;
- };
- if (getfscreatecon(&prev_context)<0) {
- freecon(passwd_context);
- return PAM_AUTHTOK_ERR;
- }
- if (setfscreatecon(passwd_context)) {
- freecon(passwd_context);
- freecon(prev_context);
- return PAM_AUTHTOK_ERR;
- }
- freecon(passwd_context);
- }
-#endif
- pwfile = fopen(OPW_TMPFILE, "w");
- umask(oldmask);
- if (pwfile == NULL) {
- err = 1;
- goto done;
- }
-
- opwfile = fopen(OLD_PASSWORDS_FILE, "r");
- if (opwfile == NULL) {
- fclose(pwfile);
- err = 1;
- goto done;
- }
-
- if (fstat(fileno(opwfile), &st) == -1) {
- fclose(opwfile);
- fclose(pwfile);
- err = 1;
- goto done;
- }
-
- if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
- fclose(opwfile);
- fclose(pwfile);
- err = 1;
- goto done;
- }
- if (fchmod(fileno(pwfile), st.st_mode) == -1) {
- fclose(opwfile);
- fclose(pwfile);
- err = 1;
- goto done;
- }
-
- while (fgets(buf, 16380, opwfile)) {
- if (!strncmp(buf, forwho, strlen(forwho))) {
- char *sptr;
- buf[strlen(buf) - 1] = '\0';
- s_luser = strtok_r(buf, ":", &sptr);
- s_uid = strtok_r(NULL, ":", &sptr);
- s_npas = strtok_r(NULL, ":", &sptr);
- s_pas = strtok_r(NULL, ":", &sptr);
- npas = strtol(s_npas, NULL, 10) + 1;
- while (npas > howmany) {
- s_pas = strpbrk(s_pas, ",");
- if (s_pas != NULL)
- s_pas++;
- npas--;
- }
- pass = crypt_md5_wrapper(oldpass);
- if (s_pas == NULL)
- snprintf(nbuf, sizeof(nbuf), "%s:%s:%d:%s\n",
- s_luser, s_uid, npas, pass);
- else
- snprintf(nbuf, sizeof(nbuf),"%s:%s:%d:%s,%s\n",
- s_luser, s_uid, npas, s_pas, pass);
- _pam_delete(pass);
- if (fputs(nbuf, pwfile) < 0) {
- err = 1;
- break;
- }
- found = 1;
- } else if (fputs(buf, pwfile) < 0) {
- err = 1;
- break;
- }
- }
- fclose(opwfile);
-
- if (!found) {
- pwd = pam_modutil_getpwnam(pamh, forwho);
- if (pwd == NULL) {
- err = 1;
- } else {
- pass = crypt_md5_wrapper(oldpass);
- snprintf(nbuf, sizeof(nbuf), "%s:%lu:1:%s\n",
- forwho, (unsigned long)pwd->pw_uid, pass);
- _pam_delete(pass);
- if (fputs(nbuf, pwfile) < 0) {
- err = 1;
- }
- }
- }
-
- if (fclose(pwfile)) {
- D(("error writing entries to old passwords file: %m"));
- err = 1;
- }
-
-done:
- if (!err) {
- if (rename(OPW_TMPFILE, OLD_PASSWORDS_FILE))
- err = 1;
- }
-#ifdef WITH_SELINUX
- if (SELINUX_ENABLED) {
- if (setfscreatecon(prev_context)) {
- err = 1;
- }
- if (prev_context)
- freecon(prev_context);
- prev_context=NULL;
- }
-#endif
- if (!err) {
- return PAM_SUCCESS;
- } else {
- unlink(OPW_TMPFILE);
- return PAM_AUTHTOK_ERR;
- }
-}
-
-static int _update_passwd(pam_handle_t *pamh,
- const char *forwho, const char *towhat)
-{
- struct passwd *tmpent = NULL;
- struct stat st;
- FILE *pwfile, *opwfile;
- int err = 1;
- int oldmask;
-
- oldmask = umask(077);
-#ifdef WITH_SELINUX
- if (SELINUX_ENABLED) {
- security_context_t passwd_context=NULL;
- if (getfilecon("/etc/passwd",&passwd_context)<0) {
- return PAM_AUTHTOK_ERR;
- };
- if (getfscreatecon(&prev_context)<0) {
- freecon(passwd_context);
- return PAM_AUTHTOK_ERR;
- }
- if (setfscreatecon(passwd_context)) {
- freecon(passwd_context);
- freecon(prev_context);
- return PAM_AUTHTOK_ERR;
- }
- freecon(passwd_context);
- }
-#endif
- pwfile = fopen(PW_TMPFILE, "w");
- umask(oldmask);
- if (pwfile == NULL) {
- err = 1;
- goto done;
- }
-
- opwfile = fopen("/etc/passwd", "r");
- if (opwfile == NULL) {
- fclose(pwfile);
- err = 1;
- goto done;
- }
-
- if (fstat(fileno(opwfile), &st) == -1) {
- fclose(opwfile);
- fclose(pwfile);
- err = 1;
- goto done;
- }
-
- if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
- fclose(opwfile);
- fclose(pwfile);
- err = 1;
- goto done;
- }
- if (fchmod(fileno(pwfile), st.st_mode) == -1) {
- fclose(opwfile);
- fclose(pwfile);
- err = 1;
- goto done;
- }
-
- tmpent = fgetpwent(opwfile);
- while (tmpent) {
- if (!strcmp(tmpent->pw_name, forwho)) {
- /* To shut gcc up */
- union {
- const char *const_charp;
- char *charp;
- } assigned_passwd;
- assigned_passwd.const_charp = towhat;
-
- tmpent->pw_passwd = assigned_passwd.charp;
- err = 0;
- }
- if (putpwent(tmpent, pwfile)) {
- D(("error writing entry to password file: %m"));
- err = 1;
- break;
- }
- tmpent = fgetpwent(opwfile);
- }
- fclose(opwfile);
-
- if (fclose(pwfile)) {
- D(("error writing entries to password file: %m"));
- err = 1;
- }
-
-done:
- if (!err) {
- if (!rename(PW_TMPFILE, "/etc/passwd"))
- pam_syslog(pamh, LOG_NOTICE, "password changed for %s", forwho);
- else
- err = 1;
- }
-#ifdef WITH_SELINUX
- if (SELINUX_ENABLED) {
- if (setfscreatecon(prev_context)) {
- err = 1;
- }
- if (prev_context)
- freecon(prev_context);
- prev_context=NULL;
- }
-#endif
- if (!err) {
- return PAM_SUCCESS;
- } else {
- unlink(PW_TMPFILE);
- return PAM_AUTHTOK_ERR;
- }
-}
-
-static int _update_shadow(pam_handle_t *pamh, const char *forwho, char *towhat)
-{
- struct spwd *spwdent = NULL, *stmpent = NULL;
- struct stat st;
- FILE *pwfile, *opwfile;
- int err = 1;
- int oldmask;
-
- spwdent = getspnam(forwho);
- if (spwdent == NULL) {
- return PAM_USER_UNKNOWN;
- }
- oldmask = umask(077);
-
-#ifdef WITH_SELINUX
- if (SELINUX_ENABLED) {
- security_context_t shadow_context=NULL;
- if (getfilecon("/etc/shadow",&shadow_context)<0) {
- return PAM_AUTHTOK_ERR;
- };
- if (getfscreatecon(&prev_context)<0) {
- freecon(shadow_context);
- return PAM_AUTHTOK_ERR;
- }
- if (setfscreatecon(shadow_context)) {
- freecon(shadow_context);
- freecon(prev_context);
- return PAM_AUTHTOK_ERR;
- }
- freecon(shadow_context);
- }
-#endif
- pwfile = fopen(SH_TMPFILE, "w");
- umask(oldmask);
- if (pwfile == NULL) {
- err = 1;
- goto done;
- }
-
- opwfile = fopen("/etc/shadow", "r");
- if (opwfile == NULL) {
- fclose(pwfile);
- err = 1;
- goto done;
- }
-
- if (fstat(fileno(opwfile), &st) == -1) {
- fclose(opwfile);
- fclose(pwfile);
- err = 1;
- goto done;
- }
-
- if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
- fclose(opwfile);
- fclose(pwfile);
- err = 1;
- goto done;
- }
- if (fchmod(fileno(pwfile), st.st_mode) == -1) {
- fclose(opwfile);
- fclose(pwfile);
- err = 1;
- goto done;
- }
-
- stmpent = fgetspent(opwfile);
- while (stmpent) {
-
- if (!strcmp(stmpent->sp_namp, forwho)) {
- stmpent->sp_pwdp = towhat;
- stmpent->sp_lstchg = time(NULL) / (60 * 60 * 24);
- err = 0;
- D(("Set password %s for %s", stmpent->sp_pwdp, forwho));
- }
-
- if (putspent(stmpent, pwfile)) {
- D(("error writing entry to shadow file: %m"));
- err = 1;
- break;
- }
-
- stmpent = fgetspent(opwfile);
- }
- fclose(opwfile);
-
- if (fclose(pwfile)) {
- D(("error writing entries to shadow file: %m"));
- err = 1;
- }
-
- done:
- if (!err) {
- if (!rename(SH_TMPFILE, "/etc/shadow"))
- pam_syslog(pamh, LOG_NOTICE, "password changed for %s", forwho);
- else
- err = 1;
- }
-
-#ifdef WITH_SELINUX
- if (SELINUX_ENABLED) {
- if (setfscreatecon(prev_context)) {
- err = 1;
- }
- if (prev_context)
- freecon(prev_context);
- prev_context=NULL;
- }
-#endif
-
- if (!err) {
- return PAM_SUCCESS;
- } else {
- unlink(SH_TMPFILE);
- return PAM_AUTHTOK_ERR;
- }
-}
-
static int _do_setpass(pam_handle_t* pamh, const char *forwho,
const char *fromwhat,
char *towhat, unsigned int ctrl, int remember)
@@ -768,9 +315,7 @@ static int _do_setpass(pam_handle_t* pamh, const char *forwho,
enum clnt_stat err;
/* Unlock passwd file to avoid deadlock */
-#ifdef USE_LCKPWDF
- ulckpwdf();
-#endif
+ unlock_pwdf();
unlocked = 1;
/* Initialize password information */
@@ -830,129 +375,63 @@ static int _do_setpass(pam_handle_t* pamh, const char *forwho,
}
if (_unix_comesfromsource(pamh, forwho, 1, 0)) {
-#ifdef USE_LCKPWDF
if(unlocked) {
- int i = 0;
- /* These values for the number of attempts and the sleep time
- are, of course, completely arbitrary.
- My reading of the PAM docs is that, once pam_chauthtok() has been
- called with PAM_UPDATE_AUTHTOK, we are obliged to take any
- reasonable steps to make sure the token is updated; so retrying
- for 1/10 sec. isn't overdoing it. */
- while((retval = lckpwdf()) != 0 && i < 100) {
- usleep(1000);
- i++;
- }
- if(retval != 0) {
+ if (lock_pwdf() != PAM_SUCCESS) {
return PAM_AUTHTOK_LOCK_BUSY;
}
}
+#ifdef WITH_SELINUX
+ if (unix_selinux_confined())
+ return _unix_run_update_binary(pamh, ctrl, forwho, fromwhat, towhat, remember);
#endif
/* first, save old password */
- if (save_old_password(pamh, forwho, fromwhat, remember)) {
+ if (save_old_password(forwho, fromwhat, remember)) {
retval = PAM_AUTHTOK_ERR;
goto done;
}
- if (on(UNIX_SHADOW, ctrl) || _unix_shadowed(pwd)) {
- retval = _update_shadow(pamh, forwho, towhat);
-#ifdef WITH_SELINUX
- if (retval != PAM_SUCCESS && SELINUX_ENABLED)
- retval = _unix_run_shadow_binary(pamh, ctrl, forwho, fromwhat, towhat);
-#endif
+ if (on(UNIX_SHADOW, ctrl) || is_pwd_shadowed(pwd)) {
+ retval = unix_update_shadow(pamh, forwho, towhat);
if (retval == PAM_SUCCESS)
- if (!_unix_shadowed(pwd))
- retval = _update_passwd(pamh, forwho, "x");
+ if (!is_pwd_shadowed(pwd))
+ retval = unix_update_passwd(pamh, forwho, "x");
} else {
- retval = _update_passwd(pamh, forwho, towhat);
+ retval = unix_update_passwd(pamh, forwho, towhat);
}
}
done:
-#ifdef USE_LCKPWDF
- ulckpwdf();
-#endif
+ unlock_pwdf();
return retval;
}
static int _unix_verify_shadow(pam_handle_t *pamh, const char *user, unsigned int ctrl)
{
- struct passwd *pwd = NULL; /* Password and shadow password */
- struct spwd *spwdent = NULL; /* file entries for the user */
- time_t curdays;
- int retval = PAM_SUCCESS;
+ struct passwd *pwent = NULL; /* Password and shadow password */
+ struct spwd *spent = NULL; /* file entries for the user */
+ int daysleft;
+ int retval;
- /* UNIX passwords area */
- pwd = getpwnam(user); /* Get password file entry... */
- if (pwd == NULL)
- return PAM_AUTHINFO_UNAVAIL; /* We don't need to do the rest... */
+ retval = get_account_info(pamh, user, &pwent, &spent);
+ if (retval == PAM_USER_UNKNOWN) {
+ return retval;
+ }
- if (_unix_shadowed(pwd)) {
- /* ...and shadow password file entry for this user, if shadowing
- is enabled */
- setspent();
- spwdent = getspnam(user);
- endspent();
+ if (retval == PAM_SUCCESS && spent == NULL)
+ return PAM_SUCCESS;
-#ifdef WITH_SELINUX
- if (spwdent == NULL && SELINUX_ENABLED )
- spwdent = _unix_run_verify_binary(pamh, ctrl, user);
-#endif
- if (spwdent == NULL)
- return PAM_AUTHINFO_UNAVAIL;
- } else {
- if (strcmp(pwd->pw_passwd,"*NP*") == 0) { /* NIS+ */
- uid_t save_uid;
-
- save_uid = geteuid();
- seteuid (pwd->pw_uid);
- spwdent = getspnam( user );
- seteuid (save_uid);
-
- if (spwdent == NULL)
- return PAM_AUTHINFO_UNAVAIL;
- } else
- spwdent = NULL;
+ if (retval == PAM_UNIX_RUN_HELPER) {
+ retval = _unix_run_verify_binary(pamh, ctrl, user, &daysleft);
+ if (retval == PAM_AUTH_ERR || retval == PAM_USER_UNKNOWN)
+ return retval;
}
+ else if (retval == PAM_SUCCESS)
+ retval = check_shadow_expiry(pamh, spent, &daysleft);
+
+ if (on(UNIX__IAMROOT, ctrl) || retval == PAM_NEW_AUTHTOK_REQD)
+ return PAM_SUCCESS;
- if (spwdent != NULL) {
- /* We have the user's information, now let's check if their account
- has expired (60 * 60 * 24 = number of seconds in a day) */
-
- if (off(UNIX__IAMROOT, ctrl)) {
- /* Get the current number of days since 1970 */
- curdays = time(NULL) / (60 * 60 * 24);
- if (curdays < spwdent->sp_lstchg) {
- pam_syslog(pamh, LOG_DEBUG,
- "account %s has password changed in future",
- user);
- curdays = spwdent->sp_lstchg;
- }
- if ((curdays - spwdent->sp_lstchg < spwdent->sp_min)
- && (spwdent->sp_min != -1))
- /*
- * The last password change was too recent.
- */
- retval = PAM_AUTHTOK_ERR;
- else if ((curdays - spwdent->sp_lstchg > spwdent->sp_max)
- && (curdays - spwdent->sp_lstchg > spwdent->sp_inact)
- && (curdays - spwdent->sp_lstchg >
- spwdent->sp_max + spwdent->sp_inact)
- && (spwdent->sp_max != -1) && (spwdent->sp_inact != -1)
- && (spwdent->sp_lstchg != 0))
- /*
- * Their password change has been put off too long,
- */
- retval = PAM_ACCT_EXPIRED;
- else if ((curdays > spwdent->sp_expire) && (spwdent->sp_expire != -1)
- && (spwdent->sp_lstchg != 0))
- /*
- * OR their account has just plain expired
- */
- retval = PAM_ACCT_EXPIRED;
- }
- }
return retval;
}
@@ -1020,8 +499,9 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
int argc, const char **argv)
{
unsigned int ctrl, lctrl;
- int retval, i;
+ int retval;
int remember = -1;
+ int rounds = -1;
/* <DO NOT free() THESE> */
const char *user;
@@ -1030,7 +510,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
D(("called."));
- ctrl = _set_ctrl(pamh, flags, &remember, argc, argv);
+ ctrl = _set_ctrl(pamh, flags, &remember, &rounds, argc, argv);
/*
* First get the name of a user
@@ -1239,40 +719,23 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
pass_new = pass_old = NULL; /* tidy up */
return retval;
}
-#ifdef USE_LCKPWDF
- /* These values for the number of attempts and the sleep time
- are, of course, completely arbitrary.
- My reading of the PAM docs is that, once pam_chauthtok() has been
- called with PAM_UPDATE_AUTHTOK, we are obliged to take any
- reasonable steps to make sure the token is updated; so retrying
- for 1/10 sec. isn't overdoing it. */
- i=0;
- while((retval = lckpwdf()) != 0 && i < 100) {
- usleep(1000);
- i++;
- }
- if(retval != 0) {
+ if (lock_pwdf() != PAM_SUCCESS) {
return PAM_AUTHTOK_LOCK_BUSY;
}
-#endif
if (pass_old) {
retval = _unix_verify_password(pamh, user, pass_old, ctrl);
if (retval != PAM_SUCCESS) {
pam_syslog(pamh, LOG_NOTICE, "user password changed by another process");
-#ifdef USE_LCKPWDF
- ulckpwdf();
-#endif
+ unlock_pwdf();
return retval;
}
}
retval = _unix_verify_shadow(pamh, user, ctrl);
if (retval != PAM_SUCCESS) {
- pam_syslog(pamh, LOG_NOTICE, "user not authenticated 2");
-#ifdef USE_LCKPWDF
- ulckpwdf();
-#endif
+ pam_syslog(pamh, LOG_NOTICE, "user shadow entry expired");
+ unlock_pwdf();
return retval;
}
@@ -1281,9 +744,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
pam_syslog(pamh, LOG_NOTICE,
"new password not acceptable 2");
pass_new = pass_old = NULL; /* tidy up */
-#ifdef USE_LCKPWDF
- ulckpwdf();
-#endif
+ unlock_pwdf();
return retval;
}
@@ -1296,51 +757,13 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
* First we encrypt the new password.
*/
- if (on(UNIX_MD5_PASS, ctrl)) {
- tpass = crypt_md5_wrapper(pass_new);
- } else {
- /*
- * Salt manipulation is stolen from Rick Faith's passwd
- * program. Sorry Rick :) -- alex
- */
-
- time_t tm;
- char salt[3];
-
- time(&tm);
- salt[0] = bin_to_ascii(tm & 0x3f);
- salt[1] = bin_to_ascii((tm >> 6) & 0x3f);
- salt[2] = '\0';
-
- if (off(UNIX_BIGCRYPT, ctrl) && strlen(pass_new) > 8) {
- /*
- * to avoid using the _extensions_ of the bigcrypt()
- * function we truncate the newly entered password
- * [Problems that followed from this are fixed as per
- * Bug 521314.]
- */
- char *temp = malloc(9);
-
- if (temp == NULL) {
- pam_syslog(pamh, LOG_CRIT,
- "out of memory for password");
- pass_new = pass_old = NULL; /* tidy up */
-#ifdef USE_LCKPWDF
- ulckpwdf();
-#endif
- return PAM_BUF_ERR;
- }
- /* copy first 8 bytes of password */
- strncpy(temp, pass_new, 8);
- temp[8] = '\0';
-
- /* no longer need cleartext */
- tpass = bigcrypt(temp, salt);
-
- _pam_delete(temp); /* tidy up */
- } else {
- tpass = bigcrypt(pass_new, salt);
- }
+ tpass = create_password_hash(pass_new, ctrl, rounds);
+ if (tpass == NULL) {
+ pam_syslog(pamh, LOG_CRIT,
+ "out of memory for password");
+ pass_new = pass_old = NULL; /* tidy up */
+ unlock_pwdf();
+ return PAM_BUF_ERR;
}
D(("password processed"));
@@ -1349,7 +772,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
retval = _do_setpass(pamh, user, pass_old, tpass, ctrl,
remember);
- /* _do_setpass has called ulckpwdf for us */
+ /* _do_setpass has called unlock_pwdf for us */
_pam_delete(tpass);
pass_old = pass_new = NULL;
diff --git a/Linux-PAM/modules/pam_unix/pam_unix_sess.c b/Linux-PAM/modules/pam_unix/pam_unix_sess.c
index b7af4503..3379df42 100644
--- a/Linux-PAM/modules/pam_unix/pam_unix_sess.c
+++ b/Linux-PAM/modules/pam_unix/pam_unix_sess.c
@@ -1,5 +1,5 @@
/*
- * $Id: pam_unix_sess.c,v 1.9 2006/06/17 16:44:58 kukuk Exp $
+ * $Id: pam_unix_sess.c,v 1.10 2008/01/23 15:35:13 t8m Exp $
*
* Copyright Alexander O. Yuriev, 1996. All rights reserved.
* Copyright Jan Rêkorajski, 1999. All rights reserved.
@@ -73,7 +73,7 @@ PAM_EXTERN int pam_sm_open_session(pam_handle_t * pamh, int flags,
D(("called."));
- ctrl = _set_ctrl(pamh, flags, NULL, argc, argv);
+ ctrl = _set_ctrl(pamh, flags, NULL, NULL, argc, argv);
retval = pam_get_item(pamh, PAM_USER, (void *) &user_name);
if (user_name == NULL || *user_name == '\0' || retval != PAM_SUCCESS) {
@@ -107,7 +107,7 @@ PAM_EXTERN int pam_sm_close_session(pam_handle_t * pamh, int flags,
D(("called."));
- ctrl = _set_ctrl(pamh, flags, NULL, argc, argv);
+ ctrl = _set_ctrl(pamh, flags, NULL, NULL, argc, argv);
retval = pam_get_item(pamh, PAM_USER, (void *) &user_name);
if (user_name == NULL || *user_name == '\0' || retval != PAM_SUCCESS) {
diff --git a/Linux-PAM/modules/pam_unix/passverify.c b/Linux-PAM/modules/pam_unix/passverify.c
new file mode 100644
index 00000000..6d588e63
--- /dev/null
+++ b/Linux-PAM/modules/pam_unix/passverify.c
@@ -0,0 +1,1129 @@
+/*
+ * Copyright information at end of file.
+ */
+#include "config.h"
+#include <security/_pam_macros.h>
+#include <security/pam_modules.h>
+#include "support.h"
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <shadow.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#ifdef HAVE_CRYPT_H
+#include <crypt.h>
+#endif
+
+#include "md5.h"
+#include "bigcrypt.h"
+#include "passverify.h"
+
+#ifdef WITH_SELINUX
+#include <selinux/selinux.h>
+#define SELINUX_ENABLED is_selinux_enabled()>0
+#else
+#define SELINUX_ENABLED 0
+#endif
+
+#ifdef HELPER_COMPILE
+#define pam_modutil_getpwnam(h,n) getpwnam(n)
+#define pam_modutil_getspnam(h,n) getspnam(n)
+#define pam_syslog(h,a,b,c) helper_log_err(a,b,c)
+#else
+#include <security/pam_modutil.h>
+#include <security/pam_ext.h>
+#endif
+
+#if defined(USE_LCKPWDF) && !defined(HAVE_LCKPWDF)
+# include "./lckpwdf.-c"
+#endif
+
+static void
+strip_hpux_aging(char *hash)
+{
+ static const char valid[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789./";
+ if ((*hash != '$') && (strlen(hash) > 13)) {
+ for (hash += 13; *hash != '\0'; hash++) {
+ if (strchr(valid, *hash) == NULL) {
+ *hash = '\0';
+ break;
+ }
+ }
+ }
+}
+
+int
+verify_pwd_hash(const char *p, char *hash, unsigned int nullok)
+{
+ size_t hash_len;
+ char *pp = NULL;
+ int retval;
+ D(("called"));
+
+ strip_hpux_aging(hash);
+ hash_len = strlen(hash);
+ if (!hash_len) {
+ /* the stored password is NULL */
+ if (nullok) { /* this means we've succeeded */
+ D(("user has empty password - access granted"));
+ retval = PAM_SUCCESS;
+ } else {
+ D(("user has empty password - access denied"));
+ retval = PAM_AUTH_ERR;
+ }
+ } else if (!p || *hash == '*' || *hash == '!') {
+ retval = PAM_AUTH_ERR;
+ } else {
+ if (!strncmp(hash, "$1$", 3)) {
+ pp = Goodcrypt_md5(p, hash);
+ if (pp && strcmp(pp, hash) != 0) {
+ _pam_delete(pp);
+ pp = Brokencrypt_md5(p, hash);
+ }
+ } else if (*hash != '$' && hash_len >= 13) {
+ pp = bigcrypt(p, hash);
+ if (pp && hash_len == 13 && strlen(pp) > hash_len) {
+ _pam_overwrite(pp + hash_len);
+ }
+ } else {
+ /*
+ * Ok, we don't know the crypt algorithm, but maybe
+ * libcrypt knows about it? We should try it.
+ */
+#ifdef HAVE_CRYPT_R
+ struct crypt_data *cdata;
+ cdata = malloc(sizeof(*cdata));
+ if (cdata != NULL) {
+ cdata->initialized = 0;
+ pp = x_strdup(crypt_r(p, hash, cdata));
+ memset(cdata, '\0', sizeof(*cdata));
+ free(cdata);
+ }
+#else
+ pp = x_strdup(crypt(p, hash));
+#endif
+ }
+ p = NULL; /* no longer needed here */
+
+ /* the moment of truth -- do we agree with the password? */
+ D(("comparing state of pp[%s] and salt[%s]", pp, salt));
+
+ if (pp && strcmp(pp, hash) == 0) {
+ retval = PAM_SUCCESS;
+ } else {
+ retval = PAM_AUTH_ERR;
+ }
+ }
+
+ if (pp)
+ _pam_delete(pp);
+ D(("done [%d].", retval));
+
+ return retval;
+}
+
+int
+is_pwd_shadowed(const struct passwd *pwd)
+{
+ if (pwd != NULL) {
+ if (strcmp(pwd->pw_passwd, "x") == 0) {
+ return 1;
+ }
+ if ((pwd->pw_passwd[0] == '#') &&
+ (pwd->pw_passwd[1] == '#') &&
+ (strcmp(pwd->pw_name, pwd->pw_passwd + 2) == 0)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+#ifdef HELPER_COMPILE
+int
+get_account_info(const char *name,
+ struct passwd **pwd, struct spwd **spwdent)
+#else
+int
+get_account_info(pam_handle_t *pamh, const char *name,
+ struct passwd **pwd, struct spwd **spwdent)
+#endif
+{
+ /* UNIX passwords area */
+ *pwd = pam_modutil_getpwnam(pamh, name); /* Get password file entry... */
+ *spwdent = NULL;
+
+ if (*pwd != NULL) {
+ if (strcmp((*pwd)->pw_passwd, "*NP*") == 0)
+ { /* NIS+ */
+#ifdef HELPER_COMPILE
+ uid_t save_euid, save_uid;
+
+ save_euid = geteuid();
+ save_uid = getuid();
+ if (save_uid == (*pwd)->pw_uid)
+ setreuid(save_euid, save_uid);
+ else {
+ setreuid(0, -1);
+ if (setreuid(-1, (*pwd)->pw_uid) == -1) {
+ setreuid(-1, 0);
+ setreuid(0, -1);
+ if(setreuid(-1, (*pwd)->pw_uid) == -1)
+ return PAM_CRED_INSUFFICIENT;
+ }
+ }
+
+ *spwdent = pam_modutil_getspnam(pamh, name);
+ if (save_uid == (*pwd)->pw_uid)
+ setreuid(save_uid, save_euid);
+ else {
+ setreuid(-1, 0);
+ setreuid(save_uid, -1);
+ setreuid(-1, save_euid);
+ }
+
+ if (*spwdent == NULL || (*spwdent)->sp_pwdp == NULL)
+ return PAM_AUTHINFO_UNAVAIL;
+#else
+ /* we must run helper for NIS+ passwords */
+ return PAM_UNIX_RUN_HELPER;
+#endif
+ } else if (is_pwd_shadowed(*pwd)) {
+ /*
+ * ...and shadow password file entry for this user,
+ * if shadowing is enabled
+ */
+#ifndef HELPER_COMPILE
+ if (geteuid() || SELINUX_ENABLED)
+ return PAM_UNIX_RUN_HELPER;
+#endif
+ *spwdent = pam_modutil_getspnam(pamh, name);
+ if (*spwdent == NULL || (*spwdent)->sp_pwdp == NULL)
+ return PAM_AUTHINFO_UNAVAIL;
+ }
+ } else {
+ return PAM_USER_UNKNOWN;
+ }
+ return PAM_SUCCESS;
+}
+
+#ifdef HELPER_COMPILE
+int
+get_pwd_hash(const char *name,
+ struct passwd **pwd, char **hash)
+#else
+int
+get_pwd_hash(pam_handle_t *pamh, const char *name,
+ struct passwd **pwd, char **hash)
+#endif
+{
+ int retval;
+ struct spwd *spwdent = NULL;
+
+#ifdef HELPER_COMPILE
+ retval = get_account_info(name, pwd, &spwdent);
+#else
+ retval = get_account_info(pamh, name, pwd, &spwdent);
+#endif
+ if (retval != PAM_SUCCESS) {
+ return retval;
+ }
+
+ if (spwdent)
+ *hash = x_strdup(spwdent->sp_pwdp);
+ else
+ *hash = x_strdup((*pwd)->pw_passwd);
+ if (*hash == NULL)
+ return PAM_BUF_ERR;
+
+ return PAM_SUCCESS;
+}
+
+#ifdef HELPER_COMPILE
+int
+check_shadow_expiry(struct spwd *spent, int *daysleft)
+#else
+int
+check_shadow_expiry(pam_handle_t *pamh, struct spwd *spent, int *daysleft)
+#endif
+{
+ long int curdays;
+ *daysleft = -1;
+ curdays = (long int)(time(NULL) / (60 * 60 * 24));
+ D(("today is %d, last change %d", curdays, spent->sp_lstchg));
+ if ((curdays > spent->sp_expire) && (spent->sp_expire != -1)) {
+ D(("account expired"));
+ return PAM_ACCT_EXPIRED;
+ }
+ if (spent->sp_lstchg == 0) {
+ D(("need a new password"));
+ *daysleft = 0;
+ return PAM_NEW_AUTHTOK_REQD;
+ }
+ if (curdays < spent->sp_lstchg) {
+ pam_syslog(pamh, LOG_DEBUG,
+ "account %s has password changed in future",
+ spent->sp_namp);
+ return PAM_SUCCESS;
+ }
+ if ((curdays - spent->sp_lstchg > spent->sp_max)
+ && (curdays - spent->sp_lstchg > spent->sp_inact)
+ && (curdays - spent->sp_lstchg > spent->sp_max + spent->sp_inact)
+ && (spent->sp_max != -1) && (spent->sp_inact != -1)) {
+ *daysleft = (int)((spent->sp_lstchg + spent->sp_max) - curdays);
+ D(("authtok expired"));
+ return PAM_AUTHTOK_EXPIRED;
+ }
+ if ((curdays - spent->sp_lstchg > spent->sp_max) && (spent->sp_max != -1)) {
+ D(("need a new password 2"));
+ return PAM_NEW_AUTHTOK_REQD;
+ }
+ if ((curdays - spent->sp_lstchg > spent->sp_max - spent->sp_warn)
+ && (spent->sp_max != -1) && (spent->sp_warn != -1)) {
+ *daysleft = (int)((spent->sp_lstchg + spent->sp_max) - curdays);
+ D(("warn before expiry"));
+ }
+ return PAM_SUCCESS;
+
+}
+
+/* passwd/salt conversion macros */
+
+#define PW_TMPFILE "/etc/npasswd"
+#define SH_TMPFILE "/etc/nshadow"
+#define OPW_TMPFILE "/etc/security/nopasswd"
+
+/*
+ * i64c - convert an integer to a radix 64 character
+ */
+static int
+i64c(int i)
+{
+ if (i < 0)
+ return ('.');
+ else if (i > 63)
+ return ('z');
+ if (i == 0)
+ return ('.');
+ if (i == 1)
+ return ('/');
+ if (i >= 2 && i <= 11)
+ return ('0' - 2 + i);
+ if (i >= 12 && i <= 37)
+ return ('A' - 12 + i);
+ if (i >= 38 && i <= 63)
+ return ('a' - 38 + i);
+ return ('\0');
+}
+
+/* <where> must point to a buffer of at least <length>+1 length */
+static void
+crypt_make_salt(char *where, int length)
+{
+ struct timeval tv;
+ MD5_CTX ctx;
+ unsigned char tmp[16];
+ unsigned char *src = (unsigned char *)where;
+ int i;
+#ifdef PAM_PATH_RANDOMDEV
+ int fd;
+ int rv;
+
+ if ((rv = fd = open(PAM_PATH_RANDOMDEV, O_RDONLY)) != -1) {
+ while ((rv = read(fd, where, length)) != length && errno == EINTR);
+ close (fd);
+ }
+ if (rv != length) {
+#endif
+ /*
+ * Code lifted from Marek Michalkiewicz's shadow suite. (CG)
+ * removed use of static variables (AGM)
+ *
+ * will work correctly only for length <= 16 */
+ src = tmp;
+ GoodMD5Init(&ctx);
+ gettimeofday(&tv, (struct timezone *) 0);
+ GoodMD5Update(&ctx, (void *) &tv, sizeof tv);
+ i = getpid();
+ GoodMD5Update(&ctx, (void *) &i, sizeof i);
+ i = clock();
+ GoodMD5Update(&ctx, (void *) &i, sizeof i);
+ GoodMD5Update(&ctx, src, length);
+ GoodMD5Final(tmp, &ctx);
+#ifdef PAM_PATH_RANDOMDEV
+ }
+#endif
+ for (i = 0; i < length; i++)
+ *where++ = i64c(src[i] & 077);
+ *where = '\0';
+}
+
+char *
+crypt_md5_wrapper(const char *pass_new)
+{
+ unsigned char result[16];
+ char *cp = (char *) result;
+
+ cp = stpcpy(cp, "$1$"); /* magic for the MD5 */
+ crypt_make_salt(cp, 8);
+
+ /* no longer need cleartext */
+ cp = Goodcrypt_md5(pass_new, (const char *) result);
+ pass_new = NULL;
+
+ return cp;
+}
+
+char *
+create_password_hash(const char *password, unsigned int ctrl, int rounds)
+{
+ const char *algoid;
+ char salt[64]; /* contains rounds number + max 16 bytes of salt + algo id */
+ char *sp;
+
+ if (on(UNIX_MD5_PASS, ctrl)) {
+ return crypt_md5_wrapper(password);
+ }
+ if (on(UNIX_SHA256_PASS, ctrl)) {
+ algoid = "$5$";
+ } else if (on(UNIX_SHA512_PASS, ctrl)) {
+ algoid = "$6$";
+ } else { /* must be crypt/bigcrypt */
+ char tmppass[9];
+ char *crypted;
+
+ crypt_make_salt(salt, 2);
+ if (off(UNIX_BIGCRYPT, ctrl) && strlen(password) > 8) {
+ strncpy(tmppass, password, sizeof(tmppass)-1);
+ tmppass[sizeof(tmppass)-1] = '\0';
+ password = tmppass;
+ }
+ crypted = bigcrypt(password, salt);
+ memset(tmppass, '\0', sizeof(tmppass));
+ password = NULL;
+ return crypted;
+ }
+
+ sp = stpcpy(salt, algoid);
+ if (on(UNIX_ALGO_ROUNDS, ctrl)) {
+ sp += snprintf(sp, sizeof(salt) - 3, "rounds=%u$", rounds);
+ }
+ crypt_make_salt(sp, 8);
+ /* For now be conservative so the resulting hashes
+ * are not too long. 8 bytes of salt prevents dictionary
+ * attacks well enough. */
+ sp = crypt(password, salt);
+ if (strncmp(algoid, sp, strlen(algoid)) != 0) {
+ /* libc doesn't know the algorithm, use MD5 */
+ memset(sp, '\0', strlen(sp));
+ return crypt_md5_wrapper(password);
+ }
+
+ return x_strdup(sp);
+}
+
+#ifdef WITH_SELINUX
+int
+unix_selinux_confined(void)
+{
+ static int confined = -1;
+ int fd;
+ char tempfile[]="/etc/.pwdXXXXXX";
+
+ if (confined != -1)
+ return confined;
+
+ /* cannot be confined without SELinux enabled */
+ if (!SELINUX_ENABLED){
+ confined = 0;
+ return confined;
+ }
+
+ /* let's try opening shadow read only */
+ if ((fd=open("/etc/shadow", O_RDONLY)) != -1) {
+ close(fd);
+ confined = 0;
+ return confined;
+ }
+
+ if (errno == EACCES) {
+ confined = 1;
+ return confined;
+ }
+
+ /* shadow opening failed because of other reasons let's try
+ creating a file in /etc */
+ if ((fd=mkstemp(tempfile)) != -1) {
+ unlink(tempfile);
+ close(fd);
+ confined = 0;
+ return confined;
+ }
+
+ confined = 1;
+ return confined;
+}
+
+#else
+int
+unix_selinux_confined(void)
+{
+ return 0;
+}
+#endif
+
+#ifdef USE_LCKPWDF
+int
+lock_pwdf(void)
+{
+ int i;
+ int retval;
+
+#ifndef HELPER_COMPILE
+ if (unix_selinux_confined()) {
+ return PAM_SUCCESS;
+ }
+#endif
+ /* These values for the number of attempts and the sleep time
+ are, of course, completely arbitrary.
+ My reading of the PAM docs is that, once pam_chauthtok() has been
+ called with PAM_UPDATE_AUTHTOK, we are obliged to take any
+ reasonable steps to make sure the token is updated; so retrying
+ for 1/10 sec. isn't overdoing it. */
+ i=0;
+ while((retval = lckpwdf()) != 0 && i < 100) {
+ usleep(1000);
+ i++;
+ }
+ if(retval != 0) {
+ return PAM_AUTHTOK_LOCK_BUSY;
+ }
+ return PAM_SUCCESS;
+}
+
+void
+unlock_pwdf(void)
+{
+#ifndef HELPER_COMPILE
+ if (unix_selinux_confined()) {
+ return;
+ }
+#endif
+ ulckpwdf();
+}
+#else
+int
+lock_pwdf(void)
+{
+ return PAM_SUCCESS;
+}
+
+void
+unlock_pwdf(void)
+{
+ return;
+}
+#endif
+
+int
+save_old_password(const char *forwho, const char *oldpass,
+ int howmany)
+{
+ static char buf[16384];
+ static char nbuf[16384];
+ char *s_luser, *s_uid, *s_npas, *s_pas, *pass;
+ int npas;
+ FILE *pwfile, *opwfile;
+ int err = 0;
+ int oldmask;
+ int found = 0;
+ struct passwd *pwd = NULL;
+ struct stat st;
+#ifdef WITH_SELINUX
+ security_context_t prev_context=NULL;
+#endif
+
+ if (howmany < 0) {
+ return PAM_SUCCESS;
+ }
+
+ if (oldpass == NULL) {
+ return PAM_SUCCESS;
+ }
+
+ oldmask = umask(077);
+
+#ifdef WITH_SELINUX
+ if (SELINUX_ENABLED) {
+ security_context_t passwd_context=NULL;
+ if (getfilecon("/etc/passwd",&passwd_context)<0) {
+ return PAM_AUTHTOK_ERR;
+ };
+ if (getfscreatecon(&prev_context)<0) {
+ freecon(passwd_context);
+ return PAM_AUTHTOK_ERR;
+ }
+ if (setfscreatecon(passwd_context)) {
+ freecon(passwd_context);
+ freecon(prev_context);
+ return PAM_AUTHTOK_ERR;
+ }
+ freecon(passwd_context);
+ }
+#endif
+ pwfile = fopen(OPW_TMPFILE, "w");
+ umask(oldmask);
+ if (pwfile == NULL) {
+ err = 1;
+ goto done;
+ }
+
+ opwfile = fopen(OLD_PASSWORDS_FILE, "r");
+ if (opwfile == NULL) {
+ fclose(pwfile);
+ err = 1;
+ goto done;
+ }
+
+ if (fstat(fileno(opwfile), &st) == -1) {
+ fclose(opwfile);
+ fclose(pwfile);
+ err = 1;
+ goto done;
+ }
+
+ if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
+ fclose(opwfile);
+ fclose(pwfile);
+ err = 1;
+ goto done;
+ }
+ if (fchmod(fileno(pwfile), st.st_mode) == -1) {
+ fclose(opwfile);
+ fclose(pwfile);
+ err = 1;
+ goto done;
+ }
+
+ while (fgets(buf, 16380, opwfile)) {
+ if (!strncmp(buf, forwho, strlen(forwho))) {
+ char *sptr = NULL;
+ found = 1;
+ if (howmany == 0)
+ continue;
+ buf[strlen(buf) - 1] = '\0';
+ s_luser = strtok_r(buf, ":", &sptr);
+ s_uid = strtok_r(NULL, ":", &sptr);
+ s_npas = strtok_r(NULL, ":", &sptr);
+ s_pas = strtok_r(NULL, ":", &sptr);
+ npas = strtol(s_npas, NULL, 10) + 1;
+ while (npas > howmany) {
+ s_pas = strpbrk(s_pas, ",");
+ if (s_pas != NULL)
+ s_pas++;
+ npas--;
+ }
+ pass = crypt_md5_wrapper(oldpass);
+ if (s_pas == NULL)
+ snprintf(nbuf, sizeof(nbuf), "%s:%s:%d:%s\n",
+ s_luser, s_uid, npas, pass);
+ else
+ snprintf(nbuf, sizeof(nbuf),"%s:%s:%d:%s,%s\n",
+ s_luser, s_uid, npas, s_pas, pass);
+ _pam_delete(pass);
+ if (fputs(nbuf, pwfile) < 0) {
+ err = 1;
+ break;
+ }
+ } else if (fputs(buf, pwfile) < 0) {
+ err = 1;
+ break;
+ }
+ }
+ fclose(opwfile);
+
+ if (!found) {
+ pwd = getpwnam(forwho);
+ if (pwd == NULL) {
+ err = 1;
+ } else {
+ pass = crypt_md5_wrapper(oldpass);
+ snprintf(nbuf, sizeof(nbuf), "%s:%lu:1:%s\n",
+ forwho, (unsigned long)pwd->pw_uid, pass);
+ _pam_delete(pass);
+ if (fputs(nbuf, pwfile) < 0) {
+ err = 1;
+ }
+ }
+ }
+
+ if (fclose(pwfile)) {
+ D(("error writing entries to old passwords file: %m"));
+ err = 1;
+ }
+
+done:
+ if (!err) {
+ if (rename(OPW_TMPFILE, OLD_PASSWORDS_FILE))
+ err = 1;
+ }
+#ifdef WITH_SELINUX
+ if (SELINUX_ENABLED) {
+ if (setfscreatecon(prev_context)) {
+ err = 1;
+ }
+ if (prev_context)
+ freecon(prev_context);
+ prev_context=NULL;
+ }
+#endif
+ if (!err) {
+ return PAM_SUCCESS;
+ } else {
+ unlink(OPW_TMPFILE);
+ return PAM_AUTHTOK_ERR;
+ }
+}
+
+#ifdef HELPER_COMPILE
+int
+unix_update_passwd(const char *forwho, const char *towhat)
+#else
+int
+unix_update_passwd(pam_handle_t *pamh, const char *forwho, const char *towhat)
+#endif
+{
+ struct passwd *tmpent = NULL;
+ struct stat st;
+ FILE *pwfile, *opwfile;
+ int err = 1;
+ int oldmask;
+#ifdef WITH_SELINUX
+ security_context_t prev_context=NULL;
+#endif
+
+ oldmask = umask(077);
+#ifdef WITH_SELINUX
+ if (SELINUX_ENABLED) {
+ security_context_t passwd_context=NULL;
+ if (getfilecon("/etc/passwd",&passwd_context)<0) {
+ return PAM_AUTHTOK_ERR;
+ };
+ if (getfscreatecon(&prev_context)<0) {
+ freecon(passwd_context);
+ return PAM_AUTHTOK_ERR;
+ }
+ if (setfscreatecon(passwd_context)) {
+ freecon(passwd_context);
+ freecon(prev_context);
+ return PAM_AUTHTOK_ERR;
+ }
+ freecon(passwd_context);
+ }
+#endif
+ pwfile = fopen(PW_TMPFILE, "w");
+ umask(oldmask);
+ if (pwfile == NULL) {
+ err = 1;
+ goto done;
+ }
+
+ opwfile = fopen("/etc/passwd", "r");
+ if (opwfile == NULL) {
+ fclose(pwfile);
+ err = 1;
+ goto done;
+ }
+
+ if (fstat(fileno(opwfile), &st) == -1) {
+ fclose(opwfile);
+ fclose(pwfile);
+ err = 1;
+ goto done;
+ }
+
+ if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
+ fclose(opwfile);
+ fclose(pwfile);
+ err = 1;
+ goto done;
+ }
+ if (fchmod(fileno(pwfile), st.st_mode) == -1) {
+ fclose(opwfile);
+ fclose(pwfile);
+ err = 1;
+ goto done;
+ }
+
+ tmpent = fgetpwent(opwfile);
+ while (tmpent) {
+ if (!strcmp(tmpent->pw_name, forwho)) {
+ /* To shut gcc up */
+ union {
+ const char *const_charp;
+ char *charp;
+ } assigned_passwd;
+ assigned_passwd.const_charp = towhat;
+
+ tmpent->pw_passwd = assigned_passwd.charp;
+ err = 0;
+ }
+ if (putpwent(tmpent, pwfile)) {
+ D(("error writing entry to password file: %m"));
+ err = 1;
+ break;
+ }
+ tmpent = fgetpwent(opwfile);
+ }
+ fclose(opwfile);
+
+ if (fclose(pwfile)) {
+ D(("error writing entries to password file: %m"));
+ err = 1;
+ }
+
+done:
+ if (!err) {
+ if (!rename(PW_TMPFILE, "/etc/passwd"))
+#ifdef HELPER_COMPILE
+ helper_log_err(
+#else
+ pam_syslog(pamh,
+#endif
+ LOG_NOTICE, "password changed for %s", forwho);
+ else
+ err = 1;
+ }
+#ifdef WITH_SELINUX
+ if (SELINUX_ENABLED) {
+ if (setfscreatecon(prev_context)) {
+ err = 1;
+ }
+ if (prev_context)
+ freecon(prev_context);
+ prev_context=NULL;
+ }
+#endif
+ if (!err) {
+ return PAM_SUCCESS;
+ } else {
+ unlink(PW_TMPFILE);
+ return PAM_AUTHTOK_ERR;
+ }
+}
+
+#ifdef HELPER_COMPILE
+int
+unix_update_shadow(const char *forwho, char *towhat)
+#else
+int
+unix_update_shadow(pam_handle_t *pamh, const char *forwho, char *towhat)
+#endif
+{
+ struct spwd *spwdent = NULL, *stmpent = NULL;
+ struct stat st;
+ FILE *pwfile, *opwfile;
+ int err = 1;
+ int oldmask;
+#ifdef WITH_SELINUX
+ security_context_t prev_context=NULL;
+#endif
+
+ spwdent = getspnam(forwho);
+ if (spwdent == NULL) {
+ return PAM_USER_UNKNOWN;
+ }
+ oldmask = umask(077);
+
+#ifdef WITH_SELINUX
+ if (SELINUX_ENABLED) {
+ security_context_t shadow_context=NULL;
+ if (getfilecon("/etc/shadow",&shadow_context)<0) {
+ return PAM_AUTHTOK_ERR;
+ };
+ if (getfscreatecon(&prev_context)<0) {
+ freecon(shadow_context);
+ return PAM_AUTHTOK_ERR;
+ }
+ if (setfscreatecon(shadow_context)) {
+ freecon(shadow_context);
+ freecon(prev_context);
+ return PAM_AUTHTOK_ERR;
+ }
+ freecon(shadow_context);
+ }
+#endif
+ pwfile = fopen(SH_TMPFILE, "w");
+ umask(oldmask);
+ if (pwfile == NULL) {
+ err = 1;
+ goto done;
+ }
+
+ opwfile = fopen("/etc/shadow", "r");
+ if (opwfile == NULL) {
+ fclose(pwfile);
+ err = 1;
+ goto done;
+ }
+
+ if (fstat(fileno(opwfile), &st) == -1) {
+ fclose(opwfile);
+ fclose(pwfile);
+ err = 1;
+ goto done;
+ }
+
+ if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
+ fclose(opwfile);
+ fclose(pwfile);
+ err = 1;
+ goto done;
+ }
+ if (fchmod(fileno(pwfile), st.st_mode) == -1) {
+ fclose(opwfile);
+ fclose(pwfile);
+ err = 1;
+ goto done;
+ }
+
+ stmpent = fgetspent(opwfile);
+ while (stmpent) {
+
+ if (!strcmp(stmpent->sp_namp, forwho)) {
+ stmpent->sp_pwdp = towhat;
+ stmpent->sp_lstchg = time(NULL) / (60 * 60 * 24);
+ err = 0;
+ D(("Set password %s for %s", stmpent->sp_pwdp, forwho));
+ }
+
+ if (putspent(stmpent, pwfile)) {
+ D(("error writing entry to shadow file: %m"));
+ err = 1;
+ break;
+ }
+
+ stmpent = fgetspent(opwfile);
+ }
+ fclose(opwfile);
+
+ if (fclose(pwfile)) {
+ D(("error writing entries to shadow file: %m"));
+ err = 1;
+ }
+
+ done:
+ if (!err) {
+ if (!rename(SH_TMPFILE, "/etc/shadow"))
+#ifdef HELPER_COMPILE
+ helper_log_err(
+#else
+ pam_syslog(pamh,
+#endif
+ LOG_NOTICE, "password changed for %s", forwho);
+ else
+ err = 1;
+ }
+
+#ifdef WITH_SELINUX
+ if (SELINUX_ENABLED) {
+ if (setfscreatecon(prev_context)) {
+ err = 1;
+ }
+ if (prev_context)
+ freecon(prev_context);
+ prev_context=NULL;
+ }
+#endif
+
+ if (!err) {
+ return PAM_SUCCESS;
+ } else {
+ unlink(SH_TMPFILE);
+ return PAM_AUTHTOK_ERR;
+ }
+}
+
+#ifdef HELPER_COMPILE
+
+int
+helper_verify_password(const char *name, const char *p, int nullok)
+{
+ struct passwd *pwd = NULL;
+ char *salt = NULL;
+ int retval;
+
+ retval = get_pwd_hash(name, &pwd, &salt);
+
+ if (pwd == NULL || salt == NULL) {
+ helper_log_err(LOG_WARNING, "check pass; user unknown");
+ retval = PAM_USER_UNKNOWN;
+ } else {
+ retval = verify_pwd_hash(p, salt, nullok);
+ }
+
+ if (salt) {
+ _pam_overwrite(salt);
+ _pam_drop(salt);
+ }
+
+ p = NULL; /* no longer needed here */
+
+ return retval;
+}
+
+void
+helper_log_err(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ openlog(HELPER_COMPILE, LOG_CONS | LOG_PID, LOG_AUTHPRIV);
+ vsyslog(err, format, args);
+ va_end(args);
+ closelog();
+}
+
+static void
+su_sighandler(int sig)
+{
+#ifndef SA_RESETHAND
+ /* emulate the behaviour of the SA_RESETHAND flag */
+ if ( sig == SIGILL || sig == SIGTRAP || sig == SIGBUS || sig = SIGSERV )
+ signal(sig, SIG_DFL);
+#endif
+ if (sig > 0) {
+ _exit(sig);
+ }
+}
+
+void
+setup_signals(void)
+{
+ struct sigaction action; /* posix signal structure */
+
+ /*
+ * Setup signal handlers
+ */
+ (void) memset((void *) &action, 0, sizeof(action));
+ action.sa_handler = su_sighandler;
+#ifdef SA_RESETHAND
+ action.sa_flags = SA_RESETHAND;
+#endif
+ (void) sigaction(SIGILL, &action, NULL);
+ (void) sigaction(SIGTRAP, &action, NULL);
+ (void) sigaction(SIGBUS, &action, NULL);
+ (void) sigaction(SIGSEGV, &action, NULL);
+ action.sa_handler = SIG_IGN;
+ action.sa_flags = 0;
+ (void) sigaction(SIGTERM, &action, NULL);
+ (void) sigaction(SIGHUP, &action, NULL);
+ (void) sigaction(SIGINT, &action, NULL);
+ (void) sigaction(SIGQUIT, &action, NULL);
+}
+
+char *
+getuidname(uid_t uid)
+{
+ struct passwd *pw;
+ static char username[256];
+
+ pw = getpwuid(uid);
+ if (pw == NULL)
+ return NULL;
+
+ strncpy(username, pw->pw_name, sizeof(username));
+ username[sizeof(username) - 1] = '\0';
+
+ return username;
+}
+
+int
+read_passwords(int fd, int npass, char **passwords)
+{
+ int rbytes = 0;
+ int offset = 0;
+ int i = 0;
+ char *pptr;
+ while (npass > 0) {
+ rbytes = read(fd, passwords[i]+offset, MAXPASS-offset);
+
+ if (rbytes < 0) {
+ if (errno == EINTR) continue;
+ break;
+ }
+ if (rbytes == 0)
+ break;
+
+ while (npass > 0 && (pptr=memchr(passwords[i]+offset, '\0', rbytes))
+ != NULL) {
+ rbytes -= pptr - (passwords[i]+offset) + 1;
+ i++;
+ offset = 0;
+ npass--;
+ if (rbytes > 0) {
+ if (npass > 0)
+ memcpy(passwords[i], pptr+1, rbytes);
+ memset(pptr+1, '\0', rbytes);
+ }
+ }
+ offset += rbytes;
+ }
+
+ /* clear up */
+ if (offset > 0 && npass > 0) {
+ memset(passwords[i], '\0', offset);
+ }
+
+ return i;
+}
+
+#endif
+/* ****************************************************************** *
+ * Copyright (c) Jan Rêkorajski 1999.
+ * Copyright (c) Andrew G. Morgan 1996-8.
+ * Copyright (c) Alex O. Yuriev, 1996.
+ * Copyright (c) Cristian Gafton 1996.
+ * Copyright (c) Red Hat, Inc. 1996, 2007, 2008.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/Linux-PAM/modules/pam_unix/passverify.h b/Linux-PAM/modules/pam_unix/passverify.h
new file mode 100644
index 00000000..e8e112d0
--- /dev/null
+++ b/Linux-PAM/modules/pam_unix/passverify.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright information at end of file.
+ */
+
+#include <sys/types.h>
+#include <pwd.h>
+#include <security/pam_modules.h>
+
+#define PAM_UNIX_RUN_HELPER PAM_CRED_INSUFFICIENT
+
+#define MAXPASS 200 /* the maximum length of a password */
+
+#define OLD_PASSWORDS_FILE "/etc/security/opasswd"
+
+int
+verify_pwd_hash(const char *p, char *hash, unsigned int nullok);
+
+int
+is_pwd_shadowed(const struct passwd *pwd);
+
+char *
+crypt_md5_wrapper(const char *pass_new);
+
+char *
+create_password_hash(const char *password, unsigned int ctrl, int rounds);
+
+int
+unix_selinux_confined(void);
+
+int
+lock_pwdf(void);
+
+void
+unlock_pwdf(void);
+
+int
+save_old_password(const char *forwho, const char *oldpass,
+ int howmany);
+
+#ifdef HELPER_COMPILE
+void
+helper_log_err(int err, const char *format,...);
+
+int
+helper_verify_password(const char *name, const char *p, int nullok);
+
+void
+setup_signals(void);
+
+char *
+getuidname(uid_t uid);
+
+int
+read_passwords(int fd, int npass, char **passwords);
+
+int
+get_account_info(const char *name,
+ struct passwd **pwd, struct spwd **spwdent);
+
+int
+get_pwd_hash(const char *name,
+ struct passwd **pwd, char **hash);
+
+int
+check_shadow_expiry(struct spwd *spent, int *daysleft);
+
+int
+unix_update_passwd(const char *forwho, const char *towhat);
+
+int
+unix_update_shadow(const char *forwho, char *towhat);
+#else
+int
+get_account_info(pam_handle_t *pamh, const char *name,
+ struct passwd **pwd, struct spwd **spwdent);
+
+int
+get_pwd_hash(pam_handle_t *pamh, const char *name,
+ struct passwd **pwd, char **hash);
+
+int
+check_shadow_expiry(pam_handle_t *pamh, struct spwd *spent, int *daysleft);
+
+int
+unix_update_passwd(pam_handle_t *pamh, const char *forwho, const char *towhat);
+
+int
+unix_update_shadow(pam_handle_t *pamh, const char *forwho, char *towhat);
+#endif
+
+/* ****************************************************************** *
+ * Copyright (c) Red Hat, Inc. 2007.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/Linux-PAM/modules/pam_unix/support.c b/Linux-PAM/modules/pam_unix/support.c
index 1472b966..b82cad26 100644
--- a/Linux-PAM/modules/pam_unix/support.c
+++ b/Linux-PAM/modules/pam_unix/support.c
@@ -26,9 +26,8 @@
#include <security/pam_ext.h>
#include <security/pam_modutil.h>
-#include "md5.h"
#include "support.h"
-#include "bigcrypt.h"
+#include "passverify.h"
#ifdef WITH_SELINUX
#include <selinux/selinux.h>
#define SELINUX_ENABLED is_selinux_enabled()>0
@@ -53,8 +52,8 @@ int _make_remark(pam_handle_t * pamh, unsigned int ctrl,
* set the control flags for the UNIX module.
*/
-int _set_ctrl(pam_handle_t *pamh, int flags, int *remember, int argc,
- const char **argv)
+int _set_ctrl(pam_handle_t *pamh, int flags, int *remember, int *rounds,
+ int argc, const char **argv)
{
unsigned int ctrl;
@@ -110,6 +109,16 @@ int _set_ctrl(pam_handle_t *pamh, int flags, int *remember, int argc,
*remember = 400;
}
}
+ if (rounds != NULL) {
+ if (j == UNIX_ALGO_ROUNDS) {
+ *rounds = strtol(*argv + 7, NULL, 10);
+ if ((*rounds < 1000) || (*rounds == INT_MAX))
+ /* don't care about bogus values */
+ unset(UNIX_ALGO_ROUNDS, ctrl);
+ if (*rounds >= 10000000)
+ *rounds = 9999999;
+ }
+ }
}
++argv; /* step to next argument */
@@ -377,95 +386,6 @@ int _unix_comesfromsource(pam_handle_t *pamh,
}
/*
- * _unix_blankpasswd() is a quick check for a blank password
- *
- * returns TRUE if user does not have a password
- * - to avoid prompting for one in such cases (CG)
- */
-
-int
-_unix_blankpasswd (pam_handle_t *pamh, unsigned int ctrl, const char *name)
-{
- struct passwd *pwd = NULL;
- struct spwd *spwdent = NULL;
- char *salt = NULL;
- int retval;
-
- D(("called"));
-
- /*
- * This function does not have to be too smart if something goes
- * wrong, return FALSE and let this case to be treated somewhere
- * else (CG)
- */
-
- if (on(UNIX__NONULL, ctrl))
- return 0; /* will fail but don't let on yet */
-
- /* UNIX passwords area */
-
- /* Get password file entry... */
- pwd = pam_modutil_getpwnam (pamh, name);
-
- if (pwd != NULL) {
- if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
- { /* NIS+ */
- uid_t save_euid, save_uid;
-
- save_euid = geteuid();
- save_uid = getuid();
- if (save_uid == pwd->pw_uid)
- setreuid( save_euid, save_uid );
- else {
- setreuid( 0, -1 );
- if (setreuid( -1, pwd->pw_uid ) == -1) {
- setreuid( -1, 0 );
- setreuid( 0, -1 );
- if(setreuid( -1, pwd->pw_uid ) == -1)
- /* Will fail elsewhere. */
- return 0;
- }
- }
-
- spwdent = pam_modutil_getspnam (pamh, name);
- if (save_uid == pwd->pw_uid)
- setreuid( save_uid, save_euid );
- else {
- if (setreuid( -1, 0 ) == -1)
- setreuid( save_uid, -1 );
- setreuid( -1, save_euid );
- }
- } else if (_unix_shadowed(pwd)) {
- /*
- * ...and shadow password file entry for this user,
- * if shadowing is enabled
- */
- spwdent = pam_modutil_getspnam(pamh, name);
- }
- if (spwdent)
- salt = x_strdup(spwdent->sp_pwdp);
- else
- salt = x_strdup(pwd->pw_passwd);
- }
- /* Does this user have a password? */
- if (salt == NULL) {
- retval = 0;
- } else {
- if (strlen(salt) == 0)
- retval = 1;
- else
- retval = 0;
- }
-
- /* tidy up */
-
- if (salt)
- _pam_delete(salt);
-
- return retval;
-}
-
-/*
* verify the password of a user
*/
@@ -519,7 +439,7 @@ static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd,
}
}
- if (SELINUX_ENABLED && geteuid() == 0) {
+ if (geteuid() == 0) {
/* must set the real uid to 0 so the helper will not error
out if pam is called from setuid binary (su, sudo...) */
setuid(0);
@@ -573,13 +493,66 @@ static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd,
return retval;
}
+/*
+ * _unix_blankpasswd() is a quick check for a blank password
+ *
+ * returns TRUE if user does not have a password
+ * - to avoid prompting for one in such cases (CG)
+ */
+
+int
+_unix_blankpasswd (pam_handle_t *pamh, unsigned int ctrl, const char *name)
+{
+ struct passwd *pwd = NULL;
+ char *salt = NULL;
+ int retval;
+
+ D(("called"));
+
+ /*
+ * This function does not have to be too smart if something goes
+ * wrong, return FALSE and let this case to be treated somewhere
+ * else (CG)
+ */
+
+ if (on(UNIX__NONULL, ctrl))
+ return 0; /* will fail but don't let on yet */
+
+ /* UNIX passwords area */
+
+ retval = get_pwd_hash(pamh, name, &pwd, &salt);
+
+ if (retval == PAM_UNIX_RUN_HELPER) {
+ /* salt will not be set here so we can return immediately */
+ if (_unix_run_helper_binary(pamh, NULL, ctrl, name) == PAM_SUCCESS)
+ return 1;
+ else
+ return 0;
+ }
+
+ /* Does this user have a password? */
+ if (salt == NULL) {
+ retval = 0;
+ } else {
+ if (strlen(salt) == 0)
+ retval = 1;
+ else
+ retval = 0;
+ }
+
+ /* tidy up */
+
+ if (salt)
+ _pam_delete(salt);
+
+ return retval;
+}
+
int _unix_verify_password(pam_handle_t * pamh, const char *name
,const char *p, unsigned int ctrl)
{
struct passwd *pwd = NULL;
- struct spwd *spwdent = NULL;
char *salt = NULL;
- char *pp = NULL;
char *data_name;
int retval;
@@ -597,48 +570,7 @@ int _unix_verify_password(pam_handle_t * pamh, const char *name
D(("locating user's record"));
- /* UNIX passwords area */
- pwd = pam_modutil_getpwnam (pamh, name); /* Get password file entry... */
-
- if (pwd != NULL) {
- if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
- { /* NIS+ */
- uid_t save_euid, save_uid;
-
- save_euid = geteuid();
- save_uid = getuid();
- if (save_uid == pwd->pw_uid)
- setreuid( save_euid, save_uid );
- else {
- setreuid( 0, -1 );
- if (setreuid( -1, pwd->pw_uid ) == -1) {
- setreuid( -1, 0 );
- setreuid( 0, -1 );
- if(setreuid( -1, pwd->pw_uid ) == -1)
- return PAM_CRED_INSUFFICIENT;
- }
- }
-
- spwdent = pam_modutil_getspnam (pamh, name);
- if (save_uid == pwd->pw_uid)
- setreuid( save_uid, save_euid );
- else {
- if (setreuid( -1, 0 ) == -1)
- setreuid( save_uid, -1 );
- setreuid( -1, save_euid );
- }
- } else if (_unix_shadowed(pwd)) {
- /*
- * ...and shadow password file entry for this user,
- * if shadowing is enabled
- */
- spwdent = pam_modutil_getspnam (pamh, name);
- }
- if (spwdent)
- salt = x_strdup(spwdent->sp_pwdp);
- else
- salt = x_strdup(pwd->pw_passwd);
- }
+ retval = get_pwd_hash(pamh, name, &pwd, &salt);
data_name = (char *) malloc(sizeof(FAIL_PREFIX) + strlen(name));
if (data_name == NULL) {
@@ -648,20 +580,13 @@ int _unix_verify_password(pam_handle_t * pamh, const char *name
strcpy(data_name + sizeof(FAIL_PREFIX) - 1, name);
}
- retval = PAM_SUCCESS;
- if (pwd == NULL || salt == NULL || !strcmp(salt, "x") || ((salt[0] == '#') && (salt[1] == '#') && !strcmp(salt + 2, name))) {
-
- if (pwd != NULL && (geteuid() || SELINUX_ENABLED)) {
- /* we are not root perhaps this is the reason? Run helper */
+ if (retval != PAM_SUCCESS) {
+ if (retval == PAM_UNIX_RUN_HELPER) {
D(("running helper binary"));
retval = _unix_run_helper_binary(pamh, p, ctrl, name);
} else {
D(("user's record unavailable"));
p = NULL;
- if (pwd == NULL)
- retval = PAM_USER_UNKNOWN;
- else
- retval = PAM_AUTHINFO_UNAVAIL;
if (on(UNIX_AUDIT, ctrl)) {
/* this might be a typo and the user has given a password
instead of a username. Careful with this. */
@@ -679,48 +604,7 @@ int _unix_verify_password(pam_handle_t * pamh, const char *name
}
}
} else {
- size_t salt_len = strlen(salt);
- if (!salt_len) {
- /* the stored password is NULL */
- if (off(UNIX__NONULL, ctrl)) {/* this means we've succeeded */
- D(("user has empty password - access granted"));
- retval = PAM_SUCCESS;
- } else {
- D(("user has empty password - access denied"));
- retval = PAM_AUTH_ERR;
- }
- } else if (!p || *salt == '*' || *salt == '!') {
- retval = PAM_AUTH_ERR;
- } else {
- if (!strncmp(salt, "$1$", 3)) {
- pp = Goodcrypt_md5(p, salt);
- if (pp && strcmp(pp, salt) != 0) {
- _pam_delete(pp);
- pp = Brokencrypt_md5(p, salt);
- }
- } else if (*salt != '$' && salt_len >= 13) {
- pp = bigcrypt(p, salt);
- if (pp && salt_len == 13 && strlen(pp) > salt_len) {
- _pam_overwrite(pp + salt_len);
- }
- } else {
- /*
- * Ok, we don't know the crypt algorithm, but maybe
- * libcrypt nows about it? We should try it.
- */
- pp = x_strdup (crypt(p, salt));
- }
- p = NULL; /* no longer needed here */
-
- /* the moment of truth -- do we agree with the password? */
- D(("comparing state of pp[%s] and salt[%s]", pp, salt));
-
- if (pp && strcmp(pp, salt) == 0) {
- retval = PAM_SUCCESS;
- } else {
- retval = PAM_AUTH_ERR;
- }
- }
+ retval = verify_pwd_hash(p, salt, off(UNIX__NONULL, ctrl));
}
if (retval == PAM_SUCCESS) {
@@ -809,8 +693,6 @@ cleanup:
_pam_delete(data_name);
if (salt)
_pam_delete(salt);
- if (pp)
- _pam_delete(pp);
D(("done [%d].", retval));
@@ -971,26 +853,12 @@ int _unix_read_password(pam_handle_t * pamh
return PAM_SUCCESS;
}
-int _unix_shadowed(const struct passwd *pwd)
-{
- if (pwd != NULL) {
- if (strcmp(pwd->pw_passwd, "x") == 0) {
- return 1;
- }
- if ((pwd->pw_passwd[0] == '#') &&
- (pwd->pw_passwd[1] == '#') &&
- (strcmp(pwd->pw_name, pwd->pw_passwd + 2) == 0)) {
- return 1;
- }
- }
- return 0;
-}
-
/* ****************************************************************** *
* Copyright (c) Jan Rêkorajski 1999.
* Copyright (c) Andrew G. Morgan 1996-8.
* Copyright (c) Alex O. Yuriev, 1996.
* Copyright (c) Cristian Gafton 1996.
+ * Copyright (c) Red Hat, Inc. 2007.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
diff --git a/Linux-PAM/modules/pam_unix/support.h b/Linux-PAM/modules/pam_unix/support.h
index 1eb662fc..3ccdc5c7 100644
--- a/Linux-PAM/modules/pam_unix/support.h
+++ b/Linux-PAM/modules/pam_unix/support.h
@@ -1,5 +1,5 @@
/*
- * $Id: support.h,v 1.12 2005/09/26 14:27:09 t8m Exp $
+ * $Id: support.h,v 1.14 2008/01/23 15:35:13 t8m Exp $
*/
#ifndef _PAM_UNIX_SUPPORT_H
@@ -84,8 +84,12 @@ typedef struct {
#define UNIX_NOREAP 21 /* don't reap child process */
#define UNIX_BROKEN_SHADOW 22 /* ignore errors reading password aging
* information during acct management */
+#define UNIX_SHA256_PASS 23 /* new password hashes will use SHA256 */
+#define UNIX_SHA512_PASS 24 /* new password hashes will use SHA512 */
+#define UNIX_ALGO_ROUNDS 25 /* optional number of rounds for new
+ password hash algorithms */
/* -------------- */
-#define UNIX_CTRLS_ 23 /* number of ctrl arguments defined */
+#define UNIX_CTRLS_ 26 /* number of ctrl arguments defined */
static const UNIX_Ctrls unix_args[UNIX_CTRLS_] =
@@ -116,6 +120,9 @@ static const UNIX_Ctrls unix_args[UNIX_CTRLS_] =
/* UNIX_REMEMBER_PASSWD */ {"remember=", _ALL_ON_, 02000000},
/* UNIX_NOREAP */ {"noreap", _ALL_ON_, 04000000},
/* UNIX_BROKEN_SHADOW */ {"broken_shadow", _ALL_ON_, 010000000},
+/* UNIX_SHA256_PASS */ {"sha256", _ALL_ON_^(040420000), 020000000},
+/* UNIX_SHA512_PASS */ {"sha512", _ALL_ON_^(020420000), 040000000},
+/* UNIX_ALGO_ROUNDS */ {"rounds=", _ALL_ON_, 0100000000},
};
#define UNIX_DEFAULTS (unix_args[UNIX__NONULL].flag)
@@ -131,8 +138,8 @@ static const UNIX_Ctrls unix_args[UNIX_CTRLS_] =
extern int _make_remark(pam_handle_t * pamh, unsigned int ctrl
,int type, const char *text);
-extern int _set_ctrl(pam_handle_t * pamh, int flags, int *remember, int argc,
- const char **argv);
+extern int _set_ctrl(pam_handle_t * pamh, int flags, int *remember, int *rounds,
+ int argc, const char **argv);
extern int _unix_getpwnam (pam_handle_t *pamh,
const char *name, int files, int nis,
struct passwd **ret);
@@ -149,7 +156,7 @@ extern int _unix_read_password(pam_handle_t * pamh
,const char *prompt2
,const char *data_name
,const void **pass);
-extern int _unix_shadowed(const struct passwd *pwd);
-extern struct spwd *_unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user);
+extern int _unix_run_verify_binary(pam_handle_t *pamh,
+ unsigned int ctrl, const char *user, int *daysleft);
#endif /* _PAM_UNIX_SUPPORT_H */
diff --git a/Linux-PAM/modules/pam_unix/unix_chkpwd.8 b/Linux-PAM/modules/pam_unix/unix_chkpwd.8
index 02ccfe4a..6c7f0647 100644
--- a/Linux-PAM/modules/pam_unix/unix_chkpwd.8
+++ b/Linux-PAM/modules/pam_unix/unix_chkpwd.8
@@ -1,80 +1,38 @@
-.\" Copyright (C) 2003 International Business Machines Corporation
-.\" This file is distributed according to the GNU General Public License.
-.\" See the file COPYING in the top level source directory for details.
+.\" Title: unix_chkpwd
+.\" Author:
+.\" Generator: DocBook XSL Stylesheets v1.73.1 <http://docbook.sf.net/>
+.\" Date: 01/28/2008
+.\" Manual: Linux-PAM Manual
+.\" Source: Linux-PAM Manual
.\"
-.de Sh \" Subsection
-.br
-.if t .Sp
-.ne 5
-.PP
-\fB\\$1\fR
-.PP
-..
-.de Sp \" Vertical space (when we can't use .PP)
-.if t .sp .5v
-.if n .sp
-..
-.de Ip \" List item
-.br
-.ie \\n(.$>=3 .ne \\$3
-.el .ne 3
-.IP "\\$1" \\$2
-..
-.TH "UNIX_CHKPWD" 8 "2003-03-21" "Linux-PAM 0.76" "Linux-PAM Manual"
-.SH NAME
-unix_chkpwd \- helper binary that verifies the password of the current user
-.SH "SYNOPSIS"
+.TH "UNIX_CHKPWD" "8" "01/28/2008" "Linux-PAM Manual" "Linux\-PAM Manual"
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
.ad l
-.hy 0
-
-/sbin/unix_chkpwd [\fIusername\fR]
-.sp
-.ad
-.hy
+.SH "NAME"
+unix_chkpwd - Helper binary that verifies the password of the current user
+.SH "SYNOPSIS"
+.HP 12
+\fBunix_chkpwd\fR [\.\.\.]
.SH "DESCRIPTION"
.PP
-\fBunix_chkpwd\fR is a helper program for the pam_unix module that verifies
-the password of the current user. It is not intended to be run directly from
-the command line and logs a security violation if done so.
-
-It is typically installed setuid root or setgid shadow.
-.SH "OPTIONS"
+\fIunix_chkpwd\fR
+is a helper program for the
+\fIpam_unix\fR
+module that verifies the password of the current user\. It also checks password and account expiration dates in
+\fIshadow\fR\. It is not intended to be run directly from the command line and logs a security violation if done so\.
.PP
-unix_pwdchk optionally takes the following argument:
-.TP
-\fIusername\fR
-The username of the user whose password you want to check: this must match the current user id.
-
-.SH "INPUTS"
+It is typically installed setuid root or setgid shadow\.
.PP
-unix_pwdchk expects the following inputs via stdin:
-.TP
-\fIoption\fR
-Either nullok or nonull, depending on whether the user can have an empty password.
-.TP
-\fIpassword\fR
-The password to verify.
-
-.SH "RETURN CODES"
-.PP
-\fBunix_chkpwd\fR has the following return codes:
-.TP
-1
-unix_chkpwd was inappropriately called from the command line or the password is incorrect.
-
-.TP
-0
-The password is correct.
-
-.SH "HISTORY"
-Written by Andrew Morgan
-
+The interface of the helper \- command line options, and input/output data format are internal to the
+\fIpam_unix\fR
+module and it should not be called directly from applications\.
.SH "SEE ALSO"
-
.PP
-\fBpam\fR(8)
-
-.SH AUTHOR
-Emily Ratliff.
+\fBpam_unix\fR(8)
+.SH "AUTHOR"
+.PP
+Written by Andrew Morgan and other various people\.
diff --git a/Linux-PAM/modules/pam_unix/unix_chkpwd.8.xml b/Linux-PAM/modules/pam_unix/unix_chkpwd.8.xml
new file mode 100644
index 00000000..a10dbe33
--- /dev/null
+++ b/Linux-PAM/modules/pam_unix/unix_chkpwd.8.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding='UTF-8'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+ "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd">
+
+<refentry id="unix_chkpwd">
+
+ <refmeta>
+ <refentrytitle>unix_chkpwd</refentrytitle>
+ <manvolnum>8</manvolnum>
+ <refmiscinfo class="sectdesc">Linux-PAM Manual</refmiscinfo>
+ </refmeta>
+
+ <refnamediv id="unix_chkpwd-name">
+ <refname>unix_chkpwd</refname>
+ <refpurpose>Helper binary that verifies the password of the current user</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis id="unix_chkpwd-cmdsynopsis">
+ <command>unix_chkpwd</command>
+ <arg choice="opt">
+ ...
+ </arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1 id="unix_chkpwd-description">
+
+ <title>DESCRIPTION</title>
+
+ <para>
+ <emphasis>unix_chkpwd</emphasis> is a helper program for the
+ <emphasis>pam_unix</emphasis> module that verifies the
+ password of the current user. It also checks password and account
+ expiration dates in <emphasis>shadow</emphasis>. It is not intended to
+ be run directly from the command line and logs a security violation if
+ done so.
+ </para>
+
+ <para>
+ It is typically installed setuid root or setgid shadow.
+ </para>
+
+ <para>
+ The interface of the helper - command line options, and input/output
+ data format are internal to the <emphasis>pam_unix</emphasis>
+ module and it should not be called directly from applications.
+ </para>
+ </refsect1>
+
+ <refsect1 id='unix_chkpwd-see_also'>
+ <title>SEE ALSO</title>
+ <para>
+ <citerefentry>
+ <refentrytitle>pam_unix</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>
+ </para>
+ </refsect1>
+
+ <refsect1 id='unix_chkpwd-author'>
+ <title>AUTHOR</title>
+ <para>
+ Written by Andrew Morgan and other various people.
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/Linux-PAM/modules/pam_unix/unix_chkpwd.c b/Linux-PAM/modules/pam_unix/unix_chkpwd.c
index 486a8498..11ac3aac 100644
--- a/Linux-PAM/modules/pam_unix/unix_chkpwd.c
+++ b/Linux-PAM/modules/pam_unix/unix_chkpwd.c
@@ -13,7 +13,6 @@
#include "config.h"
-#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -25,401 +24,34 @@
#include <shadow.h>
#include <signal.h>
#include <time.h>
-#ifdef WITH_SELINUX
-#include <selinux/selinux.h>
-#define SELINUX_ENABLED (selinux_enabled!=-1 ? selinux_enabled : (selinux_enabled=is_selinux_enabled()>0))
-static security_context_t prev_context=NULL;
-static int selinux_enabled=-1;
-#else
-#define SELINUX_ENABLED 0
-#endif
-
-#define MAXPASS 200 /* the maximum length of a password */
#include <security/_pam_types.h>
#include <security/_pam_macros.h>
-#include "md5.h"
-#include "bigcrypt.h"
-
-/* syslogging function for errors and other information */
-
-static void _log_err(int err, const char *format,...)
-{
- va_list args;
-
- va_start(args, format);
- openlog("unix_chkpwd", LOG_CONS | LOG_PID, LOG_AUTHPRIV);
- vsyslog(err, format, args);
- va_end(args);
- closelog();
-}
-
-static int _unix_shadowed(const struct passwd *pwd)
-{
- char hashpass[1024];
- if (pwd != NULL) {
- if (strcmp(pwd->pw_passwd, "x") == 0) {
- return 1;
- }
- if (strlen(pwd->pw_name) < sizeof(hashpass) - 2) {
- strcpy(hashpass, "##");
- strcpy(hashpass + 2, pwd->pw_name);
- if (strcmp(pwd->pw_passwd, hashpass) == 0) {
- return 1;
- }
- }
- }
- return 0;
-}
-
-static void su_sighandler(int sig)
-{
-#ifndef SA_RESETHAND
- /* emulate the behaviour of the SA_RESETHAND flag */
- if ( sig == SIGILL || sig == SIGTRAP || sig == SIGBUS || sig = SIGSERV )
- signal(sig, SIG_DFL);
-#endif
- if (sig > 0) {
- _log_err(LOG_NOTICE, "caught signal %d.", sig);
- exit(sig);
- }
-}
-
-static void setup_signals(void)
-{
- struct sigaction action; /* posix signal structure */
-
- /*
- * Setup signal handlers
- */
- (void) memset((void *) &action, 0, sizeof(action));
- action.sa_handler = su_sighandler;
-#ifdef SA_RESETHAND
- action.sa_flags = SA_RESETHAND;
-#endif
- (void) sigaction(SIGILL, &action, NULL);
- (void) sigaction(SIGTRAP, &action, NULL);
- (void) sigaction(SIGBUS, &action, NULL);
- (void) sigaction(SIGSEGV, &action, NULL);
- action.sa_handler = SIG_IGN;
- action.sa_flags = 0;
- (void) sigaction(SIGTERM, &action, NULL);
- (void) sigaction(SIGHUP, &action, NULL);
- (void) sigaction(SIGINT, &action, NULL);
- (void) sigaction(SIGQUIT, &action, NULL);
-}
+#include "passverify.h"
-static int _verify_account(const char * const uname)
+static int _check_expiry(const char *uname)
{
struct spwd *spent;
struct passwd *pwent;
+ int retval;
+ int daysleft;
- pwent = getpwnam(uname);
- if (!pwent) {
- _log_err(LOG_ALERT, "could not identify user (from getpwnam(%s))", uname);
- return PAM_USER_UNKNOWN;
+ retval = get_account_info(uname, &pwent, &spent);
+ if (retval != PAM_SUCCESS) {
+ helper_log_err(LOG_ALERT, "could not obtain user info (%s)", uname);
+ printf("-1\n");
+ return retval;
}
-
- spent = getspnam( uname );
- if (!spent) {
- _log_err(LOG_ALERT, "could not get username from shadow (%s))", uname);
- return PAM_AUTHINFO_UNAVAIL; /* Couldn't get username from shadow */
+
+ if (spent == NULL) {
+ printf("-1\n");
+ return retval;
}
- printf("%ld:%ld:%ld:%ld:%ld:%ld",
- spent->sp_lstchg, /* last password change */
- spent->sp_min, /* days until change allowed. */
- spent->sp_max, /* days before change required */
- spent->sp_warn, /* days warning for expiration */
- spent->sp_inact, /* days before account inactive */
- spent->sp_expire); /* date when account expires */
-
- return PAM_SUCCESS;
-}
-
-static int _unix_verify_password(const char *name, const char *p, int nullok)
-{
- struct passwd *pwd = NULL;
- struct spwd *spwdent = NULL;
- char *salt = NULL;
- char *pp = NULL;
- int retval = PAM_AUTH_ERR;
- size_t salt_len;
-
- /* UNIX passwords area */
- setpwent();
- pwd = getpwnam(name); /* Get password file entry... */
- endpwent();
- if (pwd != NULL) {
- if (_unix_shadowed(pwd)) {
- /*
- * ...and shadow password file entry for this user,
- * if shadowing is enabled
- */
- setspent();
- spwdent = getspnam(name);
- endspent();
- if (spwdent != NULL)
- salt = x_strdup(spwdent->sp_pwdp);
- else
- pwd = NULL;
- } else {
- if (strcmp(pwd->pw_passwd, "*NP*") == 0) { /* NIS+ */
- uid_t save_uid;
-
- save_uid = geteuid();
- seteuid(pwd->pw_uid);
- spwdent = getspnam(name);
- seteuid(save_uid);
-
- salt = x_strdup(spwdent->sp_pwdp);
- } else {
- salt = x_strdup(pwd->pw_passwd);
- }
- }
- }
- if (pwd == NULL || salt == NULL) {
- _log_err(LOG_WARNING, "check pass; user unknown");
- p = NULL;
- return PAM_USER_UNKNOWN;
- }
-
- salt_len = strlen(salt);
- if (salt_len == 0) {
- return (nullok == 0) ? PAM_AUTH_ERR : PAM_SUCCESS;
- }
- if (p == NULL || strlen(p) == 0) {
- _pam_overwrite(salt);
- _pam_drop(salt);
- return PAM_AUTHTOK_ERR;
- }
-
- /* the moment of truth -- do we agree with the password? */
- retval = PAM_AUTH_ERR;
- if (!strncmp(salt, "$1$", 3)) {
- pp = Goodcrypt_md5(p, salt);
- if (pp && strcmp(pp, salt) == 0) {
- retval = PAM_SUCCESS;
- } else {
- _pam_overwrite(pp);
- _pam_drop(pp);
- pp = Brokencrypt_md5(p, salt);
- if (pp && strcmp(pp, salt) == 0)
- retval = PAM_SUCCESS;
- }
- } else if (*salt == '$') {
- /*
- * Ok, we don't know the crypt algorithm, but maybe
- * libcrypt nows about it? We should try it.
- */
- pp = x_strdup (crypt(p, salt));
- if (pp && strcmp(pp, salt) == 0) {
- retval = PAM_SUCCESS;
- }
- } else if (*salt == '*' || *salt == '!' || salt_len < 13) {
- retval = PAM_AUTH_ERR;
- } else {
- pp = bigcrypt(p, salt);
- /*
- * Note, we are comparing the bigcrypt of the password with
- * the contents of the password field. If the latter was
- * encrypted with regular crypt (and not bigcrypt) it will
- * have been truncated for storage relative to the output
- * of bigcrypt here. As such we need to compare only the
- * stored string with the subset of bigcrypt's result.
- * Bug 521314.
- */
- if (pp && salt_len == 13 && strlen(pp) > salt_len) {
- _pam_overwrite(pp+salt_len);
- }
-
- if (pp && strcmp(pp, salt) == 0) {
- retval = PAM_SUCCESS;
- }
- }
- p = NULL; /* no longer needed here */
-
- /* clean up */
- _pam_overwrite(pp);
- _pam_drop(pp);
-
- return retval;
-}
-
-static char *getuidname(uid_t uid)
-{
- struct passwd *pw;
- static char username[32];
-
- pw = getpwuid(uid);
- if (pw == NULL)
- return NULL;
-
- strncpy(username, pw->pw_name, sizeof(username));
- username[sizeof(username) - 1] = '\0';
-
- return username;
-}
-#define SH_TMPFILE "/etc/nshadow"
-static int _update_shadow(const char *forwho)
-{
- struct spwd *spwdent = NULL, *stmpent = NULL;
- FILE *pwfile, *opwfile;
- int err = 1;
- int oldmask;
- struct stat st;
- char pass[MAXPASS + 1];
- char towhat[MAXPASS + 1];
- int npass=0;
-
- /* read the password from stdin (a pipe from the pam_unix module) */
-
- npass = read(STDIN_FILENO, pass, MAXPASS);
-
- if (npass < 0) { /* is it a valid password? */
-
- _log_err(LOG_DEBUG, "no password supplied");
- return PAM_AUTHTOK_ERR;
-
- } else if (npass >= MAXPASS) {
-
- _log_err(LOG_DEBUG, "password too long");
- return PAM_AUTHTOK_ERR;
-
- } else {
- /* does pass agree with the official one? */
- int retval=0;
- pass[npass] = '\0'; /* NUL terminate */
- retval = _unix_verify_password(forwho, pass, 0);
- if (retval != PAM_SUCCESS) {
+ retval = check_shadow_expiry(spent, &daysleft);
+ printf("%d\n", daysleft);
return retval;
- }
- }
-
- /* read the password from stdin (a pipe from the pam_unix module) */
-
- npass = read(STDIN_FILENO, towhat, MAXPASS);
-
- if (npass < 0) { /* is it a valid password? */
-
- _log_err(LOG_DEBUG, "no new password supplied");
- return PAM_AUTHTOK_ERR;
-
- } else if (npass >= MAXPASS) {
-
- _log_err(LOG_DEBUG, "new password too long");
- return PAM_AUTHTOK_ERR;
-
- }
-
- towhat[npass] = '\0'; /* NUL terminate */
- spwdent = getspnam(forwho);
- if (spwdent == NULL) {
- return PAM_USER_UNKNOWN;
- }
- oldmask = umask(077);
-
-#ifdef WITH_SELINUX
- if (SELINUX_ENABLED) {
- security_context_t shadow_context=NULL;
- if (getfilecon("/etc/shadow",&shadow_context)<0) {
- return PAM_AUTHTOK_ERR;
- };
- if (getfscreatecon(&prev_context)<0) {
- freecon(shadow_context);
- return PAM_AUTHTOK_ERR;
- }
- if (setfscreatecon(shadow_context)) {
- freecon(shadow_context);
- freecon(prev_context);
- return PAM_AUTHTOK_ERR;
- }
- freecon(shadow_context);
- }
-#endif
- pwfile = fopen(SH_TMPFILE, "w");
- umask(oldmask);
- if (pwfile == NULL) {
- err = 1;
- goto done;
- }
-
- opwfile = fopen("/etc/shadow", "r");
- if (opwfile == NULL) {
- fclose(pwfile);
- err = 1;
- goto done;
- }
-
- if (fstat(fileno(opwfile), &st) == -1) {
- fclose(opwfile);
- fclose(pwfile);
- err = 1;
- goto done;
- }
-
- if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
- fclose(opwfile);
- fclose(pwfile);
- err = 1;
- goto done;
- }
- if (fchmod(fileno(pwfile), st.st_mode) == -1) {
- fclose(opwfile);
- fclose(pwfile);
- err = 1;
- goto done;
- }
-
- stmpent = fgetspent(opwfile);
- while (stmpent) {
-
- if (!strcmp(stmpent->sp_namp, forwho)) {
- stmpent->sp_pwdp = towhat;
- stmpent->sp_lstchg = time(NULL) / (60 * 60 * 24);
- err = 0;
- D(("Set password %s for %s", stmpent->sp_pwdp, forwho));
- }
-
- if (putspent(stmpent, pwfile)) {
- D(("error writing entry to shadow file: %m"));
- err = 1;
- break;
- }
-
- stmpent = fgetspent(opwfile);
- }
- fclose(opwfile);
-
- if (fclose(pwfile)) {
- D(("error writing entries to shadow file: %m"));
- err = 1;
- }
-
- done:
- if (!err) {
- if (rename(SH_TMPFILE, "/etc/shadow"))
- err = 1;
- }
-
-#ifdef WITH_SELINUX
- if (SELINUX_ENABLED) {
- if (setfscreatecon(prev_context)) {
- err = 1;
- }
- if (prev_context)
- freecon(prev_context);
- prev_context=NULL;
- }
-#endif
-
- if (!err) {
- return PAM_SUCCESS;
- } else {
- unlink(SH_TMPFILE);
- return PAM_AUTHTOK_ERR;
- }
}
int main(int argc, char *argv[])
@@ -427,9 +59,10 @@ int main(int argc, char *argv[])
char pass[MAXPASS + 1];
char *option;
int npass, nullok;
- int force_failure = 0;
+ int blankpass = 0;
int retval = PAM_AUTH_ERR;
char *user;
+ char *passwords[] = { pass };
/*
* Catch or ignore as many signal as possible.
@@ -446,7 +79,7 @@ int main(int argc, char *argv[])
*/
if (isatty(STDIN_FILENO) || argc != 3 ) {
- _log_err(LOG_NOTICE
+ helper_log_err(LOG_NOTICE
,"inappropriate use of Unix helper binary [UID=%d]"
,getuid());
fprintf(stderr
@@ -458,11 +91,9 @@ int main(int argc, char *argv[])
/*
* Determine what the current user's name is.
- * On a SELinux enabled system with a strict policy leaving the
- * existing check prevents shadow password authentication from working.
* We must thus skip the check if the real uid is 0.
*/
- if (SELINUX_ENABLED && getuid() == 0) {
+ if (getuid() == 0) {
user=argv[1];
}
else {
@@ -476,63 +107,49 @@ int main(int argc, char *argv[])
option=argv[2];
- if (strncmp(argv[2], "verify", 8) == 0) {
- /* Get the account information from the shadow file */
- return _verify_account(argv[1]);
- }
-
- if (strncmp(option, "shadow", 8) == 0) {
- /* Attempting to change the password */
- return _update_shadow(argv[1]);
- }
-
+ if (strcmp(option, "chkexpiry") == 0)
+ /* Check account information from the shadow file */
+ return _check_expiry(argv[1]);
/* read the nullok/nonull option */
- if (strncmp(option, "nullok", 8) == 0)
+ else if (strcmp(option, "nullok") == 0)
nullok = 1;
- else
+ else if (strcmp(option, "nonull") == 0)
nullok = 0;
+ else
+ return PAM_SYSTEM_ERR;
/* read the password from stdin (a pipe from the pam_unix module) */
- npass = read(STDIN_FILENO, pass, MAXPASS);
+ npass = read_passwords(STDIN_FILENO, 1, passwords);
- if (npass < 0) { /* is it a valid password? */
-
- _log_err(LOG_DEBUG, "no password supplied");
-
- } else if (npass >= MAXPASS) {
-
- _log_err(LOG_DEBUG, "password too long");
-
- } else {
- if (npass == 0) {
- /* the password is NULL */
-
- retval = _unix_verify_password(user, NULL, nullok);
-
- } else {
- /* does pass agree with the official one? */
-
- pass[npass] = '\0'; /* NUL terminate */
- retval = _unix_verify_password(user, pass, nullok);
+ if (npass != 1) { /* is it a valid password? */
+ helper_log_err(LOG_DEBUG, "no password supplied");
+ *pass = '\0';
+ }
- }
+ if (*pass == '\0') {
+ blankpass = 1;
}
+ retval = helper_verify_password(user, pass, nullok);
+
memset(pass, '\0', MAXPASS); /* clear memory of the password */
/* return pass or fail */
- if ((retval != PAM_SUCCESS) || force_failure) {
- _log_err(LOG_NOTICE, "password check failed for user (%s)", user);
- return PAM_AUTH_ERR;
+ if (retval != PAM_SUCCESS) {
+ if (!nullok || !blankpass)
+ /* no need to log blank pass test */
+ helper_log_err(LOG_NOTICE, "password check failed for user (%s)", user);
+ return PAM_AUTH_ERR;
} else {
- return PAM_SUCCESS;
+ return PAM_SUCCESS;
}
}
/*
* Copyright (c) Andrew G. Morgan, 1996. All rights reserved
+ * Copyright (c) Red Hat, Inc., 2007,2008. All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
diff --git a/Linux-PAM/modules/pam_unix/unix_update.8 b/Linux-PAM/modules/pam_unix/unix_update.8
new file mode 100644
index 00000000..f0be3de1
--- /dev/null
+++ b/Linux-PAM/modules/pam_unix/unix_update.8
@@ -0,0 +1,37 @@
+.\" Title: unix_update
+.\" Author:
+.\" Generator: DocBook XSL Stylesheets v1.73.1 <http://docbook.sf.net/>
+.\" Date: 01/28/2008
+.\" Manual: Linux-PAM Manual
+.\" Source: Linux-PAM Manual
+.\"
+.TH "UNIX_UPDATE" "8" "01/28/2008" "Linux-PAM Manual" "Linux\-PAM Manual"
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.SH "NAME"
+unix_update - Helper binary that updates the password of a given user
+.SH "SYNOPSIS"
+.HP 12
+\fBunix_update\fR [\.\.\.]
+.SH "DESCRIPTION"
+.PP
+
+\fIunix_update\fR
+is a helper program for the
+\fIpam_unix\fR
+module that updates the password of a given user\. It is not intended to be run directly from the command line and logs a security violation if done so\.
+.PP
+The purpose of the helper is to enable tighter confinement of login and password changing services\. The helper is thus called only when SELinux is enabled and in the enforcing mode on the system\.
+.PP
+The interface of the helper \- command line options, and input/output data format are internal to the
+\fIpam_unix\fR
+module and it should not be called directly from applications\.
+.SH "SEE ALSO"
+.PP
+
+\fBpam_unix\fR(8)
+.SH "AUTHOR"
+.PP
+Written by Tomas Mraz and other various people\.
diff --git a/Linux-PAM/modules/pam_unix/unix_update.8.xml b/Linux-PAM/modules/pam_unix/unix_update.8.xml
new file mode 100644
index 00000000..07695951
--- /dev/null
+++ b/Linux-PAM/modules/pam_unix/unix_update.8.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding='UTF-8'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+ "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd">
+
+<refentry id="unix_update">
+
+ <refmeta>
+ <refentrytitle>unix_update</refentrytitle>
+ <manvolnum>8</manvolnum>
+ <refmiscinfo class="sectdesc">Linux-PAM Manual</refmiscinfo>
+ </refmeta>
+
+ <refnamediv id="unix_update-name">
+ <refname>unix_update</refname>
+ <refpurpose>Helper binary that updates the password of a given user</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis id="unix_update-cmdsynopsis">
+ <command>unix_update</command>
+ <arg choice="opt">
+ ...
+ </arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1 id="unix_update-description">
+
+ <title>DESCRIPTION</title>
+
+ <para>
+ <emphasis>unix_update</emphasis> is a helper program for the
+ <emphasis>pam_unix</emphasis> module that updates the
+ password of a given user. It is not intended to be run directly
+ from the command line and logs a security violation if done so.
+ </para>
+
+ <para>
+ The purpose of the helper is to enable tighter confinement of
+ login and password changing services. The helper is thus called only
+ when SELinux is enabled and in the enforcing mode on the system.
+ </para>
+
+ <para>
+ The interface of the helper - command line options, and input/output
+ data format are internal to the <emphasis>pam_unix</emphasis>
+ module and it should not be called directly from applications.
+ </para>
+ </refsect1>
+
+ <refsect1 id='unix_update-see_also'>
+ <title>SEE ALSO</title>
+ <para>
+ <citerefentry>
+ <refentrytitle>pam_unix</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>
+ </para>
+ </refsect1>
+
+ <refsect1 id='unix_update-author'>
+ <title>AUTHOR</title>
+ <para>
+ Written by Tomas Mraz and other various people.
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/Linux-PAM/modules/pam_unix/unix_update.c b/Linux-PAM/modules/pam_unix/unix_update.c
new file mode 100644
index 00000000..595b7f8b
--- /dev/null
+++ b/Linux-PAM/modules/pam_unix/unix_update.c
@@ -0,0 +1,187 @@
+/*
+ * This program is designed to run setuid(root) or with sufficient
+ * privilege to read all of the unix password databases. It is designed
+ * to provide a mechanism for the current user (defined by this
+ * process' uid) to verify their own password.
+ *
+ * The password is read from the standard input. The exit status of
+ * this program indicates whether the user is authenticated or not.
+ *
+ * Copyright information is located at the end of the file.
+ *
+ */
+
+#include "config.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <shadow.h>
+#include <signal.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include <security/_pam_types.h>
+#include <security/_pam_macros.h>
+
+#include "passverify.h"
+
+static int
+set_password(const char *forwho, const char *shadow, const char *remember)
+{
+ struct passwd *pwd = NULL;
+ int retval;
+ char pass[MAXPASS + 1];
+ char towhat[MAXPASS + 1];
+ int npass = 0;
+ /* we don't care about number format errors because the helper
+ should be called internally only */
+ int doshadow = atoi(shadow);
+ int nremember = atoi(remember);
+ char *passwords[] = { pass, towhat };
+
+ /* read the password from stdin (a pipe from the pam_unix module) */
+
+ npass = read_passwords(STDIN_FILENO, 2, passwords);
+
+ if (npass != 2) { /* is it a valid password? */
+ if (npass == 1) {
+ helper_log_err(LOG_DEBUG, "no new password supplied");
+ memset(pass, '\0', MAXPASS);
+ } else {
+ helper_log_err(LOG_DEBUG, "no valid passwords supplied");
+ }
+ return PAM_AUTHTOK_ERR;
+ }
+
+ if (lock_pwdf() != PAM_SUCCESS)
+ return PAM_AUTHTOK_LOCK_BUSY;
+
+ pwd = getpwnam(forwho);
+
+ if (pwd == NULL) {
+ retval = PAM_USER_UNKNOWN;
+ goto done;
+ }
+
+ /* does pass agree with the official one?
+ we always allow change from null pass */
+ retval = helper_verify_password(forwho, pass, 1);
+ if (retval != PAM_SUCCESS) {
+ goto done;
+ }
+
+ /* first, save old password */
+ if (save_old_password(forwho, pass, nremember)) {
+ retval = PAM_AUTHTOK_ERR;
+ goto done;
+ }
+
+ if (doshadow || is_pwd_shadowed(pwd)) {
+ retval = unix_update_shadow(forwho, towhat);
+ if (retval == PAM_SUCCESS)
+ if (!is_pwd_shadowed(pwd))
+ retval = unix_update_passwd(forwho, "x");
+ } else {
+ retval = unix_update_passwd(forwho, towhat);
+ }
+
+done:
+ memset(pass, '\0', MAXPASS);
+ memset(towhat, '\0', MAXPASS);
+
+ unlock_pwdf();
+
+ if (retval == PAM_SUCCESS) {
+ return PAM_SUCCESS;
+ } else {
+ return PAM_AUTHTOK_ERR;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ char *option;
+
+ /*
+ * Catch or ignore as many signal as possible.
+ */
+ setup_signals();
+
+ /*
+ * we establish that this program is running with non-tty stdin.
+ * this is to discourage casual use. It does *NOT* prevent an
+ * intruder from repeatadly running this program to determine the
+ * password of the current user (brute force attack, but one for
+ * which the attacker must already have gained access to the user's
+ * account).
+ */
+
+ if (isatty(STDIN_FILENO) || argc != 5 ) {
+ helper_log_err(LOG_NOTICE
+ ,"inappropriate use of Unix helper binary [UID=%d]"
+ ,getuid());
+ fprintf(stderr
+ ,"This binary is not designed for running in this way\n"
+ "-- the system administrator has been informed\n");
+ sleep(10); /* this should discourage/annoy the user */
+ return PAM_SYSTEM_ERR;
+ }
+
+ /* We must be root to read/update shadow.
+ */
+ if (geteuid() != 0) {
+ return PAM_CRED_INSUFFICIENT;
+ }
+
+ option = argv[2];
+
+ if (strcmp(option, "update") == 0) {
+ /* Attempting to change the password */
+ return set_password(argv[1], argv[3], argv[4]);
+ }
+
+ return PAM_SYSTEM_ERR;
+}
+
+/*
+ * Copyright (c) Andrew G. Morgan, 1996. All rights reserved
+ * Copyright (c) Red Hat, Inc., 2007, 2008. All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions. (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */