Coverage Report

Created: 2026-08-31 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/p11-kit/common/pem.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2012 Red Hat Inc.
3
 *
4
 * Redistribution and use in source and binary forms, with or without
5
 * modification, are permitted provided that the following conditions
6
 * are met:
7
 *
8
 *     * Redistributions of source code must retain the above
9
 *       copyright notice, this list of conditions and the
10
 *       following disclaimer.
11
 *     * Redistributions in binary form must reproduce the
12
 *       above copyright notice, this list of conditions and
13
 *       the following disclaimer in the documentation and/or
14
 *       other materials provided with the distribution.
15
 *     * The names of contributors to this software may not be
16
 *       used to endorse or promote products derived from this
17
 *       software without specific prior written permission.
18
 *
19
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
29
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
30
 * DAMAGE.
31
 *
32
 * Author: Stef Walter <stefw@redhat.com>
33
 */
34
35
#include "config.h"
36
37
#include "compat.h"
38
#include "base64.h"
39
#include "buffer.h"
40
#include "debug.h"
41
#include "pem.h"
42
43
#include <assert.h>
44
#include <ctype.h>
45
#include <stdlib.h>
46
#include <string.h>
47
48
194k
#define ARMOR_SUFF          "-----"
49
385k
#define ARMOR_SUFF_L        5
50
102k
#define ARMOR_PREF_BEGIN    "-----BEGIN "
51
293k
#define ARMOR_PREF_BEGIN_L  11
52
97.5k
#define ARMOR_PREF_END      "-----END "
53
193k
#define ARMOR_PREF_END_L    9
54
55
enum {
56
  NONE = 0,
57
  TRUSTED_CERTIFICATE,
58
  CERTIFICATE
59
};
60
61
static const char *
62
pem_find_begin (const char *data,
63
                size_t n_data,
64
                char **type)
65
102k
{
66
102k
  const char *pref, *suff;
67
68
  /* Look for a prefix */
69
102k
  pref = strnstr ((char *)data, ARMOR_PREF_BEGIN, n_data);
70
102k
  if (!pref)
71
4.01k
    return NULL;
72
73
98.0k
  n_data -= (pref - data) + ARMOR_PREF_BEGIN_L;
74
98.0k
  data = pref + ARMOR_PREF_BEGIN_L;
75
76
  /* Look for the end of that begin */
77
98.0k
  suff = strnstr ((char *)data, ARMOR_SUFF, n_data);
78
98.0k
  if (!suff)
79
277
    return NULL;
80
81
  /* Make sure on the same line */
82
97.8k
  if (memchr (pref, '\n', suff - pref))
83
249
    return NULL;
84
85
97.5k
  if (type) {
86
97.5k
    pref += ARMOR_PREF_BEGIN_L;
87
97.5k
    assert (suff >= pref);
88
97.5k
    *type = strndup (pref, suff - pref);
89
97.5k
    return_val_if_fail (*type != NULL, NULL);
90
97.5k
  }
91
92
  /* The byte after this ---BEGIN--- */
93
97.5k
  return suff + ARMOR_SUFF_L;
94
97.5k
}
95
96
static const char *
97
pem_find_end (const char *data,
98
              size_t n_data,
99
              const char *type)
100
97.5k
{
101
97.5k
  const char *pref;
102
97.5k
  size_t n_type;
103
104
  /* Look for a prefix */
105
97.5k
  pref = strnstr (data, ARMOR_PREF_END, n_data);
106
97.5k
  if (!pref)
107
613
    return NULL;
108
109
96.9k
  n_data -= (pref - data) + ARMOR_PREF_END_L;
110
96.9k
  data = pref + ARMOR_PREF_END_L;
111
112
  /* Next comes the type string */
113
96.9k
  n_type = strlen (type);
114
96.9k
  if (n_type > n_data || strncmp ((char *)data, type, n_type) != 0)
115
606
    return NULL;
116
117
96.3k
  n_data -= n_type;
118
96.3k
  data += n_type;
119
120
  /* Next comes the suffix */
121
96.3k
  if (ARMOR_SUFF_L > n_data || strncmp ((char *)data, ARMOR_SUFF, ARMOR_SUFF_L) != 0)
122
481
    return NULL;
123
124
  /* The end of the data */
125
95.8k
  return pref;
126
96.3k
}
127
128
static unsigned char *
129
pem_parse_block (const char *data,
130
                 size_t n_data,
131
                 size_t *n_decoded)
