File lighttpd-1.4.x_log_injection.patch of Package lighttpd.4364
commit 427120b41a141626dbb40a752c848f199fc9f7a8
Author: Stefan Bühler <stbuehler@web.de>
Date: Thu May 28 15:47:14 2015 +0000
escape all strings for logging (fixes #2646 log file injection, reported by Jaanus Kääp)
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
Index: lighttpd-1.4.35/src/buffer.c
===================================================================
--- lighttpd-1.4.35.orig/src/buffer.c 2017-03-06 15:44:36.464809153 +0100
+++ lighttpd-1.4.35/src/buffer.c 2017-03-06 18:00:53.509347023 +0100
@@ -853,6 +853,153 @@ int buffer_append_string_encoded(buffer
return 0;
}
+static size_t buffer_align_size(size_t size) {
+ size_t align = BUFFER_PIECE_SIZE - (size % BUFFER_PIECE_SIZE);
+ /* overflow on unsinged size_t is defined to wrap around */
+ if (size + align < size) return size;
+ return size + align;
+}
+
+int buffer_string_is_empty(const buffer *b) {
+ return 0 == buffer_string_length(b);
+}
+
+/* make sure buffer is at least "size" big. keep old data */
+static void buffer_realloc(buffer *b, size_t size) {
+ force_assert(NULL != b);
+ if (0 == size) size = 1;
+
+ if (size <= b->size) return;
+
+ b->size = buffer_align_size(size);
+ b->ptr = realloc(b->ptr, b->size);
+
+ force_assert(NULL != b->ptr);
+}
+
+static void buffer_alloc(buffer *b, size_t size) {
+ force_assert(NULL != b);
+ if (0 == size) size = 1;
+
+ if (size <= b->size) return;
+
+ if (NULL != b->ptr) free(b->ptr);
+
+ b->used = 0;
+ b->size = buffer_align_size(size);
+ b->ptr = malloc(b->size);
+
+ force_assert(NULL != b->ptr);
+}
+
+char* buffer_string_prepare_copy(buffer *b, size_t size) {
+ force_assert(NULL != b);
+ force_assert(size + 1 > size);
+
+ buffer_alloc(b, size + 1);
+
+ b->used = 1;
+ b->ptr[0] = '\0';
+
+ return b->ptr;
+}
+
+char* buffer_string_prepare_append(buffer *b, size_t size) {
+ force_assert(NULL != b);
+
+ if (buffer_string_is_empty(b)) {
+ return buffer_string_prepare_copy(b, size);
+ } else {
+ size_t req_size = b->used + size;
+
+ /* not empty, b->used already includes a terminating 0 */
+ force_assert(req_size >= b->used);
+
+ /* check for overflow: unsigned overflow is defined to wrap around */
+ force_assert(req_size >= b->used);
+
+ buffer_realloc(b, req_size);
+
+ return b->ptr + b->used - 1;
+ }
+}
+
+void buffer_commit(buffer *b, size_t size)
+{
+ force_assert(NULL != b);
+ force_assert(b->size > 0);
+
+ if (0 == b->used) b->used = 1;
+
+ if (size > 0) {
+ /* check for overflow: unsigned overflow is defined to wrap around */
+ force_assert(b->used + size > b->used);
+
+ force_assert(b->used + size <= b->size);
+ b->used += size;
+ }
+
+ b->ptr[b->used - 1] = '\0';
+}
+
+
+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;
+ }
+ }
+}
/* decodes url-special-chars inplace.
* replaces non-printable characters with '_'
Index: lighttpd-1.4.35/src/log.c
===================================================================
--- lighttpd-1.4.35.orig/src/log.c 2017-03-06 15:44:36.464809153 +0100
+++ lighttpd-1.4.35/src/log.c 2017-03-06 17:49:13.808419104 +0100
@@ -267,12 +267,12 @@ static void log_buffer_append_printf(buf
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(buf
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);
Index: lighttpd-1.4.35/src/buffer.h
===================================================================
--- lighttpd-1.4.35.orig/src/buffer.h 2017-03-06 15:44:36.464809153 +0100
+++ lighttpd-1.4.35/src/buffer.h 2017-03-06 18:07:49.409855576 +0100
@@ -33,6 +33,38 @@ typedef struct {
size_t size;
} read_buffer;
+/* reset b. if NULL != b && NULL != src, move src content to b. reset src. */
+void buffer_move(buffer *b, buffer *src);
+
+/* make sure buffer is large enough to store a string of given size
+ * and a terminating zero.
+ * sets b to an empty string, and may drop old content.
+ * @return b->ptr
+ */
+char* buffer_string_prepare_copy(buffer *b, size_t size);
+
+/* allocate buffer large enough to be able to append a string of given size
+ * if b was empty (used == 0) it will contain an empty string (used == 1)
+ * afterwards
+ * "used" data is preserved; if not empty buffer must contain a
+ * zero terminated string.
+ */
+char* buffer_string_prepare_append(buffer *b, size_t size);
+
+/* use after prepare_(copy,append) when you have written data to the buffer
+ * to increase the buffer length by size. also sets the terminating zero.
+ * requires enough space is present for the terminating zero (prepare with the
+ * same size to be sure).
+ */
+void buffer_commit(buffer *b, size_t size);
+int buffer_string_is_empty(const buffer *b);
+
+static inline size_t buffer_string_length(const buffer *b); /* buffer string length without terminating 0 */
+static inline size_t buffer_string_length(const buffer *b) {
+ return NULL != b && 0 != b->used ? b->used - 1 : 0;
+}
+
+
buffer_array* buffer_array_init(void);
void buffer_array_free(buffer_array *b);
void buffer_array_reset(buffer_array *b);
@@ -95,6 +127,8 @@ typedef enum {
} buffer_encoding_t;
int 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);
int buffer_urldecode_path(buffer *url);
int buffer_urldecode_query(buffer *url);
@@ -123,7 +157,8 @@ int light_isalnum(int c);
if (x->used > 1 && x->ptr[x->used - 2] != '/') { BUFFER_APPEND_STRING_CONST(x, "/"); }
#define CONST_STR_LEN(x) x, x ? sizeof(x) - 1 : 0
-#define CONST_BUF_LEN(x) x->ptr, x->used ? x->used - 1 : 0
+// #define CONST_BUF_LEN(x) x->ptr, x->used ? x->used - 1 : 0
+#define CONST_BUF_LEN(x) ((x) ? (x)->ptr : NULL), buffer_string_length(x)
#define UNUSED(x) ( (void)(x) )