fixed memleak
[strongswan.git] / src / libstrongswan / asn1 / pem.c
1 /*
2 * Copyright (C) 2001-2004 Andreas Steffen, Zuercher Hochschule Winterthur
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * for more details.
13 */
14
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <unistd.h>
18 #include <errno.h>
19 #include <string.h>
20 #include <stddef.h>
21 #include <sys/types.h>
22
23 #include "pem.h"
24
25 #include <library.h>
26 #include <debug.h>
27 #include <asn1/asn1.h>
28 #include <asn1/ttodata.h>
29
30 #include <utils/lexparser.h>
31 #include <crypto/hashers/hasher.h>
32 #include <crypto/crypters/crypter.h>
33
34 #define PKCS5_SALT_LEN 8 /* bytes */
35
36 /**
37 * check the presence of a pattern in a character string
38 */
39 static bool present(const char* pattern, chunk_t* ch)
40 {
41 u_int pattern_len = strlen(pattern);
42
43 if (ch->len >= pattern_len && strncmp(ch->ptr, pattern, pattern_len) == 0)
44 {
45 ch->ptr += pattern_len;
46 ch->len -= pattern_len;
47 return TRUE;
48 }
49 return FALSE;
50 }
51
52 /**
53 * find a boundary of the form -----tag name-----
54 */
55 static bool find_boundary(const char* tag, chunk_t *line)
56 {
57 chunk_t name = chunk_empty;
58
59 if (!present("-----", line))
60 return FALSE;
61 if (!present(tag, line))
62 return FALSE;
63 if (*line->ptr != ' ')
64 return FALSE;
65 line->ptr++; line->len--;
66
67 /* extract name */
68 name.ptr = line->ptr;
69 while (line->len > 0)
70 {
71 if (present("-----", line))
72 {
73 DBG2(" -----%s %.*s-----", tag, (int)name.len, name.ptr);
74 return TRUE;
75 }
76 line->ptr++; line->len--; name.len++;
77 }
78 return FALSE;
79 }
80
81 /*
82 * decrypts a passphrase protected encrypted data block
83 */
84 static err_t pem_decrypt(chunk_t *blob, encryption_algorithm_t alg, size_t key_size,
85 chunk_t *iv, chunk_t *passphrase)
86 {
87 hasher_t *hasher;
88 crypter_t *crypter;
89 chunk_t salt = { iv->ptr, PKCS5_SALT_LEN };
90 chunk_t hash;
91 chunk_t decrypted;
92 chunk_t key = {alloca(key_size), key_size};
93 u_int8_t padding, *last_padding_pos, *first_padding_pos;
94
95 if (passphrase == NULL || passphrase->len == 0)
96 return "missing passphrase";
97
98 /* build key from passphrase and IV */
99 hasher = hasher_create(HASH_MD5);
100 hash.len = hasher->get_hash_size(hasher);
101 hash.ptr = alloca(hash.len);
102 hasher->get_hash(hasher, *passphrase, NULL);
103 hasher->get_hash(hasher, salt, hash.ptr);
104 memcpy(key.ptr, hash.ptr, hash.len);
105
106 if (key.len > hash.len)
107 {
108 hasher->get_hash(hasher, hash, NULL);
109 hasher->get_hash(hasher, *passphrase, NULL);
110 hasher->get_hash(hasher, salt, hash.ptr);
111 memcpy(key.ptr + hash.len, hash.ptr, key.len - hash.len);
112 }
113 hasher->destroy(hasher);
114
115 /* decrypt blob */
116 crypter = crypter_create(alg, key_size);
117 crypter->set_key(crypter, key);
118 if (crypter->decrypt(crypter, *blob, *iv, &decrypted) != SUCCESS)
119 {
120 crypter->destroy(crypter);
121 return "data size is not multiple of block size";
122 }
123 crypter->destroy(crypter);
124 memcpy(blob->ptr, decrypted.ptr, blob->len);
125 chunk_free(&decrypted);
126
127 /* determine amount of padding */
128 last_padding_pos = blob->ptr + blob->len - 1;
129 padding = *last_padding_pos;
130 first_padding_pos = (padding > blob->len) ? blob->ptr : last_padding_pos - padding;
131
132 /* check the padding pattern */
133 while (--last_padding_pos > first_padding_pos)
134 {
135 if (*last_padding_pos != padding)
136 return "invalid passphrase";
137 }
138 /* remove padding */
139 blob->len -= padding;
140 return NULL;
141 }
142
143 /* Converts a PEM encoded file into its binary form
144 *
145 * RFC 1421 Privacy Enhancement for Electronic Mail, February 1993
146 * RFC 934 Message Encapsulation, January 1985
147 */
148 err_t pem_to_bin(chunk_t *blob, chunk_t *passphrase, bool *pgp)
149 {
150 typedef enum {
151 PEM_PRE = 0,
152 PEM_MSG = 1,
153 PEM_HEADER = 2,
154 PEM_BODY = 3,
155 PEM_POST = 4,
156 PEM_ABORT = 5
157 } state_t;
158
159 encryption_algorithm_t alg = ENCR_UNDEFINED;
160 size_t key_size = 0;
161
162 bool encrypted = FALSE;
163
164 state_t state = PEM_PRE;
165
166 chunk_t src = *blob;
167 chunk_t dst = *blob;
168 chunk_t line = chunk_empty;
169 chunk_t iv = chunk_empty;
170
171 u_char iv_buf[16]; /* MD5 digest size */
172
173 /* zero size of converted blob */
174 dst.len = 0;
175
176 /* zero size of IV */
177 iv.ptr = iv_buf;
178 iv.len = 0;
179
180 while (fetchline(&src, &line))
181 {
182 if (state == PEM_PRE)
183 {
184 if (find_boundary("BEGIN", &line))
185 {
186 state = PEM_MSG;
187 }
188 continue;
189 }
190 else
191 {
192 if (find_boundary("END", &line))
193 {
194 state = PEM_POST;
195 break;
196 }
197 if (state == PEM_MSG)
198 {
199 state = (memchr(line.ptr, ':', line.len) == NULL) ? PEM_BODY : PEM_HEADER;
200 }
201 if (state == PEM_HEADER)
202 {
203 err_t ugh = NULL;
204 chunk_t name = chunk_empty;
205 chunk_t value = chunk_empty;
206
207 /* an empty line separates HEADER and BODY */
208 if (line.len == 0)
209 {
210 state = PEM_BODY;
211 continue;
212 }
213
214 /* we are looking for a parameter: value pair */
215 DBG2(" %.*s", (int)line.len, line.ptr);
216 ugh = extract_parameter_value(&name, &value, &line);
217 if (ugh != NULL)
218 continue;
219
220 if (match("Proc-Type", &name) && *value.ptr == '4')
221 encrypted = TRUE;
222 else if (match("DEK-Info", &name))
223 {
224 size_t len = 0;
225 chunk_t dek;
226
227 if (!extract_token(&dek, ',', &value))
228 dek = value;
229
230 if (match("DES-EDE3-CBC", &dek))
231 {
232 alg = ENCR_3DES;
233 key_size = 24;
234 }
235 else if (match("AES-128-CBC", &dek))
236 {
237 alg = ENCR_AES_CBC;
238 key_size = 16;
239 }
240 else if (match("AES-192-CBC", &dek))
241 {
242 alg = ENCR_AES_CBC;
243 key_size = 24;
244 }
245 else if (match("AES-256-CBC", &dek))
246 {
247 alg = ENCR_AES_CBC;
248 key_size = 32;
249 }
250 else
251 {
252 return "encryption algorithm not supported";
253 }
254
255 eat_whitespace(&value);
256 ugh = ttodata(value.ptr, value.len, 16, iv.ptr, 16, &len);
257 if (ugh)
258 return "error in IV";
259
260 iv.len = len;
261 }
262 }
263 else /* state is PEM_BODY */
264 {
265 const char *ugh = NULL;
266 size_t len = 0;
267 chunk_t data;
268
269 /* remove any trailing whitespace */
270 if (!extract_token(&data ,' ', &line))
271 {
272 data = line;
273 }
274
275 /* check for PGP armor checksum */
276 if (*data.ptr == '=')
277 {
278 *pgp = TRUE;
279 data.ptr++;
280 data.len--;
281 DBG2(" Armor checksum: %.*s", (int)data.len, data.ptr);
282 continue;
283 }
284
285 ugh = ttodata(data.ptr, data.len, 64, dst.ptr, blob->len - dst.len, &len);
286 if (ugh)
287 {
288 state = PEM_ABORT;
289 break;
290 }
291 else
292 {
293 dst.ptr += len;
294 dst.len += len;
295 }
296 }
297 }
298 }
299 /* set length to size of binary blob */
300 blob->len = dst.len;
301
302 if (state != PEM_POST)
303 return "file coded in unknown format, discarded";
304
305 return (encrypted)? pem_decrypt(blob, alg, key_size, &iv, passphrase) : NULL;
306 }
307
308 /* load a coded key or certificate file with autodetection
309 * of binary DER or base64 PEM ASN.1 formats and armored PGP format
310 */
311 bool pem_asn1_load_file(const char *filename, chunk_t *passphrase,
312 const char *type, chunk_t *blob, bool *pgp)
313 {
314 err_t ugh = NULL;
315
316 FILE *fd = fopen(filename, "r");
317
318 if (fd)
319 {
320 int bytes;
321 fseek(fd, 0, SEEK_END );
322 blob->len = ftell(fd);
323 rewind(fd);
324 blob->ptr = malloc(blob->len);
325 bytes = fread(blob->ptr, 1, blob->len, fd);
326 fclose(fd);
327 DBG1(" loading %s file '%s' (%d bytes)", type, filename, bytes);
328
329 *pgp = FALSE;
330
331 /* try DER format */
332 if (is_asn1(*blob))
333 {
334 DBG2(" file coded in DER format");
335 return TRUE;
336 }
337
338 if (passphrase != NULL)
339 DBG4(" passphrase:", passphrase->ptr, passphrase->len);
340
341 /* try PEM format */
342 ugh = pem_to_bin(blob, passphrase, pgp);
343
344 if (ugh == NULL)
345 {
346 if (*pgp)
347 {
348 DBG2(" file coded in armored PGP format");
349 return TRUE;
350 }
351 if (is_asn1(*blob))
352 {
353 DBG2(" file coded in PEM format");
354 return TRUE;
355 }
356 ugh = "file coded in unknown format, discarded";
357 }
358
359 /* a conversion error has occured */
360 DBG1(" %s", ugh);
361 chunk_free(blob);
362 }
363 else
364 {
365 DBG1(" could not open %s file '%s'", type, filename);
366 }
367 return FALSE;
368 }