132
94.8k
{
133
94.8k
  const char *x, *hbeg, *hend;
134
94.8k
  const char *p, *end;
135
94.8k
  unsigned char *decoded;
136
94.8k
  size_t length;
137
94.8k
  int ret;
138
139
94.8k
  assert (data != NULL);
140
94.8k
  assert (n_data != 0);
141
94.8k
  assert (n_decoded != NULL);
142
143
94.8k
  p = data;
144
94.8k
  end = p + n_data;
145
146
94.8k
  hbeg = hend = NULL;
147
148
  /* Try and find a pair of blank lines with only white space between */
149
232k
  while (hend == NULL) {
150
230k
    x = memchr (p, '\n', end - p);
151
230k
    if (!x)
152
93.5k
      break;
153
137k
    ++x;
154
137k
    while (isspace (*x)) {
155
      /* Found a second line, with only spaces between */
156
1.57k
      if (*x == '\n') {
157
1.22k
        hbeg = data;
158
1.22k
        hend = x;
159
1.22k
        break;
160
      /* Found a space between two lines */
161
1.22k
      } else {
162
348
        ++x;
163
348
      }
164
1.57k
    }
165
166
    /* Try next line */
167
137k
    p = x;
168
137k
  }
169
170
  /* Headers found? */
171
94.8k
  if (hbeg && hend) {
172
1.22k
    data = hend;
173
1.22k
    n_data = end - data;
174
1.22k
  }
175
176
94.8k
  length = (n_data * 3) / 4 + 1;
177
94.8k
  decoded = malloc (length);
178
94.8k
  return_val_if_fail (decoded != NULL, 0);
179
180
94.8k
  ret = p11_b64_pton (data, n_data, decoded, length);
181
94.8k
  if (ret < 0) {
182
12.9k
    free (decoded);
183
12.9k
    return NULL;
184
12.9k
  }
185
186
  /* No need to parse headers for our use cases */
187
188
81.8k
  *n_decoded = ret;
189
81.8k
  return decoded;
190
94.8k
}
191
192
unsigned int
193
p11_pem_parse (const char *data,
194
               size_t n_data,
195
               p11_pem_sink sink,
196
               void *user_data)
197
6.23k
{
198
6.23k
  const char *beg, *end;
199
6.23k
  unsigned int nfound = 0;
200
6.23k
  unsigned char *decoded = NULL;
201
6.23k
  size_t n_decoded = 0;
202
6.23k
  char *type;
203
204
6.23k
  assert (data != NULL);
205
206
102k
  while (n_data > 0) {
207
208
    /* This returns the first character after the PEM BEGIN header */
209
102k
    beg = pem_find_begin (data, n_data, &type);
210
102k
    if (beg == NULL)
211
4.53k
      break;
212
213
102k
    assert (type != NULL);
214
215
    /* This returns the character position before the PEM END header */
216
97.5k
    end = pem_find_end (beg, n_data - (beg - data), type);
217
97.5k
    if (end == NULL) {
218
1.70k
      free (type);
219
1.70k
      break;
220
1.70k
    }
221
222
95.8k
    if (beg != end) {
223
94.8k
      decoded = pem_parse_block (beg, end - beg, &n_decoded);
224
94.8k
      if (decoded) {
225
81.8k
        if (sink != NULL)
226
81.8k
          (sink) (type, decoded, n_decoded, user_data);
227
81.8k
        ++nfound;
228
81.8k
        free (decoded);
229
81.8k
      }
230
94.8k
    }
231
232
95.8k
    free (type);
233
234
    /* Try for another block */
235
95.8k
    end += ARMOR_SUFF_L;
236
95.8k
    n_data -= (const char *)end - (const char *)data;
237
95.8k
    data = end;
238
95.8k
  }
239
240
6.23k
  return nfound;
241
6.23k
}
242
243
bool
244
p11_pem_write (const unsigned char *contents,
245
               size_t length,
246
               const char *type,
247
               p11_buffer *buf)
248
0
{
249
0
  size_t estimate;
250
0
  size_t prefix;
251
0
  char *target;
252
0
  int len;
253
254
0
  return_val_if_fail (contents || !length, false);
255
0
  return_val_if_fail (type, false);
256
0
  return_val_if_fail (buf, false);
257
258
  /* Estimate from base64 data. Algorithm from Glib reference */
259
0
  estimate = length * 4 / 3 + 7;
260
0
  estimate += estimate / 64 + 1;
261
262
0
  p11_buffer_add (buf, ARMOR_PREF_BEGIN, ARMOR_PREF_BEGIN_L);
263
0
  p11_buffer_add (buf, type, -1);
264
0
  p11_buffer_add (buf, ARMOR_SUFF, ARMOR_SUFF_L);
265
266
0
  prefix = buf->len;
267
0
  target = p11_buffer_append (buf, estimate);
268
0
  return_val_if_fail (target != NULL, false);
269
270
  /*
271
   * OpenSSL is absolutely certain that it wants its PEM base64
272
   * lines to be 64 characters in len.
273
   */
274
275
0
  len = p11_b64_ntop (contents, length, target, estimate, 64);
276
277
0
  assert (len > 0);
278
0
  assert (len <= estimate);
279
0
  buf->len = prefix + len;
280
281
0
  p11_buffer_add (buf, "\n", 1);
282
0
  p11_buffer_add (buf, ARMOR_PREF_END, ARMOR_PREF_END_L);
283
0
  p11_buffer_add (buf, type, -1);
284
0
  p11_buffer_add (buf, ARMOR_SUFF, ARMOR_SUFF_L);
285
0
  p11_buffer_add (buf, "\n", 1);
286
287
  return p11_buffer_ok (buf);
288
0
}