]> granicus.if.org Git - linux-pam/commitdiff
Relevant BUGIDs:
authorTomas Mraz <tm@t8m.info>
Wed, 23 Jan 2008 15:35:12 +0000 (15:35 +0000)
committerTomas Mraz <tm@t8m.info>
Wed, 23 Jan 2008 15:35:12 +0000 (15:35 +0000)
Purpose of commit: cleanup, new feature

Commit summary:
---------------
Merging the the refactorization pam_unix_ref branch into the trunk.
Added support for sha256 and sha512 password hashes to pam_unix
when the libcrypt supports them.

19 files changed:
ChangeLog
NEWS
configure.in
modules/pam_unix/.cvsignore
modules/pam_unix/Makefile.am
modules/pam_unix/pam_unix.8.xml
modules/pam_unix/pam_unix_acct.c
modules/pam_unix/pam_unix_auth.c
modules/pam_unix/pam_unix_passwd.c
modules/pam_unix/pam_unix_sess.c
modules/pam_unix/passverify.c
modules/pam_unix/passverify.h
modules/pam_unix/support.c
modules/pam_unix/support.h
modules/pam_unix/unix_chkpwd.8 [deleted file]
modules/pam_unix/unix_chkpwd.8.xml [new file with mode: 0644]
modules/pam_unix/unix_chkpwd.c
modules/pam_unix/unix_update.8.xml [new file with mode: 0644]
modules/pam_unix/unix_update.c [new file with mode: 0644]

index 4198a142f5bc9c04fd3804ccee472404543a7896..a1fee209fb0d47067a6b764a07535af4201fcf64 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,66 @@
+2008-01-23  Tomas Mraz  <t8m@centrum.cz>
+
+       * modules/pam_unix/Makefile.am: Add unix_update.8 manpage generated from
+       XML, generate also unix_chkpwd.8 from XML.
+       * modules/pam_unix/pam_unix_acct.c: Add rounds parameter to _set_ctrl().
+       * modules/pam_unix/pam_unix_auth.c: Likewise.
+       * modules/pam_unix/pam_unix_sess.c: Likewise.
+       * modules/pam_unix/pam_unix_passwd.c: Likewise.
+       * modules/pam_unix/support.c(_set_ctrl): Likewise.
+       * modules/pam_unix/support.h: Likewise. Add UNIX_SHA256_PASS,
+       UNIX_SHA512_PASS, and UNIX_ALGO_ROUNDS ctrls.
+       (pam_sm_chauthtok): Refactor out new password encryption.
+       * modules/pam_unix/passverify.c(crypt_make_salt): New function.
+       (crypt_md5_wrapper): Call crypt_make_salt().
+       (create_password_hash): New function refactored out of
+       pam_sm_chauthtok(). Support for new password hashes.
+       * modules/pam_unix/passverify.h: Drop ascii_to_bin() and bin_to_ascii()
+       macros. Add prototype for create_password_hash().
+       * modules/pam_unix/unix_update.8.xml: New file.
+       * modules/pam_unix/unix_chkpwd.8.xml: Likewise.
+       
+       * modules/pam_unix/Makefile.am: Add unix_update helper.
+       * modules/pam_unix/pam_unix_passwd.c: Move functions i64c(),
+       crypt_md5_wrapper(), save_old_password(), _update_passwd() and
+       _update_shadow() to passverify.c file. Rename _unix_run_shadow_binary()
+       to _unix_run_update_binary(), which also verifies old password and
+       does all writing.
+       (_do_setpass, pam_sm_chauthtok): lckpwdf()->lock_pwdf(), the same for unlock.
+       Call _unix_run_update_binary() appropriately.
+       _update_passwd()->unix_update_passwd(), the same for shadow.
+       * modules/pam_unix/passverify.c: Add new functions moved from
+       pam_unix_passwd.c and unix_chkpwd.c.
+       * modules/pam_unix/passverify.h: Likewise.
+       * modules/pam_unix/unix_chkpwd.c: Remove SELinux checks. Move
+       su_sighandler(), setup_signals(), getuidname() to passverify.c.
+       (main): Remove 'shadow' option. Refactor out read_passwords() and
+       call it. More strict checking how the binary is called.
+       * modules/pam_unix/unix_update.c: New helper binary - non-setuid,
+       called from SELinux confined apps only.
+
+       * modules/pam_unix/pam_unix_acct.c (_unix_run_verify_binary): Return
+       status and daysleft instead of fake shadow entry.
+       (pam_sm_acct_mgmt): Call _unix_run_verify_binary() appropriately.
+       * modules/pam_unix/pam_unix_passwd.c (_unix_verify_shadow): Call
+       get_account_info() and check_shadow_expiry().
+       * modules/pam_unix/support.h: Adjust _unix_run_verify_binary()
+       prototype.
+       * modules/pam_unix/support.c (_unix_run_helper_binary): Remove check
+       on selinux enabled/disabled.
+       * modules/pam_unix/unix_chkpwd.c (_verify_account): Rename to
+       _check_expiry(), now checks shadow expiry info.
+       (main): Remove check on selinux enabled/disabled. Check shadow
+       expiry through _check_expiry().
+
+       * modules/pam_unix/pam_unix_acct.c (pam_sm_acct_mgmt): Call
+       get_account_info() and check_shadow_expiry().
+       * modules/pam_unix/passverify.c: Add get_account_info() to
+       obtain shadow and passwd entry. Add check_shadow_expiry() to
+       for shadow password expiry check.
+       (get_pwd_hash): Call get_account_info().
+       * modules/pam_unix/passverify.h: Add prototypes for get_account_info()
+       and check_shadow_expiry().
+
 2008-01-08  Thorsten Kukuk  <kukuk@thkukuk.de>
 
        * doc/man/Makefile.am: Fix manual page dependencies,
