diff options
Diffstat (limited to 'src/lib/encoding')
-rw-r--r-- | src/lib/encoding/.may_include | 1 | ||||
-rw-r--r-- | src/lib/encoding/binascii.c | 26 | ||||
-rw-r--r-- | src/lib/encoding/binascii.h | 5 | ||||
-rw-r--r-- | src/lib/encoding/confline.c | 51 | ||||
-rw-r--r-- | src/lib/encoding/confline.h | 7 | ||||
-rw-r--r-- | src/lib/encoding/cstring.c | 2 | ||||
-rw-r--r-- | src/lib/encoding/cstring.h | 2 | ||||
-rw-r--r-- | src/lib/encoding/include.am | 6 | ||||
-rw-r--r-- | src/lib/encoding/keyval.c | 2 | ||||
-rw-r--r-- | src/lib/encoding/keyval.h | 4 | ||||
-rw-r--r-- | src/lib/encoding/kvline.c | 297 | ||||
-rw-r--r-- | src/lib/encoding/kvline.h | 27 | ||||
-rw-r--r-- | src/lib/encoding/lib_encoding.md | 6 | ||||
-rw-r--r-- | src/lib/encoding/pem.c | 4 | ||||
-rw-r--r-- | src/lib/encoding/pem.h | 4 | ||||
-rw-r--r-- | src/lib/encoding/qstring.c | 90 | ||||
-rw-r--r-- | src/lib/encoding/qstring.h | 18 | ||||
-rw-r--r-- | src/lib/encoding/time_fmt.c | 37 | ||||
-rw-r--r-- | src/lib/encoding/time_fmt.h | 8 |
19 files changed, 569 insertions, 28 deletions
diff --git a/src/lib/encoding/.may_include b/src/lib/encoding/.may_include index 7c2ef36929..c9bf4b1786 100644 --- a/src/lib/encoding/.may_include +++ b/src/lib/encoding/.may_include @@ -1,5 +1,6 @@ orconfig.h lib/cc/*.h +lib/container/*.h lib/ctime/*.h lib/encoding/*.h lib/intmath/*.h diff --git a/src/lib/encoding/binascii.c b/src/lib/encoding/binascii.c index bd063440d6..5f68da183f 100644 --- a/src/lib/encoding/binascii.c +++ b/src/lib/encoding/binascii.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001, Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -84,7 +84,7 @@ base32_encode(char *dest, size_t destlen, const char *src, size_t srclen) } /** Implements base32 decoding as in RFC 4648. - * Returns 0 if successful, -1 otherwise. + * Return the number of bytes decoded if successful; -1 otherwise. */ int base32_decode(char *dest, size_t destlen, const char *src, size_t srclen) @@ -147,7 +147,7 @@ base32_decode(char *dest, size_t destlen, const char *src, size_t srclen) memset(tmp, 0, srclen); /* on the heap, this should be safe */ tor_free(tmp); tmp = NULL; - return 0; + return i; } #define BASE64_OPENSSL_LINELEN 64 @@ -179,6 +179,18 @@ base64_encode_size(size_t srclen, int flags) return enclen; } +/** Return an upper bound on the number of bytes that might be needed to hold + * the data from decoding the base64 string <b>srclen</b>. This is only an + * upper bound, since some part of the base64 string might be padding or + * space. */ +size_t +base64_decode_maxsize(size_t srclen) +{ + tor_assert(srclen < INT_MAX / 3); + + return CEIL_DIV(srclen * 3, 4); +} + /** Internal table mapping 6 bit values to the Base64 alphabet. */ static const char base64_encode_table[64] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', @@ -263,7 +275,7 @@ base64_encode(char *dest, size_t destlen, const char *src, size_t srclen, } switch (n_idx) { case 0: - /* 0 leftover bits, no pading to add. */ + /* 0 leftover bits, no padding to add. */ break; case 1: /* 8 leftover bits, pad to 12 bits, write the 2 6-bit values followed @@ -309,8 +321,10 @@ base64_encode(char *dest, size_t destlen, const char *src, size_t srclen, return (int) enclen; } -/** As base64_encode, but do not add any internal spaces or external padding - * to the output stream. */ +/** As base64_encode, but do not add any internal spaces, and remove external + * padding from the output stream. + * dest must be at least base64_encode_size(srclen, 0), including space for + * the removed external padding. */ int base64_encode_nopad(char *dest, size_t destlen, const uint8_t *src, size_t srclen) diff --git a/src/lib/encoding/binascii.h b/src/lib/encoding/binascii.h index 7e3cc04f09..9cb03bab62 100644 --- a/src/lib/encoding/binascii.h +++ b/src/lib/encoding/binascii.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001, Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -42,6 +42,7 @@ const char *hex_str(const char *from, size_t fromlen); #define BASE64_ENCODE_MULTILINE 1 size_t base64_encode_size(size_t srclen, int flags); +size_t base64_decode_maxsize(size_t srclen); int base64_encode(char *dest, size_t destlen, const char *src, size_t srclen, int flags); int base64_decode(char *dest, size_t destlen, const char *src, size_t srclen); @@ -57,4 +58,4 @@ size_t base32_encoded_size(size_t srclen); void base16_encode(char *dest, size_t destlen, const char *src, size_t srclen); int base16_decode(char *dest, size_t destlen, const char *src, size_t srclen); -#endif /* !defined(TOR_UTIL_FORMAT_H) */ +#endif /* !defined(TOR_BINASCII_H) */ diff --git a/src/lib/encoding/confline.c b/src/lib/encoding/confline.c index 8110f3dd9c..613e4a00c6 100644 --- a/src/lib/encoding/confline.c +++ b/src/lib/encoding/confline.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -82,6 +82,19 @@ config_line_find(const config_line_t *lines, return NULL; } +/** As config_line_find(), but perform a case-insensitive comparison. */ +const config_line_t * +config_line_find_case(const config_line_t *lines, + const char *key) +{ + const config_line_t *cl; + for (cl = lines; cl; cl = cl->next) { + if (!strcasecmp(cl->key, key)) + return cl; + } + return NULL; +} + /** Auxiliary function that does all the work of config_get_lines. * <b>recursion_level</b> is the count of how many nested %includes we have. * <b>opened_lst</b> will have a list of opened files if provided. @@ -138,6 +151,8 @@ config_get_lines_aux(const char *string, config_line_t **result, int extended, if (allow_include && !strcmp(k, "%include") && handle_include) { tor_free(k); include_used = 1; + log_notice(LD_CONFIG, "Processing configuration path \"%s\" at " + "recursion level %d.", v, recursion_level); config_line_t *include_list; if (handle_include(v, recursion_level, extended, &include_list, @@ -148,9 +163,6 @@ config_get_lines_aux(const char *string, config_line_t **result, int extended, tor_free(v); return -1; } - log_notice(LD_CONFIG, "Included configuration file or " - "directory at recursion level %d: \"%s\".", - recursion_level, v); *next = include_list; if (list_last) next = &list_last->next; @@ -240,10 +252,39 @@ config_lines_dup_and_filter(const config_line_t *inp, return result; } +/** + * Given a linelist <b>inp</b> beginning with the key <b>header</b>, find the + * next line with that key, and remove that instance and all following lines + * from the list. Return the lines that were removed. Operate + * case-insensitively. + * + * For example, if the header is "H", and <b>inp</b> contains "H, A, B, H, C, + * H, D", this function will alter <b>inp</b> to contain only "H, A, B", and + * return the elements "H, C, H, D" as a separate list. + **/ +config_line_t * +config_lines_partition(config_line_t *inp, const char *header) +{ + if (BUG(inp == NULL)) + return NULL; + if (BUG(strcasecmp(inp->key, header))) + return NULL; + + /* Advance ptr until it points to the link to the next segment of this + list. */ + config_line_t **ptr = &inp->next; + while (*ptr && strcasecmp((*ptr)->key, header)) { + ptr = &(*ptr)->next; + } + config_line_t *remainder = *ptr; + *ptr = NULL; + return remainder; +} + /** Return true iff a and b contain identical keys and values in identical * order. */ int -config_lines_eq(config_line_t *a, config_line_t *b) +config_lines_eq(const config_line_t *a, const config_line_t *b) { while (a && b) { if (strcasecmp(a->key, b->key) || strcmp(a->value, b->value)) diff --git a/src/lib/encoding/confline.h b/src/lib/encoding/confline.h index 3d9ae8a662..ce0d6c6e17 100644 --- a/src/lib/encoding/confline.h +++ b/src/lib/encoding/confline.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -48,7 +48,10 @@ config_line_t *config_lines_dup_and_filter(const config_line_t *inp, const char *key); const config_line_t *config_line_find(const config_line_t *lines, const char *key); -int config_lines_eq(config_line_t *a, config_line_t *b); +const config_line_t *config_line_find_case(const config_line_t *lines, + const char *key); +config_line_t *config_lines_partition(config_line_t *inp, const char *header); +int config_lines_eq(const config_line_t *a, const config_line_t *b); int config_count_key(const config_line_t *a, const char *key); void config_free_lines_(config_line_t *front); #define config_free_lines(front) \ diff --git a/src/lib/encoding/cstring.c b/src/lib/encoding/cstring.c index 29d3714126..54c330fca3 100644 --- a/src/lib/encoding/cstring.c +++ b/src/lib/encoding/cstring.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/lib/encoding/cstring.h b/src/lib/encoding/cstring.h index 904a2c9c1c..2a3f6d0fc4 100644 --- a/src/lib/encoding/cstring.h +++ b/src/lib/encoding/cstring.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/lib/encoding/include.am b/src/lib/encoding/include.am index 2d2aa3988a..48d0120bfc 100644 --- a/src/lib/encoding/include.am +++ b/src/lib/encoding/include.am @@ -4,12 +4,15 @@ if UNITTESTS_ENABLED noinst_LIBRARIES += src/lib/libtor-encoding-testing.a endif +# ADD_C_FILE: INSERT SOURCES HERE. src_lib_libtor_encoding_a_SOURCES = \ src/lib/encoding/binascii.c \ src/lib/encoding/confline.c \ src/lib/encoding/cstring.c \ src/lib/encoding/keyval.c \ + src/lib/encoding/kvline.c \ src/lib/encoding/pem.c \ + src/lib/encoding/qstring.c \ src/lib/encoding/time_fmt.c src_lib_libtor_encoding_testing_a_SOURCES = \ @@ -17,10 +20,13 @@ src_lib_libtor_encoding_testing_a_SOURCES = \ src_lib_libtor_encoding_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS) src_lib_libtor_encoding_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) +# ADD_C_FILE: INSERT HEADERS HERE. noinst_HEADERS += \ src/lib/encoding/binascii.h \ src/lib/encoding/confline.h \ src/lib/encoding/cstring.h \ src/lib/encoding/keyval.h \ + src/lib/encoding/kvline.h \ src/lib/encoding/pem.h \ + src/lib/encoding/qstring.h \ src/lib/encoding/time_fmt.h diff --git a/src/lib/encoding/keyval.c b/src/lib/encoding/keyval.c index c5da5a0bfc..0eb1219d43 100644 --- a/src/lib/encoding/keyval.c +++ b/src/lib/encoding/keyval.c @@ -1,6 +1,6 @@ /* Copyright (c) 2003, Roger Dingledine * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/lib/encoding/keyval.h b/src/lib/encoding/keyval.h index cd327b7a82..b4966b01de 100644 --- a/src/lib/encoding/keyval.h +++ b/src/lib/encoding/keyval.h @@ -1,6 +1,6 @@ /* Copyright (c) 2003, Roger Dingledine * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -14,4 +14,4 @@ int string_is_key_value(int severity, const char *string); -#endif +#endif /* !defined(TOR_KEYVAL_H) */ diff --git a/src/lib/encoding/kvline.c b/src/lib/encoding/kvline.c new file mode 100644 index 0000000000..5b220605d6 --- /dev/null +++ b/src/lib/encoding/kvline.c @@ -0,0 +1,297 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file kvline.c + * + * \brief Manipulating lines of key-value pairs. + **/ + +#include "orconfig.h" + +#include "lib/container/smartlist.h" +#include "lib/encoding/confline.h" +#include "lib/encoding/cstring.h" +#include "lib/encoding/kvline.h" +#include "lib/encoding/qstring.h" +#include "lib/malloc/malloc.h" +#include "lib/string/compat_ctype.h" +#include "lib/string/printf.h" +#include "lib/string/util_string.h" +#include "lib/log/escape.h" +#include "lib/log/util_bug.h" + +#include <stdbool.h> +#include <stddef.h> +#include <string.h> + +/** Return true iff we need to quote and escape the string <b>s</b> to encode + * it. + * + * kvline_can_encode_lines() also uses this (with + * <b>as_keyless_val</b> true) to check whether a key would require + * quoting. + */ +static bool +needs_escape(const char *s, bool as_keyless_val) +{ + if (as_keyless_val && *s == 0) + return true; + /* Keyless values containing '=' need to be escaped. */ + if (as_keyless_val && strchr(s, '=')) + return true; + + for (; *s; ++s) { + if (*s >= 127 || TOR_ISSPACE(*s) || ! TOR_ISPRINT(*s) || + *s == '\'' || *s == '\"') { + return true; + } + } + return false; +} + +/** + * Return true iff the key in <b>line</b> is not set. + **/ +static bool +line_has_no_key(const config_line_t *line) +{ + return line->key == NULL || strlen(line->key) == 0; +} + +/** + * Return true iff the value in <b>line</b> is not set. + **/ +static bool +line_has_no_val(const config_line_t *line) +{ + return line->value == NULL || strlen(line->value) == 0; +} + +/** + * Return true iff the all the lines in <b>line</b> can be encoded + * using <b>flags</b>. + **/ +static bool +kvline_can_encode_lines(const config_line_t *line, unsigned flags) +{ + for ( ; line; line = line->next) { + const bool keyless = line_has_no_key(line); + if (keyless && ! (flags & KV_OMIT_KEYS)) { + /* If KV_OMIT_KEYS is not set, we can't encode a line with no key. */ + return false; + } + + if (needs_escape(line->value, keyless) && ! (flags & (KV_QUOTED|KV_RAW))) { + /* If both KV_QUOTED and KV_RAW are false, we can't encode a + value that needs quotes. */ + return false; + } + if (!keyless && needs_escape(line->key, true)) { + /* We can't handle keys that need quoting. */ + return false; + } + } + return true; +} + +/** + * Encode a linked list of lines in <b>line</b> as a series of 'Key=Value' + * pairs, using the provided <b>flags</b> to encode it. Return a newly + * allocated string on success, or NULL on failure. + * + * If KV_QUOTED is set in <b>flags</b>, then all values that contain + * spaces or unusual characters are escaped and quoted. Otherwise, such + * values are not allowed. Mutually exclusive with KV_RAW. + * + * If KV_OMIT_KEYS is set in <b>flags</b>, then pairs with empty keys are + * allowed, and are encoded as 'Value'. Otherwise, such pairs are not + * allowed. + * + * If KV_OMIT_VALS is set in <b>flags</b>, then an empty value is + * encoded as 'Key', not as 'Key=' or 'Key=""'. Mutually exclusive with + * KV_OMIT_KEYS. + * + * If KV_RAW is set in <b>flags</b>, then don't apply any quoting to + * the value, and assume that the caller has adequately quoted it. + * (The control protocol has some quirks that make this necessary.) + * Mutually exclusive with KV_QUOTED. + * + * KV_QUOTED_QSTRING is not supported. + */ +char * +kvline_encode(const config_line_t *line, + unsigned flags) +{ + tor_assert(! (flags & KV_QUOTED_QSTRING)); + + tor_assert((flags & (KV_OMIT_KEYS|KV_OMIT_VALS)) != + (KV_OMIT_KEYS|KV_OMIT_VALS)); + tor_assert((flags & (KV_QUOTED|KV_RAW)) != (KV_QUOTED|KV_RAW)); + + if (!kvline_can_encode_lines(line, flags)) + return NULL; + + smartlist_t *elements = smartlist_new(); + + for (; line; line = line->next) { + + const char *k = ""; + const char *eq = "="; + const char *v = ""; + const bool keyless = line_has_no_key(line); + bool esc = needs_escape(line->value, keyless); + char *tmp = NULL; + + if (! keyless) { + k = line->key; + } else { + eq = ""; + } + + if ((flags & KV_OMIT_VALS) && line_has_no_val(line)) { + eq = ""; + v = ""; + } else if (!(flags & KV_RAW) && esc) { + tmp = esc_for_log(line->value); + v = tmp; + } else { + v = line->value; + } + + smartlist_add_asprintf(elements, "%s%s%s", k, eq, v); + tor_free(tmp); + } + + char *result = smartlist_join_strings(elements, " ", 0, NULL); + + SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp)); + smartlist_free(elements); + + return result; +} + +/** + * Decode a <b>line</b> containing a series of space-separated 'Key=Value' + * pairs, using the provided <b>flags</b> to decode it. Return a newly + * allocated list of pairs on success, or NULL on failure. + * + * If KV_QUOTED is set in <b>flags</b>, then (double-)quoted values are + * allowed and handled as C strings. Otherwise, such values are not allowed. + * + * If KV_OMIT_KEYS is set in <b>flags</b>, then values without keys are + * allowed. Otherwise, such values are not allowed. + * + * If KV_OMIT_VALS is set in <b>flags</b>, then keys without values are + * allowed. Otherwise, such keys are not allowed. Mutually exclusive with + * KV_OMIT_KEYS. + * + * If KV_QUOTED_QSTRING is set in <b>flags</b>, then double-quoted values + * are allowed and handled as QuotedStrings per qstring.c. Do not add + * new users of this flag. + * + * KV_RAW is not supported. + */ +config_line_t * +kvline_parse(const char *line, unsigned flags) +{ + tor_assert((flags & (KV_OMIT_KEYS|KV_OMIT_VALS)) != + (KV_OMIT_KEYS|KV_OMIT_VALS)); + tor_assert(!(flags & KV_RAW)); + + const char *cp = line, *cplast = NULL; + const bool omit_keys = (flags & KV_OMIT_KEYS) != 0; + const bool omit_vals = (flags & KV_OMIT_VALS) != 0; + const bool quoted = (flags & (KV_QUOTED|KV_QUOTED_QSTRING)) != 0; + const bool c_quoted = (flags & (KV_QUOTED)) != 0; + + config_line_t *result = NULL; + config_line_t **next_line = &result; + + char *key = NULL; + char *val = NULL; + + while (*cp) { + key = val = NULL; + /* skip all spaces */ + { + size_t idx = strspn(cp, " \t\r\v\n"); + cp += idx; + } + if (BUG(cp == cplast)) { + /* If we didn't parse anything since the last loop, this code is + * broken. */ + goto err; // LCOV_EXCL_LINE + } + cplast = cp; + if (! *cp) + break; /* End of string; we're done. */ + + /* Possible formats are K=V, K="V", K, V, and "V", depending on flags. */ + + /* Find where the key ends */ + if (*cp != '\"') { + size_t idx = strcspn(cp, " \t\r\v\n="); + + if (cp[idx] == '=') { + key = tor_memdup_nulterm(cp, idx); + cp += idx + 1; + } else if (omit_vals) { + key = tor_memdup_nulterm(cp, idx); + cp += idx; + goto commit; + } else { + if (!omit_keys) + goto err; + } + } + + if (*cp == '\"') { + /* The type is "V". */ + if (!quoted) + goto err; + size_t len=0; + if (c_quoted) { + cp = unescape_string(cp, &val, &len); + } else { + cp = decode_qstring(cp, strlen(cp), &val, &len); + } + if (cp == NULL || len != strlen(val)) { + // The string contains a NUL or is badly coded. + goto err; + } + } else { + size_t idx = strcspn(cp, " \t\r\v\n"); + val = tor_memdup_nulterm(cp, idx); + cp += idx; + } + + commit: + if (key && strlen(key) == 0) { + /* We don't allow empty keys. */ + goto err; + } + + *next_line = tor_malloc_zero(sizeof(config_line_t)); + (*next_line)->key = key ? key : tor_strdup(""); + (*next_line)->value = val ? val : tor_strdup(""); + next_line = &(*next_line)->next; + key = val = NULL; + } + + if (! (flags & KV_QUOTED_QSTRING)) { + if (!kvline_can_encode_lines(result, flags)) { + goto err; + } + } + return result; + + err: + tor_free(key); + tor_free(val); + config_free_lines(result); + return NULL; +} diff --git a/src/lib/encoding/kvline.h b/src/lib/encoding/kvline.h new file mode 100644 index 0000000000..34c52908e3 --- /dev/null +++ b/src/lib/encoding/kvline.h @@ -0,0 +1,27 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file kvline.h + * + * \brief Header for kvline.c + **/ + +#ifndef TOR_KVLINE_H +#define TOR_KVLINE_H + +struct config_line_t; + +#define KV_QUOTED (1u<<0) +#define KV_OMIT_KEYS (1u<<1) +#define KV_OMIT_VALS (1u<<2) +#define KV_QUOTED_QSTRING (1u<<3) +#define KV_RAW (1u<<4) + +struct config_line_t *kvline_parse(const char *line, unsigned flags); +char *kvline_encode(const struct config_line_t *line, unsigned flags); + +#endif /* !defined(TOR_KVLINE_H) */ diff --git a/src/lib/encoding/lib_encoding.md b/src/lib/encoding/lib_encoding.md new file mode 100644 index 0000000000..66dd9d8caf --- /dev/null +++ b/src/lib/encoding/lib_encoding.md @@ -0,0 +1,6 @@ +@dir /lib/encoding +@brief lib/encoding: Encoding data in various forms, types, and transformations + +Here we have time formats (timefmt.c), quoted strings (qstring.c), C strings +(string.c) base-16/32/64 (binascii.c), and more. + diff --git a/src/lib/encoding/pem.c b/src/lib/encoding/pem.c index 51f37d0840..6c9f10e085 100644 --- a/src/lib/encoding/pem.c +++ b/src/lib/encoding/pem.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001, Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -42,7 +42,7 @@ pem_encoded_size(size_t src_len, const char *objtype) /** * PEM-encode the <b>srclen</b>-byte object at <b>src</b> into the - * <b>destlen<\b>-byte buffer at <b>dest</b>, tagging it with <b>objtype</b>. + * <b>destlen</b>-byte buffer at <b>dest</b>, tagging it with <b>objtype</b>. * Return 0 on success and -1 on failure. */ int diff --git a/src/lib/encoding/pem.h b/src/lib/encoding/pem.h index 0bbb06a794..027c31c315 100644 --- a/src/lib/encoding/pem.h +++ b/src/lib/encoding/pem.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001, Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -23,4 +23,4 @@ int pem_encode(char *dest, size_t destlen, const uint8_t *src, size_t srclen, int pem_decode(uint8_t *dest, size_t destlen, const char *src, size_t srclen, const char *objtype); -#endif +#endif /* !defined(TOR_PEM_H) */ diff --git a/src/lib/encoding/qstring.c b/src/lib/encoding/qstring.c new file mode 100644 index 0000000000..5a34924eab --- /dev/null +++ b/src/lib/encoding/qstring.c @@ -0,0 +1,90 @@ +/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file qstring.c + * \brief Implement QuotedString parsing. + * + * Note that this is only used for controller authentication; do not + * create new users for this. Instead, prefer the cstring.c functions. + **/ + +#include "orconfig.h" +#include "lib/encoding/qstring.h" +#include "lib/malloc/malloc.h" +#include "lib/log/util_bug.h" + +/** If the first <b>in_len_max</b> characters in <b>start</b> contain a + * QuotedString, return the length of that + * string (as encoded, including quotes). Otherwise return -1. */ +static inline int +get_qstring_length(const char *start, size_t in_len_max, + int *chars_out) +{ + const char *cp, *end; + int chars = 0; + + if (*start != '\"') + return -1; + + cp = start+1; + end = start+in_len_max; + + /* Calculate length. */ + while (1) { + if (cp >= end) { + return -1; /* Too long. */ + } else if (*cp == '\\') { + if (++cp == end) + return -1; /* Can't escape EOS. */ + ++cp; + ++chars; + } else if (*cp == '\"') { + break; + } else { + ++cp; + ++chars; + } + } + if (chars_out) + *chars_out = chars; + return (int)(cp - start+1); +} + +/** Given a pointer to a string starting at <b>start</b> containing + * <b>in_len_max</b> characters, decode a string beginning with one double + * quote, containing any number of non-quote characters or characters escaped + * with a backslash, and ending with a final double quote. Place the resulting + * string (unquoted, unescaped) into a newly allocated string in *<b>out</b>; + * store its length in <b>out_len</b>. On success, return a pointer to the + * character immediately following the escaped string. On failure, return + * NULL. */ +const char * +decode_qstring(const char *start, size_t in_len_max, + char **out, size_t *out_len) +{ + const char *cp, *end; + char *outp; + int len, n_chars = 0; + + len = get_qstring_length(start, in_len_max, &n_chars); + if (len<0) + return NULL; + + end = start+len-1; /* Index of last quote. */ + tor_assert(*end == '\"'); + outp = *out = tor_malloc(len+1); + *out_len = n_chars; + + cp = start+1; + while (cp < end) { + if (*cp == '\\') + ++cp; + *outp++ = *cp++; + } + *outp = '\0'; + tor_assert((outp - *out) == (int)*out_len); + + return end+1; +} diff --git a/src/lib/encoding/qstring.h b/src/lib/encoding/qstring.h new file mode 100644 index 0000000000..f19a7dad87 --- /dev/null +++ b/src/lib/encoding/qstring.h @@ -0,0 +1,18 @@ +/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file qstring.h + * \brief Header for qstring.c + */ + +#ifndef TOR_ENCODING_QSTRING_H +#define TOR_ENCODING_QSTRING_H + +#include <stddef.h> + +const char *decode_qstring(const char *start, size_t in_len_max, + char **out, size_t *out_len); + +#endif /* !defined(TOR_ENCODING_QSTRING_H) */ diff --git a/src/lib/encoding/time_fmt.c b/src/lib/encoding/time_fmt.c index 40543d41e0..5e58d36698 100644 --- a/src/lib/encoding/time_fmt.c +++ b/src/lib/encoding/time_fmt.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001, Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -13,6 +13,7 @@ * and handles a larger variety of types. It converts between different time * formats, and encodes and decodes them from strings. **/ +#define TIME_FMT_PRIVATE #include "lib/encoding/time_fmt.h" #include "lib/log/log.h" @@ -25,6 +26,7 @@ #include <string.h> #include <time.h> +#include <errno.h> #ifdef HAVE_SYS_TIME_H #include <sys/time.h> @@ -92,8 +94,8 @@ static const int days_per_month[] = /** Compute a time_t given a struct tm. The result is given in UTC, and * does not account for leap seconds. Return 0 on success, -1 on failure. */ -int -tor_timegm(const struct tm *tm, time_t *time_out) +ATTR_UNUSED STATIC int +tor_timegm_impl(const struct tm *tm, time_t *time_out) { /* This is a pretty ironclad timegm implementation, snarfed from Python2.2. * It's way more brute-force than fiddling with tzset(). @@ -162,6 +164,35 @@ tor_timegm(const struct tm *tm, time_t *time_out) return 0; } +/** Compute a time_t given a struct tm. The result here should be an inverse + * of the system's gmtime() function. Return 0 on success, -1 on failure. + */ +int +tor_timegm(const struct tm *tm, time_t *time_out) +{ +#ifdef HAVE_TIMEGM + /* If the system gives us a timegm(), use it: if the system's time_t + * includes leap seconds, then we can hope that its timegm() knows too. + * + * https://k5wiki.kerberos.org/wiki/Leap_second_handling says the in + * general we can rely on any system with leap seconds also having a + * timegm implementation. Let's hope it's right! + * */ + time_t result = timegm((struct tm *) tm); + if (result == -1) { + log_warn(LD_BUG, "timegm() could not convert time: %s", strerror(errno)); + *time_out = 0; + return -1; + } else { + *time_out = result; + return 0; + } +#else + /* The system doesn't have timegm; we'll have to use our own. */ + return tor_timegm_impl(tm, time_out); +#endif +} + /* strftime is locale-specific, so we need to replace those parts */ /** A c-locale array of 3-letter names of weekdays, starting with Sun. */ diff --git a/src/lib/encoding/time_fmt.h b/src/lib/encoding/time_fmt.h index 0ddeca57fc..4adccb5990 100644 --- a/src/lib/encoding/time_fmt.h +++ b/src/lib/encoding/time_fmt.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001, Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -18,6 +18,8 @@ #include <sys/types.h> #endif +#include "lib/testsupport/testsupport.h" + struct tm; struct timeval; @@ -41,4 +43,8 @@ int parse_iso_time_nospace(const char *cp, time_t *t); int parse_http_time(const char *buf, struct tm *tm); int format_time_interval(char *out, size_t out_len, long interval); +#ifdef TIME_FMT_PRIVATE +STATIC int tor_timegm_impl(const struct tm *tm, time_t *time_out); #endif + +#endif /* !defined(TOR_TIME_FMT_H) */ |