/src/tor/src/lib/log/escape.c
Line | Count | Source |
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 | 4.90M | { |
32 | 4.90M | const char *cp; |
33 | 4.90M | char *result, *outp; |
34 | 4.90M | size_t len = 3; |
35 | 4.90M | if (!s) { |
36 | 0 | return tor_strdup("(null)"); |
37 | 0 | } |
38 | | |
39 | 43.8M | for (cp = s; *cp; ++cp) { |
40 | 38.9M | switch (*cp) { |
41 | 4.75k | case '\\': |
42 | 22.4k | case '\"': |
43 | 95.0k | case '\'': |
44 | 107k | case '\r': |
45 | 111k | case '\n': |
46 | 119k | case '\t': |
47 | 119k | len += 2; |
48 | 119k | break; |
49 | 38.8M | default: |
50 | 38.8M | if (TOR_ISPRINT(*cp) && ((uint8_t)*cp)<127) |
51 | 9.35M | ++len; |
52 | 29.4M | else |
53 | 29.4M | len += 4; |
54 | 38.8M | break; |
55 | 38.9M | } |
56 | 38.9M | } |
57 | | |
58 | 4.90M | tor_assert(len <= SSIZE_MAX); |
59 | | |
60 | 4.90M | result = outp = tor_malloc(len); |
61 | 4.90M | *outp++ = '\"'; |
62 | 43.8M | 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 | 38.9M | tor_assert((outp-result) < (ssize_t)len-2); |
66 | 38.9M | switch (*cp) { |
67 | 4.75k | case '\\': |
68 | 22.4k | case '\"': |
69 | 95.0k | case '\'': |
70 | 95.0k | *outp++ = '\\'; |
71 | 95.0k | *outp++ = *cp; |
72 | 95.0k | break; |
73 | 3.53k | case '\n': |
74 | 3.53k | *outp++ = '\\'; |
75 | 3.53k | *outp++ = 'n'; |
76 | 3.53k | break; |
77 | 8.42k | case '\t': |
78 | 8.42k | *outp++ = '\\'; |
79 | 8.42k | *outp++ = 't'; |
80 | 8.42k | break; |
81 | 12.6k | case '\r': |
82 | 12.6k | *outp++ = '\\'; |
83 | 12.6k | *outp++ = 'r'; |
84 | 12.6k | break; |
85 | 38.8M | default: |
86 | 38.8M | if (TOR_ISPRINT(*cp) && ((uint8_t)*cp)<127) { |
87 | 9.35M | *outp++ = *cp; |
88 | 29.4M | } else { |
89 | 29.4M | tor_assert((outp-result) < (ssize_t)len-4); |
90 | 29.4M | tor_snprintf(outp, 5, "\\%03o", (int)(uint8_t) *cp); |
91 | 29.4M | outp += 4; |
92 | 29.4M | } |
93 | 38.8M | break; |
94 | 38.9M | } |
95 | 38.9M | } |
96 | | |
97 | 4.90M | tor_assert((outp-result) <= (ssize_t)len-2); |
98 | 4.90M | *outp++ = '\"'; |
99 | 4.90M | *outp++ = 0; |
100 | | |
101 | 4.90M | return result; |
102 | 4.90M | } |
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 | 0 | { |
112 | 0 | char *string = tor_strndup(chars, n); |
113 | 0 | char *string_escaped = esc_for_log(string); |
114 | 0 | tor_free(string); |
115 | 0 | return string_escaped; |
116 | 0 | } |
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 | 717k | { |
128 | 717k | static char *escaped_val_ = NULL; |
129 | 717k | tor_free(escaped_val_); |
130 | | |
131 | 717k | if (s) |
132 | 700k | escaped_val_ = esc_for_log(s); |
133 | 16.9k | else |
134 | 16.9k | escaped_val_ = NULL; |
135 | | |
136 | 717k | return escaped_val_; |
137 | 717k | } |