Coverage Report

Created: 2022-12-08 06:10

/src/gnupg/common/percent.c
Line
Count
Source (jump to first uncovered line)
1
/* percent.c - Percent escaping
2
 * Copyright (C) 2008, 2009 Free Software Foundation, Inc.
3
 *
4
 * This file is part of GnuPG.
5
 *
6
 * This file is free software; you can redistribute it and/or modify
7
 * it under the terms of either
8
 *
9
 *   - the GNU Lesser General Public License as published by the Free
10
 *     Software Foundation; either version 3 of the License, or (at
11
 *     your option) any later version.
12
 *
13
 * or
14
 *
15
 *   - the GNU General Public License as published by the Free
16
 *     Software Foundation; either version 2 of the License, or (at
17
 *     your option) any later version.
18
 *
19
 * or both in parallel, as here.
20
 *
21
 * This file is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 * GNU General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU General Public License
27
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
28
 */
29
30
#include <config.h>
31
#include <stdlib.h>
32
#include <errno.h>
33
#include <ctype.h>
34
#include <assert.h>
35
36
#include "util.h"
37
38
39
/* Create a newly alloced string from STRING with all spaces and
40
 * control characters converted to plus signs or %xx sequences.  The
41
 * function returns the new string or NULL in case of a malloc
42
 * failure.
43
 *
44
 * Note that this function also escapes the quote character to work
45
 * around a bug in the mingw32 runtime which does not correctly handle
46
 * command line quoting.  We correctly double the quote mark when
47
 * calling a program (i.e. gpg-protect-tool), but the pre-main code
48
 * does not notice the double quote as an escaped quote.  We do this
49
 * also on POSIX systems for consistency.  */
50
char *
51
percent_plus_escape (const char *string)
52
0
{
53
0
  char *buffer, *p;
54
0
  const char *s;
55
0
  size_t length;
56
57
0
  for (length=1, s=string; *s; s++)
58
0
    {
59
0
      if (*s == '+' || *s == '\"' || *s == '%'
60
0
          || *(const unsigned char *)s < 0x20)
61
0
        length += 3;
62
0
      else
63
0
        length++;
64
0
    }
65
66
0
  buffer = p = xtrymalloc (length);
67
0
  if (!buffer)
68
0
    return NULL;
69
70
0
  for (s=string; *s; s++)
71
0
    {
72
0
      if (*s == '+' || *s == '\"' || *s == '%'
73
0
          || *(const unsigned char *)s < 0x20)
74
0
        {
75
0
          snprintf (p, 4, "%%%02X", *(unsigned char *)s);
76
0
          p += 3;
77
0
        }
78
0
      else if (*s == ' ')
79
0
        *p++ = '+';
80
0
      else
81
0
        *p++ = *s;
82
0
    }
83
0
  *p = 0;
84
85
0
  return buffer;
86
87
0
}
88
89
90
/* Create a newly malloced string from (DATA,DATALEN) with embedded
91
 * nuls quoted as %00.  The standard percent unescaping can be used to
92
 * reverse this encoding.  With PLUS_ESCAPE set plus-escaping (spaces
93
 * are replaced by a '+') and escaping of characters with values less
94
 * than 0x20 is used.  If PREFIX is not NULL it will be prepended to
95
 * the output in standard escape format; that is PLUS_ESCAPING is
96
 * ignored for PREFIX. */
97
char *
98
percent_data_escape (int plus_escape, const char *prefix,
99
                     const void *data, size_t datalen)
100
0
{
101
0
  char *buffer, *p;
102
0
  const unsigned char *s;
103
0
  size_t n;
104
0
  size_t length = 1;
105
106
0
  if (prefix)
107
0
    {
108
0
      for (s = prefix; *s; s++)
109
0
        {
110
0
          if (*s == '%' || *s < 0x20)
111
0
            length += 3;
112
0
          else
113
0
            length++;
114
0
        }
115
0
    }
116
117
0
  for (s=data, n=datalen; n; s++, n--)
118
0
    {
119
0
      if (!*s || *s == '%' || (plus_escape && (*s < ' ' || *s == '+')))
120
0
        length += 3;
121
0
      else
122
0
        length++;
123
0
    }
124
125
0
  buffer = p = xtrymalloc (length);
126
0
  if (!buffer)
127
0
    return NULL;
128
129
0
  if (prefix)
130
0
    {
131
0
      for (s = prefix; *s; s++)
132
0
        {
133
0
          if (*s == '%' || *s < 0x20)
134
0
            {
135
0
              snprintf (p, 4, "%%%02X", *s);
136
0
              p += 3;
137
0
            }
138
0
          else
139
0
            *p++ = *s;
140
0
        }
141
0
    }
142
143
0
  for (s=data, n=datalen; n; s++, n--)
144
0
    {
145
0
      if (!*s)
146
0
        {
147
0
          memcpy (p, "%00", 3);
148
0
          p += 3;
149
0
        }
150
0
      else if (*s == '%')
151
0
        {
152
0
          memcpy (p, "%25", 3);
153
0
          p += 3;
154
0
        }
155
0
      else if (plus_escape && *s == ' ')
156
0
        {
157
0
          *p++ = '+';
158
0
        }
159
0
      else if (plus_escape && (*s < ' ' || *s == '+'))
160
0
        {
161
0
          snprintf (p, 4, "%%%02X", *s);
162
0
          p += 3;
163
0
        }
164
0
      else
165
0
        *p++ = *s;
166
0
    }
167
0
  *p = 0;
168
169
0
  return buffer;
170
0
}
171
172
173
/* Do the percent and plus/space unescaping from STRING to BUFFER and
174
   return the length of the valid buffer.  Plus unescaping is only
175
   done if WITHPLUS is true.  An escaped Nul character will be
176
   replaced by NULREPL.  */
