[Libguestfs] [nbdkit PATCH v2 1/2] common/utils: Add uri_quote and tests

Eric Blake eblake at redhat.com
Wed Jun 26 17:09:42 UTC 2019


Normally, when using captive mode --run coupled with a Unix socket,
the user is using -U -, and we get lucky that the typcial values of
$TMPDIR plus our use of mktemp produce a name that needs neither shell
quoting nor which requires %-encoding for use in a URI.  But a
determined user can use an explicit -U /odd/#path or setting of
$TMPDIR that gets correctly shell-quoted, but which would break
interpretation in a URI parser. In preparation for adding a $uri
variable under --run, it's time to ensure that we can always produce a
valid unambiguous URI by adding a new helper function.  It is
intentional that uri_quote of an empty string produces no output, as
the intended audience of uri_quote is always a smaller component
within a larger URI (different from shell_quote which does have use on
a stand-alone empty string).

It's also worth testing the existing shell_quote (including the bug
fixed in bdda3f30) as well as the new uri_quote.  The test includes an
XXX comment that matches the counterpart XXX in shell_quote(); an
audit of shell_quote() callers shows that the iso plugin might be
affected if someone ever used prog=a=b, but that is fringe enough to
put off to another day.

Signed-off-by: Eric Blake <eblake at redhat.com>
---
 common/utils/utils.h       |   1 +
 common/utils/test-quotes.c | 108 +++++++++++++++++++++++++++++++++++++
 common/utils/utils.c       |  27 ++++++++++
 .gitignore                 |   1 +
 common/utils/Makefile.am   |  11 ++++
 5 files changed, 148 insertions(+)
 create mode 100644 common/utils/test-quotes.c

diff --git a/common/utils/utils.h b/common/utils/utils.h
index 4d1abf03..3f10cdc0 100644
--- a/common/utils/utils.h
+++ b/common/utils/utils.h
@@ -34,6 +34,7 @@
 #define NBDKIT_UTILS_H

 extern void shell_quote (const char *str, FILE *fp);
+extern void uri_quote (const char *str, FILE *fp);
 extern int exit_status_to_nbd_error (int status, const char *cmd);

 #endif /* NBDKIT_UTILS_H */
diff --git a/common/utils/test-quotes.c b/common/utils/test-quotes.c
new file mode 100644
index 00000000..33b8299e
--- /dev/null
+++ b/common/utils/test-quotes.c
@@ -0,0 +1,108 @@
+/* nbdkit
+ * Copyright (C) 2019 Red Hat Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Red Hat nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Unit tests of utils quoting code. */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdbool.h>
+
+#include "utils.h"
+
+static bool
+test (const char *orig, const char *fnname, void (*fn) (const char *, FILE *),
+      const char *exp)
+{
+  char *str = NULL;
+  size_t str_len = 0;
+  FILE *fp;
+
+  fp = open_memstream (&str, &str_len);
+  assert (fp);
+  fn (orig, fp);
+  if (fclose (fp) == EOF)
+    assert (false);
+  if (str_len == 0 && !str)
+    str = strdup ("");
+  assert (str);
+
+  if (strcmp (str, exp)) {
+    fprintf (stderr, "%s failed, got '%s' expected '%s'\n",
+             fnname, str, exp);
+    free (str);
+    return true;
+  }
+  free (str);
+  return false;
+}
+
+int
+main (void)
+{
+  struct {
+    const char *orig;
+    const char *shell;
+    const char *uri;
+  } tests[] = {
+    { "a-b_c.0", "a-b_c.0", "a-b_c.0" },
+    { "/Safe/Path", "/Safe/Path", "/Safe/Path" },
+    { "a~b", "\"a~b\"", "a~b" },
+    { "", "\"\"", "" },
+    { "a=b", "a=b", "a%3Db" }, /* XXX shell wrong if used as argv[0] */
+    { "a;b", "\"a;b\"", "a%3Bb" },
+    { "a b", "\"a b\"", "a%20b" },
+    { "a%b", "\"a%b\"", "a%25b" },
+    { "a'b\"c$d`e\\f", "\"a'b\\\"c\\$d\\`e\\\\f\"", "a%27b%22c%24d%60e%5Cf" },
+  };
+  size_t i;
+  bool fail = false;
+
+  for (i = 0; i < sizeof tests / sizeof tests[0]; i++) {
+    fail |= test (tests[i].orig, "shell_quote", shell_quote, tests[i].shell);
+    fail |= test (tests[i].orig, "uri_quote", uri_quote, tests[i].uri);
+  }
+  return fail ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
+/* Unrelated utils code uses nbdkit_error, normally provided by the main
+ * server program.  So we have to provide it here.
+ */
+void
+nbdkit_error (const char *fs, ...)
+{
+  /* XXX split utils.c to avoid needing this linker stub? */
+  assert (false);
+}
diff --git a/common/utils/utils.c b/common/utils/utils.c
index 7a584e25..7534a13d 100644
--- a/common/utils/utils.c
+++ b/common/utils/utils.c
@@ -75,6 +75,33 @@ shell_quote (const char *str, FILE *fp)
   fputc ('"', fp);
 }

+/* Print str to fp, URI quoting if necessary.
+ * The resulting string is safe for use in a URI path or query component,
+ * and can be passed through the shell without further quoting.
+ */
+void
+uri_quote (const char *str, FILE *fp)
+{
+  /* safe_chars contains the RFC 3986 unreserved characters plus '/'. */
+  const char *safe_chars =
+    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_~/";
+  size_t i, len;
+
+  /* If the string consists only of safe characters, output it as-is. */
+  len = strlen (str);
+  if (len == strspn (str, safe_chars)) {
+    fputs (str, fp);
+    return;
+  }
+
+  for (i = 0; i < len; ++i) {
+    if (strchr (safe_chars, str[i]))
+      fputc (str[i], fp);
+    else
+      fprintf (fp, "%%%02X", str[i] & 0xff);
+  }
+}
+
 /* Convert exit status to nbd_error.  If the exit status was nonzero
  * or another failure then -1 is returned.
  */
diff --git a/.gitignore b/.gitignore
index 3770d3ba..46ab5dc6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,6 +35,7 @@ Makefile.in
 /common/include/test-random
 /common/include/test-tvdiff
 /common/protocol/protostrings.c
+/common/utils/test-quotes
 /compile
 /config.guess
 /config.h
diff --git a/common/utils/Makefile.am b/common/utils/Makefile.am
index f4f17005..cc364300 100644
--- a/common/utils/Makefile.am
+++ b/common/utils/Makefile.am
@@ -43,3 +43,14 @@ libutils_la_CPPFLAGS = \
 	-I$(top_srcdir)/include
 libutils_la_CFLAGS = \
         $(WARNINGS_CFLAGS)
+
+# Unit tests.
+
+TESTS = test-quotes
+check_PROGRAMS = test-quotes
+
+test_quotes_SOURCES = test-quotes.c utils.c utils.h
+test_quotes_CPPFLAGS = \
+	-I$(top_srcdir)/common/utils
+test_quotes_CFLAGS = \
+        $(WARNINGS_CFLAGS)
-- 
2.20.1




More information about the Libguestfs mailing list