--- Linux-PAM-1.0.3/ChangeLog	2008-12-09 08:28:06.000000000 +0100
+++ Linux-PAM-1.0.4/ChangeLog	2009-03-03 13:07:41.000000000 +0100
@@ -1,3 +1,39 @@
+2009-03-03  Thorsten Kukuk  <kukuk@thkukuk.de>
+
+	* release version 1.0.4
+
+	* libpam/Makefile.am (libpam_la_LDFLAGS): Bump version number.
+	* tests/tst-pam_mkargv.c: Fix include for out of tree build.
+	* xtests/Makefile.am: Add tst-pam_unix4.pamd and tst-pam_unix4.sh.
+
+2009-03-03  Tomas Mraz <t8m@centrum.cz>
+
+	* modules/pam_unix/pam_unix_acct.c(_unix_run_verify_binary): Test
+        for abnormal exit of the helper binary.
+	* modules/pam_unix/pam_unix_passwd.c(_unix_run_update_binary): Likewise.
+	* modules/pam_unix/support.c(_unix_run_helper_binary): Likewise.
+
+2009-02-26  Tomas Mraz <t8m@centrum.cz>
+
+	* xtests/Makefile.am: Add tst-pam_unix4.
+	* xtests/tst-pam_unix4.c: New test for password change
+        and shadow min days limit.
+	* xtests/tst-pam_unix4.pamd: Likewise.
+	* xtests/tst-pam_unix4.sh: Likewise.
+
+	* modules/pam_unix/pam_unix_acct.c (pam_sm_acct_mgmt): Ignore
+        PAM_AUTHTOK_ERR on shadow verification.
+	* modules/pam_unix/passverify.c (check_shadow_expiry): Return
+        PAM_AUTHTOK_ERR if sp_min limit for password change is defied.
+
+2009-02-25  Thorsten Kukuk  <kukuk@thkukuk.de>
+
+        * libpam/pam_misc.c (_pam_StrTok): Use unsigned char
+        instead of int. Reported by Marcus Granado.
+        * tests/Makefile.am (TESTS): Add tst-pam_mkargv.
+        * tests/tst-pam_mkargv.c (main): Test case for
+        _pam_mkargv.
+
 2008-12-09  Thorsten Kukuk  <kukuk@thkukuk.de>
 
 	* release version 1.0.3
--- Linux-PAM-1.0.3/configure.in	2008-12-06 13:11:46.000000000 +0100
+++ Linux-PAM-1.0.4/configure.in	2009-03-02 16:06:04.000000000 +0100
@@ -1,6 +1,6 @@
 dnl Process this file with autoconf to produce a configure script.
 AC_INIT(conf/pam_conv1/pam_conv_y.y)
-AM_INIT_AUTOMAKE("Linux-PAM", 1.0.3)
+AM_INIT_AUTOMAKE("Linux-PAM", 1.0.4)
 AC_PREREQ([2.60])
 AM_CONFIG_HEADER(config.h)
 AC_CANONICAL_HOST
--- Linux-PAM-1.0.3/libpam/Makefile.am	2008-04-16 10:57:09.000000000 +0200
+++ Linux-PAM-1.0.4/libpam/Makefile.am	2009-03-02 16:07:26.000000000 +0100
@@ -20,7 +20,7 @@
 noinst_HEADERS = pam_prelude.h pam_private.h pam_tokens.h \
 		pam_modutil_private.h pam_static_modules.h
 
-libpam_la_LDFLAGS = -no-undefined -version-info 81:12:81
+libpam_la_LDFLAGS = -no-undefined -version-info 81:13:81
 libpam_la_LIBADD = @LIBAUDIT@ $(LIBPRELUDE_LIBS) @LIBDL@
 
 if STATIC_MODULES
--- Linux-PAM-1.0.3/libpam/pam_misc.c	2007-12-06 21:20:07.000000000 +0100
+++ Linux-PAM-1.0.4/libpam/pam_misc.c	2009-02-25 16:34:53.000000000 +0100
@@ -59,10 +59,11 @@
 
      /* initialize table */
      for (i=1; i<256; table[i++] = '\0');
-     for (i=0; format[i] ; table[(int)format[i++]] = 'y');
+     for (i=0; format[i] ;
+	  table[(unsigned char)format[i++]] = 'y');
 
      /* look for first non-format char */
-     while (*from && table[(int)*from]) {
+     while (*from && table[(unsigned char)*from]) {
 	  ++from;
      }
 
@@ -92,7 +93,7 @@
             remains */
      } else if (*from) {
 	 /* simply look for next blank char */
-	 for (end=from; *end && !table[(int)*end]; ++end);
+	 for (end=from; *end && !table[(unsigned char)*end]; ++end);
      } else {
 	 return (*next = NULL);                    /* no tokens left */
      }
