Fix leak in pki --pkcs7 --decrypt
[strongswan.git] / src / pki / commands / pkcs7.c
1 /*
2 * Copyright (C) 2012 Martin Willi
3 * Copyright (C) 2012 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "pki.h"
17
18 #include <credentials/containers/pkcs7.h>
19 #include <credentials/sets/mem_cred.h>
20
21 /**
22 * Read input data as chunk
23 */
24 static chunk_t read_from_stream(FILE *stream)
25 {
26 char buf[8096];
27 size_t len, total = 0;
28
29 while (TRUE)
30 {
31 len = fread(buf + total, 1, sizeof(buf) - total, stream);
32 if (len < 0)
33 {
34 return chunk_empty;
35 }
36 if (len == 0)
37 {
38 return chunk_clone(chunk_create(buf, total));
39 }
40 total += len;
41 if (total == sizeof(buf))
42 {
43 fprintf(stderr, "buffer too small to read input!\n");
44 return chunk_empty;
45 }
46 }
47 }
48
49 /**
50 * Write output data from chunk to stream
51 */
52 static bool write_to_stream(FILE *stream, chunk_t data)
53 {
54 size_t len, total = 0;
55
56 while (total < data.len)
57 {
58 len = fwrite(data.ptr + total, 1, data.len - total, stream);
59 if (len <= 0)
60 {
61 return FALSE;
62 }
63 total += len;
64 }
65 return TRUE;
66 }
67
68 /**
69 * Verify PKCS#7 signed-data
70 */
71 static int verify(chunk_t chunk)
72 {
73 container_t *container;
74 enumerator_t *enumerator;
75 certificate_t *cert;
76 auth_cfg_t *auth;
77 chunk_t data;
78 bool verified = FALSE;
79
80 container = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS7,
81 BUILD_BLOB_ASN1_DER, chunk, BUILD_END);
82 if (!container)
83 {
84 return 1;
85 }
86
87 if (container->get_type(container) != CONTAINER_PKCS7_SIGNED_DATA)
88 {
89 fprintf(stderr, "verification failed, container is %N\n",
90 container_type_names, container->get_type(container));
91 container->destroy(container);
92 return 1;
93 }
94
95 enumerator = container->create_signature_enumerator(container);
96 while (enumerator->enumerate(enumerator, &auth))
97 {
98 verified = TRUE;
99 cert = auth->get(auth, AUTH_RULE_SUBJECT_CERT);
100 if (cert)
101 {
102 fprintf(stderr, "signed by '%Y'\n", cert->get_subject(cert));
103 }
104 }
105 enumerator->destroy(enumerator);
106
107 if (!verified)
108 {
109 fprintf(stderr, "no trusted signature found\n");
110 }
111
112 if (verified)
113 {
114 if (container->get_data(container, &data))
115 {
116 write_to_stream(stdout, data);
117 free(data.ptr);
118 }
119 else
120 {
121 verified = FALSE;
122 }
123 }
124 container->destroy(container);
125
126 return verified ? 0 : 1;
127 }
128
129 /**
130 * Sign data into PKCS#7 signed-data
131 */
132 static int sign(chunk_t chunk, certificate_t *cert, private_key_t *key)
133 {
134 container_t *container;
135 chunk_t encoding;
136 int res = 1;
137
138 container = lib->creds->create(lib->creds,
139 CRED_CONTAINER, CONTAINER_PKCS7_SIGNED_DATA,
140 BUILD_BLOB, chunk,
141 BUILD_SIGNING_CERT, cert,
142 BUILD_SIGNING_KEY, key,
143 BUILD_END);
144 if (container)
145 {
146 if (container->get_encoding(container, &encoding))
147 {
148 write_to_stream(stdout, encoding);
149 free(encoding.ptr);
150 }
151 container->destroy(container);
152 }
153 return res;
154 }
155
156 /**
157 * Encrypt data to a PKCS#7 enveloped-data
158 */
159 static int encrypt(chunk_t chunk, certificate_t *cert)
160 {
161 container_t *container;
162 chunk_t encoding;
163 int res = 1;
164
165 container = lib->creds->create(lib->creds,
166 CRED_CONTAINER, CONTAINER_PKCS7_ENVELOPED_DATA,
167 BUILD_BLOB, chunk, BUILD_CERT, cert,
168 BUILD_END);
169 if (container)
170 {
171 if (container->get_encoding(container, &encoding))
172 {
173 write_to_stream(stdout, encoding);
174 free(encoding.ptr);
175 }
176 container->destroy(container);
177 }
178 return res;
179 }
180
181 /**
182 * Decrypt PKCS#7 enveloped-data
183 */
184 static int decrypt(chunk_t chunk)
185 {
186 container_t *container;
187 chunk_t data;
188
189 container = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS7,
190 BUILD_BLOB_ASN1_DER, chunk, BUILD_END);
191 if (!container)
192 {
193 return 1;
194 }
195 if (container->get_type(container) != CONTAINER_PKCS7_ENVELOPED_DATA)
196 {
197 fprintf(stderr, "decryption failed, container is %N\n",
198 container_type_names, container->get_type(container));
199 container->destroy(container);
200 return 1;
201 }
202 if (!container->get_data(container, &data))
203 {
204 fprintf(stderr, "PKCS#7 decryption failed\n");
205 container->destroy(container);
206 return 1;
207 }
208 container->destroy(container);
209
210 write_to_stream(stdout, data);
211 free(data.ptr);
212
213 return 0;
214 }
215
216 /**
217 * Wrap/Unwrap PKCs#7 containers
218 */
219 static int pkcs7()
220 {
221 char *arg, *file = NULL;
222 private_key_t *key = NULL;
223 certificate_t *cert = NULL;
224 chunk_t data = chunk_empty;
225 mem_cred_t *creds;
226 int res = 1;
227 FILE *in;
228 enum {
229 OP_NONE,
230 OP_SIGN,
231 OP_VERIFY,
232 OP_ENCRYPT,
233 OP_DECRYPT,
234 } op = OP_NONE;
235
236 creds = mem_cred_create();
237
238 while (TRUE)
239 {
240 switch (command_getopt(&arg))
241 {
242 case 'h':
243 return command_usage(NULL);
244 case 'i':
245 file = arg;
246 continue;
247 case 's':
248 if (op != OP_NONE)
249 {
250 goto invalid;
251 }
252 op = OP_SIGN;
253 continue;
254 case 'u':
255 if (op != OP_NONE)
256 {
257 goto invalid;
258 }
259 op = OP_VERIFY;
260 continue;
261 case 'e':
262 if (op != OP_NONE)
263 {
264 goto invalid;
265 }
266 op = OP_ENCRYPT;
267 continue;
268 case 'd':
269 if (op != OP_NONE)
270 {
271 goto invalid;
272 }
273 op = OP_DECRYPT;
274 continue;
275 case 'k':
276 key = lib->creds->create(lib->creds,
277 CRED_PRIVATE_KEY, KEY_RSA,
278 BUILD_FROM_FILE, arg, BUILD_END);
279 if (!key)
280 {
281 fprintf(stderr, "parsing private key failed\n");
282 goto end;
283 }
284 creds->add_key(creds, key);
285 continue;
286 case 'c':
287 cert = lib->creds->create(lib->creds,
288 CRED_CERTIFICATE, CERT_X509,
289 BUILD_FROM_FILE, arg, BUILD_END);
290 if (!cert)
291 {
292 fprintf(stderr, "parsing certificate failed\n");
293 goto end;
294 }
295 creds->add_cert(creds, TRUE, cert);
296 continue;
297 case EOF:
298 break;
299 default:
300 invalid:
301 creds->destroy(creds);
302 return command_usage("invalid --pkcs7 option");
303 }
304 break;
305 }
306
307 if (file)
308 {
309 in = fopen(file, "r");
310 if (in)
311 {
312 data = read_from_stream(in);
313 fclose(in);
314 }
315 }
316 else
317 {
318 data = read_from_stream(stdin);
319 }
320
321 if (!data.len)
322 {
323 fprintf(stderr, "reading input failed!\n");
324 goto end;
325 }
326 if (!cert)
327 {
328 fprintf(stderr, "requiring a certificate!\n");
329 goto end;
330 }
331
332 lib->credmgr->add_local_set(lib->credmgr, &creds->set, FALSE);
333
334 switch (op)
335 {
336 case OP_SIGN:
337 if (!key)
338 {
339 fprintf(stderr, "signing requires a private key\n");
340 res = 1;
341 break;
342 }
343 res = sign(data, cert, key);
344 break;
345 case OP_VERIFY:
346 res = verify(data);
347 break;
348 case OP_ENCRYPT:
349 res = encrypt(data, cert);
350 break;
351 case OP_DECRYPT:
352 if (!key)
353 {
354 fprintf(stderr, "decryption requires a private key\n");
355 res = 1;
356 break;
357 }
358 res = decrypt(data);
359 break;
360 default:
361 res = 1;
362 break;
363 }
364 lib->credmgr->remove_local_set(lib->credmgr, &creds->set);
365
366 end:
367 creds->destroy(creds);
368 free(data.ptr);
369 return res;
370 }
371
372 /**
373 * Register the command.
374 */
375 static void __attribute__ ((constructor))reg()
376 {
377 command_register((command_t) {
378 pkcs7, '7', "pkcs7", "PKCS#7 wrap/unwrap functions",
379 {"--sign | --verify | --encrypt | --decrypt",
380 "--certificate+ [--key]"},
381 {
382 {"help", 'h', 0, "show usage information"},
383 {"sign", 's', 0, "create PKCS#7 signed-data"},
384 {"verify", 'u', 0, "verify PKCS#7 signed-data"},
385 {"encrypt", 'e', 0, "create PKCS#7 enveloped-data"},
386 {"decrypt", 'd', 0, "decrypt PKCS#7 enveloped-data"},
387 {"in", 'i', 1, "input file, default: stdin"},
388 {"key", 'k', 1, "path to private key for sign/decryp"},
389 {"cert", 'c', 1, "path to certificate for sign/verify/encryp"},
390 }
391 });
392 }