summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/compat_libevent.c9
-rw-r--r--src/common/compat_libevent.h1
-rw-r--r--src/common/compress.c49
-rw-r--r--src/common/compress.h1
-rw-r--r--src/common/compress_lzma.c13
-rw-r--r--src/common/compress_none.c53
-rw-r--r--src/common/compress_none.h20
-rw-r--r--src/common/confline.c211
-rw-r--r--src/common/confline.h4
-rw-r--r--src/common/include.am4
-rw-r--r--src/common/sandbox.c1
-rw-r--r--src/common/util.c35
-rw-r--r--src/common/util.h1
-rw-r--r--src/common/util_format.h8
14 files changed, 375 insertions, 35 deletions
diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c
index 1146d02168..31eb4ac496 100644
--- a/src/common/compat_libevent.c
+++ b/src/common/compat_libevent.c
@@ -280,6 +280,15 @@ tor_gettimeofday_cache_set(const struct timeval *tv)
tor_assert(tv);
memcpy(&cached_time_hires, tv, sizeof(*tv));
}
+
+/** For testing: called post-fork to make libevent reinitialize
+ * kernel structures. */
+void
+tor_libevent_postfork(void)
+{
+ int r = event_reinit(tor_libevent_get_base());
+ tor_assert(r == 0);
+}
#endif
#endif
diff --git a/src/common/compat_libevent.h b/src/common/compat_libevent.h
index 96c48d53a1..904938415c 100644
--- a/src/common/compat_libevent.h
+++ b/src/common/compat_libevent.h
@@ -54,6 +54,7 @@ void tor_gettimeofday_cached(struct timeval *tv);
void tor_gettimeofday_cache_clear(void);
#ifdef TOR_UNIT_TESTS
void tor_gettimeofday_cache_set(const struct timeval *tv);
+void tor_libevent_postfork(void);
#endif
#ifdef COMPAT_LIBEVENT_PRIVATE
diff --git a/src/common/compress.c b/src/common/compress.c
index 8502dee25c..6513029f9c 100644
--- a/src/common/compress.c
+++ b/src/common/compress.c
@@ -24,6 +24,7 @@
#include "torlog.h"
#include "compress.h"
#include "compress_lzma.h"
+#include "compress_none.h"
#include "compress_zlib.h"
#include "compress_zstd.h"
@@ -67,8 +68,12 @@ guess_compress_size(int compress, compress_method_t method,
size_t in_len)
{
// ignore these for now.
- (void)method;
(void)compression_level;
+ if (method == NO_METHOD) {
+ /* Guess that we'll need an extra byte, to avoid a needless realloc
+ * for nul-termination */
+ return (in_len < SIZE_MAX) ? in_len + 1 : in_len;
+ }
/* Always guess a factor of 2. */
if (compress) {
@@ -255,8 +260,8 @@ detect_compression_method(const char *in, size_t in_len)
} else if (in_len > 2 && (in[0] & 0x0f) == 8 &&
(ntohs(get_uint16(in)) % 31) == 0) {
return ZLIB_METHOD;
- } else if (in_len > 3 &&
- fast_memeq(in, "\x5d\x00\x00\x00", 4)) {
+ } else if (in_len > 2 &&
+ fast_memeq(in, "\x5d\x00\x00", 3)) {
return LZMA_METHOD;
} else if (in_len > 3 &&
fast_memeq(in, "\x28\xb5\x2f\xfd", 4)) {
@@ -279,6 +284,7 @@ tor_compress_supports_method(compress_method_t method)
case ZSTD_METHOD:
return tor_zstd_method_supported();
case NO_METHOD:
+ return 1;
case UNKNOWN_METHOD:
default:
return 0;
@@ -313,7 +319,9 @@ static const struct {
} compression_method_names[] = {
{ "gzip", GZIP_METHOD },
{ "deflate", ZLIB_METHOD },
- { "x-lzma", LZMA_METHOD },
+ // We call this "x-tor-lzma" rather than "x-lzma", because we impose a
+ // lower maximum memory usage on the decoding side.
+ { "x-tor-lzma", LZMA_METHOD },
{ "x-zstd" , ZSTD_METHOD },
{ "identity", NO_METHOD },
@@ -335,6 +343,32 @@ compression_method_get_name(compress_method_t method)
return NULL;
}
+/** Table of compression human readable method names. */
+static const struct {
+ compress_method_t method;
+ const char *name;
+} compression_method_human_names[] = {
+ { NO_METHOD, "uncompressed" },
+ { GZIP_METHOD, "gzipped" },
+ { ZLIB_METHOD, "deflated" },
+ { LZMA_METHOD, "LZMA compressed" },
+ { ZSTD_METHOD, "Zstandard compressed" },
+ { UNKNOWN_METHOD, "unknown encoding" },
+};
+
+/** Return a human readable string representation of the compression method
+ * <b>method</b>, or NULL if the method isn't recognized. */
+const char *
+compression_method_get_human_name(compress_method_t method)
+{
+ unsigned i;
+ for (i = 0; i < ARRAY_LENGTH(compression_method_human_names); ++i) {
+ if (method == compression_method_human_names[i].method)
+ return compression_method_human_names[i].name;
+ }
+ return NULL;
+}
+
/** Return the compression method represented by the string <b>name</b>, or
* UNKNOWN_METHOD if the string isn't recognized. */
compress_method_t
@@ -456,7 +490,9 @@ tor_compress_new(int compress, compress_method_t method,
state->u.zstd_state = zstd_state;
break;
}
- case NO_METHOD:
+ case NO_METHOD: {
+ break;
+ }
case UNKNOWN_METHOD:
goto err;
}
@@ -504,6 +540,8 @@ tor_compress_process(tor_compress_state_t *state,
out, out_len, in, in_len,
finish);
case NO_METHOD:
+ return tor_cnone_compress_process(out, out_len, in, in_len,
+ finish);
case UNKNOWN_METHOD:
goto err;
}
@@ -531,6 +569,7 @@ tor_compress_free(tor_compress_state_t *state)
tor_zstd_compress_free(state->u.zstd_state);
break;
case NO_METHOD:
+ break;
case UNKNOWN_METHOD:
break;
}
diff --git a/src/common/compress.h b/src/common/compress.h
index 5b47c5d458..7c0dc14061 100644
--- a/src/common/compress.h
+++ b/src/common/compress.h
@@ -50,6 +50,7 @@ int tor_compress_is_compression_bomb(size_t size_in, size_t size_out);
int tor_compress_supports_method(compress_method_t method);
unsigned tor_compress_get_supported_method_bitmask(void);
const char * compression_method_get_name(compress_method_t method);
+const char *compression_method_get_human_name(compress_method_t method);
compress_method_t compression_method_get_by_name(const char *name);
const char *tor_compress_version_str(compress_method_t method);
diff --git a/src/common/compress_lzma.c b/src/common/compress_lzma.c
index b721bae8aa..b5393a6ba6 100644
--- a/src/common/compress_lzma.c
+++ b/src/common/compress_lzma.c
@@ -22,6 +22,9 @@
#include <lzma.h>
#endif
+/** The maximum amount of memory we allow the LZMA decoder to use, in bytes. */
+#define MEMORY_LIMIT (16 * 1024 * 1024)
+
/** Total number of bytes allocated for LZMA state. */
static atomic_counter_t total_lzma_allocation;
@@ -33,9 +36,9 @@ memory_level(compression_level_t level)
switch (level) {
default:
case BEST_COMPRESSION:
- case HIGH_COMPRESSION: return 9;
- case MEDIUM_COMPRESSION: return 6;
- case LOW_COMPRESSION: return 3;
+ case HIGH_COMPRESSION: return 6;
+ case MEDIUM_COMPRESSION: return 4;
+ case LOW_COMPRESSION: return 2;
}
}
@@ -191,9 +194,7 @@ tor_lzma_compress_new(int compress,
goto err;
}
} else {
- // FIXME(ahf): This should be something more sensible than
- // UINT64_MAX: See #21665.
- retval = lzma_alone_decoder(&result->stream, UINT64_MAX);
+ retval = lzma_alone_decoder(&result->stream, MEMORY_LIMIT);
if (retval != LZMA_OK) {
log_warn(LD_GENERAL, "Error from LZMA decoder: %s (%u).",
diff --git a/src/common/compress_none.c b/src/common/compress_none.c
new file mode 100644
index 0000000000..b76e6010ec
--- /dev/null
+++ b/src/common/compress_none.c
@@ -0,0 +1,53 @@
+/* Copyright (c) 2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file compress_lzma.c
+ * \brief Compression backend for identity compression.
+ *
+ * We actually define this backend so that we can treat the identity transform
+ * as another case of compression.
+ *
+ * This module should never be invoked directly. Use the compress module
+ * instead.
+ **/
+
+#include "orconfig.h"
+
+#include "util.h"
+#include "torlog.h"
+#include "compress.h"
+#include "compress_none.h"
+
+/** Transfer some bytes using the identity transformation. Read up to
+ * *<b>in_len</b> bytes from *<b>in</b>, and write up to *<b>out_len</b> bytes
+ * to *<b>out</b>, adjusting the values as we go. If <b>finish</b> is true,
+ * we've reached the end of the input.
+ *
+ * Return TOR_COMPRESS_DONE if we've finished the entire
+ * compression/decompression.
+ * Return TOR_COMPRESS_OK if we're processed everything from the input.
+ * Return TOR_COMPRESS_BUFFER_FULL if we're out of space on <b>out</b>.
+ * Return TOR_COMPRESS_ERROR if the stream is corrupt.
+ */
+tor_compress_output_t
+tor_cnone_compress_process(char **out, size_t *out_len,
+ const char **in, size_t *in_len,
+ int finish)
+{
+ size_t n_to_copy = MIN(*in_len, *out_len);
+
+ memcpy(*out, *in, n_to_copy);
+ *out += n_to_copy;
+ *in += n_to_copy;
+ *out_len -= n_to_copy;
+ *in_len -= n_to_copy;
+ if (*in_len == 0) {
+ return finish ? TOR_COMPRESS_DONE : TOR_COMPRESS_OK;
+ } else {
+ return TOR_COMPRESS_BUFFER_FULL;
+ }
+}
+
diff --git a/src/common/compress_none.h b/src/common/compress_none.h
new file mode 100644
index 0000000000..d1ebb4b625
--- /dev/null
+++ b/src/common/compress_none.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 2003, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file compress_none.h
+ * \brief Header for compress_none.c
+ **/
+
+#ifndef TOR_COMPRESS_NONE_H
+#define TOR_COMPRESS_NONE_H
+
+tor_compress_output_t
+tor_cnone_compress_process(char **out, size_t *out_len,
+ const char **in, size_t *in_len,
+ int finish);
+
+#endif // TOR_COMPRESS_NONE_H.
+
diff --git a/src/common/confline.c b/src/common/confline.c
index d4468f80ea..e078b1da04 100644
--- a/src/common/confline.c
+++ b/src/common/confline.c
@@ -8,6 +8,19 @@
#include "confline.h"
#include "torlog.h"
#include "util.h"
+#include "container.h"
+
+static int config_get_lines_aux(const char *string, config_line_t **result,
+ int extended, int allow_include,
+ int *has_include, int recursion_level,
+ config_line_t **last);
+static smartlist_t *config_get_file_list(const char *path);
+static int config_get_included_list(const char *path, int recursion_level,
+ int extended, config_line_t **list,
+ config_line_t **list_last);
+static int config_process_include(const char *path, int recursion_level,
+ int extended, config_line_t ***next,
+ config_line_t **list_last);
/** Helper: allocate a new configuration option mapping 'key' to 'val',
* append it to *<b>lst</b>. */
@@ -65,19 +78,25 @@ config_line_find(const config_line_t *lines,
return NULL;
}
-/** Helper: parse the config string and strdup into key/value
- * strings. Set *result to the list, or NULL if parsing the string
- * failed. Return 0 on success, -1 on failure. Warn and ignore any
- * misformatted lines.
- *
- * If <b>extended</b> is set, then treat keys beginning with / and with + as
- * indicating "clear" and "append" respectively. */
-int
-config_get_lines(const char *string, config_line_t **result, int extended)
+/** 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.
+ * Returns the a pointer to the last element of the <b>result</b> in
+ * <b>last</b>. */
+static int
+config_get_lines_aux(const char *string, config_line_t **result, int extended,
+ int allow_include, int *has_include, int recursion_level,
+ config_line_t **last)
{
- config_line_t *list = NULL, **next;
+ config_line_t *list = NULL, **next, *list_last = NULL;
char *k, *v;
const char *parse_err;
+ int include_used = 0;
+
+ if (recursion_level > MAX_INCLUDE_RECURSION_LEVEL) {
+ log_warn(LD_CONFIG, "Error while parsing configuration: more than %d "
+ "nested %%includes.", MAX_INCLUDE_RECURSION_LEVEL);
+ return -1;
+ }
next = &list;
do {
@@ -108,25 +127,179 @@ config_get_lines(const char *string, config_line_t **result, int extended)
command = CONFIG_LINE_CLEAR;
}
}
- /* This list can get long, so we keep a pointer to the end of it
- * rather than using config_line_append over and over and getting
- * n^2 performance. */
- *next = tor_malloc_zero(sizeof(config_line_t));
- (*next)->key = k;
- (*next)->value = v;
- (*next)->next = NULL;
- (*next)->command = command;
- next = &((*next)->next);
+
+ if (allow_include && !strcmp(k, "%include")) {
+ tor_free(k);
+ include_used = 1;
+
+ if (config_process_include(v, recursion_level, extended, &next,
+ &list_last) < 0) {
+ log_warn(LD_CONFIG, "Error reading included configuration "
+ "file or directory: \"%s\".", v);
+ config_free_lines(list);
+ tor_free(v);
+ return -1;
+ }
+ tor_free(v);
+ } else {
+ /* This list can get long, so we keep a pointer to the end of it
+ * rather than using config_line_append over and over and getting
+ * n^2 performance. */
+ *next = tor_malloc_zero(sizeof(**next));
+ (*next)->key = k;
+ (*next)->value = v;
+ (*next)->next = NULL;
+ (*next)->command = command;
+ list_last = *next;
+ next = &((*next)->next);
+ }
} else {
tor_free(k);
tor_free(v);
}
} while (*string);
+ if (last) {
+ *last = list_last;
+ }
+ if (has_include) {
+ *has_include = include_used;
+ }
*result = list;
return 0;
}
+/** Helper: parse the config string and strdup into key/value
+ * strings. Set *result to the list, or NULL if parsing the string
+ * failed. Set *has_include to 1 if <b>result</b> has values from
+ * %included files. Return 0 on success, -1 on failure. Warn and ignore any
+ * misformatted lines.
+ *
+ * If <b>extended</b> is set, then treat keys beginning with / and with + as
+ * indicating "clear" and "append" respectively. */
+int
+config_get_lines_include(const char *string, config_line_t **result,
+ int extended, int *has_include)
+{
+ return config_get_lines_aux(string, result, extended, 1, has_include, 1,
+ NULL);
+}
+
+/** Same as config_get_lines_include but does not allow %include */
+int
+config_get_lines(const char *string, config_line_t **result, int extended)
+{
+ return config_get_lines_aux(string, result, extended, 0, NULL, 1, NULL);
+}
+
+/** Adds a list of configuration files present on <b>path</b> to
+ * <b>file_list</b>. <b>path</b> can be a file or a directory. If it is a file,
+ * only that file will be added to <b>file_list</b>. If it is a directory,
+ * all paths for files on that directory root (no recursion) except for files
+ * whose name starts with a dot will be added to <b>file_list</b>.
+ * Return 0 on success, -1 on failure. Ignores empty files.
+ */
+static smartlist_t *
+config_get_file_list(const char *path)
+{
+ smartlist_t *file_list = smartlist_new();
+ file_status_t file_type = file_status(path);
+ if (file_type == FN_FILE) {
+ smartlist_add_strdup(file_list, path);
+ return file_list;
+ } else if (file_type == FN_DIR) {
+ smartlist_t *all_files = tor_listdir(path);
+ if (!all_files) {
+ smartlist_free(file_list);
+ return NULL;
+ }
+ smartlist_sort_strings(all_files);
+ SMARTLIST_FOREACH_BEGIN(all_files, char *, f) {
+ if (f[0] == '.') {
+ tor_free(f);
+ continue;
+ }
+
+ char *fullname;
+ tor_asprintf(&fullname, "%s"PATH_SEPARATOR"%s", path, f);
+ tor_free(f);
+
+ if (file_status(fullname) != FN_FILE) {
+ tor_free(fullname);
+ continue;
+ }
+ smartlist_add(file_list, fullname);
+ } SMARTLIST_FOREACH_END(f);
+ smartlist_free(all_files);
+ return file_list;
+ } else if (file_type == FN_EMPTY) {
+ return file_list;
+ } else {
+ smartlist_free(file_list);
+ return NULL;
+ }
+}
+
+/** Creates a list of config lines present on included <b>path</b>.
+ * Set <b>list</b> to the list and <b>list_last</b> to the last element of
+ * <b>list</b>. Return 0 on success, -1 on failure. */
+static int
+config_get_included_list(const char *path, int recursion_level, int extended,
+ config_line_t **list, config_line_t **list_last)
+{
+ char *included_conf = read_file_to_str(path, 0, NULL);
+ if (!included_conf) {
+ return -1;
+ }
+
+ if (config_get_lines_aux(included_conf, list, extended, 1, NULL,
+ recursion_level+1, list_last) < 0) {
+ tor_free(included_conf);
+ return -1;
+ }
+
+ tor_free(included_conf);
+ return 0;
+}
+
+/** Process an %include <b>path</b> in a config file. Set <b>next</b> to a
+ * pointer to the next pointer of the last element of the config_line_t list
+ * obtained from the config file and <b>list_last</b> to the last element of
+ * the same list. Return 0 on success, -1 on failure. */
+static int
+config_process_include(const char *path, int recursion_level, int extended,
+ config_line_t ***next, config_line_t **list_last)
+{
+ char *unquoted_path = get_unquoted_path(path);
+ if (!unquoted_path) {
+ return -1;
+ }
+
+ smartlist_t *config_files = config_get_file_list(unquoted_path);
+ if (!config_files) {
+ tor_free(unquoted_path);
+ return -1;
+ }
+ tor_free(unquoted_path);
+
+ SMARTLIST_FOREACH_BEGIN(config_files, char *, config_file) {
+ config_line_t *included_list = NULL;
+ if (config_get_included_list(config_file, recursion_level, extended,
+ &included_list, list_last) < 0) {
+ SMARTLIST_FOREACH(config_files, char *, f, tor_free(f));
+ smartlist_free(config_files);
+ return -1;
+ }
+ tor_free(config_file);
+
+ **next = included_list;
+ *next = &(*list_last)->next;
+
+ } SMARTLIST_FOREACH_END(config_file);
+ smartlist_free(config_files);
+ return 0;
+}
+
/**
* Free all the configuration lines on the linked list <b>front</b>.
*/
diff --git a/src/common/confline.h b/src/common/confline.h
index 477c6929a2..eb863e8fd8 100644
--- a/src/common/confline.h
+++ b/src/common/confline.h
@@ -15,6 +15,8 @@
/* Removes all previous configuration for an option. */
#define CONFIG_LINE_CLEAR 2
+#define MAX_INCLUDE_RECURSION_LEVEL 31
+
/** A linked list of lines in a config file, or elsewhere */
typedef struct config_line_t {
char *key;
@@ -41,6 +43,8 @@ const config_line_t *config_line_find(const config_line_t *lines,
int config_lines_eq(config_line_t *a, config_line_t *b);
int config_count_key(const config_line_t *a, const char *key);
int config_get_lines(const char *string, config_line_t **result, int extended);
+int config_get_lines_include(const char *string, config_line_t **result,
+ int extended, int *has_include);
void config_free_lines(config_line_t *front);
const char *parse_config_line_from_str_verbose(const char *line,
char **key_out, char **value_out,
diff --git a/src/common/include.am b/src/common/include.am
index b37b363b82..1253888815 100644
--- a/src/common/include.am
+++ b/src/common/include.am
@@ -112,6 +112,7 @@ LIBOR_CRYPTO_A_SRC = \
src/common/aes.c \
src/common/compress.c \
src/common/compress_lzma.c \
+ src/common/compress_none.c \
src/common/compress_zlib.c \
src/common/compress_zstd.c \
src/common/crypto.c \
@@ -154,8 +155,9 @@ COMMONHEADERS = \
src/common/compat_rust.h \
src/common/compat_threads.h \
src/common/compat_time.h \
- src/common/compress.h \
+ src/common/compress.h \
src/common/compress_lzma.h \
+ src/common/compress_none.h \
src/common/compress_zlib.h \
src/common/compress_zstd.h \
src/common/confline.h \
diff --git a/src/common/sandbox.c b/src/common/sandbox.c
index ab2de52592..7826b2d40c 100644
--- a/src/common/sandbox.c
+++ b/src/common/sandbox.c
@@ -155,6 +155,7 @@ static int filter_nopar_gen[] = {
#ifdef __NR_getgid32
SCMP_SYS(getgid32),
#endif
+ SCMP_SYS(getpid),
#ifdef __NR_getrlimit
SCMP_SYS(getrlimit),
#endif
diff --git a/src/common/util.c b/src/common/util.c
index ca2a0c4a9c..5c455585e7 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -3045,6 +3045,41 @@ unescape_string(const char *s, char **result, size_t *size_out)
}
}
+/** Removes enclosing quotes from <b>path</b> and unescapes quotes between the
+ * enclosing quotes. Backslashes are not unescaped. Return the unquoted
+ * <b>path</b> on sucess or 0 if <b>path</b> is not quoted correctly. */
+char *
+get_unquoted_path(const char *path)
+{
+ int len = strlen(path);
+
+ if (len == 0) {
+ return tor_strdup("");
+ }
+
+ int has_start_quote = (path[0] == '\"');
+ int has_end_quote = (len > 0 && path[len-1] == '\"');
+ if (has_start_quote != has_end_quote || (len == 1 && has_start_quote)) {
+ return NULL;
+ }
+
+ char *unquoted_path = tor_malloc(len - has_start_quote - has_end_quote + 1);
+ char *s = unquoted_path;
+ int i;
+ for (i = has_start_quote; i < len - has_end_quote; i++) {
+ if (path[i] == '\"' && (i > 0 && path[i-1] == '\\')) {
+ *(s-1) = path[i];
+ } else if (path[i] != '\"') {
+ *s++ = path[i];
+ } else { /* unescaped quote */
+ tor_free(unquoted_path);
+ return NULL;
+ }
+ }
+ *s = '\0';
+ return unquoted_path;
+}
+
/** Expand any homedir prefix on <b>filename</b>; return a newly allocated
* string. */
char *
diff --git a/src/common/util.h b/src/common/util.h
index 18eb57f1bc..d56abcee2e 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -389,6 +389,7 @@ char *read_file_to_str_until_eof(int fd, size_t max_bytes_to_read,
size_t *sz_out)
ATTR_MALLOC;
const char *unescape_string(const char *s, char **result, size_t *size_out);
+char *get_unquoted_path(const char *path);
char *expand_filename(const char *filename);
MOCK_DECL(struct smartlist_t *, tor_listdir, (const char *dirname));
int path_is_relative(const char *filename);
diff --git a/src/common/util_format.h b/src/common/util_format.h
index adf48c0077..4af8832bbe 100644
--- a/src/common/util_format.h
+++ b/src/common/util_format.h
@@ -23,11 +23,11 @@
#define BASE32_BUFSIZE(n) (BASE32_LEN(n) + 1)
#define BASE16_BUFSIZE(n) (BASE16_LEN(n) + 1)
-#define BASE64_NOPAD_LEN(n) (CEIL_DIV((n) * 4, 3)
-#define BASE32_NOPAD_LEN(n) (CEIL_DIV((n) * 8, 5)
+#define BASE64_NOPAD_LEN(n) (CEIL_DIV((n) * 4, 3))
+#define BASE32_NOPAD_LEN(n) (CEIL_DIV((n) * 8, 5))
-#define BASE64_NOPAD_BUFSIZE(n) (BASE64_NOPAD_LEN(n) + 1))
-#define BASE32_NOPAD_BUFSIZE(n) (BASE32_NOPAD_LEN(n) + 1))
+#define BASE64_NOPAD_BUFSIZE(n) (BASE64_NOPAD_LEN(n) + 1)
+#define BASE32_NOPAD_BUFSIZE(n) (BASE32_NOPAD_LEN(n) + 1)
/** @} */
#define BASE64_ENCODE_MULTILINE 1