common: Extend function percent_data_escape.
authorWerner Koch <wk@gnupg.org>
Thu, 24 Jan 2019 09:02:52 +0000 (10:02 +0100)
committerWerner Koch <wk@gnupg.org>
Thu, 24 Jan 2019 09:02:52 +0000 (10:02 +0100)
* common/percent.c (percent_data_escape): Add new args prefix and
plus_escape.
* agent/command.c (cmd_put_secret): Adjust for changed function

* common/t-percent.c (test_percent_data_escape): Extend test for the
prefix.
(test_percent_data_escape_plus): new test for the plus escaping.

Signed-off-by: Werner Koch <wk@gnupg.org>
agent/command.c
common/percent.c
common/t-percent.c
common/util.h

index 7fbf1de..c395b1e 100644 (file)
@@ -2751,7 +2751,7 @@ cmd_put_secret (assuan_context_t ctx, char *line)
    * into a string.  Instead of resorting to base64 encoding we use a
    * special percent escaping which only quoted the Nul and the
    * percent character. */
-  string = percent_data_escape (value? value : valstr, valuelen);
+  string = percent_data_escape (0, NULL, value? value : valstr, valuelen);
   if (!string)
     {
       err = gpg_error_from_syserror ();
index 7b81768..ecc6a19 100644 (file)
 
 
 /* Create a newly alloced string from STRING with all spaces and
  control characters converted to plus signs or %xx sequences.  The
  function returns the new string or NULL in case of a malloc
  failure.
-
-   Note that we also escape the quote character to work around a bug
  in the mingw32 runtime which does not correctly handle command line
-   quoting.  We correctly double the quote mark when calling a program
  (i.e. gpg-protect-tool), but the pre-main code does not notice the
  double quote as an escaped quote.  We do this also on POSIX systems
  for consistency.  */
* control characters converted to plus signs or %xx sequences.  The
* function returns the new string or NULL in case of a malloc
* failure.
+ *
+ * Note that this fucntion also escapes the quote character to work
* around a bug in the mingw32 runtime which does not correctly handle
+ * command line quoting.  We correctly double the quote mark when
* calling a program (i.e. gpg-protect-tool), but the pre-main code
* does not notice the double quote as an escaped quote.  We do this
* also on POSIX systems for consistency.  */
 char *
 percent_plus_escape (const char *string)
 {
@@ -87,19 +87,36 @@ percent_plus_escape (const char *string)
 }
 
 
-/* Create a newly alloced string from (DATA,DATALEN) with embedded
- * Nuls quoted as %00.  The standard percent unescaping can be
- * used to reverse this encoding.   */
+/* Create a newly malloced string from (DATA,DATALEN) with embedded
+ * nuls quoted as %00.  The standard percent unescaping can be used to
+ * reverse this encoding.  With PLUS_ESCAPE set plus-escaping (spaces
+ * are replaced by a '+') and escaping of characters with values less
+ * than 0x20 is used.  If PREFIX is not NULL it will be prepended to
+ * the output in standard escape format; that is PLUS_ESCAPING is
+ * ignored for PREFIX. */
 char *
-percent_data_escape (const void *data, size_t datalen)
+percent_data_escape (int plus_escape, const char *prefix,
+                     const void *data, size_t datalen)
 {
   char *buffer, *p;
-  const char *s;
-  size_t n, length;
+  const unsigned char *s;
+  size_t n;
+  size_t length = 1;
+
+  if (prefix)
+    {
+      for (s = prefix; *s; s++)
+        {
+          if (*s == '%' || *s < 0x20)
+            length += 3;
+          else
+            length++;
+        }
+    }
 
-  for (length=1, s=data, n=datalen; n; s++, n--)
+  for (s=data, n=datalen; n; s++, n--)
     {
-      if (!*s || *s == '%')
+      if (!*s || *s == '%' || (plus_escape && (*s < ' ' || *s == '+')))
         length += 3;
       else
         length++;
@@ -109,6 +126,20 @@ percent_data_escape (const void *data, size_t datalen)
   if (!buffer)
     return NULL;
 
+  if (prefix)
+    {
+      for (s = prefix; *s; s++)
+        {
+          if (*s == '%' || *s < 0x20)
+            {
+              snprintf (p, 4, "%%%02X", *s);
+              p += 3;
+            }
+          else
+            *p++ = *s;
+        }
+    }
+
   for (s=data, n=datalen; n; s++, n--)
     {
       if (!*s)
@@ -121,13 +152,21 @@ percent_data_escape (const void *data, size_t datalen)
           memcpy (p, "%25", 3);
           p += 3;
         }
+      else if (plus_escape && *s == ' ')
+        {
+          *p++ = '+';
+        }
+      else if (plus_escape && (*s < ' ' || *s == '+'))
+        {
+          snprintf (p, 4, "%%%02X", *s);
+          p += 3;
+        }
       else
         *p++ = *s;
     }
   *p = 0;
 
   return buffer;
-
 }
 
 
index 94ece92..774fa80 100644 (file)
@@ -103,25 +103,182 @@ static void
 test_percent_data_escape (void)
 {
   static struct {
+    const char *prefix;
     const char *data;
     size_t datalen;
     const char *expect;
   } tbl[] = {
     {
+      NULL,
       "", 0,
       ""
     }, {
+      NULL,
       "a", 1,
       "a",
     }, {
+      NULL,
       "%22", 3,
       "%2522"
     }, {
+      NULL,
       "%%", 3,
       "%25%25%00"
     }, {
+      NULL,
       "\n \0BC\t", 6,
       "\n %00BC\t"
+    }, {
+      "",
+      "", 0,
+      ""
+    }, {
+      "",
+      "a", 1,
+      "a",
+    }, {
+      "",
+      "%22", 3,
+      "%2522"
+    }, {
+      "",
+      "%%", 3,
+      "%25%25%00"
+    }, {
+      "",
+      "\n \0BC\t", 6,
+      "\n %00BC\t"
+    }, {
+      "a",
+      "", 0,
+      "a"
+    }, {
+      "a",
+      "a", 1,
+      "aa",
+    }, {
+      "a",
+      "%22", 3,
+      "a%2522"
+    }, {
+      "a",
+      "%%", 3,
+      "a%25%25%00"
+    }, {
+      "a",
+      "\n \0BC\t", 6,
+      "a\n %00BC\t"
+    }, {
+      " ",
+      "%%", 3,
+      " %25%25%00"
+    }, {
+      "+",
+      "%%", 3,
+      "+%25%25%00"
+    }, {
+      "%",
+      "%%", 3,
+      "%25%25%25%00"
+    }, {
+      "a b",
+      "%%", 3,
+      "a b%25%25%00"
+    }, {
+      "a%2Bb",
+      "%%", 3,
+      "a%252Bb%25%25%00"
+    }, {
+      "\n",
+      "%%", 3,
+      "%0A%25%25%00"
+    }, {
+      NULL,
+      NULL, 0,
+      NULL }
+  };
+  char *buf;
+  int i;
+  size_t len, prefixlen;
+
+  for (i=0; tbl[i].data; i++)
+    {
+      buf = percent_data_escape (0, tbl[i].prefix, tbl[i].data, tbl[i].datalen);
+      if (!buf)
+        {
+          fprintf (stderr, "out of core: %s\n", strerror (errno));
+          exit (2);
+        }
+      if (strcmp (buf, tbl[i].expect))
+        {
+          fail (i);
+        }
+      len = percent_plus_unescape_inplace (buf, 0);
+      prefixlen = tbl[i].prefix? strlen (tbl[i].prefix) : 0;
+      if (len != tbl[i].datalen + prefixlen)
+        fail (i);
+      else if (tbl[i].prefix && memcmp (buf, tbl[i].prefix, prefixlen)
+               && !(prefixlen == 1 && *tbl[i].prefix == '+' && *buf == ' '))
+        {
+          /* Note extra condition above handles the one test case
+           * which reverts a plus to a space due to the use of the
+           * plus-unescape fucntion also for the prefix part.  */
+          fail (i);
+        }
+      else if (memcmp (buf+prefixlen, tbl[i].data, tbl[i].datalen))
+        {
+          fail (i);
+        }
+      xfree (buf);
+    }
+}
+
+
+
+static void
+test_percent_data_escape_plus (void)
+{
+  static struct {
+    const char *data;
+    size_t datalen;
+    const char *expect;
+  } tbl[] = {
+    {
+      "", 0,
+      ""
+    }, {
+      "a", 1,
+      "a",
+    }, {
+      "%22", 3,
+      "%2522"
+    }, {
+      "%%", 3,
+      "%25%25%00"
+    }, {
+      "\n \0BC\t", 6,
+      "%0A+%00BC%09"
+    }, {
+      " ", 1,
+      "+"
+    }, {
+      "  ", 2,
+      "++"
+    }, {
+      "+ +", 3,
+      "%2B+%2B"
+    }, {
+      "\" \"", 3,  /* Note: This function does not escape quotes.  */
+      "\"+\""
+    }, {
+      "%22", 3,
+      "%2522"
+    }, {
+      "%% ", 3,
+      "%25%25+"
+    }, {
+      "\n ABC\t", 6,
+      "%0A+ABC%09"
     }, { NULL, 0, NULL }
   };
   char *buf;
@@ -130,14 +287,16 @@ test_percent_data_escape (void)
 
   for (i=0; tbl[i].data; i++)
     {
-      buf = percent_data_escape (tbl[i].data, tbl[i].datalen);
+      buf = percent_data_escape (1, NULL, tbl[i].data, tbl[i].datalen);
       if (!buf)
         {
           fprintf (stderr, "out of core: %s\n", strerror (errno));
           exit (2);
         }
       if (strcmp (buf, tbl[i].expect))
-        fail (i);
+        {
+          fail (i);
+        }
       len = percent_plus_unescape_inplace (buf, 0);
       if (len != tbl[i].datalen)
         fail (i);
@@ -148,16 +307,15 @@ test_percent_data_escape (void)
 }
 
 
-
 int
 main (int argc, char **argv)
 {
   (void)argc;
   (void)argv;
 
-  /* FIXME: We escape_unescape is not tested - only
-     percent_plus_unescape.  */
+  /* FIXME: escape_unescape is not tested - only percent_plus_unescape.  */
   test_percent_plus_escape ();
   test_percent_data_escape ();
+  test_percent_data_escape_plus ();
   return 0;
 }
index 682415d..d3a846f 100644 (file)
@@ -201,7 +201,8 @@ char *hex2str_alloc (const char *hexstring, size_t *r_count);
 
 /*-- percent.c --*/
 char *percent_plus_escape (const char *string);
-char *percent_data_escape (const void *data, size_t datalen);
+char *percent_data_escape (int plus, const char *prefix,
+                           const void *data, size_t datalen);
 char *percent_plus_unescape (const char *string, int nulrepl);
 char *percent_unescape (const char *string, int nulrepl);