Blame SOURCES/pam-1.3.0-pwhistory-helper.patch

73cfcf
diff -up Linux-PAM-1.3.0/modules/pam_pwhistory/Makefile.am.pwhhelper Linux-PAM-1.3.0/modules/pam_pwhistory/Makefile.am
73cfcf
--- Linux-PAM-1.3.0/modules/pam_pwhistory/Makefile.am.pwhhelper	2016-03-24 12:45:42.000000000 +0100
73cfcf
+++ Linux-PAM-1.3.0/modules/pam_pwhistory/Makefile.am	2016-05-06 15:18:42.307637933 +0200
73cfcf
@@ -1,5 +1,6 @@
73cfcf
 #
73cfcf
 # Copyright (c) 2008, 2009 Thorsten Kukuk <kukuk@suse.de>
73cfcf
+# Copyright (c) 2013 Red Hat, Inc.
73cfcf
 #
73cfcf
 
73cfcf
 CLEANFILES = *~
73cfcf
@@ -9,25 +10,34 @@ EXTRA_DIST = README $(MANS) $(XMLS) tst-
73cfcf
 
73cfcf
 TESTS = tst-pam_pwhistory
73cfcf
 
73cfcf
-man_MANS = pam_pwhistory.8
73cfcf
+man_MANS = pam_pwhistory.8 pwhistory_helper.8
73cfcf
 
73cfcf
-XMLS = README.xml pam_pwhistory.8.xml
73cfcf
+XMLS = README.xml pam_pwhistory.8.xml pwhistory_helper.8.xml
73cfcf
 
73cfcf
 securelibdir = $(SECUREDIR)
73cfcf
 secureconfdir = $(SCONFIGDIR)
73cfcf
 
73cfcf
-AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include
73cfcf
-AM_LDFLAGS = -no-undefined -avoid-version -module
73cfcf
+AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
73cfcf
+	    -DPWHISTORY_HELPER=\"$(sbindir)/pwhistory_helper\"
73cfcf
+
73cfcf
+pam_pwhistory_la_LDFLAGS = -no-undefined -avoid-version -module
73cfcf
 if HAVE_VERSIONING
73cfcf
-  AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
73cfcf
+  pam_pwhistory_la_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
73cfcf
 endif
73cfcf
 
73cfcf
 noinst_HEADERS = opasswd.h
73cfcf
 
73cfcf
 securelib_LTLIBRARIES = pam_pwhistory.la
73cfcf
-pam_pwhistory_la_LIBADD = $(top_builddir)/libpam/libpam.la @LIBCRYPT@
73cfcf
+pam_pwhistory_la_CFLAGS = $(AM_CFLAGS)
73cfcf
+pam_pwhistory_la_LIBADD = $(top_builddir)/libpam/libpam.la @LIBCRYPT@ @LIBSELINUX@
73cfcf
 pam_pwhistory_la_SOURCES = pam_pwhistory.c opasswd.c
73cfcf
 
73cfcf
+sbin_PROGRAMS = pwhistory_helper
73cfcf
+pwhistory_helper_CFLAGS = $(AM_CFLAGS) -DHELPER_COMPILE=\"pwhistory_helper\" @PIE_CFLAGS@
73cfcf
+pwhistory_helper_SOURCES = pwhistory_helper.c opasswd.c
73cfcf
+pwhistory_helper_LDFLAGS = -Wl,-z,now @PIE_LDFLAGS@
73cfcf
+pwhistory_helper_LDADD = $(top_builddir)/libpam/libpam.la @LIBCRYPT@
73cfcf
+
73cfcf
 if ENABLE_REGENERATE_MAN
73cfcf
 noinst_DATA = README
73cfcf
 README: pam_pwhistory.8.xml
