]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
string-util: add common implementation of function that converts sized character...
authorLennart Poettering <lennart@poettering.net>
Fri, 20 Jan 2023 14:36:09 +0000 (15:36 +0100)
committerLennart Poettering <lennart@poettering.net>
Sat, 21 Jan 2023 09:45:25 +0000 (10:45 +0100)
src/basic/string-util.c
src/basic/string-util.h
src/creds/creds.c
src/cryptsetup/cryptsetup-tokens/cryptsetup-token-util.c
src/libsystemd-network/dhcp-option.c
src/libsystemd-network/sd-dhcp-lease.c
src/resolve/resolved-dns-packet.c
src/shared/creds-util.c
src/test/test-string-util.c

index 577afd37f42a674267b2f966d4a4a7818cda537b..ad8c9863bdda531ac78c93d7f354e612588fdd04 100644 (file)
@@ -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;
 
index 684b7d5c118639cf332f5ecdcd61d4557778170d..e0a47a21a97dc1617fccc23225531d86572fcd37 100644 (file)
@@ -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);
index 71bf355b383ede14d95e11e055c3903a3033b234..3eb94cd36a291bd6b22aa2a0268b5dbbe3ec061d 100644 (file)
@@ -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)
index e305d8ba7958f8676adfafa3a085b8a426dca9b2..4e3090bc9297b7e5ef85c98d1998a836412ecac1 100644 (file)
@@ -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);
 }
index 7f49da7c2d7189c826756a7e3d5066256d9f3e5b..a52259c238a480c64f1b3d348675d62460937e36 100644 (file)
@@ -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;
index b14ad57071dfecb814fac5a511ff4abe906fce59..02c6a6e0d79133e90eb65c5c7dd65563e87bc981 100644 (file)
@@ -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);
         }
index f90e5974c4dc32031854a5d0269ce4c2ba082897..574a1a4be92bb62144bdc380d1b5e46ff83cf5b6 100644 (file)
@@ -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))
-                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;
index b416e873afbb5b189ffedf84be66e691f37c61e7..074440e99b91b710b90bf405bc5cde88b2eb8eae 100644 (file)
@@ -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.");
index 4047139c269a1881b11659d150eb6954b5701c18..b3ff7d65c1cf1ad853ba19b42686097886f66d40 100644 (file)
@@ -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);