From 427120b41a141626dbb40a752c848f199fc9f7a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20B=C3=BChler?= Date: Thu, 28 May 2015 15:47:14 +0000 Subject: [PATCH 29/29] =?UTF-8?q?escape=20all=20strings=20for=20logging=20?= =?UTF-8?q?(fixes=20#2646=20log=20file=20injection,=20reported=20by=20Jaan?= =?UTF-8?q?us=20K=C3=A4=C3=A4p)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From: Stefan Bühler git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-1.4.x@2989 152afb58-edef-0310-8abb-c4023f1b3aa9 --- NEWS | 5 +++-- src/buffer.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/buffer.h | 5 ++++- src/log.c | 8 ++++---- 4 files changed, 70 insertions(+), 7 deletions(-) diff --git a/NEWS b/NEWS index dd2d1b8..18007fc 100644 --- a/NEWS +++ b/NEWS @@ -19,6 +19,7 @@ NEWS * [mod_auth] use crypt_r instead of crypt if available * fix error message for T_CONFIG_ARRAY config values if an entry value is not a string * fix segfaults in many plugins if they failed configuration + * escape all strings for logging (fixes #2646 log file injection, reported by Jaanus Kääp) - 1.4.35 - 2014-03-12 * [network/ssl] fix build error if TLSEXT is disabled @@ -557,10 +558,10 @@ NEWS * ignore empty packets from STDERR stream. #998 * fix a crash for files with an mtime of 0 reported by cubiq on irc [1519] CVE-2007-1870 - * allow empty passwords with ldap (Jörg Sonnenberger) [1516] + * allow empty passwords with ldap (Jörg Sonnenberger) [1516] * mod_scgi.c segfault fix #964 [1501] * Added round-robin support to mod_fastcgi [1500] - * Handle DragonFlyBSD the same way as Freebsd (Jörg Sonnenberger) [1492,1676] + * Handle DragonFlyBSD the same way as Freebsd (Jörg Sonnenberger) [1492,1676] * added now and weeks support to mod_expire. #943 * fix cpu hog in certain requests [1473] CVE-2007-1869 * fix for handling hostnames with trailing dot [1406] diff --git a/src/buffer.c b/src/buffer.c index 57c1613..36995a0 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -731,6 +731,65 @@ void buffer_append_string_encoded(buffer *b, const char *s, size_t s_len, buffer } } +void buffer_append_string_c_escaped(buffer *b, const char *s, size_t s_len) { + unsigned char *ds, *d; + size_t d_len, ndx; + + force_assert(NULL != b); + force_assert(NULL != s || 0 == s_len); + + if (0 == s_len) return; + + /* count to-be-encoded-characters */ + for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) { + if ((*ds < 0x20) /* control character */ + || (*ds >= 0x7f)) { /* DEL + non-ASCII characters */ + switch (*ds) { + case '\t': + case '\r': + case '\n': + d_len += 2; + break; + default: + d_len += 4; /* \xCC */ + break; + } + } else { + d_len++; + } + } + + d = (unsigned char*) buffer_string_prepare_append(b, d_len); + buffer_commit(b, d_len); /* fill below */ + force_assert('\0' == *d); + + for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) { + if ((*ds < 0x20) /* control character */ + || (*ds >= 0x7f)) { /* DEL + non-ASCII characters */ + d[d_len++] = '\\'; + switch (*ds) { + case '\t': + d[d_len++] = 't'; + break; + case '\r': + d[d_len++] = 'r'; + break; + case '\n': + d[d_len++] = 'n'; + break; + default: + d[d_len++] = 'x'; + d[d_len++] = hex_chars[((*ds) >> 4) & 0x0F]; + d[d_len++] = hex_chars[(*ds) & 0x0F]; + break; + } + } else { + d[d_len++] = *ds; + } + } +} + + void buffer_copy_string_encoded_cgi_varnames(buffer *b, const char *s, size_t s_len, int is_http_header) { size_t i, j; diff --git a/src/buffer.h b/src/buffer.h index b6065d4..5f659df 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -133,6 +133,9 @@ typedef enum { void buffer_append_string_encoded(buffer *b, const char *s, size_t s_len, buffer_encoding_t encoding); +/* escape non-printable characters; simple escapes for \t, \r, \n; fallback to \xCC */ +void buffer_append_string_c_escaped(buffer *b, const char *s, size_t s_len); + /* to upper case, replace non alpha-numerics with '_'; if is_http_header prefix with "HTTP_" unless s is "content-type" */ void buffer_copy_string_encoded_cgi_varnames(buffer *b, const char *s, size_t s_len, int is_http_header); @@ -164,7 +167,7 @@ static inline void buffer_append_slash(buffer *b); /* append '/' no non-empty st buffer_copy_string_len(x, y, sizeof(y) - 1) #define CONST_STR_LEN(x) x, (x) ? sizeof(x) - 1 : 0 -#define CONST_BUF_LEN(x) (x)->ptr, buffer_string_length(x) +#define CONST_BUF_LEN(x) ((x) ? (x)->ptr : NULL), buffer_string_length(x) #define UNUSED(x) ( (void)(x) ) diff --git a/src/log.c b/src/log.c index 9322d2c..94f4710 100644 --- a/src/log.c +++ b/src/log.c @@ -267,12 +267,12 @@ static void log_buffer_append_printf(buffer *out, const char *fmt, va_list ap) { switch(*fmt) { case 's': /* string */ s = va_arg(ap, char *); - buffer_append_string(out, s); + buffer_append_string_c_escaped(out, s, (NULL != s) ? strlen(s) : 0); buffer_append_string_len(out, CONST_STR_LEN(" ")); break; case 'b': /* buffer */ b = va_arg(ap, buffer *); - buffer_append_string_buffer(out, b); + buffer_append_string_c_escaped(out, CONST_BUF_LEN(b)); buffer_append_string_len(out, CONST_STR_LEN(" ")); break; case 'd': /* int */ @@ -293,11 +293,11 @@ static void log_buffer_append_printf(buffer *out, const char *fmt, va_list ap) { break; case 'S': /* string */ s = va_arg(ap, char *); - buffer_append_string(out, s); + buffer_append_string_c_escaped(out, s, (NULL != s) ? strlen(s) : 0); break; case 'B': /* buffer */ b = va_arg(ap, buffer *); - buffer_append_string_buffer(out, b); + buffer_append_string_c_escaped(out, CONST_BUF_LEN(b)); break; case 'D': /* int */ d = va_arg(ap, int); -- 2.4.5