pki: Properly clean up if output format for --pkcs12 is wrong
[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 <asn1/oid.h>
19 #include <asn1/asn1.h>
20 #include <credentials/containers/pkcs7.h>
21 #include <credentials/sets/mem_cred.h>
22
23 /**
24 * Read input data as chunk
25 */
26 static chunk_t read_from_stream(FILE *stream)
27 {
28 char buf[8096];
29 size_t len, total = 0;
30
31 while (TRUE)
32 {
33 len = fread(buf + total, 1, sizeof(buf) - total, stream);
34 if (len < (sizeof(buf) - total))
35 {
36 if (ferror(stream))
37 {
38 return chunk_empty;
39 }
40 if (feof(stream))
41 {
42 return chunk_clone(chunk_create(buf, total + len));
43 }
44 }
45 total += len;
46 if (total == sizeof(buf))
47 {
48 fprintf(stderr, "buffer too small to read input!\n");
49 return chunk_empty;
50 }
51 }
52 }
53
54 /**
55 * Write output data from chunk to stream
56 */
57 static bool write_to_stream(FILE *stream, chunk_t data)
58 {
59 size_t len, total = 0;
60
61 set_file_mode(stream, CERT_ASN1_DER);
62 while (total < data.len)
63 {
64 len = fwrite(data.ptr + total, 1, data.len - total, stream);
65 if (len <= 0)
66 {
67 return FALSE;
68 }
69 total += len;
70 }
71 return TRUE;
72 }
73
74 /**
75 * Verify PKCS#7 signed-data
76 */
77 static int verify(chunk_t chunk)
78 {
79 container_t *container;
80 pkcs7_t *pkcs7;
81 enumerator_t *enumerator;
82 certificate_t *cert;
83 auth_cfg_t *auth;
84 chunk_t data;
85 time_t t;
86 bool verified = FALSE;
87
88 container = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS7,
89 BUILD_BLOB_ASN1_DER, chunk, BUILD_END);
90 if (!container)
91 {
92 return 1;
93 }
94
95 if (container->get_type(container) != CONTAINER_PKCS7_SIGNED_DATA)
96 {
97 fprintf(stderr, "verification failed, container is %N\n",
98 container_type_names, container->get_type(container));
99 container->destroy(container);
100 return 1;
101 }
102
103 pkcs7 = (pkcs7_t*)container;
104 enumerator = container->create_signature_enumerator(container);
105 while (enumerator->enumerate(enumerator, &auth))
106 {
107 verified = TRUE;
108 cert = auth->get(auth, AUTH_RULE_SUBJECT_CERT);
109 if (cert)
110 {
111 fprintf(stderr, "signed by '%Y'", cert->get_subject(cert));
112
113 if (pkcs7->get_attribute(pkcs7, OID_PKCS9_SIGNING_TIME,
114 enumerator, &data))
115 {
116 t = asn1_to_time(&data, ASN1_UTCTIME);
117 if (t != UNDEFINED_TIME)
118 {
119 fprintf(stderr, " at %T", &t, FALSE);
120 }
121 free(data.ptr);
122 }
123 fprintf(stderr, "\n");
124 }
125 }
126 enumerator->destroy(enumerator);
127
128 if (!verified)
129 {
130 fprintf(stderr, "no trusted signature found\n");
131 }
132
133 if (verified)
134 {
135 if (container->get_data(container, &data))
136 {
137 write_to_stream(stdout, data);
138 free(data.ptr);
139 }
140 else
141 {
142 verified = FALSE;
143 }
144 }
145 container->destroy(container);
146
147 return verified ? 0 : 1;
148 }
149
150 /**
151 * Sign data into PKCS#7 signed-data
152 */
153 static int sign(chunk_t chunk, certificate_t *cert, private_key_t *key)
154 {
155 container_t *container;
156 chunk_t encoding;
157 int res = 1;
158
159 container = lib->creds->create(lib->creds,
160 CRED_CONTAINER, CONTAINER_PKCS7_SIGNED_DATA,
161 BUILD_BLOB, chunk,
162 BUILD_SIGNING_CERT, cert,
163 BUILD_SIGNING_KEY, key,
164 BUILD_END);
165 if (container)
166 {
167 if (container->get_encoding(container, &encoding))
168 {
169 write_to_stream(stdout, encoding);
170 free(encoding.ptr);
171 }
172 container->destroy(container);
173 }
174 return res;
175 }
176
177 /**
178 * Encrypt data to a PKCS#7 enveloped-data
179 */
180 static int encrypt(chunk_t chunk, certificate_t *cert)
181 {
182 container_t *container;
183 chunk_t encoding;
184 int res = 1;
185
186 container = lib->creds->create(lib->creds,
187 CRED_CONTAINER, CONTAINER_PKCS7_ENVELOPED_DATA,
188 BUILD_BLOB, chunk, BUILD_CERT, cert,
189 BUILD_END);
190 if (container)
191 {
192 if (container->get_encoding(container, &encoding))
193 {
194 write_to_stream(stdout, encoding);
195 free(encoding.ptr);
196 }
197 container->destroy(container);
198 }
199 return res;
200 }
201
202 /**
203 * Decrypt PKCS#7 enveloped-data
204 */
205 static int decrypt(chunk_t chunk)
206 {
207 container_t *container;
208 chunk_t data;
209
210 container = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS7,
211 BUILD_BLOB_ASN1_DER, chunk, BUILD_END);
212 if (!container)
213 {
214 return 1;
215 }
216 if (container->get_type(container) != CONTAINER_PKCS7_ENVELOPED_DATA)
217 {
218 fprintf(stderr, "decryption failed, container is %N\n",
219 container_type_names, container->get_type(container));
220 container->destroy(container);
221 return 1;
222 }
223 if (!container->get_data(container, &data))
224 {
225 fprintf(stderr, "PKCS#7 decryption failed\n");
226 container->destroy(container);
227 return 1;
228 }
229 container->destroy(container);
230
231 write_to_stream(stdout, data);
232 free(data.ptr);
233
234 return 0;
235 }
236
237 /**
238 * Show info about PKCS#7 container
239 */
240 static int show(chunk_t chunk)
241 {
242 container_t *container;
243 pkcs7_t *pkcs7;
244 enumerator_t *enumerator;
245 certificate_t *cert;
246 chunk_t data;
247
248 container = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS7,
249 BUILD_BLOB_ASN1_DER, chunk, BUILD_END);
250 if (!container)
251 {
252 return 1;
253 }
254 fprintf(stderr, "%N\n", container_type_names, container->get_type(container));
255
256 if (container->get_type(container) == CONTAINER_PKCS7_SIGNED_DATA)
257 {
258 pkcs7 = (pkcs7_t*)container;
259 enumerator = pkcs7->create_cert_enumerator(pkcs7);
260 while (enumerator->enumerate(enumerator, &cert))
261 {
262 if (cert->get_encoding(cert, CERT_PEM, &data))
263 {
264 printf("%.*s", (int)data.len, data.ptr);
265 free(data.ptr);
266 }
267 }
268 enumerator->destroy(enumerator);
269 }
270 container->destroy(container);
271 return 0;
272 }
273
274 /**
275 * Wrap/Unwrap PKCs#7 containers
276 */
277 static int pkcs7()
278 {
279 char *arg, *file = NULL;
280 private_key_t *key = NULL;
281 certificate_t *cert = NULL;
282 chunk_t data = chunk_empty;
283 mem_cred_t *creds;
284 int res = 1;
285 FILE *in;
286 enum {
287 OP_NONE,
288 OP_SIGN,
289 OP_VERIFY,
290 OP_ENCRYPT,
291 OP_DECRYPT,
292 OP_SHOW,
293 } op = OP_NONE;
294
295 creds = mem_cred_create();
296
297 while (TRUE)
298 {
299 switch (command_getopt(&arg))
300 {
301 case 'h':
302 creds->destroy(creds);
303 return command_usage(NULL);
304 case 'i':
305 file = arg;
306 continue;
307 case 's':
308 if (op != OP_NONE)
309 {
310 goto invalid;
311 }
312 op = OP_SIGN;
313 continue;
314 case 'u':
315 if (op != OP_NONE)
316 {
317 goto invalid;
318 }
319 op = OP_VERIFY;
320 continue;
321 case 'e':
322 if (op != OP_NONE)
323 {
324 goto invalid;
325 }
326 op = OP_ENCRYPT;
327 continue;
328 case 'd':
329 if (op != OP_NONE)
330 {
331 goto invalid;
332 }
333 op = OP_DECRYPT;
334 continue;
335 case 'p':
336 if (op != OP_NONE)
337 {
338 goto invalid;
339 }
340 op = OP_SHOW;
341 continue;
342 case 'k':
343 key = lib->creds->create(lib->creds,
344 CRED_PRIVATE_KEY, KEY_RSA,
345 BUILD_FROM_FILE, arg, BUILD_END);
346 if (!key)
347 {
348 fprintf(stderr, "parsing private key failed\n");
349 goto end;
350 }
351 creds->add_key(creds, key);
352 continue;
353 case 'c':
354 cert = lib->creds->create(lib->creds,
355 CRED_CERTIFICATE, CERT_X509,
356 BUILD_FROM_FILE, arg, BUILD_END);
357 if (!cert)
358 {
359 fprintf(stderr, "parsing certificate failed\n");
360 goto end;
361 }
362 creds->add_cert(creds, TRUE, cert);
363 continue;
364 case EOF:
365 break;
366 default:
367 invalid:
368 creds->destroy(creds);
369 return command_usage("invalid --pkcs7 option");
370 }
371 break;
372 }
373
374 if (file)
375 {
376 in = fopen(file, "r");
377 if (in)
378 {
379 data = read_from_stream(in);
380 fclose(in);
381 }
382 }
383 else
384 {
385 data = read_from_stream(stdin);
386 }
387
388 if (!data.len)
389 {
390 fprintf(stderr, "reading input failed!\n");
391 goto end;
392 }
393 if (op != OP_SHOW && !cert)
394 {
395 fprintf(stderr, "requiring a certificate!\n");
396 goto end;
397 }
398
399 lib->credmgr->add_local_set(lib->credmgr, &creds->set, FALSE);
400
401 switch (op)
402 {
403 case OP_SIGN:
404 if (!key)
405 {
406 fprintf(stderr, "signing requires a private key\n");
407 res = 1;
408 break;
409 }
410 res = sign(data, cert, key);
411 break;
412 case OP_VERIFY:
413 res = verify(data);
414 break;
415 case OP_ENCRYPT:
416 res = encrypt(data, cert);
417 break;
418 case OP_DECRYPT:
419 if (!key)
420 {
421 fprintf(stderr, "decryption requires a private key\n");
422 res = 1;
423 break;
424 }
425 res = decrypt(data);
426 break;
427 case OP_SHOW:
428 res = show(data);
429 break;
430 default:
431 res = 1;
432 break;
433 }
434 lib->credmgr->remove_local_set(lib->credmgr, &creds->set);
435
436 end:
437 creds->destroy(creds);
438 free(data.ptr);
439 return res;
440 }
441
442 /**
443 * Register the command.
444 */
445 static void __attribute__ ((constructor))reg()
446 {
447 command_register((command_t) {
448 pkcs7, '7', "pkcs7", "PKCS#7 wrap/unwrap functions",
449 {"--sign|--verify|--encrypt|--decrypt|--show",
450 "[--in file] [--cert file]+ [--key file]"},
451 {
452 {"help", 'h', 0, "show usage information"},
453 {"sign", 's', 0, "create PKCS#7 signed-data"},
454 {"verify", 'u', 0, "verify PKCS#7 signed-data"},
455 {"encrypt", 'e', 0, "create PKCS#7 enveloped-data"},
456 {"decrypt", 'd', 0, "decrypt PKCS#7 enveloped-data"},
457 {"show", 'p', 0, "show info about PKCS#7, print certificates"},
458 {"in", 'i', 1, "input file, default: stdin"},
459 {"key", 'k', 1, "path to private key for sign/decrypt"},
460 {"cert", 'c', 1, "path to certificate for sign/verify/encrypt"},
461 }
462 });
463 }