Submitted By: LFS BOOK Date: 2003-10-06 Initial Package Version: 1.1.4 Origin: LFS Security List Description: See the ChangeLog entry below. diff -Naur zlib-1.1.4/ChangeLog zlib-1.1.4-vsnprintf/ChangeLog --- zlib-1.1.4/ChangeLog 2002-03-11 09:02:35.000000000 -0600 +++ zlib-1.1.4-vsnprintf/ChangeLog 2003-03-05 13:14:50.000000000 -0600 @@ -1,6 +1,13 @@ ChangeLog file for zlib +Changes in 1.1.4-patched (23 February 2003) +- fix a security vulnerability related to improper use of snprintf/vsnprintf + function. +- ./configure now detects the presence of snprintf/vsnprintf and enables it + automatically if present. +- README.vsnprintf added. + Changes in 1.1.4 (11 March 2002) - ZFREE was repeated on same allocation on some error conditions. This creates a security problem described in diff -Naur zlib-1.1.4/README.vsnprintf zlib-1.1.4-vsnprintf/README.vsnprintf --- zlib-1.1.4/README.vsnprintf 1969-12-31 18:00:00.000000000 -0600 +++ zlib-1.1.4-vsnprintf/README.vsnprintf 2003-03-05 13:14:50.000000000 -0600 @@ -0,0 +1,23 @@ +During a recent audit of zlib-1.1.4, a buffer-overflow and string-format +vulnerability was found in the gzprintf() function. This has been corrected in +this version of zlib; in addition, some ./configure checks have been added to +make sure the host system can utilize the corrections fully. + +As a result, it is now strongly recommended that your host system or compiler +provide a fully C99-compliant implementation of the vsnprintf() function. +Anything less will reduce the functionality and/or security of the gzprintf() +function. The most critical aspect is that vsnprintf() should be present and +should provide a return value. If this function is missing, one of the +fallback functions (vsprintf(), snprintf(), vsnprintf()) will have to be used, +and if so, they too should return a value. If your system is lacking in any of +these aspects, the ./configure script should warn you and refer you to this +file. + +In addition, the HAS_vsnprintf and HAS_snprintf macros are automatically +defined if these functions are available. zlib-1.1.4 and older versions did +not do this, potentially leading to a broken and vulnerable zlib even when the +host system supported the requisite functionality to avoid this. + + + -- Kelledin + diff -Naur zlib-1.1.4/configure zlib-1.1.4-vsnprintf/configure --- zlib-1.1.4/configure 1998-07-08 13:19:35.000000000 -0500 +++ zlib-1.1.4-vsnprintf/configure 2003-03-05 13:15:00.000000000 -0600 @@ -155,7 +155,212 @@ echo "Checking for unistd.h... No." fi -cat > $test.c <$test.c < +#include + +#if (defined(__MSDOS__) || defined(_WINDOWS) || defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__STDC__) || defined(__cplusplus) || defined(__OS2__)) && !defined(STDC) +# define STDC +#endif + +int main() +{ +#ifndef STDC + choke me +#endif + + return 0; +} +EOF + +if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then + echo "Checking whether to use vsnprintf() or snprintf()... using vsnprintf()" + + cat >$test.c < +#include + +int mytest(char *fmt, ...) +{ + char buf[20]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + return 0; +} + +int main() +{ + return (mytest("Hello%d\n", 1)); +} +EOF + + if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then + CFLAGS="$CFLAGS -DHAS_vsnprintf" + echo "Checking for vsnprintf() in stdio.h... Yes." + + cat >$test.c < +#include + +int mytest(char *fmt, ...) +{ + int i; + char buf[20]; + va_list ap; + + va_start(ap, fmt); + i = vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + return 0; +} + +int main() +{ + return (mytest("Hello%d\n", 1)); +} +EOF + + if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then + CFLAGS="$CFLAGS -DHAS_vsnprintf_return" + echo "Checking for return value of vsnprintf()... Yes." + else + echo "Checking for return value of vsnprintf()... No." + echo " WARNING: apparently vsnprintf() does not return a value. zlib" + echo " can build but will be open to possible string-format security" + echo " vulnerabilities." + fi + else + echo "Checking for vsnprintf() in stdio.h... No." + echo " WARNING: vsnprintf() not found, falling back to vsprintf(). zlib" + echo " can build but will be open to possible buffer-overflow security" + echo " vulnerabilities." + + cat >$test.c < +#include + +int mytest(char *fmt, ...) +{ + int i; + char buf[20]; + va_list ap; + + va_start(ap, fmt); + i = vsprintf(buf, fmt, ap); + va_end(ap); + return 0; +} + +int main() +{ + return (mytest("Hello%d\n", 1)); +} +EOF + + if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then + CFLAGS="$CFLAGS -DHAS_vsprintf_return" + echo "Checking for return value of vsprintf()... Yes." + else + echo "Checking for return value of vsprintf()... No." + echo " WARNING: apparently vsprintf() does not return a value. zlib" + echo " can build but will be open to possible string-format security" + echo " vulnerabilities." + fi + fi +else + echo "Checking whether to use vsnprintf() or snprintf()... using snprintf()" + + cat >$test.c < +#include + +int mytest() +{ + char buf[20]; + + snprintf(buf, sizeof(buf), "%s", "foo"); + return 0; +} + +int main() +{ + return (mytest()); +} +EOF + + if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then + CFLAGS="$CFLAGS -DHAS_snprintf" + echo "Checking for snprintf() in stdio.h... Yes." + + cat >$test.c < +#include + +int mytest(char *fmt, ...) +{ + int i; + char buf[20]; + + i = snprintf(buf, sizeof(buf), "%s", "foo"); + return 0; +} + +int main() +{ + return (mytest()); +} +EOF + + if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then + CFLAGS="$CFLAGS -DHAS_snprintf_return" + echo "Checking for return value of snprintf()... Yes." + else + echo "Checking for return value of snprintf()... No." + echo " WARNING: apparently snprintf() does not return a value. zlib" + echo " can build but will be open to possible string-format security" + echo " vulnerabilities." + fi + else + echo "Checking for snprintf() in stdio.h... No." + echo " WARNING: snprintf() not found, falling back to sprintf(). zlib" + echo " can build but will be open to possible buffer-overflow security" + echo " vulnerabilities." + + cat >$test.c < +#include + +int mytest(char *fmt, ...) +{ + int i; + char buf[20]; + + i = sprintf(buf, "%s", "foo"); + return 0; +} + +int main() +{ + return (mytest()); +} +EOF + + if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then + CFLAGS="$CFLAGS -DHAS_sprintf_return" + echo "Checking for return value of sprintf()... Yes." + else + echo "Checking for return value of sprintf()... No." + echo " WARNING: apparently sprintf() does not return a value. zlib" + echo " can build but will be open to possible string-format security" + echo " vulnerabilities." + fi + fi +fi + +cat >$test.c < int main() { return 0; } EOF diff -Naur zlib-1.1.4/gzio.c zlib-1.1.4-vsnprintf/gzio.c --- zlib-1.1.4/gzio.c 2002-03-11 07:16:01.000000000 -0600 +++ zlib-1.1.4-vsnprintf/gzio.c 2003-03-05 13:14:50.000000000 -0600 @@ -529,14 +529,42 @@ int len; va_start(va, format); + + /* 2003/02/23: Add proper length checking here, if possible. + * + * -- Kelledin + */ #ifdef HAS_vsnprintf - (void)vsnprintf(buf, sizeof(buf), format, va); +# ifdef HAS_vsnprintf_return + len=vsnprintf(buf, sizeof(buf), format, va); + va_end(va); + + if (len <= 0 || len >= sizeof(buf)) { + /* Resulting string too large to fit in the buffer. */ + return 0; + } +# else + vsnprintf(buf, sizeof(buf), format, va); + va_end(va); + len=strlen(buf); + if (len <= 0) return 0; +# endif #else - (void)vsprintf(buf, format, va); -#endif +# ifdef HAS_vsprintf_return + len=vsprintf(buf, format, va); + va_end(va); + + if (len <= 0 || len >= sizeof(buf)) { + /* Resulting string too large to fit in the buffer. */ + return 0; + } +# else + vsprintf(buf, format, va); va_end(va); - len = strlen(buf); /* some *sprintf don't return the nb of bytes written */ + len=strlen(buf); if (len <= 0) return 0; +# endif +#endif return gzwrite(file, buf, (unsigned)len); } @@ -552,15 +580,41 @@ char buf[Z_PRINTF_BUFSIZE]; int len; + /* 2003/02/23: Add proper length checking here when possible. + * + * -- Kelledin + */ #ifdef HAS_snprintf +# ifdef HAS_snprintf_return + len=snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + + if (len <= 0 || len >= sizeof(buf)) { + /* Resulting string too large to fit in the buffer. */ + return 0; + } +# else snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + len=strlen(buf); + if (len <= 0) return 0; +# endif #else +# ifdef HAS_sprintf_return + len=sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + + if (len <= 0 || len >= sizeof(buf)) { + /* Resulting string too large to fit in the buffer. */ + return 0; + } +# else sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); -#endif - len = strlen(buf); /* old sprintf doesn't return the nb of bytes written */ + len=strlen(buf); if (len <= 0) return 0; +# endif +#endif return gzwrite(file, buf, len); }