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 <wk@gnupg.org>
This commit is contained in:
Werner Koch 2019-01-24 10:02:52 +01:00
parent fec75a3868
commit 055f8854d3
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
4 changed files with 225 additions and 27 deletions

View 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 * into a string. Instead of resorting to base64 encoding we use a
* special percent escaping which only quoted the Nul and the * special percent escaping which only quoted the Nul and the
* percent character. */ * percent character. */
string = percent_data_escape (value? value : valstr, valuelen); string = percent_data_escape (0, NULL, value? value : valstr, valuelen);
if (!string) if (!string)
{ {
err = gpg_error_from_syserror (); err = gpg_error_from_syserror ();

View File

@ -37,16 +37,16 @@
/* Create a newly alloced string from STRING with all spaces and /* Create a newly alloced string from STRING with all spaces and
control characters converted to plus signs or %xx sequences. The * control characters converted to plus signs or %xx sequences. The
function returns the new string or NULL in case of a malloc * function returns the new string or NULL in case of a malloc
failure. * failure.
*
Note that we also escape the quote character to work around a bug * Note that this fucntion also escapes the quote character to work
in the mingw32 runtime which does not correctly handle command line * around a bug in the mingw32 runtime which does not correctly handle
quoting. We correctly double the quote mark when calling a program * command line quoting. We correctly double the quote mark when
(i.e. gpg-protect-tool), but the pre-main code does not notice the * calling a program (i.e. gpg-protect-tool), but the pre-main code
double quote as an escaped quote. We do this also on POSIX systems * does not notice the double quote as an escaped quote. We do this
for consistency. */ * also on POSIX systems for consistency. */
char * char *
percent_plus_escape (const char *string) 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 /* Create a newly malloced string from (DATA,DATALEN) with embedded
* Nuls quoted as %00. The standard percent unescaping can be * nuls quoted as %00. The standard percent unescaping can be used to
* used to reverse this encoding. */ * 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 * 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; char *buffer, *p;
const char *s; const unsigned char *s;
size_t n, length; 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; length += 3;
else else
length++; length++;
@ -109,6 +126,20 @@ percent_data_escape (const void *data, size_t datalen)
if (!buffer) if (!buffer)
return NULL; 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--) for (s=data, n=datalen; n; s++, n--)
{ {
if (!*s) if (!*s)
@ -121,13 +152,21 @@ percent_data_escape (const void *data, size_t datalen)
memcpy (p, "%25", 3); memcpy (p, "%25", 3);
p += 3; p += 3;
} }
else if (plus_escape && *s == ' ')
{
*p++ = '+';
}
else if (plus_escape && (*s < ' ' || *s == '+'))
{
snprintf (p, 4, "%%%02X", *s);
p += 3;
}
else else
*p++ = *s; *p++ = *s;
} }
*p = 0; *p = 0;
return buffer; return buffer;
} }

View File

@ -101,6 +101,142 @@ test_percent_plus_escape (void)
static void static void
test_percent_data_escape (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 { static struct {
const char *data; const char *data;
@ -121,7 +257,28 @@ test_percent_data_escape (void)
"%25%25%00" "%25%25%00"
}, { }, {
"\n \0BC\t", 6, "\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 } }, { NULL, 0, NULL }
}; };
char *buf; char *buf;
@ -130,14 +287,16 @@ test_percent_data_escape (void)
for (i=0; tbl[i].data; i++) 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) if (!buf)
{ {
fprintf (stderr, "out of core: %s\n", strerror (errno)); fprintf (stderr, "out of core: %s\n", strerror (errno));
exit (2); exit (2);
} }
if (strcmp (buf, tbl[i].expect)) if (strcmp (buf, tbl[i].expect))
fail (i); {
fail (i);
}
len = percent_plus_unescape_inplace (buf, 0); len = percent_plus_unescape_inplace (buf, 0);
if (len != tbl[i].datalen) if (len != tbl[i].datalen)
fail (i); fail (i);
@ -148,16 +307,15 @@ test_percent_data_escape (void)
} }
int int
main (int argc, char **argv) main (int argc, char **argv)
{ {
(void)argc; (void)argc;
(void)argv; (void)argv;
/* FIXME: We escape_unescape is not tested - only /* FIXME: escape_unescape is not tested - only percent_plus_unescape. */
percent_plus_unescape. */
test_percent_plus_escape (); test_percent_plus_escape ();
test_percent_data_escape (); test_percent_data_escape ();
test_percent_data_escape_plus ();
return 0; return 0;
} }

View File

@ -201,7 +201,8 @@ char *hex2str_alloc (const char *hexstring, size_t *r_count);
/*-- percent.c --*/ /*-- percent.c --*/
char *percent_plus_escape (const char *string); 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_plus_unescape (const char *string, int nulrepl);
char *percent_unescape (const char *string, int nulrepl); char *percent_unescape (const char *string, int nulrepl);