systemd: expose unbase64mem() as nm_sd_utils_unbase64mem()

glib has an base64 implementation, but g_base64_decode() et al. gives
no way to detect invalid encodings. All invalid codes are silently
ignored. That is not suitable for strictly validating user input.

Instead of reimplementing of copy-pasting the code from somewhere,
reuse systemd's unbase64mem().

But don't use "hexdecoct.h" directly. Instead, add a single accessor
function to our "nm-sd-utils-shared.h" gateway. We want to be careful
about which bits from systemd we use, because otherwise re-importing
systemd code becomes fragile as you don't know which relevant parts
changed.
This commit is contained in:
Thomas Haller
2018-12-28 19:56:19 +01:00
parent 2c537b9d21
commit 0298d54078
4 changed files with 143 additions and 0 deletions

View File

@@ -23,6 +23,7 @@
#include "nm-sd-adapt-shared.h"
#include "path-util.h"
#include "hexdecoct.h"
/*****************************************************************************/
@@ -43,3 +44,39 @@ nm_sd_utils_path_startswith (const char *path, const char *prefix)
{
return path_startswith (path, prefix);
}
/*****************************************************************************/
gboolean
nm_sd_utils_unbase64char (char ch, gboolean accept_padding_equal)
{
if ( ch == '='
&& accept_padding_equal)
return G_MAXINT;
return unbase64char (ch);
}
/**
* nm_sd_utils_unbase64mem:
* @p: a valid base64 string. Whitespace is ignored, but invalid encodings
* will cause the function to fail.
* @l: the length of @p. @p is not treated as NUL terminated string but
* merely as a buffer of ascii characters.
* @mem: (transfer full): the decoded buffer on success.
* @len: the length of @mem on success.
*
* glib provides g_base64_decode(), but that does not report any errors
* from invalid encodings. Expose systemd's implementation which does
* reject invalid inputs.
*
* Returns: a non-negative code on success. Invalid encoding let the
* function fail.
*/
int
nm_sd_utils_unbase64mem (const char *p,
size_t l,
guint8 **mem,
size_t *len)
{
return unbase64mem (p, l, (void **) mem, len);
}

View File

@@ -29,4 +29,10 @@ const char *nm_sd_utils_path_startswith (const char *path, const char *prefix);
/*****************************************************************************/
int nm_sd_utils_unbase64char (char ch, gboolean accept_padding_equal);
int nm_sd_utils_unbase64mem (const char *p, size_t l, guint8 **mem, size_t *len);
/*****************************************************************************/
#endif /* __NM_SD_UTILS_SHARED_H__ */

View File

@@ -515,6 +515,7 @@ char base64char(int x) {
"0123456789+/";
return table[x & 63];
}
#endif /* NM_IGNORED */
int unbase64char(char c) {
unsigned offset;
@@ -545,6 +546,7 @@ int unbase64char(char c) {
return -EINVAL;
}
#if 0 /* NM_IGNORED */
ssize_t base64mem(const void *p, size_t l, char **out) {
char *r, *z;
const uint8_t *x;
@@ -644,6 +646,7 @@ int base64_append(
/* leave plen on the left, keep last column free */
return base64_append_width(prefix, plen, NULL, plen, p, l, width - plen - 1);
}
#endif /* NM_IGNORED */
static int unbase64_next(const char **p, size_t *l) {
int ret;
@@ -775,6 +778,7 @@ int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) {
return 0;
}
#if 0 /* NM_IGNORED */
void hexdump(FILE *f, const void *p, size_t s) {
const uint8_t *b = p;
unsigned n = 0;

View File

@@ -225,6 +225,101 @@ test_path_equal (void)
/*****************************************************************************/
static void
_test_unbase64char (char ch, gboolean maybe_invalid)
{
int r;
r = nm_sd_utils_unbase64char (ch, FALSE);
if (ch == '=') {
g_assert (!maybe_invalid);
g_assert_cmpint (r, <, 0);
g_assert_cmpint (nm_sd_utils_unbase64char (ch, TRUE), ==, G_MAXINT);
} else {
g_assert_cmpint (r, ==, nm_sd_utils_unbase64char (ch, TRUE));
if (r >= 0)
g_assert_cmpint (r, <=, 255);
if (!maybe_invalid)
g_assert_cmpint (r, >=, 0);
}
}
static void
_test_unbase64mem_mem (const char *base64, const guint8 *expected_arr, gsize expected_len)
{
gs_free char *expected_base64 = NULL;
int r;
gs_free guint8 *exp2_arr = NULL;
gs_free guint8 *exp3_arr = NULL;
gsize exp2_len;
gsize exp3_len;
gsize i;
expected_base64 = g_base64_encode (expected_arr, expected_len);
for (i = 0; expected_base64[i]; i++)
_test_unbase64char (expected_base64[i], FALSE);
r = nm_sd_utils_unbase64mem (expected_base64, strlen (expected_base64), &exp2_arr, &exp2_len);
g_assert_cmpint (r, ==, 0);
g_assert_cmpmem (expected_arr, expected_len, exp2_arr, exp2_len);
if (!nm_streq (base64, expected_base64)) {
r = nm_sd_utils_unbase64mem (base64, strlen (base64), &exp3_arr, &exp3_len);
g_assert_cmpint (r, ==, 0);
g_assert_cmpmem (expected_arr, expected_len, exp3_arr, exp3_len);
}
}
#define _test_unbase64mem(base64, expected_str) _test_unbase64mem_mem (base64, (const guint8 *) ""expected_str"", NM_STRLEN (expected_str))
static void
_test_unbase64mem_inval (const char *base64)
{
gs_free guint8 *exp_arr = NULL;
gsize exp_len = 0;
int r;
r = nm_sd_utils_unbase64mem (base64, strlen (base64), &exp_arr, &exp_len);
g_assert_cmpint (r, <, 0);
g_assert (!exp_arr);
g_assert (exp_len == 0);
}
static void
test_nm_sd_utils_unbase64mem (void)
{
gs_free char *rnd_base64 = NULL;
guint8 rnd_buf[30];
guint i, rnd_len;
_test_unbase64mem ("", "");
_test_unbase64mem (" ", "");
_test_unbase64mem (" Y Q == ", "a");
_test_unbase64mem (" Y WJjZGV mZ 2g = ", "abcdefgh");
_test_unbase64mem_inval (" Y %WJjZGV mZ 2g = ");
_test_unbase64mem_inval (" Y %WJjZGV mZ 2g = a");
_test_unbase64mem ("YQ==", "a");
_test_unbase64mem_inval ("YQ==a");
rnd_len = nmtst_get_rand_int () % sizeof (rnd_buf);
for (i = 0; i < rnd_len; i++)
rnd_buf[i] = nmtst_get_rand_int () % 256;
rnd_base64 = g_base64_encode (rnd_buf, rnd_len);
_test_unbase64mem_mem (rnd_base64, rnd_buf, rnd_len);
_test_unbase64char ('=', FALSE);
for (i = 0; i < 10; i++) {
char ch = nmtst_get_rand_int () % 256;
if (ch != '=')
_test_unbase64char (ch, TRUE);
}
}
/*****************************************************************************/
NMTST_DEFINE ();
int
@@ -236,6 +331,7 @@ main (int argc, char **argv)
g_test_add_func ("/systemd/lldp/create", test_lldp_create);
g_test_add_func ("/systemd/sd-event", test_sd_event);
g_test_add_func ("/systemd/test_path_equal", test_path_equal);
g_test_add_func ("/systemd/test_nm_sd_utils_unbase64mem", test_nm_sd_utils_unbase64mem);
return g_test_run ();
}