--- Linux-PAM-1.0.3/modules/pam_unix/pam_unix_acct.c	2008-01-23 16:35:12.000000000 +0100
+++ Linux-PAM-1.0.4/modules/pam_unix/pam_unix_acct.c	2009-03-03 10:00:31.000000000 +0100
@@ -139,6 +139,9 @@
       if (rc<0) {
 	pam_syslog(pamh, LOG_ERR, "unix_chkpwd waitpid returned %d: %m", rc);
 	retval = PAM_AUTH_ERR;
+      } else if (!WIFEXITED(retval)) {
+        pam_syslog(pamh, LOG_ERR, "unix_chkpwd abnormal exit: %d", retval);
+        retval = PAM_AUTH_ERR;
       } else {
 	retval = WEXITSTATUS(retval);
         rc = pam_modutil_read(fds[0], buf, sizeof(buf) - 1);
@@ -250,6 +253,9 @@
 		_make_remark(pamh, ctrl, PAM_ERROR_MSG,
 			_("Your account has expired; please contact your system administrator"));
 		break;
+	case PAM_AUTHTOK_ERR:
+		retval = PAM_SUCCESS;
+		/* fallthrough */
 	case PAM_SUCCESS:
 		if (daysleft >= 0) {
 			pam_syslog(pamh, LOG_DEBUG,
--- Linux-PAM-1.0.3/modules/pam_unix/pam_unix_passwd.c	2008-02-29 16:22:03.000000000 +0100
+++ Linux-PAM-1.0.4/modules/pam_unix/pam_unix_passwd.c	2009-03-03 10:00:31.000000000 +0100
@@ -225,8 +225,11 @@
 	rc=waitpid(child, &retval, 0);  /* wait for helper to complete */
 	if (rc<0) {
 	  pam_syslog(pamh, LOG_ERR, "unix_update waitpid failed: %m");
-	  retval = PAM_AUTH_ERR;
-	} else {
+	  retval = PAM_AUTHTOK_ERR;
+	} else if (!WIFEXITED(retval)) {
+          pam_syslog(pamh, LOG_ERR, "unix_update abnormal exit: %d", retval);
+          retval = PAM_AUTHTOK_ERR;
+        } else {
 	  retval = WEXITSTATUS(retval);
 	}
     } else {
--- Linux-PAM-1.0.3/modules/pam_unix/passverify.c	2008-01-28 14:20:29.000000000 +0100
+++ Linux-PAM-1.0.4/modules/pam_unix/passverify.c	2009-03-02 16:02:22.000000000 +0100
@@ -293,8 +293,16 @@
 		*daysleft = (int)((spent->sp_lstchg + spent->sp_max) - curdays);
 		D(("warn before expiry"));
 	}
+	if ((curdays - spent->sp_lstchg < spent->sp_min)
+	    && (spent->sp_min != -1)) {
+		/* 
+		 * The last password change was too recent. This error will be ignored
+		 * if no password change is attempted.
+		 */
+		D(("password change too recent"));
+		return PAM_AUTHTOK_ERR;
+	}
 	return PAM_SUCCESS;
-
 }
 
 /* passwd/salt conversion macros */
--- Linux-PAM-1.0.3/modules/pam_unix/support.c	2008-01-23 16:35:13.000000000 +0100
+++ Linux-PAM-1.0.4/modules/pam_unix/support.c	2009-03-03 10:00:31.000000000 +0100
@@ -475,6 +475,9 @@
 	if (rc<0) {
 	  pam_syslog(pamh, LOG_ERR, "unix_chkpwd waitpid returned %d: %m", rc);
 	  retval = PAM_AUTH_ERR;
+	} else if (!WIFEXITED(retval)) {
+	  pam_syslog(pamh, LOG_ERR, "unix_chkpwd abnormal exit: %d", retval);
+	  retval = PAM_AUTH_ERR;
 	} else {
 	  retval = WEXITSTATUS(retval);
 	}
--- Linux-PAM-1.0.3/NEWS	2008-12-06 13:06:26.000000000 +0100
+++ Linux-PAM-1.0.4/NEWS	2009-03-03 13:06:26.000000000 +0100
@@ -1,6 +1,12 @@
 Linux-PAM NEWS -- history of user-visible changes.
 
 
+Release 1.0.4
+
+* Fixed CVE-2009-0579 (minimum days limit on password change is ignored)
+* Fix libpam internal config/argument parser
+
+
 Release 1.0.3
 
 * Small bug fix release
@@ -14,7 +20,7 @@
 
 Release 1.0.1
 
-* Regression fixed in pam_set_item().
+* Regression fixed in pam_set_item()
 
 
 Release 1.0.0
--- Linux-PAM-1.0.3/tests/Makefile.am	2007-09-02 19:02:53.000000000 +0200
+++ Linux-PAM-1.0.4/tests/Makefile.am	2009-02-25 16:34:53.000000000 +0100
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2006 Thorsten Kukuk <kukuk@suse.de>
+# Copyright (c) 2006, 2009 Thorsten Kukuk <kukuk@suse.de>
 #
 
 AM_CFLAGS = -DLIBPAM_COMPILE -I$(top_srcdir)/libpam/include \
@@ -11,9 +11,9 @@
 TESTS = tst-pam_start tst-pam_end tst-pam_fail_delay tst-pam_open_session \
 	tst-pam_close_session tst-pam_acct_mgmt tst-pam_authenticate \
 	tst-pam_chauthtok tst-pam_setcred tst-pam_get_item tst-pam_set_item \
-	tst-pam_getenvlist tst-pam_get_user tst-pam_set_data
+	tst-pam_getenvlist tst-pam_get_user tst-pam_set_data \
+	tst-pam_mkargv
 
 check_PROGRAMS = ${TESTS} tst-dlopen
 
 tst_dlopen_LDADD = -ldl
-
--- Linux-PAM-1.0.3/tests/tst-pam_mkargv.c	1970-01-01 01:00:00.000000000 +0100
+++ Linux-PAM-1.0.4/tests/tst-pam_mkargv.c	2009-03-02 16:20:01.000000000 +0100
@@ -0,0 +1,51 @@
+/*
+   Copyright (C) Thorsten Kukuk <kukuk@suse.de> 2009
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation in version 2 of the License.
+*/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include "pam_misc.c"
+
+/* Simple program to see if _pam_mkargv() would succeed. */
+int main(void)
+{
+  char *argvstring = "user = XENDT\\userα user=XENDT\\user1";
+  const char *argvresult[] = {"user", "=", "XENDT\\userα",
+			      "user=XENDT\\user1"};
+  int myargc;
+  char **myargv;
+  int argvlen;
+  int i;
+
+  argvlen = _pam_mkargv(argvstring, &myargv, &myargc);
+
+#if 0
+  printf ("argvlen=%i, argc=%i", argvlen, myargc);
+  for (i = 0; i < myargc; i++) {
+    printf(", argv[%d]=%s", i, myargv[i]);
+  }
+  printf ("\n");
+#endif
+
+  if (argvlen != 333)
+    return 1;
+
+  if (myargc != 4)
+    return 1;
+
+  for (i = 0; i < 4; i++)
+    {
+      if (strcmp (myargv[i], argvresult[i]) != 0)
+	return 1;
+    }
+
+  return 0;
+}
--- Linux-PAM-1.0.3/xtests/Makefile.am	2008-02-18 18:57:34.000000000 +0100
+++ Linux-PAM-1.0.4/xtests/Makefile.am	2009-03-03 12:36:23.000000000 +0100
@@ -14,7 +14,9 @@
 	tst-pam_dispatch5.pamd \
 	tst-pam_cracklib1.pamd tst-pam_cracklib2.pamd \
 	tst-pam_unix1.pamd tst-pam_unix2.pamd tst-pam_unix3.pamd \
+	tst-pam_unix4.pamd \
 	tst-pam_unix1.sh tst-pam_unix2.sh tst-pam_unix3.sh \
+	tst-pam_unix4.sh \
 	access.conf tst-pam_access1.pamd tst-pam_access1.sh \
 	tst-pam_access2.pamd tst-pam_access2.sh \
 	tst-pam_access3.pamd tst-pam_access3.sh \
@@ -33,7 +35,7 @@
 XTESTS = tst-pam_dispatch1 tst-pam_dispatch2 tst-pam_dispatch3 \
 	tst-pam_dispatch4 tst-pam_dispatch5 \
 	tst-pam_cracklib1 tst-pam_cracklib2 \
-	tst-pam_unix1 tst-pam_unix2 tst-pam_unix3 \
+	tst-pam_unix1 tst-pam_unix2 tst-pam_unix3 tst-pam_unix4 \
 	tst-pam_access1 tst-pam_access2 tst-pam_access3 \
 	tst-pam_access4 tst-pam_limits1 tst-pam_succeed_if1 \
 	tst-pam_group1 tst-pam_authfail tst-pam_authsucceed
--- Linux-PAM-1.0.3/xtests/tst-pam_unix4.c	1970-01-01 01:00:00.000000000 +0100
+++ Linux-PAM-1.0.4/xtests/tst-pam_unix4.c	2009-02-26 20:06:28.000000000 +0100
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+/*
+ * Check password change minimum days handling.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <security/pam_appl.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <unistd.h>
+
+/* A conversation function which uses an internally-stored value for
+   the responses. */
+static int
+fake_conv (int num_msg, const struct pam_message **msgm UNUSED,
+	   struct pam_response **response, void *appdata_ptr UNUSED)
+{
+  struct pam_response *reply;
+  int count;
+  static int respnum = 0;
+  static const char *resps[] = { "pamunix01", "TsTPAM01MAP", "TsTPAM01MAP" };
+
+  /* Sanity test. */
+  if (num_msg <= 0)
+    return PAM_CONV_ERR;
+
+  /* Allocate memory for the responses. */
+  reply = calloc (num_msg, sizeof (struct pam_response));
+  if (reply == NULL)
+    return PAM_CONV_ERR;
+
+  /* Answer with appropriate response from the above array. */
+  for (count = 0; count < num_msg; ++count)
+    {
+      if (msgm[count]->msg_style == PAM_PROMPT_ECHO_OFF)
+        {
+          reply[count].resp_retcode = 0;
+          reply[count].resp = strdup (resps[respnum % 3]);
+          ++respnum;
+        }
+    }
+
+  /* Set the pointers in the response structure and return. */
+  *response = reply;
+  return PAM_SUCCESS;
+}
+
+static struct pam_conv conv = {
+    fake_conv,
+    NULL
+};
+
+
+/* Check that errors of optional modules are ignored and that
+   required modules after a sufficient one are not executed.  */
+
+int
+main(int argc, char *argv[])
+{
+  pam_handle_t *pamh=NULL;
+  const char *user="tstpamunix";
+  int retval;
+  int debug = 0;
+  int fail;
+  struct passwd *pwd;
+
+  if (argc < 2 || (*argv[1] != 'f' &&
+      *argv[1] != 'p'))
+    {
+      fprintf (stderr, "Need fail or pass argument.\n");
+      return 2;
+    }
+
+  fail = *argv[1] == 'f';
+
+  if (argc > 2 && strcmp (argv[2], "-d") == 0)
+    debug = 1;
+
+  pwd = getpwnam (user);
+
+  if (pwd == NULL)
+    {
+       if (debug)
+         fprintf (stderr, "unix4: Missing tstpamunix user.\n");
+       return 2;
+    }
+
+  /* we must switch the real (not effective) user so the restrictions
+     are enforced */
+  setreuid (pwd->pw_uid, -1);
+
+  retval = pam_start("tst-pam_unix4", user, &conv, &pamh);
+  if (retval != PAM_SUCCESS)
+    {
+      if (debug)
+	fprintf (stderr, "unix4: pam_start returned %d\n", retval);
+      return 1;
+    }
+
+  retval = pam_chauthtok (pamh, 0);
+  if ((!fail && retval != PAM_SUCCESS) || (fail && retval == PAM_SUCCESS))
+    {
+      if (debug)
+	fprintf (stderr, "unix4-1: pam_chauthtok returned %d\n", retval);
+      return 1;
+    }
+
+  retval = pam_end (pamh,retval);
+  if (retval != PAM_SUCCESS)
+    {
+      if (debug)
+	fprintf (stderr, "unix4: pam_end returned %d\n", retval);
+      return 1;
+    }
+  return 0;
+}
--- Linux-PAM-1.0.3/xtests/tst-pam_unix4.pamd	1970-01-01 01:00:00.000000000 +0100
+++ Linux-PAM-1.0.4/xtests/tst-pam_unix4.pamd	2009-02-26 20:06:28.000000000 +0100
@@ -0,0 +1,6 @@
+#%PAM-1.0
+auth     required       pam_unix.so
+account  required       pam_unix.so
+password required       pam_unix.so debug
+session  required       pam_unix.so
+
--- Linux-PAM-1.0.3/xtests/tst-pam_unix4.sh	1970-01-01 01:00:00.000000000 +0100
+++ Linux-PAM-1.0.4/xtests/tst-pam_unix4.sh	2009-02-26 20:06:28.000000000 +0100
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+# pamunix01 = 0aXKZztA.d1KYIuFXArmd2jU
+/usr/sbin/useradd -p 0aXKZztA.d1KYIuFXArmd2jU tstpamunix
+# this run must successfully change the password
+./tst-pam_unix4 pass
+RET=$?
+/usr/sbin/usermod -p 0aXKZztA.d1KYIuFXArmd2jU tstpamunix
+/usr/bin/chage -m 10000 tstpamunix
+# this run must fail to change the password
+./tst-pam_unix4 fail || RET=$?
+
+/usr/sbin/userdel -r tstpamunix 2> /dev/null
+exit $RET
