Line data Source code
1 : /* Copyright (c) 2001 Matej Pfajfar.
2 : * Copyright (c) 2001-2004, Roger Dingledine.
3 : * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
4 : * Copyright (c) 2007-2021, The Tor Project, Inc. */
5 : /* See LICENSE for licensing information */
6 :
7 : /**
8 : * \file kvline.c
9 : *
10 : * \brief Manipulating lines of key-value pairs.
11 : **/
12 :
13 : #include "orconfig.h"
14 :
15 : #include "lib/container/smartlist.h"
16 : #include "lib/encoding/confline.h"
17 : #include "lib/encoding/cstring.h"
18 : #include "lib/encoding/kvline.h"
19 : #include "lib/encoding/qstring.h"
20 : #include "lib/malloc/malloc.h"
21 : #include "lib/string/compat_ctype.h"
22 : #include "lib/string/printf.h"
23 : #include "lib/string/util_string.h"
24 : #include "lib/log/escape.h"
25 : #include "lib/log/util_bug.h"
26 :
27 : #include <stdbool.h>
28 : #include <stddef.h>
29 : #include <string.h>
30 :
31 : /** Return true iff we need to quote and escape the string <b>s</b> to encode
32 : * it.
33 : *
34 : * kvline_can_encode_lines() also uses this (with
35 : * <b>as_keyless_val</b> true) to check whether a key would require
36 : * quoting.
37 : */
38 : static bool
39 325 : needs_escape(const char *s, bool as_keyless_val)
40 : {
41 325 : if (as_keyless_val && *s == 0)
42 : return true;
43 : /* Keyless values containing '=' need to be escaped. */
44 325 : if (as_keyless_val && strchr(s, '='))
45 : return true;
46 :
47 1640 : for (; *s; ++s) {
48 1368 : if (*s >= 127 || TOR_ISSPACE(*s) || ! TOR_ISPRINT(*s) ||
49 1323 : *s == '\'' || *s == '\"') {
50 : return true;
51 : }
52 : }
53 : return false;
54 : }
55 :
56 : /**
57 : * Return true iff the key in <b>line</b> is not set.
58 : **/
59 : static bool
60 205 : line_has_no_key(const config_line_t *line)
61 : {
62 205 : return line->key == NULL || strlen(line->key) == 0;
63 : }
64 :
65 : /**
66 : * Return true iff the value in <b>line</b> is not set.
67 : **/
68 : static bool
69 11 : line_has_no_val(const config_line_t *line)
70 : {
71 11 : return line->value == NULL || strlen(line->value) == 0;
72 : }
73 :
74 : /**
75 : * Return true iff the all the lines in <b>line</b> can be encoded
76 : * using <b>flags</b>.
77 : **/
78 : static bool
79 71 : kvline_can_encode_lines(const config_line_t *line, unsigned flags)
80 : {
81 204 : for ( ; line; line = line->next) {
82 139 : const bool keyless = line_has_no_key(line);
83 139 : if (keyless && ! (flags & KV_OMIT_KEYS)) {
84 : /* If KV_OMIT_KEYS is not set, we can't encode a line with no key. */
85 : return false;
86 : }
87 :
88 138 : if (needs_escape(line->value, keyless) && ! (flags & (KV_QUOTED|KV_RAW))) {
89 : /* If both KV_QUOTED and KV_RAW are false, we can't encode a
90 : value that needs quotes. */
91 : return false;
92 : }
93 135 : if (!keyless && needs_escape(line->key, true)) {
94 : /* We can't handle keys that need quoting. */
95 : return false;
96 : }
97 : }
98 : return true;
99 : }
100 :
101 : /**
102 : * Encode a linked list of lines in <b>line</b> as a series of 'Key=Value'
103 : * pairs, using the provided <b>flags</b> to encode it. Return a newly
104 : * allocated string on success, or NULL on failure.
105 : *
106 : * If KV_QUOTED is set in <b>flags</b>, then all values that contain
107 : * spaces or unusual characters are escaped and quoted. Otherwise, such
108 : * values are not allowed. Mutually exclusive with KV_RAW.
109 : *
110 : * If KV_OMIT_KEYS is set in <b>flags</b>, then pairs with empty keys are
111 : * allowed, and are encoded as 'Value'. Otherwise, such pairs are not
112 : * allowed.
113 : *
114 : * If KV_OMIT_VALS is set in <b>flags</b>, then an empty value is
115 : * encoded as 'Key', not as 'Key=' or 'Key=""'. Mutually exclusive with
116 : * KV_OMIT_KEYS.
117 : *
118 : * If KV_RAW is set in <b>flags</b>, then don't apply any quoting to
119 : * the value, and assume that the caller has adequately quoted it.
120 : * (The control protocol has some quirks that make this necessary.)
121 : * Mutually exclusive with KV_QUOTED.
122 : *
123 : * KV_QUOTED_QSTRING is not supported.
124 : */
125 : char *
126 37 : kvline_encode(const config_line_t *line,
127 : unsigned flags)
128 : {
129 37 : tor_assert(! (flags & KV_QUOTED_QSTRING));
130 :
131 37 : tor_assert((flags & (KV_OMIT_KEYS|KV_OMIT_VALS)) !=
132 : (KV_OMIT_KEYS|KV_OMIT_VALS));
133 37 : tor_assert((flags & (KV_QUOTED|KV_RAW)) != (KV_QUOTED|KV_RAW));
134 :
135 37 : if (!kvline_can_encode_lines(line, flags))
136 : return NULL;
137 :
138 32 : smartlist_t *elements = smartlist_new();
139 :
140 130 : for (; line; line = line->next) {
141 :
142 66 : const char *k = "";
143 66 : const char *eq = "=";
144 66 : const char *v = "";
145 66 : const bool keyless = line_has_no_key(line);
146 66 : bool esc = needs_escape(line->value, keyless);
147 66 : char *tmp = NULL;
148 :
149 66 : if (! keyless) {
150 55 : k = line->key;
151 : } else {
152 : eq = "";
153 : }
154 :
155 66 : if ((flags & KV_OMIT_VALS) && line_has_no_val(line)) {
156 : eq = "";
157 : v = "";
158 62 : } else if (!(flags & KV_RAW) && esc) {
159 11 : tmp = esc_for_log(line->value);
160 11 : v = tmp;
161 : } else {
162 : v = line->value;
163 : }
164 :
165 66 : smartlist_add_asprintf(elements, "%s%s%s", k, eq, v);
166 66 : tor_free(tmp);
167 : }
168 :
169 32 : char *result = smartlist_join_strings(elements, " ", 0, NULL);
170 :
171 98 : SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp));
172 32 : smartlist_free(elements);
173 :
174 32 : return result;
175 : }
176 :
177 : /**
178 : * Decode a <b>line</b> containing a series of space-separated 'Key=Value'
179 : * pairs, using the provided <b>flags</b> to decode it. Return a newly
180 : * allocated list of pairs on success, or NULL on failure.
181 : *
182 : * If KV_QUOTED is set in <b>flags</b>, then (double-)quoted values are
183 : * allowed and handled as C strings. Otherwise, such values are not allowed.
184 : *
185 : * If KV_OMIT_KEYS is set in <b>flags</b>, then values without keys are
186 : * allowed. Otherwise, such values are not allowed.
187 : *
188 : * If KV_OMIT_VALS is set in <b>flags</b>, then keys without values are
189 : * allowed. Otherwise, such keys are not allowed. Mutually exclusive with
190 : * KV_OMIT_KEYS.
191 : *
192 : * If KV_QUOTED_QSTRING is set in <b>flags</b>, then double-quoted values
193 : * are allowed and handled as QuotedStrings per qstring.c. Do not add
194 : * new users of this flag.
195 : *
196 : * KV_RAW is not supported.
197 : */
198 : config_line_t *
199 37 : kvline_parse(const char *line, unsigned flags)
200 : {
201 37 : tor_assert((flags & (KV_OMIT_KEYS|KV_OMIT_VALS)) !=
202 : (KV_OMIT_KEYS|KV_OMIT_VALS));
203 37 : tor_assert(!(flags & KV_RAW));
204 :
205 37 : const char *cp = line, *cplast = NULL;
206 37 : const bool omit_keys = (flags & KV_OMIT_KEYS) != 0;
207 37 : const bool omit_vals = (flags & KV_OMIT_VALS) != 0;
208 37 : const bool quoted = (flags & (KV_QUOTED|KV_QUOTED_QSTRING)) != 0;
209 37 : const bool c_quoted = (flags & (KV_QUOTED)) != 0;
210 :
211 37 : config_line_t *result = NULL;
212 37 : config_line_t **next_line = &result;
213 :
214 37 : char *key = NULL;
215 37 : char *val = NULL;
216 :
217 106 : while (*cp) {
218 75 : key = val = NULL;
219 : /* skip all spaces */
220 : {
221 75 : size_t idx = strspn(cp, " \t\r\v\n");
222 75 : cp += idx;
223 : }
224 75 : if (BUG(cp == cplast)) {
225 : /* If we didn't parse anything since the last loop, this code is
226 : * broken. */
227 : goto err; // LCOV_EXCL_LINE
228 : }
229 75 : cplast = cp;
230 75 : if (! *cp)
231 : break; /* End of string; we're done. */
232 :
233 : /* Possible formats are K=V, K="V", K, V, and "V", depending on flags. */
234 :
235 : /* Find where the key ends */
236 72 : if (*cp != '\"') {
237 69 : size_t idx = strcspn(cp, " \t\r\v\n=");
238 :
239 69 : if (cp[idx] == '=') {
240 59 : key = tor_memdup_nulterm(cp, idx);
241 59 : cp += idx + 1;
242 10 : } else if (omit_vals) {
243 8 : key = tor_memdup_nulterm(cp, idx);
244 8 : cp += idx;
245 8 : goto commit;
246 : } else {
247 2 : if (!omit_keys)
248 1 : goto err;
249 : }
250 : }
251 :
252 63 : if (*cp == '\"') {
253 : /* The type is "V". */
254 17 : if (!quoted)
255 1 : goto err;
256 16 : size_t len=0;
257 16 : if (c_quoted) {
258 16 : cp = unescape_string(cp, &val, &len);
259 : } else {
260 0 : cp = decode_qstring(cp, strlen(cp), &val, &len);
261 : }
262 16 : if (cp == NULL || len != strlen(val)) {
263 : // The string contains a NUL or is badly coded.
264 0 : goto err;
265 : }
266 : } else {
267 46 : size_t idx = strcspn(cp, " \t\r\v\n");
268 46 : val = tor_memdup_nulterm(cp, idx);
269 46 : cp += idx;
270 : }
271 :
272 70 : commit:
273 70 : if (key && strlen(key) == 0) {
274 : /* We don't allow empty keys. */
275 1 : goto err;
276 : }
277 :
278 69 : *next_line = tor_malloc_zero(sizeof(config_line_t));
279 69 : (*next_line)->key = key ? key : tor_strdup("");
280 69 : (*next_line)->value = val ? val : tor_strdup("");
281 69 : next_line = &(*next_line)->next;
282 69 : key = val = NULL;
283 : }
284 :
285 34 : if (! (flags & KV_QUOTED_QSTRING)) {
286 34 : if (!kvline_can_encode_lines(result, flags)) {
287 1 : goto err;
288 : }
289 : }
290 33 : return result;
291 :
292 4 : err:
293 4 : tor_free(key);
294 4 : tor_free(val);
295 4 : config_free_lines(result);
296 4 : return NULL;
297 : }
|