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;
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);
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)
}
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);
}
uint8_t code, len;
const uint8_t *option;
size_t offset = 0;
+ int r;
while (offset < buflen) {
code = options[offset ++];
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;
}
static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
+ int r;
+
assert(option);
assert(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);
}
}
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;
if (r < 0)
return r;
- if (memchr(d, 0, c))
- return -EBADMSG;
-
- t = memdup_suffix0(d, c);
- if (!t)
- return -ENOMEM;
+ r = make_cstring(d, c, MAKE_CSTRING_REFUSE_TRAILING_NUL, &t);
+ if (r < 0)
+ return r;
- if (!utf8_is_valid(t)) {
- free(t);
+ if (!utf8_is_valid(t))
return -EBADMSG;
- }
- *ret = t;
+ *ret = TAKE_PTR(t);
if (start)
*start = rewinder.saved_rindex;
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.");
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);