From 055f8854d3f49b8d06105d20f344f5ac10e4f6a6 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 24 Jan 2019 10:02:52 +0100 Subject: [PATCH] common: Extend function percent_data_escape. * 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 --- agent/command.c | 2 +- common/percent.c | 77 +++++++++++++++----- common/t-percent.c | 170 +++++++++++++++++++++++++++++++++++++++++++-- common/util.h | 3 +- 4 files changed, 225 insertions(+), 27 deletions(-) diff --git a/agent/command.c b/agent/command.c index 7fbf1de1c..c395b1e59 100644 --- a/agent/command.c +++ b/agent/command.c @@ -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 (); diff --git a/common/percent.c b/common/percent.c index 7b817684d..ecc6a1959 100644 --- a/common/percent.c +++ b/common/percent.c @@ -37,16 +37,16 @@ /* 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; - for (length=1, s=data, n=datalen; n; s++, n--) + if (prefix) { - if (!*s || *s == '%') + for (s = prefix; *s; s++) + { + if (*s == '%' || *s < 0x20) + length += 3; + else + length++; + } + } + + for (s=data, n=datalen; n; s++, n--) + { + 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; - } diff --git a/common/t-percent.c b/common/t-percent.c index 94ece9249..774fa80ee 100644 --- a/common/t-percent.c +++ b/common/t-percent.c @@ -101,6 +101,142 @@ test_percent_plus_escape (void) 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; @@ -121,7 +257,28 @@ test_percent_data_escape (void) "%25%25%00" }, { "\n \0BC\t", 6, - "\n %00BC\t" + "%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; } diff --git a/common/util.h b/common/util.h index 682415d92..d3a846f00 100644 --- a/common/util.h +++ b/common/util.h @@ -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);