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:
@@ -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);
|
||||
}
|
||||
|
@@ -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__ */
|
||||
|
@@ -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;
|
||||
|
@@ -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 ();
|
||||
}
|
||||
|
Reference in New Issue
Block a user