177
static size_t
178
do_unescape (unsigned char *buffer, const unsigned char *string,
179
             int withplus, int nulrepl)
180
0
{
181
0
  unsigned char *p = buffer;
182
183
0
  while (*string)
184
0
    {
185
0
      if (*string == '%' && string[1] && string[2])
186
0
        {
187
0
          string++;
188
0
          *p = xtoi_2 (string);
189
0
          if (!*p)
190
0
            *p = nulrepl;
191
0
          string++;
192
0
        }
193
0
      else if (*string == '+' && withplus)
194
0
        *p = ' ';
195
0
      else
196
0
        *p = *string;
197
0
      p++;
198
0
      string++;
199
0
    }
200
201
0
  return (p - buffer);
202
0
}
203
204
205
/* Count space required after unescaping STRING.  Note that this will
206
   never be larger than strlen (STRING).  */
207
static size_t
208
count_unescape (const unsigned char *string)
209
0
{
210
0
  size_t n = 0;
211
212
0
  while (*string)
213
0
    {
214
0
      if (*string == '%' && string[1] && string[2])
215
0
        {
216
0
          string++;
217
0
          string++;
218
0
        }
219
0
      string++;
220
0
      n++;
221
0
    }
222
223
0
  return n;
224
0
}
225
226
227
/* Helper.  */
228
static char *
229
do_plus_or_plain_unescape (const char *string, int withplus, int nulrepl)
230
0
{
231
0
  size_t nbytes, n;
232
0
  char *newstring;
233
234
0
  nbytes = count_unescape (string);
235
0
  newstring = xtrymalloc (nbytes+1);
236
0
  if (newstring)
237
0
    {
238
0
      n = do_unescape (newstring, string, withplus, nulrepl);
239
0
      assert (n == nbytes);
240
0
      newstring[n] = 0;
241
0
    }
242
0
  return newstring;
243
0
}
244
245
246
/* Create a new allocated string from STRING with all "%xx" sequences
247
   decoded and all plus signs replaced by a space.  Embedded Nul
248
   characters are replaced by the value of NULREPL.  The function
249
   returns the new string or NULL in case of a malloc failure.  */
250
char *
251
percent_plus_unescape (const char *string, int nulrepl)
252
0
{
253
0
  return do_plus_or_plain_unescape (string, 1, nulrepl);
254
0
}
255
256
257
/* Create a new allocated string from STRING with all "%xx" sequences
258
   decoded.  Embedded Nul characters are replaced by the value of
259
   NULREPL.  The function returns the new string or NULL in case of a
260
   malloc failure.  */
261
char *
262
percent_unescape (const char *string, int nulrepl)
263
0
{
264
0
  return do_plus_or_plain_unescape (string, 0, nulrepl);
265
0
}
266
267
268
static size_t
269
do_unescape_inplace (char *string, int withplus, int nulrepl)
270
0
{
271
0
  unsigned char *p, *p0;
272
273
0
  p = p0 = string;
274
0
  while (*string)
275
0
    {
276
0
      if (*string == '%' && string[1] && string[2])
277
0
        {
278
0
          string++;
279
0
          *p = xtoi_2 (string);
280
0
          if (!*p)
281
0
            *p = nulrepl;
282
0
          string++;
283
0
        }
284
0
      else if (*string == '+' && withplus)
285
0
        *p = ' ';
286
0
      else
287
0
        *p = *string;
288
0
      p++;
289
0
      string++;
290
0
    }
291
292
0
  return (p - p0);
293
0
}
294
295
296
/* Perform percent and plus unescaping in STRING and return the new
297
   valid length of the string.  Embedded Nul characters are replaced
298
   by the value of NULREPL.  A terminating Nul character is not
299
   inserted; the caller might want to call this function this way:
300
301
      foo[percent_plus_unescape_inplace (foo, 0)] = 0;
302
 */
303
size_t
304
percent_plus_unescape_inplace (char *string, int nulrepl)
305
0
{
306
0
  return do_unescape_inplace (string, 1, nulrepl);
307
0
}
308
309
310
/* Perform percent unescaping in STRING and return the new valid
311
   length of the string.  Embedded Nul characters are replaced by the
312
   value of NULREPL.  A terminating Nul character is not inserted; the
313
   caller might want to call this function this way:
314
315
      foo[percent_unescape_inplace (foo, 0)] = 0;
316
 */
317
size_t
318
percent_unescape_inplace (char *string, int nulrepl)
319
0
{
320
0
  return do_unescape_inplace (string, 0, nulrepl);
321
0
}