--- 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 + + * 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 + + * 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 + + * 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 + + * 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 * 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 +# Copyright (c) 2006, 2009 Thorsten Kukuk # 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 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 +#endif + +#include + +#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 +#endif + +#include +#include +#include +#include +#include +#include +#include + +/* 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