mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-03-13 12:58:20 +03:00
string-util: add common implementation of function that converts sized character buffers to NUL terminated C strings
This commit is contained in:
parent
9e2e3342ca
commit
7153213e40
@ -1187,6 +1187,49 @@ char *string_replace_char(char *str, char old_char, char new_char) {
|
||||
return str;
|
||||
}
|
||||
|
||||
int make_cstring(const char *s, size_t n, MakeCStringMode mode, char **ret) {
|
||||
char *b;
|
||||
|
||||
assert(s || n == 0);
|
||||
assert(mode >= 0);
|
||||
assert(mode < _MAKE_CSTRING_MODE_MAX);
|
||||
|
||||
/* Converts a sized character buffer into a NUL-terminated NUL string, refusing if there are embedded
|
||||
* NUL bytes. Whether to expect a trailing NUL byte can be specified via 'mode' */
|
||||
|
||||
if (n == 0) {
|
||||
if (mode == MAKE_CSTRING_REQUIRE_TRAILING_NUL)
|
||||
return -EINVAL;
|
||||
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
b = new0(char, 1);
|
||||
} else {
|
||||
const char *nul;
|
||||
|
||||
nul = memchr(s, 0, n);
|
||||
if (nul) {
|
||||
if (nul < s + n - 1 || /* embedded NUL? */
|
||||
mode == MAKE_CSTRING_REFUSE_TRAILING_NUL)
|
||||
return -EINVAL;
|
||||
|
||||
n--;
|
||||
} else if (mode == MAKE_CSTRING_REQUIRE_TRAILING_NUL)
|
||||
return -EINVAL;
|
||||
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
b = memdup_suffix0(s, n);
|
||||
}
|
||||
if (!b)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = b;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t strspn_from_end(const char *str, const char *accept) {
|
||||
size_t n = 0;
|
||||
|
||||
|
@ -239,6 +239,16 @@ bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok);
|
||||
|
||||
char *string_replace_char(char *str, char old_char, char new_char);
|
||||
|
||||
typedef enum MakeCStringMode {
|
||||
MAKE_CSTRING_REFUSE_TRAILING_NUL,
|
||||
MAKE_CSTRING_ALLOW_TRAILING_NUL,
|
||||
MAKE_CSTRING_REQUIRE_TRAILING_NUL,
|
||||
_MAKE_CSTRING_MODE_MAX,
|
||||
_MAKE_CSTRING_MODE_INVALID = -1,
|
||||
} MakeCStringMode;
|
||||
|
||||
int make_cstring(const char *s, size_t n, MakeCStringMode mode, char **ret);
|
||||
|
||||
size_t strspn_from_end(const char *str, const char *accept);
|
||||
|
||||
char *strdupspn(const char *a, const char *accept);
|
||||
|
@ -326,16 +326,12 @@ static int write_blob(FILE *f, const void *data, size_t size) {
|
||||
|
||||
if (arg_transcode == TRANSCODE_OFF &&
|
||||
arg_json_format_flags != JSON_FORMAT_OFF) {
|
||||
|
||||
_cleanup_(erase_and_freep) char *suffixed = NULL;
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
|
||||
|
||||
if (memchr(data, 0, size))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Credential data contains embedded NUL, can't parse as JSON.");
|
||||
|
||||
suffixed = memdup_suffix0(data, size);
|
||||
if (!suffixed)
|
||||
return log_oom();
|
||||
r = make_cstring(data, size, MAKE_CSTRING_REFUSE_TRAILING_NUL, &suffixed);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Unable to convert binary string to C string: %m");
|
||||
|
||||
r = json_parse(suffixed, JSON_PARSE_SENSITIVE, &v, NULL, NULL);
|
||||
if (r < 0)
|
||||
|
@ -58,27 +58,13 @@ int crypt_dump_hex_string(const char *hex_str, char **ret_dump_str) {
|
||||
}
|
||||
|
||||
int crypt_normalize_pin(const void *pin, size_t pin_size, char **ret_pin_string) {
|
||||
|
||||
_cleanup_free_ char *pin_string = NULL;
|
||||
|
||||
assert(pin || !pin_size);
|
||||
assert(pin || pin_size == 0);
|
||||
assert(ret_pin_string);
|
||||
|
||||
if (!pin) {
|
||||
if (pin_size == 0) {
|
||||
*ret_pin_string = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Refuse embedded NULL bytes, but allow trailing NULL */
|
||||
if (memchr(pin, 0, pin_size - 1))
|
||||
return -EINVAL;
|
||||
|
||||
/* Enforce trailing NULL byte if missing */
|
||||
pin_string = memdup_suffix0(pin, pin_size);
|
||||
if (!pin_string)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret_pin_string = TAKE_PTR(pin_string);
|
||||
|
||||
return 0;
|
||||
return make_cstring(pin, pin_size, MAKE_CSTRING_ALLOW_TRAILING_NUL, ret_pin_string);
|
||||
}
|
||||
|
@ -279,6 +279,7 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo
|
||||
uint8_t code, len;
|
||||
const uint8_t *option;
|
||||
size_t offset = 0;
|
||||
int r;
|
||||
|
||||
while (offset < buflen) {
|
||||
code = options[offset ++];
|
||||
@ -318,13 +319,9 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo
|
||||
if (error_message) {
|
||||
_cleanup_free_ char *string = NULL;
|
||||
|
||||
/* Accept a trailing NUL byte */
|
||||
if (memchr(option, 0, len - 1))
|
||||
return -EINVAL;
|
||||
|
||||
string = memdup_suffix0((const char *) option, len);
|
||||
if (!string)
|
||||
return -ENOMEM;
|
||||
r = make_cstring((const char*) option, len, MAKE_CSTRING_ALLOW_TRAILING_NUL, &string);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!ascii_is_valid(string))
|
||||
return -EINVAL;
|
||||
|
@ -376,6 +376,8 @@ static int lease_parse_be32(const uint8_t *option, size_t len, be32_t *ret) {
|
||||
}
|
||||
|
||||
static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
|
||||
int r;
|
||||
|
||||
assert(option);
|
||||
assert(ret);
|
||||
|
||||
@ -388,12 +390,9 @@ static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
|
||||
* One trailing NUL byte is OK, we don't mind. See:
|
||||
* https://github.com/systemd/systemd/issues/1337
|
||||
*/
|
||||
if (memchr(option, 0, len - 1))
|
||||
return -EINVAL;
|
||||
|
||||
string = memdup_suffix0((const char *) option, len);
|
||||
if (!string)
|
||||
return -ENOMEM;
|
||||
r = make_cstring((const char*) option, len, MAKE_CSTRING_ALLOW_TRAILING_NUL, &string);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
free_and_replace(*ret, string);
|
||||
}
|
||||
|
@ -1371,14 +1371,14 @@ int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start) {
|
||||
}
|
||||
|
||||
int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start) {
|
||||
assert(p);
|
||||
|
||||
_cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder = REWINDER_INIT(p);
|
||||
_cleanup_free_ char *t = NULL;
|
||||
const void *d;
|
||||
char *t;
|
||||
uint8_t c;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
|
||||
r = dns_packet_read_uint8(p, &c, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -1387,19 +1387,14 @@ int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (memchr(d, 0, c))
|
||||
r = make_cstring(d, c, MAKE_CSTRING_REFUSE_TRAILING_NUL, &t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!utf8_is_valid(t))
|
||||
return -EBADMSG;
|
||||
|
||||
t = memdup_suffix0(d, c);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!utf8_is_valid(t)) {
|
||||
free(t);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
*ret = t;
|
||||
*ret = TAKE_PTR(t);
|
||||
|
||||
if (start)
|
||||
*start = rewinder.saved_rindex;
|
||||
|
@ -1146,12 +1146,9 @@ int decrypt_credential_and_warn(
|
||||
if (le32toh(m->name_size) > 0) {
|
||||
_cleanup_free_ char *embedded_name = NULL;
|
||||
|
||||
if (memchr(m->name, 0, le32toh(m->name_size)))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Embedded credential name contains NUL byte, refusing.");
|
||||
|
||||
embedded_name = memdup_suffix0(m->name, le32toh(m->name_size));
|
||||
if (!embedded_name)
|
||||
return log_oom();
|
||||
r = make_cstring(m->name, le32toh(m->name_size), MAKE_CSTRING_REFUSE_TRAILING_NUL, &embedded_name);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Unable to convert embedded credential name to C string: %m");
|
||||
|
||||
if (!credential_name_valid(embedded_name))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Embedded credential name is not valid, refusing.");
|
||||
|
@ -1168,4 +1168,54 @@ TEST(streq_skip_trailing_chars) {
|
||||
assert_se(!streq_skip_trailing_chars("", "f", NULL));
|
||||
}
|
||||
|
||||
#define TEST_MAKE_CSTRING_ONE(x, ret, mode, expect) \
|
||||
do { \
|
||||
_cleanup_free_ char *b = NULL; \
|
||||
assert_se(make_cstring((x), ELEMENTSOF(x), (mode), &b) == (ret)); \
|
||||
assert_se(streq_ptr(b, (expect))); \
|
||||
} while(false)
|
||||
|
||||
TEST(make_cstring) {
|
||||
static const char test1[] = "this is a test",
|
||||
test2[] = "",
|
||||
test3[] = "a",
|
||||
test4[] = "aa\0aa",
|
||||
test5[] = { 'b', 'b', 0, 'b' , 'b' },
|
||||
test6[] = {},
|
||||
test7[] = { 'x' },
|
||||
test8[] = { 'x', 'y', 'z' };
|
||||
|
||||
TEST_MAKE_CSTRING_ONE(test1, -EINVAL, MAKE_CSTRING_REFUSE_TRAILING_NUL, NULL);
|
||||
TEST_MAKE_CSTRING_ONE(test1, 0, MAKE_CSTRING_ALLOW_TRAILING_NUL, "this is a test");
|
||||
TEST_MAKE_CSTRING_ONE(test1, 0, MAKE_CSTRING_REQUIRE_TRAILING_NUL, "this is a test");
|
||||
|
||||
TEST_MAKE_CSTRING_ONE(test2, -EINVAL, MAKE_CSTRING_REFUSE_TRAILING_NUL, NULL);
|
||||
TEST_MAKE_CSTRING_ONE(test2, 0, MAKE_CSTRING_ALLOW_TRAILING_NUL, "");
|
||||
TEST_MAKE_CSTRING_ONE(test2, 0, MAKE_CSTRING_REQUIRE_TRAILING_NUL, "");
|
||||
|
||||
TEST_MAKE_CSTRING_ONE(test3, -EINVAL, MAKE_CSTRING_REFUSE_TRAILING_NUL, NULL);
|
||||
TEST_MAKE_CSTRING_ONE(test3, 0, MAKE_CSTRING_ALLOW_TRAILING_NUL, "a");
|
||||
TEST_MAKE_CSTRING_ONE(test3, 0, MAKE_CSTRING_REQUIRE_TRAILING_NUL, "a");
|
||||
|
||||
TEST_MAKE_CSTRING_ONE(test4, -EINVAL, MAKE_CSTRING_REFUSE_TRAILING_NUL, NULL);
|
||||
TEST_MAKE_CSTRING_ONE(test4, -EINVAL, MAKE_CSTRING_ALLOW_TRAILING_NUL, NULL);
|
||||
TEST_MAKE_CSTRING_ONE(test4, -EINVAL, MAKE_CSTRING_REQUIRE_TRAILING_NUL, NULL);
|
||||
|
||||
TEST_MAKE_CSTRING_ONE(test5, -EINVAL, MAKE_CSTRING_REFUSE_TRAILING_NUL, NULL);
|
||||
TEST_MAKE_CSTRING_ONE(test5, -EINVAL, MAKE_CSTRING_ALLOW_TRAILING_NUL, NULL);
|
||||
TEST_MAKE_CSTRING_ONE(test5, -EINVAL, MAKE_CSTRING_REQUIRE_TRAILING_NUL, NULL);
|
||||
|
||||
TEST_MAKE_CSTRING_ONE(test6, 0, MAKE_CSTRING_REFUSE_TRAILING_NUL, "");
|
||||
TEST_MAKE_CSTRING_ONE(test6, 0, MAKE_CSTRING_ALLOW_TRAILING_NUL, "");
|
||||
TEST_MAKE_CSTRING_ONE(test6, -EINVAL, MAKE_CSTRING_REQUIRE_TRAILING_NUL, NULL);
|
||||
|
||||
TEST_MAKE_CSTRING_ONE(test7, 0, MAKE_CSTRING_REFUSE_TRAILING_NUL, "x");
|
||||
TEST_MAKE_CSTRING_ONE(test7, 0, MAKE_CSTRING_ALLOW_TRAILING_NUL, "x");
|
||||
TEST_MAKE_CSTRING_ONE(test7, -EINVAL, MAKE_CSTRING_REQUIRE_TRAILING_NUL, NULL);
|
||||
|
||||
TEST_MAKE_CSTRING_ONE(test8, 0, MAKE_CSTRING_REFUSE_TRAILING_NUL, "xyz");
|
||||
TEST_MAKE_CSTRING_ONE(test8, 0, MAKE_CSTRING_ALLOW_TRAILING_NUL, "xyz");
|
||||
TEST_MAKE_CSTRING_ONE(test8, -EINVAL, MAKE_CSTRING_REQUIRE_TRAILING_NUL, NULL);
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_DEBUG);
|
||||
|
Loading…
x
Reference in New Issue
Block a user