aboutsummaryrefslogtreecommitdiffstats
path: root/main/lighttpd/0029-escape-all-strings-for-logging-fixes-2646-log-file-i.patch
blob: 20307fb204a4c9c736ae0ea078071763e2272c8c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
From 427120b41a141626dbb40a752c848f199fc9f7a8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20B=C3=BChler?= <stbuehler@web.de>
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 <stbuehler@web.de>

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