/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 | } |