73cfcf
diff -up Linux-PAM-1.3.0/modules/pam_pwhistory/opasswd.c.pwhhelper Linux-PAM-1.3.0/modules/pam_pwhistory/opasswd.c
73cfcf
--- Linux-PAM-1.3.0/modules/pam_pwhistory/opasswd.c.pwhhelper	2016-03-24 12:45:42.000000000 +0100
73cfcf
+++ Linux-PAM-1.3.0/modules/pam_pwhistory/opasswd.c	2016-05-06 15:18:42.307637933 +0200
73cfcf
@@ -1,5 +1,6 @@
73cfcf
 /*
73cfcf
  * Copyright (c) 2008 Thorsten Kukuk <kukuk@suse.de>
73cfcf
+ * Copyright (c) 2013 Red Hat, Inc.
73cfcf
  *
73cfcf
  * Redistribution and use in source and binary forms, with or without
73cfcf
  * modification, are permitted provided that the following conditions
73cfcf
@@ -38,6 +39,7 @@
73cfcf
 #endif
73cfcf
 
73cfcf
 #include <pwd.h>
73cfcf
+#include <shadow.h>
73cfcf
 #include <time.h>
73cfcf
 #include <ctype.h>
73cfcf
 #include <errno.h>
73cfcf
@@ -47,6 +49,7 @@
73cfcf
 #include <string.h>
73cfcf
 #include <stdlib.h>
73cfcf
 #include <syslog.h>
73cfcf
+#include <stdarg.h>
73cfcf
 #include <sys/stat.h>
73cfcf
 
73cfcf
 #if defined (HAVE_XCRYPT_H)
73cfcf
@@ -55,7 +58,14 @@
73cfcf
 #include <crypt.h>
73cfcf
 #endif
73cfcf
 
73cfcf
+#ifdef HELPER_COMPILE
73cfcf
+#define pam_modutil_getpwnam(h,n) getpwnam(n)
73cfcf
+#define pam_modutil_getspnam(h,n) getspnam(n)
73cfcf
+#define pam_syslog(h,a,...) helper_log_err(a,__VA_ARGS__)
73cfcf
+#else
73cfcf
+#include <security/pam_modutil.h>
73cfcf
 #include <security/pam_ext.h>
73cfcf
+#endif
73cfcf
 #include <security/pam_modules.h>
73cfcf
 
73cfcf
 #include "opasswd.h"
73cfcf
@@ -76,6 +86,19 @@ typedef struct {
73cfcf
   char *old_passwords;
73cfcf
 } opwd;
73cfcf
 
73cfcf
+#ifdef HELPER_COMPILE
73cfcf
+void
73cfcf
+helper_log_err(int err, const char *format, ...)
73cfcf
+{
73cfcf
+  va_list args;
73cfcf
+
73cfcf
+  va_start(args, format);
73cfcf
+  openlog(HELPER_COMPILE, LOG_CONS | LOG_PID, LOG_AUTHPRIV);
73cfcf
+  vsyslog(err, format, args);
73cfcf
+  va_end(args);
73cfcf
+  closelog();
73cfcf
+}
73cfcf
+#endif
73cfcf
 
73cfcf
 static int
73cfcf
 parse_entry (char *line, opwd *data)
73cfcf
@@ -117,8 +140,8 @@ compare_password(const char *newpass, co
73cfcf
 }
73cfcf
 
73cfcf
 /* Check, if the new password is already in the opasswd file.  */
