From: Michal Privoznik <mprivozn@redhat.com> DBus allows only some characters to be specified verbatim [1]. Everything else must be specified via '%NN' syntax where NN is hex value of the escaped character. Introduce virStringEscapeDBus() helper to handle string escaping. 1: https://gitlab.freedesktop.org/dbus/dbus/-/blob/2dee5236088bcf690ba92b743a58... Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/libvirt_private.syms | 1 + src/util/virstring.c | 64 ++++++++++++++++++++++++++++++++++++++++ src/util/virstring.h | 1 + tests/virstringtest.c | 43 +++++++++++++++++++++++++++ 4 files changed, 109 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 4e57e4a8f6..ef8ddd0330 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -3478,6 +3478,7 @@ virSkipSpacesBackwards; virSkipToDigit; virStrcpy; virStringBufferIsPrintable; +virStringEscapeDBus; virStringFilterChars; virStringFormatHex; virStringHasCaseSuffix; diff --git a/src/util/virstring.c b/src/util/virstring.c index e001d76bf1..0729002d12 100644 --- a/src/util/virstring.c +++ b/src/util/virstring.c @@ -1118,3 +1118,67 @@ virStringFormatHex(const unsigned char *buf, size_t len) return g_steal_pointer(&hex); } + + +/** + * virStringEscapeDBus: + * @str: string to escape + * + * Produces new string that's safe to pass to DBUS. Specifically, + * alphanumerical characters, digits, '-', '_', '/', '\\', '*' and '.' are + * kept. Everything else is escaped using "%NN", where NN is value of the + * character in hex. + * + * Returns: newly allocated string. Caller must free. + */ +char * +virStringEscapeDBus(const char *str) +{ + size_t len; + size_t i; + size_t j = 0; + size_t alloc = 1; + char *ret; + + if (!str) + return NULL; + + len = strlen(str); + alloc = len * 1.25; /* assume some escaping */ + ret = g_new0(char, alloc + 1); + + for (i = 0; str[i]; i++) { + const char c = str[i]; + + if ((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + (c == '-') || + (c == '_') || + (c == '/') || + (c == '\\') || + (c == '*') || + (c == '.')) { + if (j >= alloc) { + ret = g_renew(char, ret, alloc + 11); + alloc += 10; + } + + ret[j++] = c; + } else { + static const char hextable[] = "0123456789ABCDEF"; + + if (j + 3 >= alloc) { + ret = g_renew(char, ret, alloc + 11); + alloc += 10; + } + + ret[j++] = '%'; + ret[j++] = hextable[(c >> 4) & 15]; + ret[j++] = hextable[c & 15]; + } + } + + ret[j++] = '\0'; + return ret; +} diff --git a/src/util/virstring.h b/src/util/virstring.h index 8c2208ece8..aadfc37e41 100644 --- a/src/util/virstring.h +++ b/src/util/virstring.h @@ -141,3 +141,4 @@ int virStringParseVersion(unsigned long long *version, void virStringListRemoveDuplicates(char ***list); char *virStringFormatHex(const unsigned char *buf, size_t len); +char *virStringEscapeDBus(const char *str); diff --git a/tests/virstringtest.c b/tests/virstringtest.c index 0792155cc3..50b0e2cc5c 100644 --- a/tests/virstringtest.c +++ b/tests/virstringtest.c @@ -486,6 +486,29 @@ static int testFilterChars(const void *args) return 0; } + +struct testDBusData { + const char *string; + const char *expected; +}; + + +static int +testDBusEscape(const void *args) +{ + const struct testDBusData *data = args; + g_autofree char *res = virStringEscapeDBus(data->string); + + if (STRNEQ_NULLABLE(res, data->expected)) { + fprintf(stderr, "%s: returned '%s', expected '%s'\n", + __FUNCTION__, NULLSTR(res), NULLSTR(data->expected)); + return -1; + } + + return 0; +} + + static int mymain(void) { @@ -767,6 +790,26 @@ mymain(void) TEST_FILTER_CHARS(NULL, NULL, NULL); TEST_FILTER_CHARS("hello 123 hello", "helo", "hellohello"); +#define TEST_DBUS_ESCAPE(str, exp) \ + do { \ + struct testDBusData dbusData = { \ + .string = str, \ + .expected = exp, \ + }; \ + if (virTestRun("DBus escape " #str, \ + testDBusEscape, &dbusData) < 0) \ + ret = -1; \ + } while (0) + + TEST_DBUS_ESCAPE(NULL, NULL); + TEST_DBUS_ESCAPE("", ""); + TEST_DBUS_ESCAPE("-_/\\*.", "-_/\\*."); + TEST_DBUS_ESCAPE("abcdefghijklmnopqrstuvwxyz", + "abcdefghijklmnopqrstuvwxyz"); + TEST_DBUS_ESCAPE("/some/~/path/with space/-_\\*./#$", + "/some/%7E/path/with%20space/-_\\*./%23%24"); + + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } -- 2.51.2