escape.c (3569B)
1 /* Copyright (c) 2003, Roger Dingledine 2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. 3 * Copyright (c) 2007-2021, The Tor Project, Inc. */ 4 /* See LICENSE for licensing information */ 5 6 /** 7 * \file escape.c 8 * \brief Escape untrusted strings before sending them to the log. 9 **/ 10 11 #include "lib/log/escape.h" 12 #include "lib/log/util_bug.h" 13 #include "lib/string/compat_ctype.h" 14 #include "lib/string/printf.h" 15 #include "lib/malloc/malloc.h" 16 17 /** Allocate and return a new string representing the contents of <b>s</b>, 18 * surrounded by quotes and using standard C escapes. 19 * 20 * Generally, we use this for logging values that come in over the network to 21 * keep them from tricking users, and for sending certain values to the 22 * controller. 23 * 24 * We trust values from the resolver, OS, configuration file, and command line 25 * to not be maliciously ill-formed. We validate incoming routerdescs and 26 * SOCKS requests and addresses from BEGIN cells as they're parsed; 27 * afterwards, we trust them as non-malicious. 28 */ 29 char * 30 esc_for_log(const char *s) 31 { 32 const char *cp; 33 char *result, *outp; 34 size_t len = 3; 35 if (!s) { 36 return tor_strdup("(null)"); 37 } 38 39 for (cp = s; *cp; ++cp) { 40 switch (*cp) { 41 case '\\': 42 case '\"': 43 case '\'': 44 case '\r': 45 case '\n': 46 case '\t': 47 len += 2; 48 break; 49 default: 50 if (TOR_ISPRINT(*cp) && ((uint8_t)*cp)<127) 51 ++len; 52 else 53 len += 4; 54 break; 55 } 56 } 57 58 tor_assert(len <= SSIZE_MAX); 59 60 result = outp = tor_malloc(len); 61 *outp++ = '\"'; 62 for (cp = s; *cp; ++cp) { 63 /* This assertion should always succeed, since we will write at least 64 * one char here, and two chars for closing quote and nul later */ 65 tor_assert((outp-result) < (ssize_t)len-2); 66 switch (*cp) { 67 case '\\': 68 case '\"': 69 case '\'': 70 *outp++ = '\\'; 71 *outp++ = *cp; 72 break; 73 case '\n': 74 *outp++ = '\\'; 75 *outp++ = 'n'; 76 break; 77 case '\t': 78 *outp++ = '\\'; 79 *outp++ = 't'; 80 break; 81 case '\r': 82 *outp++ = '\\'; 83 *outp++ = 'r'; 84 break; 85 default: 86 if (TOR_ISPRINT(*cp) && ((uint8_t)*cp)<127) { 87 *outp++ = *cp; 88 } else { 89 tor_assert((outp-result) < (ssize_t)len-4); 90 tor_snprintf(outp, 5, "\\%03o", (int)(uint8_t) *cp); 91 outp += 4; 92 } 93 break; 94 } 95 } 96 97 tor_assert((outp-result) <= (ssize_t)len-2); 98 *outp++ = '\"'; 99 *outp++ = 0; 100 101 return result; 102 } 103 104 /** Similar to esc_for_log. Allocate and return a new string representing 105 * the first n characters in <b>chars</b>, surround by quotes and using 106 * standard C escapes. If a NUL character is encountered in <b>chars</b>, 107 * the resulting string will be terminated there. 108 */ 109 char * 110 esc_for_log_len(const char *chars, size_t n) 111 { 112 char *string = tor_strndup(chars, n); 113 char *string_escaped = esc_for_log(string); 114 tor_free(string); 115 return string_escaped; 116 } 117 118 /** Allocate and return a new string representing the contents of <b>s</b>, 119 * surrounded by quotes and using standard C escapes. 120 * 121 * THIS FUNCTION IS NOT REENTRANT. Don't call it from outside the main 122 * thread. Also, each call invalidates the last-returned value, so don't 123 * try log_warn(LD_GENERAL, "%s %s", escaped(a), escaped(b)); 124 */ 125 const char * 126 escaped(const char *s) 127 { 128 static char *escaped_val_ = NULL; 129 tor_free(escaped_val_); 130 131 if (s) 132 escaped_val_ = esc_for_log(s); 133 else 134 escaped_val_ = NULL; 135 136 return escaped_val_; 137 }