73cfcf
-int
73cfcf
-check_old_pass (pam_handle_t *pamh, const char *user,
73cfcf
+PAMH_ARG_DECL(int
73cfcf
+check_old_pass, const char *user,
73cfcf
 		const char *newpass, int debug)
73cfcf
 {
73cfcf
   int retval = PAM_SUCCESS;
73cfcf
@@ -128,6 +151,11 @@ check_old_pass (pam_handle_t *pamh, cons
73cfcf
   opwd entry;
73cfcf
   int found = 0;
73cfcf
 
73cfcf
+#ifndef HELPER_COMPILE
73cfcf
+  if (SELINUX_ENABLED)
73cfcf
+    return PAM_PWHISTORY_RUN_HELPER;
73cfcf
+#endif
73cfcf
+
73cfcf
   if ((oldpf = fopen (OLD_PASSWORDS_FILE, "r")) == NULL)
73cfcf
     {
73cfcf
       if (errno != ENOENT)
73cfcf
@@ -213,9 +241,9 @@ check_old_pass (pam_handle_t *pamh, cons
73cfcf
   return retval;
73cfcf
 }
73cfcf
 
73cfcf
-int
73cfcf
-save_old_pass (pam_handle_t *pamh, const char *user, uid_t uid,
73cfcf
-	       const char *oldpass, int howmany, int debug UNUSED)
73cfcf
+PAMH_ARG_DECL(int
73cfcf
+save_old_pass, const char *user,
73cfcf
+	       int howmany, int debug UNUSED)
73cfcf
 {
73cfcf
   char opasswd_tmp[] = TMP_PASSWORDS_FILE;
73cfcf
   struct stat opasswd_stat;
73cfcf
@@ -226,10 +254,35 @@ save_old_pass (pam_handle_t *pamh, const
73cfcf
   char *buf = NULL;
73cfcf
   size_t buflen = 0;
73cfcf
   int found = 0;
73cfcf
+  struct passwd *pwd;
73cfcf
+  const char *oldpass;
73cfcf
+
73cfcf
+  pwd = pam_modutil_getpwnam (pamh, user);
73cfcf
+  if (pwd == NULL)
73cfcf
+    return PAM_USER_UNKNOWN;
73cfcf
 
73cfcf
   if (howmany <= 0)
73cfcf
     return PAM_SUCCESS;
73cfcf
 
73cfcf
+#ifndef HELPER_COMPILE
73cfcf
+  if (SELINUX_ENABLED)
73cfcf
+    return PAM_PWHISTORY_RUN_HELPER;
73cfcf
+#endif
73cfcf
+
73cfcf
+  if ((strcmp(pwd->pw_passwd, "x") == 0)  ||
73cfcf
+      ((pwd->pw_passwd[0] == '#') &&
73cfcf
+       (pwd->pw_passwd[1] == '#') &&
73cfcf
+       (strcmp(pwd->pw_name, pwd->pw_passwd + 2) == 0)))
73cfcf
+    {
73cfcf
+      struct spwd *spw = pam_modutil_getspnam (pamh, user);
73cfcf
+
73cfcf
+      if (spw == NULL)
73cfcf
+        return PAM_USER_UNKNOWN;
73cfcf
+      oldpass = spw->sp_pwdp;
73cfcf
+    }
73cfcf
+  else
73cfcf
+      oldpass = pwd->pw_passwd;
73cfcf
+
73cfcf
   if (oldpass == NULL || *oldpass == '\0')
73cfcf
     return PAM_SUCCESS;
73cfcf
 
73cfcf
@@ -452,7 +505,7 @@ save_old_pass (pam_handle_t *pamh, const
73cfcf
     {
73cfcf
       char *out;
73cfcf
 
73cfcf
-      if (asprintf (&out, "%s:%d:1:%s\n", user, uid, oldpass) < 0)
73cfcf
+      if (asprintf (&out, "%s:%d:1:%s\n", user, pwd->pw_uid, oldpass) < 0)
73cfcf
 	{
73cfcf
 	  retval = PAM_AUTHTOK_ERR;
73cfcf
 	  if (oldpf)
73cfcf
diff -up Linux-PAM-1.3.0/modules/pam_pwhistory/opasswd.h.pwhhelper Linux-PAM-1.3.0/modules/pam_pwhistory/opasswd.h
73cfcf
--- Linux-PAM-1.3.0/modules/pam_pwhistory/opasswd.h.pwhhelper	2016-03-24 12:45:42.000000000 +0100
73cfcf
+++ Linux-PAM-1.3.0/modules/pam_pwhistory/opasswd.h	2016-05-06 15:18:42.307637933 +0200
73cfcf
@@ -1,5 +1,6 @@
73cfcf
 /*
73cfcf
  * Copyright (c) 2008 Thorsten Kukuk <kukuk@suse.de>
73cfcf
+ * Copyright (c) 2013 Red Hat, Inc.
73cfcf
  *
73cfcf
  * Redistribution and use in source and binary forms, with or without
73cfcf
  * modification, are permitted provided that the following conditions
73cfcf
@@ -36,10 +37,32 @@
73cfcf
 #ifndef __OPASSWD_H__
73cfcf
 #define __OPASSWD_H__
73cfcf
 
73cfcf
-extern int check_old_pass (pam_handle_t *pamh, const char *user,
73cfcf
-			   const char *newpass, int debug);
73cfcf
-extern int save_old_pass (pam_handle_t *pamh, const char *user,
73cfcf
-			  uid_t uid, const char *oldpass,
73cfcf
-			  int howmany, int debug);
73cfcf
+#define PAM_PWHISTORY_RUN_HELPER PAM_CRED_INSUFFICIENT
73cfcf
+
73cfcf
+#ifdef WITH_SELINUX
73cfcf
+#include <selinux/selinux.h>
73cfcf
+#define SELINUX_ENABLED is_selinux_enabled()>0
73cfcf
+#else
73cfcf
+#define SELINUX_ENABLED 0
73cfcf
+#endif
73cfcf
+
73cfcf
+#ifdef HELPER_COMPILE
73cfcf
+#define PAMH_ARG_DECL(fname, ...) fname(__VA_ARGS__)
73cfcf
+#define PAMH_ARG(...)               __VA_ARGS__
73cfcf
+#else
73cfcf
+#define PAMH_ARG_DECL(fname, ...) fname(pam_handle_t *pamh, __VA_ARGS__)
73cfcf
+#define PAMH_ARG(...)               pamh, __VA_ARGS__
73cfcf
+#endif
73cfcf
+
73cfcf
+#ifdef HELPER_COMPILE
73cfcf
+void
73cfcf
+helper_log_err(int err, const char *format, ...);
73cfcf
+#endif
73cfcf
+
73cfcf
+PAMH_ARG_DECL(int
73cfcf
+check_old_pass, const char *user, const char *newpass, int debug);
73cfcf
+
73cfcf
+PAMH_ARG_DECL(int
73cfcf
+save_old_pass, const char *user, int howmany, int debug);
73cfcf
 
73cfcf
 #endif /* __OPASSWD_H__ */
73cfcf
diff -up Linux-PAM-1.3.0/modules/pam_pwhistory/pam_pwhistory.c.pwhhelper Linux-PAM-1.3.0/modules/pam_pwhistory/pam_pwhistory.c
73cfcf
--- Linux-PAM-1.3.0/modules/pam_pwhistory/pam_pwhistory.c.pwhhelper	2016-04-04 11:22:28.000000000 +0200
73cfcf
+++ Linux-PAM-1.3.0/modules/pam_pwhistory/pam_pwhistory.c	2016-05-06 15:19:31.610785512 +0200
73cfcf
@@ -1,6 +1,7 @@
73cfcf
 /*
73cfcf
  * Copyright (c) 2008, 2012 Thorsten Kukuk
73cfcf
  * Author: Thorsten Kukuk <kukuk@thkukuk.de>
73cfcf
+ * Copyright (c) 2013 Red Hat, Inc.
73cfcf
  *
73cfcf
  * Redistribution and use in source and binary forms, with or without
73cfcf
  * modification, are permitted provided that the following conditions
73cfcf
@@ -46,10 +47,14 @@
73cfcf
 #include <stdlib.h>
73cfcf
 #include <string.h>
73cfcf
 #include <unistd.h>
73cfcf
-#include <shadow.h>
73cfcf
 #include <syslog.h>
73cfcf
 #include <sys/types.h>
73cfcf
 #include <sys/stat.h>
73cfcf
+#include <sys/time.h>
73cfcf
+#include <sys/resource.h>
73cfcf
+#include <sys/wait.h>
73cfcf
+#include <signal.h>
73cfcf
+#include <fcntl.h>
73cfcf
 
73cfcf
 #include <security/pam_modules.h>
73cfcf
 #include <security/pam_modutil.h>
73cfcf
@@ -59,6 +64,7 @@
73cfcf
 #include "opasswd.h"
73cfcf
 
73cfcf
 #define DEFAULT_BUFLEN 2048
73cfcf
+#define MAX_FD_NO 20000
73cfcf
 
73cfcf
 struct options_t {
73cfcf
   int debug;
73cfcf
@@ -102,6 +108,184 @@ parse_option (pam_handle_t *pamh, const
73cfcf
     pam_syslog (pamh, LOG_ERR, "pam_pwhistory: unknown option: %s", argv);
73cfcf
 }
73cfcf
 
73cfcf
+static int
73cfcf
+run_save_helper(pam_handle_t *pamh, const char *user,
73cfcf
+		int howmany, int debug)
73cfcf
+{
73cfcf
+  int retval, child;
73cfcf
+  struct sigaction newsa, oldsa;
73cfcf
+
73cfcf
+  memset(&newsa, '\0', sizeof(newsa));
73cfcf
+  newsa.sa_handler = SIG_DFL;
73cfcf
+  sigaction(SIGCHLD, &newsa, &oldsa);
73cfcf
+
73cfcf
+  child = fork();
73cfcf
+  if (child == 0)
73cfcf
+    {
73cfcf
+      int i = 0;
73cfcf
+      struct rlimit rlim;
73cfcf
+      int dummyfds[2];
73cfcf
+      static char *envp[] = { NULL };
73cfcf
+      char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL };
73cfcf
+
73cfcf
+      /* replace std file descriptors with a dummy pipe */
73cfcf
+      if (pipe2(dummyfds, O_NONBLOCK) == 0)
73cfcf
+        {
73cfcf
+          dup2(dummyfds[0], STDIN_FILENO);
73cfcf
+          dup2(dummyfds[1], STDOUT_FILENO);
73cfcf
+          dup2(dummyfds[1], STDERR_FILENO);
73cfcf
+        }
73cfcf
+
73cfcf
+      if (getrlimit(RLIMIT_NOFILE,&rlim) == 0)
73cfcf
+        {
73cfcf
+          if (rlim.rlim_max >= MAX_FD_NO)
73cfcf
+            rlim.rlim_max = MAX_FD_NO;
73cfcf
+	  for (i = STDERR_FILENO + 1; i < (int)rlim.rlim_max; i++)
73cfcf
+             {
73cfcf
+		if (i != dummyfds[0])
73cfcf
+		  close(i);
73cfcf
+	     }
73cfcf
+	}
73cfcf
+
73cfcf
+      /* exec binary helper */
73cfcf
+      args[0] = strdup(PWHISTORY_HELPER);
73cfcf
+      args[1] = strdup("save");
73cfcf
+      args[2] = x_strdup(user);
73cfcf
+      asprintf(&args[3], "%d", howmany);
73cfcf
+      asprintf(&args[4], "%d", debug);
73cfcf
+
73cfcf
+      execve(args[0], args, envp);
73cfcf
+
73cfcf
+      _exit(PAM_SYSTEM_ERR);
73cfcf
+    }
73cfcf
+  else if (child > 0)
73cfcf
+    {
73cfcf
+      /* wait for child */
73cfcf
+      int rc = 0;
73cfcf
+      rc = waitpid(child, &retval, 0);  /* wait for helper to complete */
73cfcf
+      if (rc < 0)
73cfcf
+        {
73cfcf
+	  pam_syslog(pamh, LOG_ERR, "pwhistory_helper save waitpid returned %d: %m", rc);
73cfcf
+	  retval = PAM_SYSTEM_ERR;
73cfcf
+	}
73cfcf
+      else if (!WIFEXITED(retval))
73cfcf
+        {
73cfcf
+	  pam_syslog(pamh, LOG_ERR, "pwhistory_helper save abnormal exit: %d", retval);
73cfcf
+	  retval = PAM_SYSTEM_ERR;
73cfcf
+	}
73cfcf
+      else
73cfcf
+        {
73cfcf
+	  retval = WEXITSTATUS(retval);
73cfcf
+	}
73cfcf
+    } 
73cfcf
+  else
73cfcf
+    {
73cfcf
+	retval = PAM_SYSTEM_ERR;
73cfcf
+    }
73cfcf
+
73cfcf
+  sigaction(SIGCHLD, &oldsa, NULL);   /* restore old signal handler */
73cfcf
+
73cfcf
+  return retval;
73cfcf
+}
73cfcf
+
73cfcf
+static int
73cfcf
+run_check_helper(pam_handle_t *pamh, const char *user,
73cfcf
+		 const char *newpass, int debug)
73cfcf
+{
73cfcf
+  int retval, child, fds[2];
73cfcf
+  struct sigaction newsa, oldsa;
73cfcf
+
73cfcf
+  /* create a pipe for the password */
73cfcf
+  if (pipe(fds) != 0)
73cfcf
+    return PAM_SYSTEM_ERR;
73cfcf
+
73cfcf
+  memset(&newsa, '\0', sizeof(newsa));
73cfcf
+  newsa.sa_handler = SIG_DFL;
73cfcf
+  sigaction(SIGCHLD, &newsa, &oldsa);
73cfcf
+
73cfcf
+  child = fork();
73cfcf
+  if (child == 0)
73cfcf
+    {
73cfcf
+      int i = 0;
73cfcf
+      struct rlimit rlim;
73cfcf
+      int dummyfds[2];
73cfcf
+      static char *envp[] = { NULL };
73cfcf
+      char *args[] = { NULL, NULL, NULL, NULL, NULL };
73cfcf
+
73cfcf
+      /* reopen stdin as pipe */
73cfcf
+      dup2(fds[0], STDIN_FILENO);
73cfcf
+
73cfcf
+      /* replace std file descriptors with a dummy pipe */
73cfcf
+      if (pipe2(dummyfds, O_NONBLOCK) == 0)
73cfcf
+        {
73cfcf
+          dup2(dummyfds[1], STDOUT_FILENO);
73cfcf
+          dup2(dummyfds[1], STDERR_FILENO);
73cfcf
+        }
73cfcf
+
73cfcf
+      if (getrlimit(RLIMIT_NOFILE,&rlim) == 0)
73cfcf
+        {
73cfcf
+          if (rlim.rlim_max >= MAX_FD_NO)
73cfcf
+            rlim.rlim_max = MAX_FD_NO;
73cfcf
+	  for (i = STDERR_FILENO + 1; i < (int)rlim.rlim_max; i++)
73cfcf
+             {
73cfcf
+		if (i != dummyfds[0])
73cfcf
+		  close(i);
73cfcf
+	     }
73cfcf
+	}
73cfcf
+
73cfcf
+      /* exec binary helper */
73cfcf
+      args[0] = strdup(PWHISTORY_HELPER);
73cfcf
+      args[1] = strdup("check");
73cfcf
+      args[2] = x_strdup(user);
73cfcf
+      asprintf(&args[3], "%d", debug);
73cfcf
+
73cfcf
+      execve(args[0], args, envp);
73cfcf
+
73cfcf
+      _exit(PAM_SYSTEM_ERR);
73cfcf
+    }
73cfcf
+  else if (child > 0)
73cfcf
+    {
73cfcf
+      /* wait for child */
73cfcf
+      int rc = 0;
73cfcf
+      if (newpass == NULL)
73cfcf
+        newpass = "";
73cfcf
+ 
73cfcf
+      /* send the password to the child */
73cfcf
+      if (write(fds[1], newpass, strlen(newpass)+1) == -1)
73cfcf
+        {
73cfcf
+	  pam_syslog(pamh, LOG_ERR, "Cannot send password to helper: %m");
73cfcf
+	  retval = PAM_SYSTEM_ERR;
73cfcf
+	}
73cfcf
+      newpass = NULL;
73cfcf
+      close(fds[0]);       /* close here to avoid possible SIGPIPE above */
73cfcf
+      close(fds[1]);
73cfcf
+      rc = waitpid(child, &retval, 0);  /* wait for helper to complete */
73cfcf
+      if (rc < 0)
73cfcf
+        {
73cfcf
+	  pam_syslog(pamh, LOG_ERR, "pwhistory_helper check waitpid returned %d: %m", rc);
73cfcf
+	  retval = PAM_SYSTEM_ERR;
73cfcf
+	}
73cfcf
+      else if (!WIFEXITED(retval))
73cfcf
+        {
73cfcf
+	  pam_syslog(pamh, LOG_ERR, "pwhistory_helper check abnormal exit: %d", retval);
73cfcf
+	  retval = PAM_SYSTEM_ERR;
73cfcf
+	}
73cfcf
+      else
73cfcf
+        {
73cfcf
+	  retval = WEXITSTATUS(retval);
73cfcf
+	}
73cfcf
+    } 
73cfcf
+  else
73cfcf
+    {
73cfcf
+	close(fds[0]);
73cfcf
+	close(fds[1]);
73cfcf
+	retval = PAM_SYSTEM_ERR;
73cfcf
+    }
73cfcf
+
73cfcf
+  sigaction(SIGCHLD, &oldsa, NULL);   /* restore old signal handler */
73cfcf
+
73cfcf
+  return retval;
73cfcf
+}
73cfcf
 
73cfcf
 /* This module saves the current crypted password in /etc/security/opasswd
73cfcf
    and then compares the new password with all entries in this file. */
73cfcf
@@ -109,7 +293,6 @@ parse_option (pam_handle_t *pamh, const
73cfcf
 int
73cfcf
 pam_sm_chauthtok (pam_handle_t *pamh, int flags, int argc, const char **argv)
73cfcf
 {
73cfcf
-  struct passwd *pwd;
73cfcf
   const char *newpass;
73cfcf
   const char *user;
73cfcf
     int retval, tries;
73cfcf
@@ -154,31 +337,13 @@ pam_sm_chauthtok (pam_handle_t *pamh, in
73cfcf
       return PAM_SUCCESS;
73cfcf
     }
73cfcf
 
73cfcf
-  pwd = pam_modutil_getpwnam (pamh, user);
73cfcf
-  if (pwd == NULL)
73cfcf
-    return PAM_USER_UNKNOWN;
73cfcf
-
73cfcf
-  if ((strcmp(pwd->pw_passwd, "x") == 0)  ||
73cfcf
-      ((pwd->pw_passwd[0] == '#') &&
73cfcf
-       (pwd->pw_passwd[1] == '#') &&
73cfcf
-       (strcmp(pwd->pw_name, pwd->pw_passwd + 2) == 0)))
73cfcf
-    {
73cfcf
-      struct spwd *spw = pam_modutil_getspnam (pamh, user);
73cfcf
-      if (spw == NULL)
73cfcf
-	return PAM_USER_UNKNOWN;
73cfcf
+  retval = save_old_pass (pamh, user, options.remember, options.debug);
73cfcf
 
73cfcf
-      retval = save_old_pass (pamh, user, pwd->pw_uid, spw->sp_pwdp,
73cfcf
-			      options.remember, options.debug);
73cfcf
-      if (retval != PAM_SUCCESS)
73cfcf
-	return retval;
73cfcf
-    }
73cfcf
-  else
73cfcf
-    {
73cfcf
-      retval = save_old_pass (pamh, user, pwd->pw_uid, pwd->pw_passwd,
73cfcf
-			      options.remember, options.debug);
73cfcf
-      if (retval != PAM_SUCCESS)
73cfcf
-	return retval;
73cfcf
-    }
73cfcf
+  if (retval == PAM_PWHISTORY_RUN_HELPER) 
73cfcf
+      retval = run_save_helper(pamh, user, options.remember, options.debug);
73cfcf
+
73cfcf
+  if (retval != PAM_SUCCESS)
73cfcf
+    return retval;
73cfcf
 
73cfcf
   newpass = NULL;
73cfcf
   tries = 0;
73cfcf
@@ -207,8 +372,11 @@ pam_sm_chauthtok (pam_handle_t *pamh, in
73cfcf
       if (options.debug)
73cfcf
 	pam_syslog (pamh, LOG_DEBUG, "check against old password file");
73cfcf
 
73cfcf
-      if (check_old_pass (pamh, user, newpass,
73cfcf
-			  options.debug) != PAM_SUCCESS)
73cfcf
+      retval = check_old_pass (pamh, user, newpass, options.debug);
73cfcf
+      if (retval == PAM_PWHISTORY_RUN_HELPER)
73cfcf
+	  retval = run_check_helper(pamh, user, newpass, options.debug);
73cfcf
+
73cfcf
+      if (retval != PAM_SUCCESS)
73cfcf
 	{
73cfcf
 	  if (getuid() || options.enforce_for_root ||
73cfcf
 	      (flags & PAM_CHANGE_EXPIRED_AUTHTOK))
73cfcf
diff -up Linux-PAM-1.3.0/modules/pam_pwhistory/pwhistory_helper.c.pwhhelper Linux-PAM-1.3.0/modules/pam_pwhistory/pwhistory_helper.c
73cfcf
--- Linux-PAM-1.3.0/modules/pam_pwhistory/pwhistory_helper.c.pwhhelper	2016-05-06 15:18:42.308637957 +0200
73cfcf
+++ Linux-PAM-1.3.0/modules/pam_pwhistory/pwhistory_helper.c	2016-05-06 15:18:42.308637957 +0200
73cfcf
@@ -0,0 +1,209 @@
73cfcf
+/* 
73cfcf
+ * Copyright (c) 2013 Red Hat, Inc.
73cfcf
+ * Author: Tomas Mraz <tmraz@redhat.com>
73cfcf
+ *
73cfcf
+ * Redistribution and use in source and binary forms, with or without
73cfcf
+ * modification, are permitted provided that the following conditions
73cfcf
+ * are met:
73cfcf
+ * 1. Redistributions of source code must retain the above copyright
73cfcf
+ *    notice, and the entire permission notice in its entirety,
73cfcf
+ *    including the disclaimer of warranties.
73cfcf
+ * 2. Redistributions in binary form must reproduce the above copyright
73cfcf
+ *    notice, this list of conditions and the following disclaimer in the
73cfcf
+ *    documentation and/or other materials provided with the distribution.
73cfcf
+ * 3. The name of the author may not be used to endorse or promote
73cfcf
+ *    products derived from this software without specific prior
73cfcf
+ *    written permission.
73cfcf
+ *
73cfcf
+ * ALTERNATIVELY, this product may be distributed under the terms of
73cfcf
+ * the GNU Public License, in which case the provisions of the GPL are
73cfcf
+ * required INSTEAD OF the above restrictions.  (This clause is
73cfcf
+ * necessary due to a potential bad interaction between the GPL and
73cfcf
+ * the restrictions contained in a BSD-style copyright.)
73cfcf
+ *
73cfcf
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
73cfcf
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
73cfcf
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
73cfcf
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
73cfcf
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
73cfcf
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
73cfcf
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
73cfcf
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
73cfcf
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
73cfcf
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
73cfcf
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
73cfcf
+ */
73cfcf
+
73cfcf
+#include "config.h"
73cfcf
+
73cfcf
+#include <stdio.h>
73cfcf
+#include <stdlib.h>
73cfcf
+#include <string.h>
73cfcf
+#include <syslog.h>
73cfcf
+#include <errno.h>
73cfcf
+#include <unistd.h>
73cfcf
+#include <signal.h>
73cfcf
+#include <security/_pam_types.h>
73cfcf
+#include <security/_pam_macros.h>
73cfcf
+#include "opasswd.h"
73cfcf
+
73cfcf
+#define MAXPASS 200
73cfcf
+
73cfcf
+static void
73cfcf
+su_sighandler(int sig)
73cfcf
+{
73cfcf
+#ifndef SA_RESETHAND
73cfcf
+        /* emulate the behaviour of the SA_RESETHAND flag */
73cfcf
+        if ( sig == SIGILL || sig == SIGTRAP || sig == SIGBUS || sig = SIGSERV ) {
73cfcf
+		struct sigaction sa;
73cfcf
+		memset(&sa, '\0', sizeof(sa));
73cfcf
+		sa.sa_handler = SIG_DFL;
73cfcf
+                sigaction(sig, &sa, NULL);
73cfcf
+	}
73cfcf
+#endif
73cfcf
+        if (sig > 0) {
73cfcf
+                _exit(sig);
73cfcf
+        }
73cfcf
+}
73cfcf
+
73cfcf
+static void
73cfcf
+setup_signals(void)
73cfcf
+{
73cfcf
+  struct sigaction action;        /* posix signal structure */
73cfcf
+         
73cfcf
+  /*
73cfcf
+   * Setup signal handlers
73cfcf
+   */
73cfcf
+  (void) memset((void *) &action, 0, sizeof(action));
73cfcf
+  action.sa_handler = su_sighandler;
73cfcf
+#ifdef SA_RESETHAND
73cfcf
+  action.sa_flags = SA_RESETHAND;
73cfcf
+#endif
73cfcf
+  (void) sigaction(SIGILL, &action, NULL);
73cfcf
+  (void) sigaction(SIGTRAP, &action, NULL);
73cfcf
+  (void) sigaction(SIGBUS, &action, NULL);
73cfcf
+  (void) sigaction(SIGSEGV, &action, NULL);
73cfcf
+  action.sa_handler = SIG_IGN;
73cfcf
+  action.sa_flags = 0;
73cfcf
+  (void) sigaction(SIGTERM, &action, NULL);
73cfcf
+  (void) sigaction(SIGHUP, &action, NULL);
73cfcf
+  (void) sigaction(SIGINT, &action, NULL);
73cfcf
+  (void) sigaction(SIGQUIT, &action, NULL);
73cfcf
+}
73cfcf
+
73cfcf
+static int
73cfcf
+read_passwords(int fd, int npass, char **passwords)
73cfcf
+{
73cfcf
+  int rbytes = 0;
73cfcf
+  int offset = 0;
73cfcf
+  int i = 0;
73cfcf
+  char *pptr;
73cfcf
+  while (npass > 0)
73cfcf
+    {
73cfcf
+      rbytes = read(fd, passwords[i]+offset, MAXPASS-offset);
73cfcf
+
73cfcf
+      if (rbytes < 0)
73cfcf
+        {
73cfcf
+          if (errno == EINTR) continue;
73cfcf
+          break;
73cfcf
+        }
73cfcf
+      if (rbytes == 0)
73cfcf
+          break;
73cfcf
+
73cfcf
+      while (npass > 0 && (pptr=memchr(passwords[i]+offset, '\0', rbytes))
73cfcf
+             != NULL)
73cfcf
+        {
73cfcf
+          rbytes -= pptr - (passwords[i]+offset) + 1;
73cfcf
+          i++;
73cfcf
+          offset = 0;
73cfcf
+          npass--;
73cfcf
+          if (rbytes > 0)
73cfcf
+            {
73cfcf
+              if (npass > 0)
73cfcf
+                memcpy(passwords[i], pptr+1, rbytes);
73cfcf
+              memset(pptr+1, '\0', rbytes);
73cfcf
+            }
73cfcf
+        }
73cfcf
+      offset += rbytes;
73cfcf
+    }
73cfcf
+
73cfcf
+    /* clear up */
73cfcf
+    if (offset > 0 && npass > 0) 
73cfcf
+      memset(passwords[i], '\0', offset);
73cfcf
+
73cfcf
+   return i;
73cfcf
+}
73cfcf
+
73cfcf
+
73cfcf
+static int
73cfcf
+check_history(const char *user, const char *debug)
73cfcf
+{
73cfcf
+  char pass[MAXPASS + 1];
73cfcf
+  char *passwords[] = { pass };
73cfcf
+  int npass;
73cfcf
+  int dbg = atoi(debug); /* no need to be too fancy here */
73cfcf
+  int retval;
73cfcf
+
73cfcf
+  /* read the password from stdin (a pipe from the pam_pwhistory module) */
73cfcf
+  npass = read_passwords(STDIN_FILENO, 1, passwords);
73cfcf
+
73cfcf
+  if (npass != 1)
73cfcf
+    { /* is it a valid password? */
73cfcf
+      helper_log_err(LOG_DEBUG, "no password supplied");
73cfcf
+      return PAM_AUTHTOK_ERR;
73cfcf
+    }
73cfcf
+
73cfcf
+  retval = check_old_pass(user, pass, dbg);
73cfcf
+
73cfcf
+  memset(pass, '\0', MAXPASS);	/* clear memory of the password */
73cfcf
+
73cfcf
+  return retval;
73cfcf
+}
73cfcf
+
73cfcf
+static int
73cfcf
+save_history(const char *user, const char *howmany, const char *debug)
73cfcf
+{
73cfcf
+  int num = atoi(howmany);
73cfcf
+  int dbg = atoi(debug); /* no need to be too fancy here */
73cfcf
+  int retval;
73cfcf
+
73cfcf
+  retval = save_old_pass(user, num, dbg);
73cfcf
+
73cfcf
+  return retval;
73cfcf
+}
73cfcf
+
73cfcf
+int
73cfcf
+main(int argc, char *argv[])
73cfcf
+{
73cfcf
+  const char *option;
73cfcf
+  const char *user;
73cfcf
+
73cfcf
+  /*
73cfcf
+   * Catch or ignore as many signal as possible.
73cfcf
+   */
73cfcf
+  setup_signals();
73cfcf
+
73cfcf
+  /*
73cfcf
+   * we establish that this program is running with non-tty stdin.
73cfcf
+   * this is to discourage casual use.
73cfcf
+   */
73cfcf
+
73cfcf
+  if (isatty(STDIN_FILENO) || argc < 4)
73cfcf
+    {
73cfcf
+      fprintf(stderr,
73cfcf
+		"This binary is not designed for running in this way.\n");
73cfcf
+      sleep(10);	/* this should discourage/annoy the user */
73cfcf
+      return PAM_SYSTEM_ERR;
73cfcf
+    }
73cfcf
+
73cfcf
+  option = argv[1];
73cfcf
+  user = argv[2];
73cfcf
+
73cfcf
+  if (strcmp(option, "check") == 0 && argc == 4)
73cfcf
+    return check_history(user, argv[3]);
73cfcf
+  else if (strcmp(option, "save") == 0 && argc == 5)
73cfcf
+    return save_history(user, argv[3], argv[4]);
73cfcf
+
73cfcf
+  return PAM_SYSTEM_ERR;
73cfcf
+}
73cfcf
+
73cfcf
diff -up Linux-PAM-1.3.0/modules/pam_pwhistory/pwhistory_helper.8.xml.pwhhelper Linux-PAM-1.3.0/modules/pam_pwhistory/pwhistory_helper.8.xml
73cfcf
--- Linux-PAM-1.3.0/modules/pam_pwhistory/pwhistory_helper.8.xml.pwhhelper	2016-05-06 15:18:42.308637957 +0200
73cfcf
+++ Linux-PAM-1.3.0/modules/pam_pwhistory/pwhistory_helper.8.xml	2016-05-06 15:18:42.308637957 +0200
73cfcf
@@ -0,0 +1,68 @@
73cfcf
+
73cfcf
+
73cfcf
+	"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd">
73cfcf
+
73cfcf
+<refentry id="pwhistory_helper">
73cfcf
+
73cfcf
+  <refmeta>
73cfcf
+    <refentrytitle>pwhistory_helper</refentrytitle>
73cfcf
+    <manvolnum>8</manvolnum>
73cfcf
+    <refmiscinfo class="sectdesc">Linux-PAM Manual</refmiscinfo>
73cfcf
+  </refmeta>
73cfcf
+
73cfcf
+  <refnamediv id="pwhistory_helper-name">
73cfcf
+    <refname>pwhistory_helper</refname>
73cfcf
+    <refpurpose>Helper binary that transfers password hashes from passwd or shadow to opasswd</refpurpose>
73cfcf
+  </refnamediv>
73cfcf
+
73cfcf
+  <refsynopsisdiv>
73cfcf
+    <cmdsynopsis id="pwhistory_helper-cmdsynopsis">
73cfcf
+      <command>pwhistory_helper</command>
73cfcf
+      <arg choice="opt">
73cfcf
+        ...
73cfcf
+      </arg>
73cfcf
+    </cmdsynopsis>
73cfcf
+  </refsynopsisdiv>
73cfcf
+
73cfcf
+  <refsect1 id="pwhistory_helper-description">
73cfcf
+
73cfcf
+    <title>DESCRIPTION</title>
73cfcf
+
73cfcf
+    <para>
73cfcf
+      <emphasis>pwhistory_helper</emphasis> is a helper program for the
73cfcf
+      <emphasis>pam_pwhistory</emphasis> module that transfers password hashes
73cfcf
+      from passwd or shadow file to the opasswd file and checks a password
73cfcf
+      supplied by user against the existing hashes in the opasswd file.
73cfcf
+    </para>
73cfcf
+
73cfcf
+    <para>
73cfcf
+      The purpose of the helper is to enable tighter confinement of
73cfcf
+      login and password changing services. The helper is thus called only
73cfcf
+      when SELinux is enabled on the system.
73cfcf
+    </para>
73cfcf
+
73cfcf
+    <para>
73cfcf
+      The interface of the helper - command line options, and input/output
73cfcf
+      data format are internal to the <emphasis>pam_pwhistory</emphasis>
73cfcf
+      module and it should not be called directly from applications.
73cfcf
+    </para>
73cfcf
+  </refsect1>
73cfcf
+
73cfcf
+  <refsect1 id='pwhistory_helper-see_also'>
73cfcf
+    <title>SEE ALSO</title>
73cfcf
+    <para>
73cfcf
+      <citerefentry>
73cfcf
+	<refentrytitle>pam_pwhistory</refentrytitle><manvolnum>8</manvolnum>
73cfcf
+      </citerefentry>
73cfcf
+    </para>
73cfcf
+  </refsect1>
73cfcf
+
73cfcf
+  <refsect1 id='pwhistory_helper-author'>
73cfcf
+    <title>AUTHOR</title>
73cfcf
+      <para>
73cfcf
+        Written by Tomas Mraz based on the code originally in
73cfcf
+        <emphasis>pam_pwhistory and pam_unix</emphasis> modules.
73cfcf
+      </para>
73cfcf
+  </refsect1>
73cfcf
+
73cfcf
+</refentry>