diff --git a/NEWS b/NEWS
index e794525efcca318bd2bf0c65a32f9cea4387424c..44e93d5a67456963b6c035e69f6234104186e068 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,10 @@ Linux-PAM NEWS -- history of user-visible changes.
 * New module pam_tty_audit.so for enabling and disabling tty
   auditing.
 * New PAM items PAM_XDISPLAY and PAM_XAUTHDATA.
+* Auditing login denials based by origin (pam_access), time (pam_time),
+  and number of sessions (pam_limits) to the Linux audit subsystem.
+* Support sha256 and sha512 algorithms in pam_unix when they are supported
+  by crypt().
 
 Release 0.99.9.0
 * misc_conv no longer blocks SIGINT; applications that don't want
index 146e177a192339c2fc763e9377e36a9301e27a42..cd92f80a6f2aea2c082f4163a6e7cb7435d5d69a 100644 (file)
@@ -352,9 +352,20 @@ AM_CONDITIONAL([HAVE_AUDIT_TTY_STATUS],
 
 BACKUP_LIBS=$LIBS
 AC_SEARCH_LIBS([crypt],[xcrypt crypt], LIBCRYPT="-l$ac_lib", LIBCRYPT="")
+AC_CHECK_FUNCS(crypt_r)
 LIBS=$BACKUP_LIBS
 AC_SUBST(LIBCRYPT)
 
+AC_ARG_WITH([randomdev], AC_HELP_STRING([--with-randomdev=(<path>|yes|no)], [use specified random device instead of /dev/urandom or 'no' to disable]), opt_randomdev=$withval)
+if test "$opt_randomdev" = yes -o -z "$opt_randomdev"; then
+       opt_randomdev="/dev/urandom"
+elif test "$opt_randomdev" = no; then
+       opt_randomdev=
+fi
+if test -n "$opt_randomdev"; then
+       AC_DEFINE_UNQUOTED(PAM_PATH_RANDOMDEV, "$opt_randomdev", [Random device path.])
+fi
+
 dnl check for libdb or libndbm as fallback. Some libndbm compat
 dnl libraries are unuseable, so try libdb first.
 AC_ARG_ENABLE([db],
index 905ba473e6d4931995708f2eadd614f2e2ca1b21..01819c28f37f67542ef8e2538a390785cd8ae90f 100644 (file)
@@ -7,5 +7,8 @@ Makefile
 Makefile.in
 bigcrypt
 unix_chkpwd
+unix_update
 README
 pam_unix.8
+unix_chkpwd.8
+unix_update.8
index a74d97622b1283001812063f8ced287ef5347a59..4d2c58b8982bea7645dcd3b7cb9d26dd13eb5eb4 100644 (file)
@@ -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"
@@ -36,7 +37,7 @@ securelib_LTLIBRARIES = pam_unix.la
 
 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
 
@@ -50,10 +51,16 @@ bigcrypt_LDADD = @LIBCRYPT@
 
 unix_chkpwd_SOURCES = unix_chkpwd.c md5_good.c md5_broken.c bigcrypt.c \
        passverify.c
-unix_chkpwd_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@
+unix_chkpwd_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@ -DHELPER_COMPILE=\"unix_chkpwd\"
 unix_chkpwd_LDFLAGS = @PIE_LDFLAGS@ 
 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
 README: pam_unix.8.xml
index 417579778921744295096dd3189adba98e9c0598..290cb2b9a7c74db898d85fc0c0727271389ba4fe 100644 (file)
           </para>
         </listitem>
       </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>
index aeecb13201956d4f7bd4efb760cad06f64b16350..c09bc175094444370bdc6d4b4bcbe4d1a14b440b 100644 (file)
 #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>
 
 #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;
@@ -79,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."));
 
@@ -118,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);
@@ -127,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);
 
