On 16 December 2015 at 14:09, Tianon Gravi <tia...@debian.org> wrote:
> Just a friendly ping; any movement towards fixing or at least
> investigating this vuln?  This package is part of minbase, so IMO it
> looks a little strange to have even something as low as a CVSS 5.8
> still pending a maintainer response (even just a "naw, this isn't a
> problem and won't be fixed").  Is it a matter of crafting a patch with
> the upstream fix?  (I'm willing to try my hand at doing so if it'd be
> helpful.)

I've attached a patch for the packaging on top of the version
currently in jessie/stretch/sid that builds properly and includes the
upstream fix from 1.2.1 -- I've not yet had the opportunity to do
either a security upload or a proper NMU, but I'm willing to read and
do the work given hrefs and/or preferences, or will happily defer to
someone with more experience (maybe the maintainer, hint hint vorlon).
:)

♥,
- Tianon
  4096R / B42F 6819 007F 00F8 8E36  4FD4 036A 9C25 BF35 7DD4
diff --git a/debian/changelog b/debian/changelog
index f187739..d20a1a4 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,11 @@
+pam (1.1.8-3.2) UNRELEASED; urgency=medium
+
+  * Non-maintainer upload.
+  * Fix CVE-2015-3238: DoS/user enumeration due to blocking pipe in pam_unix
+    module (Closes: #789986)
+
+ -- Tianon Gravi <tia...@debian.org>  Wed, 23 Dec 2015 12:54:07 -0800
+
 pam (1.1.8-3.1) unstable; urgency=high
 
   * Non-maintainer upload by the Security Team.
diff --git a/debian/patches-applied/cve-2015-3238.patch b/debian/patches-applied/cve-2015-3238.patch
new file mode 100644
index 0000000..7c75ee5
--- /dev/null
+++ b/debian/patches-applied/cve-2015-3238.patch
@@ -0,0 +1,154 @@
+From e89d4c97385ff8180e6e81e84c5aa745daf28a79 Mon Sep 17 00:00:00 2001
+From: Thorsten Kukuk <ku...@thkukuk.de>
+Date: Mon, 22 Jun 2015 14:53:01 +0200
+Subject: Release version 1.2.1
+
+Security fix: CVE-2015-3238
+
+If the process executing pam_sm_authenticate or pam_sm_chauthtok method
+of pam_unix is not privileged enough to check the password, e.g.
+if selinux is enabled, the _unix_run_helper_binary function is called.
+When a long enough password is supplied (16 pages or more, i.e. 65536+
+bytes on a system with 4K pages), this helper function hangs
+indefinitely, blocked in the write(2) call while writing to a blocking
+pipe that has a limited capacity.
+With this fix, the verifiable password length will be limited to
+PAM_MAX_RESP_SIZE bytes (i.e. 512 bytes) for pam_exec and pam_unix.
+
+diff --git a/modules/pam_exec/pam_exec.8.xml b/modules/pam_exec/pam_exec.8.xml
+index 2379366..d1b00a2 100644
+--- a/modules/pam_exec/pam_exec.8.xml
++++ b/modules/pam_exec/pam_exec.8.xml
+@@ -106,7 +106,8 @@
+               During authentication the calling command can read
+               the password from <citerefentry>
+               <refentrytitle>stdin</refentrytitle><manvolnum>3</manvolnum>
+-              </citerefentry>.
++              </citerefentry>. Only first <emphasis>PAM_MAX_RESP_SIZE</emphasis>
++              bytes of a password are provided to the command.
+             </para>
+           </listitem>
+         </varlistentry>
+diff --git a/modules/pam_exec/pam_exec.c b/modules/pam_exec/pam_exec.c
+index 5ab9630..17ba6ca 100644
+--- a/modules/pam_exec/pam_exec.c
++++ b/modules/pam_exec/pam_exec.c
+@@ -178,11 +178,11 @@ call_exec (const char *pam_type, pam_handle_t *pamh,
+ 		}
+ 
+ 	      pam_set_item (pamh, PAM_AUTHTOK, resp);
+-	      authtok = strdupa (resp);
++	      authtok = strndupa (resp, PAM_MAX_RESP_SIZE);
+ 	      _pam_drop (resp);
+ 	    }
+ 	  else
+-	    authtok = void_pass;
++	    authtok = strndupa (void_pass, PAM_MAX_RESP_SIZE);
+ 
+ 	  if (pipe(fds) != 0)
+ 	    {
+diff --git a/modules/pam_unix/pam_unix.8.xml b/modules/pam_unix/pam_unix.8.xml
+index 4008402..a8b64bb 100644
+--- a/modules/pam_unix/pam_unix.8.xml
++++ b/modules/pam_unix/pam_unix.8.xml
+@@ -80,6 +80,13 @@
+     </para>
+ 
+     <para>
++      The maximum length of a password supported by the pam_unix module
++      via the helper binary is <emphasis>PAM_MAX_RESP_SIZE</emphasis>
++      - currently 512 bytes. The rest of the password provided by the
++      conversation function to the module will be ignored.
++    </para>
++
++    <para>
+       The password component of this module performs the task of updating
+       the user's password. The default encryption hash is taken from the
+       <emphasis remap='B'>ENCRYPT_METHOD</emphasis> variable from
+diff --git a/modules/pam_unix/pam_unix_passwd.c b/modules/pam_unix/pam_unix_passwd.c
+index 2d330e5..c2e5de5 100644
+--- a/modules/pam_unix/pam_unix_passwd.c
++++ b/modules/pam_unix/pam_unix_passwd.c
+@@ -240,15 +240,22 @@ static int _unix_run_update_binary(pam_handle_t *pamh, unsigned int ctrl, const
+ 	/* wait for child */
+ 	/* if the stored password is NULL */
+         int rc=0;
+-	if (fromwhat)
+-	  pam_modutil_write(fds[1], fromwhat, strlen(fromwhat)+1);
+-	else
+-	  pam_modutil_write(fds[1], "", 1);
+-	if (towhat) {
+-	  pam_modutil_write(fds[1], towhat, strlen(towhat)+1);
++	if (fromwhat) {
++	    int len = strlen(fromwhat);
++
++	    if (len > PAM_MAX_RESP_SIZE)
++	      len = PAM_MAX_RESP_SIZE;
++	    pam_modutil_write(fds[1], fromwhat, len);
+ 	}
+-	else
+-	  pam_modutil_write(fds[1], "", 1);
++        pam_modutil_write(fds[1], "", 1);
++	if (towhat) {
++	    int len = strlen(towhat);
++
++	    if (len > PAM_MAX_RESP_SIZE)
++	      len = PAM_MAX_RESP_SIZE;
++	    pam_modutil_write(fds[1], towhat, len);
++        }
++        pam_modutil_write(fds[1], "", 1);
+ 
+ 	close(fds[0]);       /* close here to avoid possible SIGPIPE above */
+ 	close(fds[1]);
+diff --git a/modules/pam_unix/passverify.c b/modules/pam_unix/passverify.c
+index b325602..e79b55e 100644
+--- a/modules/pam_unix/passverify.c
++++ b/modules/pam_unix/passverify.c
+@@ -1115,12 +1115,15 @@ getuidname(uid_t uid)
+ int
+ read_passwords(int fd, int npass, char **passwords)
+ {
++        /* The passwords array must contain npass preallocated
++         * buffers of length MAXPASS + 1
++         */
+         int rbytes = 0;
+         int offset = 0;
+         int i = 0;
+         char *pptr;
+         while (npass > 0) {
+-                rbytes = read(fd, passwords[i]+offset, MAXPASS-offset);
++                rbytes = read(fd, passwords[i]+offset, MAXPASS+1-offset);
+ 
+                 if (rbytes < 0) {
+                         if (errno == EINTR) continue;
+diff --git a/modules/pam_unix/passverify.h b/modules/pam_unix/passverify.h
+index 3de6759..caf7ae8 100644
+--- a/modules/pam_unix/passverify.h
++++ b/modules/pam_unix/passverify.h
+@@ -8,7 +8,7 @@
+ 
+ #define PAM_UNIX_RUN_HELPER PAM_CRED_INSUFFICIENT
+ 
+-#define MAXPASS		200	/* the maximum length of a password */
++#define MAXPASS PAM_MAX_RESP_SIZE  /* the maximum length of a password */
+ 
+ #define OLD_PASSWORDS_FILE      "/etc/security/opasswd"
+ 
+diff --git a/modules/pam_unix/support.c b/modules/pam_unix/support.c
+index fdb45c2..abccd82 100644
+--- a/modules/pam_unix/support.c
++++ b/modules/pam_unix/support.c
+@@ -609,7 +609,12 @@ static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd,
+ 	/* if the stored password is NULL */
+         int rc=0;
+ 	if (passwd != NULL) {            /* send the password to the child */
+-	    if (write(fds[1], passwd, strlen(passwd)+1) == -1) {
++	    int len = strlen(passwd);
++
++	    if (len > PAM_MAX_RESP_SIZE)
++	      len = PAM_MAX_RESP_SIZE;
++	    if (write(fds[1], passwd, len) == -1 ||
++	        write(fds[1], "", 1) == -1) {
+ 	      pam_syslog (pamh, LOG_ERR, "Cannot send password to helper: %m");
+ 	      retval = PAM_AUTH_ERR;
+ 	    }
diff --git a/debian/patches-applied/series b/debian/patches-applied/series
index 4e9b483..2108e86 100644
--- a/debian/patches-applied/series
+++ b/debian/patches-applied/series
@@ -23,3 +23,4 @@ lib_security_multiarch_compat
 pam-loginuid-in-containers
 cve-2013-7041.patch
 cve-2014-2583.patch
+cve-2015-3238.patch

Reply via email to