jerenkrantz 2003/05/27 21:45:46
Modified: . CHANGES configure.in
build apu-hints.m4
crypto apr_md5.c
test .cvsignore Makefile.in
Added: test testpass.c
Log:
SECURITY [httpd incident CAN-2003-0189] Address a thread safety issue with
apr_password_validate() on AIX, Linux, Mac OS X, and possibly other platforms.
We didn't move the crypt_r checks from apr to apr-util when we moved
apr_password_validate. Add testpass.c to ensure we don't regress.
CVE: CAN-2003-0189
Reviewed by: Justin, Jim, Jeff
Revision Changes Path
1.113 +4 -0 apr-util/CHANGES
Index: CHANGES
===================================================================
RCS file: /home/cvs/apr-util/CHANGES,v
retrieving revision 1.112
retrieving revision 1.113
diff -u -u -r1.112 -r1.113
--- CHANGES 9 May 2003 10:59:20 -0000 1.112
+++ CHANGES 28 May 2003 04:45:46 -0000 1.113
@@ -1,5 +1,9 @@
Changes with APR-util 0.9.4
+ *) SECURITY [httpd incident CAN-2003-0189] Address a thread safety
+ issue with apr_password_validate() on AIX, Linux, Mac OS X, and
+ possibly other platforms. [Jeff Trawick, Justin Erenkrantz]
+
*) Fix a problem with LDAP configuration which caused subsequent
configure tests to fail since LIBS contained LDAP libraries for
subsequent tests but LDFLAGS no longer included the path to such
1.66 +15 -0 apr-util/configure.in
Index: configure.in
===================================================================
RCS file: /home/cvs/apr-util/configure.in,v
retrieving revision 1.65
retrieving revision 1.66
diff -u -u -r1.65 -r1.66
--- configure.in 28 Apr 2003 12:10:41 -0000 1.65
+++ configure.in 28 May 2003 04:45:46 -0000 1.66
@@ -94,6 +94,21 @@
APU_FIND_EXPAT
APU_FIND_ICONV
+AC_SEARCH_LIBS(crypt, crypt ufc)
+AC_MSG_CHECKING(if system crypt() function is threadsafe)
+if test "x$apu_crypt_threadsafe" = "x1"; then
+ AC_DEFINE(APU_CRYPT_THREADSAFE, 1, [Define if the system crypt() function
is threadsafe])
+ msg="yes"
+else
+ msg="no"
+fi
+AC_MSG_RESULT([$msg])
+
+AC_CHECK_FUNCS(crypt_r, [ crypt_r="1" ], [ crypt_r="0" ])
+if test "$crypt_r" = "1"; then
+ APR_CHECK_CRYPT_R_STYLE
+fi
+
so_ext=$APR_SO_EXT
lib_target=$APR_LIB_TARGET
AC_SUBST(so_ext)
1.3 +11 -1 apr-util/build/apu-hints.m4
Index: apu-hints.m4
===================================================================
RCS file: /home/cvs/apr-util/build/apu-hints.m4,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -u -r1.2 -r1.3
--- apu-hints.m4 15 Dec 2002 19:36:41 -0000 1.2
+++ apu-hints.m4 28 May 2003 04:45:46 -0000 1.3
@@ -18,11 +18,21 @@
echo "Applying apr-util hints file rules for $host"
case "$host" in
- *-ibm-aix*)
+ *-dec-osf*)
+ APR_SETIFNULL(apu_crypt_threadsafe, [1])
+ ;;
+ *-hp-hpux11.*)
+ APR_SETIFNULL(apu_crypt_threadsafe, [1])
+ ;;
+ *-ibm-aix*)
APR_SETIFNULL(apu_iconv_inbuf_const, [1])
;;
+ *-ibm-os390)
+ APR_SETIFNULL(apu_crypt_threadsafe, [1])
+ ;;
*-solaris2*)
APR_SETIFNULL(apu_iconv_inbuf_const, [1])
+ APR_SETIFNULL(apu_crypt_threadsafe, [1])
;;
*-sco3.2v5*)
APR_SETIFNULL(apu_db_xtra_libs, [-lsocket])
1.6 +43 -5 apr-util/crypto/apr_md5.c
Index: apr_md5.c
===================================================================
RCS file: /home/cvs/apr-util/crypto/apr_md5.c,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -u -r1.5 -r1.6
--- apr_md5.c 12 Mar 2003 20:20:00 -0000 1.5
+++ apr_md5.c 28 May 2003 04:45:46 -0000 1.6
@@ -98,6 +98,7 @@
#include "apr_strings.h"
#include "apr_md5.h"
#include "apr_lib.h"
+#include "apu_config.h"
#if APR_HAVE_STRING_H
#include <string.h>
@@ -108,6 +109,9 @@
#if APR_HAVE_UNISTD_H
#include <unistd.h>
#endif
+#if APR_HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
/* Constants for MD5Transform routine.
*/
@@ -671,6 +675,32 @@
return APR_SUCCESS;
}
+#if !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
+#if defined(APU_CRYPT_THREADSAFE) || !APR_HAS_THREADS
+
+#define crypt_mutex_lock()
+#define crypt_mutex_unlock()
+
+#elif APR_HAVE_PTHREAD_H && defined(PTHREAD_MUTEX_INITIALIZER)
+
+static pthread_mutex_t crypt_mutex = PTHREAD_MUTEX_INITIALIZER;
+static void crypt_mutex_lock(void)
+{
+ pthread_mutex_lock(&crypt_mutex);
+}
+
+static void crypt_mutex_unlock(void)
+{
+ pthread_mutex_unlock(&crypt_mutex);
+}
+
+#else
+
+#error apr_password_validate() is not threadsafe. rebuild APR without
thread support.
+
+#endif
+#endif
+
/*
* Validate a plaintext password against a smashed one. Use either
* crypt() (if available) or apr_md5_encode(), depending upon the format
@@ -714,14 +744,22 @@
crypt_pw = crypt_r(passwd, hash, &buffer);
apr_cpystrn(sample, crypt_pw, sizeof(sample) - 1);
#else
- /* XXX if this is a threaded build, we should hold a mutex
- * around the next two lines... but note that on some
- * platforms (e.g., Solaris, HP-UX, OS/390) crypt()
- * returns a pointer to thread-specific data so we don't
- * want a mutex on those platforms
+ /* Do a bit of sanity checking since we know that crypt_r()
+ * should always be used for threaded builds on AIX, and
+ * problems in configure logic can result in the wrong
+ * choice being made.
+ */
+#if defined(_AIX) && defined(APR_HAS_THREADS)
+#error Configuration error! crypt_r() should have been selected!
+#endif
+
+ /* Handle thread safety issues by holding a mutex around the
+ * call to crypt().
*/
+ crypt_mutex_lock();
crypt_pw = crypt(passwd, hash);
apr_cpystrn(sample, crypt_pw, sizeof(sample) - 1);
+ crypt_mutex_unlock();
#endif
}
return (strcmp(sample, hash) == 0) ? APR_SUCCESS : APR_EMISMATCH;
1.9 +1 -0 apr-util/test/.cvsignore
Index: .cvsignore
===================================================================
RCS file: /home/cvs/apr-util/test/.cvsignore,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -u -r1.8 -r1.9
--- .cvsignore 7 Oct 2002 16:34:59 -0000 1.8
+++ .cvsignore 28 May 2003 04:45:46 -0000 1.9
@@ -8,6 +8,7 @@
testdbm
testmd4
testmd5
+testpass
testqueue
testreslist
testrmm
1.37 +6 -1 apr-util/test/Makefile.in
Index: Makefile.in
===================================================================
RCS file: /home/cvs/apr-util/test/Makefile.in,v
retrieving revision 1.36
retrieving revision 1.37
diff -u -u -r1.36 -r1.37
--- Makefile.in 23 Apr 2003 12:25:48 -0000 1.36
+++ Makefile.in 28 May 2003 04:45:46 -0000 1.37
@@ -3,7 +3,7 @@
INCLUDES = @APRUTIL_PRIV_INCLUDES@ @APR_INCLUDES@ @APRUTIL_INCLUDES@
PROGRAMS = testdbm testdate testmd4 testmd5 testxml testrmm teststrmatch \
- testuuid testreslist testqueue testuri
+ testuuid testreslist testqueue testuri testpass
TARGETS = $(PROGRAMS)
[EMAIL PROTECTED]@
@@ -80,4 +80,9 @@
testuri_LDADD = $(TARGET_LIB_PATH)
testuri: $(testuri_OBJECTS) $(testuri_LDADD)
$(LINK) $(APRUTIL_LDFLAGS) $(testuri_OBJECTS) $(testuri_LDADD)
$(PROGRAM_DEPENDENCIES)
+
+testpass_OBJECTS = testpass.lo
+testpass_LDADD = $(TARGET_LIB_PATH)
+testpass: $(testpass_OBJECTS) $(testpass_LDADD)
+ $(LINK) $(APRUTIL_LDFLAGS) $(testpass_OBJECTS) $(testpass_LDADD)
$(PROGRAM_DEPENDENCIES)
1.1 apr-util/test/testpass.c
Index: testpass.c
===================================================================
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000-2003 The Apache Software Foundation. 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, this list of conditions and the following disclaimer.
*
* 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 end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact [EMAIL PROTECTED]
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "apr_errno.h"
#include "apr_strings.h"
#include "apr_file_io.h"
#include "apr_thread_proc.h"
#include "apr_md5.h"
static struct {
const char *password;
const char *hash;
} passwords[] =
{
/*
passwords and hashes created with Apache's htpasswd utility like this:
htpasswd -c -b passwords pass1 pass1
htpasswd -b passwords pass2 pass2
htpasswd -b passwords pass3 pass3
htpasswd -b passwords pass4 pass4
htpasswd -b passwords pass5 pass5
htpasswd -b passwords pass6 pass6
htpasswd -b passwords pass7 pass7
htpasswd -b passwords pass8 pass8
(insert Perl one-liner to convert to initializer :) )
*/
{"pass1", "1fWDc9QWYCWrQ"},
{"pass2", "1fiGx3u7QoXaM"},
{"pass3", "1fzijMylTiwCs"},
{"pass4", "nHUYc8U2UOP7s"},
{"pass5", "nHpETGLGPwAmA"},
{"pass6", "nHbsbWmJ3uyhc"},
{"pass7", "nHQ3BbF0Y9vpI"},
{"pass8", "nHZA1rViSldQk"}
};
static int num_passwords = sizeof(passwords) / sizeof(passwords[0]);
static void check_rv(apr_status_t rv)
{
if (rv != APR_SUCCESS) {
fprintf(stderr, "bailing\n");
exit(1);
}
}
static void test(void)
{
int i;
for (i = 0; i < num_passwords; i++) {
apr_status_t rv = apr_password_validate(passwords[i].password,
passwords[i].hash);
assert(rv == APR_SUCCESS);
}
}
#if APR_HAS_THREADS
static void * APR_THREAD_FUNC testing_thread(apr_thread_t *thd,
void *data)
{
int i;
for (i = 0; i < 100; i++) {
test();
}
return APR_SUCCESS;
}
static void thread_safe_test(apr_pool_t *p)
{
#define NUM_THR 20
apr_thread_t *my_threads[NUM_THR];
int i;
apr_status_t rv;
for (i = 0; i < NUM_THR; i++) {
rv = apr_thread_create(&my_threads[i], NULL, testing_thread, NULL, p);
check_rv(rv);
}
for (i = 0; i < NUM_THR; i++) {
apr_thread_join(&rv, my_threads[i]);
}
}
#endif
int main(void)
{
apr_status_t rv;
apr_pool_t *p;
rv = apr_initialize();
check_rv(rv);
rv = apr_pool_create(&p, NULL);
check_rv(rv);
atexit(apr_terminate);
/* before creating any threads, test it first just to check
* for problems with the test driver
*/
printf("dry run\n");
test();
#if APR_HAS_THREADS
printf("thread-safe test\n");
thread_safe_test(p);
#endif
return 0;
}