aboutsummaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorDavid Goulet <dgoulet@torproject.org>2019-04-30 11:50:36 -0400
committerDavid Goulet <dgoulet@torproject.org>2019-04-30 11:50:36 -0400
commit43c119fedbdf599241cd2e3e4292ae5fd55d527c (patch)
tree9af1e66268831467018499a2dcf65a20e778509e /src/lib
parente543c4e20c50035df61cdfaae75ee4dbb61409bf (diff)
parenta5cced2b7acf4fb1818d6cd84c09a9ffe4dd1e89 (diff)
downloadtor-43c119fedbdf599241cd2e3e4292ae5fd55d527c.tar.gz
tor-43c119fedbdf599241cd2e3e4292ae5fd55d527c.zip
Merge branch 'tor-github/pr/980'
Signed-off-by: David Goulet <dgoulet@torproject.org>
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/encoding/confline.c13
-rw-r--r--src/lib/encoding/confline.h2
-rw-r--r--src/lib/encoding/include.am2
-rw-r--r--src/lib/encoding/kvline.c72
-rw-r--r--src/lib/encoding/kvline.h2
-rw-r--r--src/lib/encoding/qstring.c90
-rw-r--r--src/lib/encoding/qstring.h18
7 files changed, 188 insertions, 11 deletions
diff --git a/src/lib/encoding/confline.c b/src/lib/encoding/confline.c
index 8110f3dd9c..fdb575e03f 100644
--- a/src/lib/encoding/confline.c
+++ b/src/lib/encoding/confline.c
@@ -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.
diff --git a/src/lib/encoding/confline.h b/src/lib/encoding/confline.h
index 3d9ae8a662..56ea36bf61 100644
--- a/src/lib/encoding/confline.h
+++ b/src/lib/encoding/confline.h
@@ -48,6 +48,8 @@ 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);
+const config_line_t *config_line_find_case(const config_line_t *lines,
+ const char *key);
int config_lines_eq(config_line_t *a, config_line_t *b);
int config_count_key(const config_line_t *a, const char *key);
void config_free_lines_(config_line_t *front);
diff --git a/src/lib/encoding/include.am b/src/lib/encoding/include.am
index 83e9211b6f..8272e4e5fa 100644
--- a/src/lib/encoding/include.am
+++ b/src/lib/encoding/include.am
@@ -11,6 +11,7 @@ src_lib_libtor_encoding_a_SOURCES = \
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 = \
@@ -25,4 +26,5 @@ noinst_HEADERS += \
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/kvline.c b/src/lib/encoding/kvline.c
index 307adc3f12..d4a8f510ba 100644
--- a/src/lib/encoding/kvline.c
+++ b/src/lib/encoding/kvline.c
@@ -16,6 +16,7 @@
#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"
@@ -54,6 +55,15 @@ line_has_no_key(const config_line_t *line)
}
/**
+ * 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>.
**/
@@ -98,14 +108,25 @@ kvline_can_encode_lines(const config_line_t *line, unsigned flags)
* 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.
+ *
+ * KV_QUOTED_QSTRING is not supported.
*/
char *
kvline_encode(const config_line_t *line,
unsigned flags)
{
+ tor_assert(! (flags & KV_QUOTED_QSTRING));
+
if (!kvline_can_encode_lines(line, flags))
return NULL;
+ tor_assert((flags & (KV_OMIT_KEYS|KV_OMIT_VALS)) !=
+ (KV_OMIT_KEYS|KV_OMIT_VALS));
+
smartlist_t *elements = smartlist_new();
for (; line; line = line->next) {
@@ -126,7 +147,10 @@ kvline_encode(const config_line_t *line,
}
}
- if (esc) {
+ if ((flags & KV_OMIT_VALS) && line_has_no_val(line)) {
+ eq = "";
+ v = "";
+ } else if (esc) {
tmp = esc_for_log(line->value);
v = tmp;
} else {
@@ -151,17 +175,30 @@ kvline_encode(const config_line_t *line,
* 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. Otherwise, such values are not allowed.
+ * 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.
*/
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));
+
const char *cp = line, *cplast = NULL;
- bool omit_keys = (flags & KV_OMIT_KEYS) != 0;
- bool quoted = (flags & KV_QUOTED) != 0;
+ 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;
@@ -171,27 +208,33 @@ kvline_parse(const char *line, unsigned flags)
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, this code is broken. */
+ /* 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", V, and "V", depending on flags. */
+ /* Possible formats are K=V, K="V", K, V, and "V", depending on flags. */
- /* Find the key. */
+ /* 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;
@@ -203,7 +246,11 @@ kvline_parse(const char *line, unsigned flags)
if (!quoted)
goto err;
size_t len=0;
- cp = unescape_string(cp, &val, &len);
+ 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;
@@ -214,6 +261,7 @@ kvline_parse(const char *line, unsigned flags)
cp += idx;
}
+ commit:
if (key && strlen(key) == 0) {
/* We don't allow empty keys. */
goto err;
@@ -221,13 +269,15 @@ kvline_parse(const char *line, unsigned flags)
*next_line = tor_malloc_zero(sizeof(config_line_t));
(*next_line)->key = key ? key : tor_strdup("");
- (*next_line)->value = val;
+ (*next_line)->value = val ? val : tor_strdup("");
next_line = &(*next_line)->next;
key = val = NULL;
}
- if (!kvline_can_encode_lines(result, flags)) {
- goto err;
+ if (! (flags & KV_QUOTED_QSTRING)) {
+ if (!kvline_can_encode_lines(result, flags)) {
+ goto err;
+ }
}
return result;
diff --git a/src/lib/encoding/kvline.h b/src/lib/encoding/kvline.h
index 4eed30a223..dea2ce1809 100644
--- a/src/lib/encoding/kvline.h
+++ b/src/lib/encoding/kvline.h
@@ -17,6 +17,8 @@ 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)
struct config_line_t *kvline_parse(const char *line, unsigned flags);
char *kvline_encode(const struct config_line_t *line, unsigned flags);
diff --git a/src/lib/encoding/qstring.c b/src/lib/encoding/qstring.c
new file mode 100644
index 0000000000..a92d28c706
--- /dev/null
+++ b/src/lib/encoding/qstring.c
@@ -0,0 +1,90 @@
+/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, 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..fe15b655f1
--- /dev/null
+++ b/src/lib/encoding/qstring.h
@@ -0,0 +1,18 @@
+/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2019, 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