diff options
Diffstat (limited to 'src/lib/log/escape.c')
-rw-r--r-- | src/lib/log/escape.c | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/src/lib/log/escape.c b/src/lib/log/escape.c new file mode 100644 index 0000000000..6ca01c6963 --- /dev/null +++ b/src/lib/log/escape.c @@ -0,0 +1,137 @@ +/* Copyright (c) 2003, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file escape.c + * \brief Escape untrusted strings before sending them to the log. + **/ + +#include "lib/log/escape.h" +#include "lib/log/util_bug.h" +#include "lib/string/compat_ctype.h" +#include "lib/string/printf.h" +#include "lib/malloc/malloc.h" + +/** Allocate and return a new string representing the contents of <b>s</b>, + * surrounded by quotes and using standard C escapes. + * + * Generally, we use this for logging values that come in over the network to + * keep them from tricking users, and for sending certain values to the + * controller. + * + * We trust values from the resolver, OS, configuration file, and command line + * to not be maliciously ill-formed. We validate incoming routerdescs and + * SOCKS requests and addresses from BEGIN cells as they're parsed; + * afterwards, we trust them as non-malicious. + */ +char * +esc_for_log(const char *s) +{ + const char *cp; + char *result, *outp; + size_t len = 3; + if (!s) { + return tor_strdup("(null)"); + } + + for (cp = s; *cp; ++cp) { + switch (*cp) { + case '\\': + case '\"': + case '\'': + case '\r': + case '\n': + case '\t': + len += 2; + break; + default: + if (TOR_ISPRINT(*cp) && ((uint8_t)*cp)<127) + ++len; + else + len += 4; + break; + } + } + + tor_assert(len <= SSIZE_MAX); + + result = outp = tor_malloc(len); + *outp++ = '\"'; + for (cp = s; *cp; ++cp) { + /* This assertion should always succeed, since we will write at least + * one char here, and two chars for closing quote and nul later */ + tor_assert((outp-result) < (ssize_t)len-2); + switch (*cp) { + case '\\': + case '\"': + case '\'': + *outp++ = '\\'; + *outp++ = *cp; + break; + case '\n': + *outp++ = '\\'; + *outp++ = 'n'; + break; + case '\t': + *outp++ = '\\'; + *outp++ = 't'; + break; + case '\r': + *outp++ = '\\'; + *outp++ = 'r'; + break; + default: + if (TOR_ISPRINT(*cp) && ((uint8_t)*cp)<127) { + *outp++ = *cp; + } else { + tor_assert((outp-result) < (ssize_t)len-4); + tor_snprintf(outp, 5, "\\%03o", (int)(uint8_t) *cp); + outp += 4; + } + break; + } + } + + tor_assert((outp-result) <= (ssize_t)len-2); + *outp++ = '\"'; + *outp++ = 0; + + return result; +} + +/** Similar to esc_for_log. Allocate and return a new string representing + * the first n characters in <b>chars</b>, surround by quotes and using + * standard C escapes. If a NUL character is encountered in <b>chars</b>, + * the resulting string will be terminated there. + */ +char * +esc_for_log_len(const char *chars, size_t n) +{ + char *string = tor_strndup(chars, n); + char *string_escaped = esc_for_log(string); + tor_free(string); + return string_escaped; +} + +/** Allocate and return a new string representing the contents of <b>s</b>, + * surrounded by quotes and using standard C escapes. + * + * THIS FUNCTION IS NOT REENTRANT. Don't call it from outside the main + * thread. Also, each call invalidates the last-returned value, so don't + * try log_warn(LD_GENERAL, "%s %s", escaped(a), escaped(b)); + */ +const char * +escaped(const char *s) +{ + static char *escaped_val_ = NULL; + tor_free(escaped_val_); + + if (s) + escaped_val_ = esc_for_log(s); + else + escaped_val_ = NULL; + + return escaped_val_; +} |