Optionally let parse_header_time() sanity check strptime() results before trusting...
authorFabian Keil <fk@fabiankeil.de>
Sun, 9 Dec 2012 12:27:01 +0000 (12:27 +0000)
committerFabian Keil <fk@fabiankeil.de>
Sun, 9 Dec 2012 12:27:01 +0000 (12:27 +0000)
Broken strptime() implementations have caused problems in the
past and the most recent offender seems to be FreeBSD's libc:
http://www.freebsd.org/cgi/query-pr.cgi?pr=173421

acconfig.h
configure.in
parsers.c

index c7dfab6..286ff62 100644 (file)
  */
 #undef FEATURE_STATISTICS
 
  */
 #undef FEATURE_STATISTICS
 
+/*
+ * Enable strptime() sanity checks.
+ */
+#undef FEATURE_STRPTIME_SANITY_CHECKS
+
 /*
  * Allow Privoxy to be "disabled" so it is just a normal non-blocking
  * non-anonymizing proxy.  This is useful if you're trying to access a
 /*
  * Allow Privoxy to be "disabled" so it is just a normal non-blocking
  * non-anonymizing proxy.  This is useful if you're trying to access a
index 16bbcc4..3194fe6 100644 (file)
@@ -1,6 +1,6 @@
 dnl Process this file with autoconf to produce a configure script.
 dnl
 dnl Process this file with autoconf to produce a configure script.
 dnl
-dnl $Id: configure.in,v 1.173 2012/10/12 11:15:17 fabiankeil Exp $
+dnl $Id: configure.in,v 1.174 2012/10/12 11:17:48 fabiankeil Exp $
 dnl
 dnl Written by and Copyright (C) 2001-2010 the
 dnl Privoxy team. http://www.privoxy.org/
 dnl
 dnl Written by and Copyright (C) 2001-2010 the
 dnl Privoxy team. http://www.privoxy.org/
@@ -32,7 +32,7 @@ dnl =================================================================
 dnl AutoConf Initialization
 dnl =================================================================
 
 dnl AutoConf Initialization
 dnl =================================================================
 
-AC_REVISION($Revision: 1.173 $)
+AC_REVISION($Revision: 1.174 $)
 AC_INIT(jcc.c)
 
 if test ! -f config.h.in; then
 AC_INIT(jcc.c)
 
 if test ! -f config.h.in; then
@@ -968,6 +968,14 @@ AC_ARG_ENABLE(accept-filter,
   AC_DEFINE(FEATURE_ACCEPT_FILTER)
 fi])
 
   AC_DEFINE(FEATURE_ACCEPT_FILTER)
 fi])
 
+AC_ARG_ENABLE(strptime-sanity-checks,
+[  --enable-strptime-sanity-checks Only trust strptime() results if an additional strftime()/strptime()
+                                  conversion doesn't change the result. Can be useful if strptime() is
+                                  known or suspected to be broken.],
+[if test $enableval = yes; then
+  AC_DEFINE(FEATURE_STRPTIME_SANITY_CHECKS)
+fi])
+
 dnl pcre/pcrs is needed for CGI anyway, so
 dnl the choice is only between static and
 dnl dynamic:
 dnl pcre/pcrs is needed for CGI anyway, so
 dnl the choice is only between static and
 dnl dynamic:
index 069bbae..47be919 100644 (file)
--- a/parsers.c
+++ b/parsers.c
@@ -1,4 +1,4 @@
-const char parsers_rcs[] = "$Id: parsers.c,v 1.270 2012/12/07 12:43:55 fabiankeil Exp $";
+const char parsers_rcs[] = "$Id: parsers.c,v 1.271 2012/12/07 12:50:17 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/parsers.c,v $
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/parsers.c,v $
@@ -4212,6 +4212,44 @@ static jb_err parse_header_time(const char *header_time, time_t *result)
             continue;
          }
          *result = timegm(&gmt);
             continue;
          }
          *result = timegm(&gmt);
+
+#ifdef FEATURE_STRPTIME_SANITY_CHECKS
+         /*
+          * Verify that parsing the date recreated from the first
+          * parse operation gets the previous result. If it doesn't,
+          * either strptime() or strftime() are malfunctioning.
+          *
+          * We could string-compare the recreated date with the original
+          * header date, but this leads to false positives as strptime()
+          * may let %a accept all day formats while strftime() will only
+          * create one.
+          */
+         {
+            char recreated_date[100];
+            struct tm *tm;
+            time_t result2;
+
+            tm = gmtime(result);
+            strftime(recreated_date, sizeof(recreated_date), time_formats[i], tm);
+            memset(&gmt, 0, sizeof(gmt));
+            if (NULL == strptime(recreated_date, time_formats[i], &gmt))
+            {
+               log_error(LOG_LEVEL_ERROR,
+                  "Failed to parse '%s' generated with '%s' to recreate '%s'.",
+                  recreated_date, time_formats[i], header_time);
+               continue;
+            }
+            result2 = timegm(&gmt);
+            if (*result != result2)
+            {
+               log_error(LOG_LEVEL_ERROR, "strftime() and strptime() disagree. "
+                  "Format: '%s'. In: '%s', out: '%s'. %d != %d. Rejecting.",
+                  time_formats[i], header_time, recreated_date, *result, result2);
+               continue;
+            }
+         }
+#endif
+
          return JB_ERR_OK;
       }
    }
          return JB_ERR_OK;
       }
    }