@@ -135,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) {
@@ -147,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");
@@ -175,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.
@@ -196,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;
@@ -215,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);
+                       _("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);
+                       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;
 }
 
 
index 3004bee848eb4e8d15f69a06c538e98a5356bf4e..dfedd608b84086eb9fa11416065f1883ca082132 100644 (file)
@@ -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(). */
index 3a61925e47929a2ff2578e8c2cd8070b3a09703d..432f687f1028c18d0bd536d311fc81a5655435d5 100644 (file)
@@ -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
 
@@ -92,15 +92,6 @@ extern int getrpcport(const char *host, unsigned long prognum,
                      unsigned long versnum, unsigned int proto);
 #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
@@ -109,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)
 {
@@ -218,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;
@@ -248,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 */
 
@@ -271,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"));
@@ -298,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);
@@ -355,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)
@@ -769,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 */
@@ -831,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;
 }
 
@@ -1021,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;
@@ -1031,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
@@ -1240,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;
                }
 
@@ -1282,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;
                }
 
@@ -1297,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"));
@@ -1350,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;
index d8d966870f7d88a9c323511deb8676f44ffbaf18..e984578c56ab1255fc129fd29bc717e5ea76fb4c 100644 (file)
@@ -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) {
index 6587bace7d2cfe8b786294102f30d570635711f7..6fc4dcce8cab699164966c323163f65276209c26 100644 (file)
@@ -7,12 +7,43 @@
 #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>
 
 #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
+
 int
 verify_pwd_hash(const char *p, const char *hash, unsigned int nullok)
 {
@@ -70,7 +101,8 @@ verify_pwd_hash(const char *p, const char *hash, unsigned int nullok)
        return retval;
 }
 
-int _unix_shadowed(const struct passwd *pwd)
+int
+is_pwd_shadowed(const struct passwd *pwd)
 {
        if (pwd != NULL) {
                if (strcmp(pwd->pw_passwd, "x") == 0) {
@@ -85,12 +117,946 @@ int _unix_shadowed(const struct passwd *pwd)
        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;
+    security_context_t prev_context=NULL;
+
+    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;
+    security_context_t prev_context=NULL;
+
+    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;
+    security_context_t prev_context=NULL;
+
+    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. 2007.
+ * 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
index a3ae92106963b05eedccab03678b4b01e705273b..196e0e33c2bdf28ec06dcfc59d0854287ffb13a4 100644 (file)
@@ -1,11 +1,92 @@
 /*
  * 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, const char *hash, unsigned int nullok);
 
 int
-_unix_shadowed(const struct passwd *pwd);
+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.
index 60acc9583983bb6565e5f6a8cb6d9382d1684a8d..b82cad26bba62e50d06baf613739e2061e3ca262 100644 (file)
@@ -52,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;
 
@@ -109,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 */
@@ -375,95 +385,6 @@ int _unix_comesfromsource(pam_handle_t *pamh,
        return _unix_getpwnam(pamh, name, files, nis, NULL);
 }
 
-/*
- * _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
  */
@@ -518,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);
@@ -572,11 +493,65 @@ 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 *data_name;
        int retval;
@@ -595,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) {
@@ -646,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. */
@@ -931,6 +858,7 @@ int _unix_read_password(pam_handle_t * pamh
  * 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
index 94a9b393e21379f87582bb424ca9c56dd77cf2f8..9d4f8b85db0eac37896acc028740f828604cefa7 100644 (file)
@@ -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);
@@ -150,5 +157,6 @@ extern int _unix_read_password(pam_handle_t * pamh
                        ,const char *data_name
                        ,const void **pass);
 
-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/modules/pam_unix/unix_chkpwd.8 b/modules/pam_unix/unix_chkpwd.8
deleted file mode 100644 (file)
index 02ccfe4..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-.\" 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.
-.\"
-.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"
-.ad l
-.hy 0
-
-/sbin/unix_chkpwd [\fIusername\fR]
-.sp
-.ad
-.hy
-.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"
-.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"
-.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
-
-.SH "SEE ALSO"
-
-.PP
-\fBpam\fR(8)
-
-.SH AUTHOR
-Emily Ratliff.
-
diff --git a/modules/pam_unix/unix_chkpwd.8.xml b/modules/pam_unix/unix_chkpwd.8.xml
new file mode 100644 (file)
index 0000000..a10dbe3
--- /dev/null
@@ -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>
index 1e8944e9d6d67f5335e76586f7bac45548b3185d..11ac3aac6354993691596900c4b7964c2680abff 100644 (file)
@@ -13,7 +13,6 @@
 
 #include "config.h"
 
-#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #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 "passverify.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 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);
-}
-
-static int _verify_account(const char * const uname)
+static int _check_expiry(const char *uname)
 {
        struct spwd *spent;
        struct passwd *pwent;
-
-       pwent = getpwnam(uname);
-       if (!pwent) {
-               _log_err(LOG_ALERT, "could not identify user (from getpwnam(%s))", uname);
-               return PAM_USER_UNKNOWN;
-       }
-
-       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 */
-       }
-       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;
-       int retval = PAM_AUTH_ERR;
-
-       /* 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);
-                       }
-               }
+       int retval;
+       int daysleft;
+
+       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;
        }
-       if (pwd == NULL || salt == NULL) {
-               _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);
+       
+       if (spent == NULL) {
+               printf("-1\n");
+               return retval;
        }
 
-       p = NULL;               /* no longer needed here */
-
-       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[])
@@ -355,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.
@@ -374,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
@@ -386,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 {
@@ -404,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/modules/pam_unix/unix_update.8.xml b/modules/pam_unix/unix_update.8.xml
new file mode 100644 (file)
index 0000000..0769595
--- /dev/null
@@ -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/modules/pam_unix/unix_update.c b/modules/pam_unix/unix_update.c
new file mode 100644 (file)
index 0000000..6dc8ace
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * 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>
+#ifdef WITH_SELINUX
+#include <selinux/selinux.h>
+#define SELINUX_ENABLED (selinux_enabled!=-1 ? selinux_enabled : (selinux_enabled=is_selinux_enabled()>0))
+static int selinux_enabled=-1;
+#else
+#define SELINUX_ENABLED 0
+#endif
+
+#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.
+ */