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 "nm-sd-adapt-shared.h"
|
||||||
|
|
||||||
#include "path-util.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);
|
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__ */
|
#endif /* __NM_SD_UTILS_SHARED_H__ */
|
||||||
|
@@ -515,6 +515,7 @@ char base64char(int x) {
|
|||||||
"0123456789+/";
|
"0123456789+/";
|
||||||
return table[x & 63];
|
return table[x & 63];
|
||||||
}
|
}
|
||||||
|
#endif /* NM_IGNORED */
|
||||||
|
|
||||||
int unbase64char(char c) {
|
int unbase64char(char c) {
|
||||||
unsigned offset;
|
unsigned offset;
|
||||||
@@ -545,6 +546,7 @@ int unbase64char(char c) {
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0 /* NM_IGNORED */
|
||||||
ssize_t base64mem(const void *p, size_t l, char **out) {
|
ssize_t base64mem(const void *p, size_t l, char **out) {
|
||||||
char *r, *z;
|
char *r, *z;
|
||||||
const uint8_t *x;
|
const uint8_t *x;
|
||||||
@@ -644,6 +646,7 @@ int base64_append(
|
|||||||
/* leave plen on the left, keep last column free */
|
/* leave plen on the left, keep last column free */
|
||||||
return base64_append_width(prefix, plen, NULL, plen, p, l, width - plen - 1);
|
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) {
|
static int unbase64_next(const char **p, size_t *l) {
|
||||||
int ret;
|
int ret;
|
||||||
@@ -775,6 +778,7 @@ int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0 /* NM_IGNORED */
|
||||||
void hexdump(FILE *f, const void *p, size_t s) {
|
void hexdump(FILE *f, const void *p, size_t s) {
|
||||||
const uint8_t *b = p;
|
const uint8_t *b = p;
|
||||||
unsigned n = 0;
|
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 ();
|
NMTST_DEFINE ();
|
||||||
|
|
||||||
int
|
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/lldp/create", test_lldp_create);
|
||||||
g_test_add_func ("/systemd/sd-event", test_sd_event);
|
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_path_equal", test_path_equal);
|
||||||
|
g_test_add_func ("/systemd/test_nm_sd_utils_unbase64mem", test_nm_sd_utils_unbase64mem);
|
||||||
|
|
||||||
return g_test_run ();
|
return g_test_run ();
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user