diff options
author | cvs2svn <admin@example.com> | 2025-04-14 17:32:06 +0000 |
---|---|---|
committer | cvs2svn <admin@example.com> | 2025-04-14 17:32:06 +0000 |
commit | eb8dd9dca1228af0cd132f515509051ecfabf6f6 (patch) | |
tree | edb6da6af7e865d488dc1a29309f1e1ec226e603 /src/usr.bin/openssl/smime.c | |
parent | 247f0352e0ed72a4f476db9dc91f4d982bc83eb2 (diff) | |
download | openbsd-tb_20250414.tar.gz openbsd-tb_20250414.tar.bz2 openbsd-tb_20250414.zip |
This commit was manufactured by cvs2git to create tag 'tb_20250414'.tb_20250414
Diffstat (limited to 'src/usr.bin/openssl/smime.c')
-rw-r--r-- | src/usr.bin/openssl/smime.c | 1103 |
1 files changed, 0 insertions, 1103 deletions
diff --git a/src/usr.bin/openssl/smime.c b/src/usr.bin/openssl/smime.c deleted file mode 100644 index 46bfa08679..0000000000 --- a/src/usr.bin/openssl/smime.c +++ /dev/null | |||
@@ -1,1103 +0,0 @@ | |||
1 | /* $OpenBSD: smime.c,v 1.20 2023/04/14 15:27:13 tb Exp $ */ | ||
2 | /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL | ||
3 | * project. | ||
4 | */ | ||
5 | /* ==================================================================== | ||
6 | * Copyright (c) 1999-2004 The OpenSSL Project. All rights reserved. | ||
7 | * | ||
8 | * Redistribution and use in source and binary forms, with or without | ||
9 | * modification, are permitted provided that the following conditions | ||
10 | * are met: | ||
11 | * | ||
12 | * 1. Redistributions of source code must retain the above copyright | ||
13 | * notice, this list of conditions and the following disclaimer. | ||
14 | * | ||
15 | * 2. Redistributions in binary form must reproduce the above copyright | ||
16 | * notice, this list of conditions and the following disclaimer in | ||
17 | * the documentation and/or other materials provided with the | ||
18 | * distribution. | ||
19 | * | ||
20 | * 3. All advertising materials mentioning features or use of this | ||
21 | * software must display the following acknowledgment: | ||
22 | * "This product includes software developed by the OpenSSL Project | ||
23 | * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" | ||
24 | * | ||
25 | * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to | ||
26 | * endorse or promote products derived from this software without | ||
27 | * prior written permission. For written permission, please contact | ||
28 | * licensing@OpenSSL.org. | ||
29 | * | ||
30 | * 5. Products derived from this software may not be called "OpenSSL" | ||
31 | * nor may "OpenSSL" appear in their names without prior written | ||
32 | * permission of the OpenSSL Project. | ||
33 | * | ||
34 | * 6. Redistributions of any form whatsoever must retain the following | ||
35 | * acknowledgment: | ||
36 | * "This product includes software developed by the OpenSSL Project | ||
37 | * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" | ||
38 | * | ||
39 | * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY | ||
40 | * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
41 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
42 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR | ||
43 | * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
44 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
45 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
46 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
47 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | ||
48 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
49 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED | ||
50 | * OF THE POSSIBILITY OF SUCH DAMAGE. | ||
51 | * ==================================================================== | ||
52 | * | ||
53 | * This product includes cryptographic software written by Eric Young | ||
54 | * (eay@cryptsoft.com). This product includes software written by Tim | ||
55 | * Hudson (tjh@cryptsoft.com). | ||
56 | * | ||
57 | */ | ||
58 | |||
59 | /* S/MIME utility function */ | ||
60 | |||
61 | #include <stdio.h> | ||
62 | #include <string.h> | ||
63 | |||
64 | #include "apps.h" | ||
65 | |||
66 | #include <openssl/crypto.h> | ||
67 | #include <openssl/err.h> | ||
68 | #include <openssl/pem.h> | ||
69 | #include <openssl/x509_vfy.h> | ||
70 | #include <openssl/x509v3.h> | ||
71 | |||
72 | static int save_certs(char *signerfile, STACK_OF(X509) *signers); | ||
73 | |||
74 | #define SMIME_OP 0x10 | ||
75 | #define SMIME_IP 0x20 | ||
76 | #define SMIME_SIGNERS 0x40 | ||
77 | #define SMIME_ENCRYPT (1 | SMIME_OP) | ||
78 | #define SMIME_DECRYPT (2 | SMIME_IP) | ||
79 | #define SMIME_SIGN (3 | SMIME_OP | SMIME_SIGNERS) | ||
80 | #define SMIME_VERIFY (4 | SMIME_IP) | ||
81 | #define SMIME_PK7OUT (5 | SMIME_IP | SMIME_OP) | ||
82 | #define SMIME_RESIGN (6 | SMIME_IP | SMIME_OP | SMIME_SIGNERS) | ||
83 | |||
84 | static struct { | ||
85 | char *CAfile; | ||
86 | char *CApath; | ||
87 | char *certfile; | ||
88 | const EVP_CIPHER *cipher; | ||
89 | char *contfile; | ||
90 | int flags; | ||
91 | char *from; | ||
92 | int indef; | ||
93 | char *infile; | ||
94 | int informat; | ||
95 | char *keyfile; | ||
96 | int keyform; | ||
97 | int operation; | ||
98 | char *outfile; | ||
99 | int outformat; | ||
100 | char *passargin; | ||
101 | char *recipfile; | ||
102 | const EVP_MD *sign_md; | ||
103 | char *signerfile; | ||
104 | STACK_OF(OPENSSL_STRING) *skkeys; | ||
105 | STACK_OF(OPENSSL_STRING) *sksigners; | ||
106 | char *subject; | ||
107 | char *to; | ||
108 | X509_VERIFY_PARAM *vpm; | ||
109 | } cfg; | ||
110 | |||
111 | static const EVP_CIPHER * | ||
112 | get_cipher_by_name(char *name) | ||
113 | { | ||
114 | if (name == NULL || strcmp(name, "") == 0) | ||
115 | return (NULL); | ||
116 | #ifndef OPENSSL_NO_AES | ||
117 | else if (strcmp(name, "aes128") == 0) | ||
118 | return EVP_aes_128_cbc(); | ||
119 | else if (strcmp(name, "aes192") == 0) | ||
120 | return EVP_aes_192_cbc(); | ||
121 | else if (strcmp(name, "aes256") == 0) | ||
122 | return EVP_aes_256_cbc(); | ||
123 | #endif | ||
124 | #ifndef OPENSSL_NO_CAMELLIA | ||
125 | else if (strcmp(name, "camellia128") == 0) | ||
126 | return EVP_camellia_128_cbc(); | ||
127 | else if (strcmp(name, "camellia192") == 0) | ||
128 | return EVP_camellia_192_cbc(); | ||
129 | else if (strcmp(name, "camellia256") == 0) | ||
130 | return EVP_camellia_256_cbc(); | ||
131 | #endif | ||
132 | #ifndef OPENSSL_NO_DES | ||
133 | else if (strcmp(name, "des") == 0) | ||
134 | return EVP_des_cbc(); | ||
135 | else if (strcmp(name, "des3") == 0) | ||
136 | return EVP_des_ede3_cbc(); | ||
137 | #endif | ||
138 | #ifndef OPENSSL_NO_RC2 | ||
139 | else if (!strcmp(name, "rc2-40")) | ||
140 | return EVP_rc2_40_cbc(); | ||
141 | else if (!strcmp(name, "rc2-64")) | ||
142 | return EVP_rc2_64_cbc(); | ||
143 | else if (!strcmp(name, "rc2-128")) | ||
144 | return EVP_rc2_cbc(); | ||
145 | #endif | ||
146 | else | ||
147 | return NULL; | ||
148 | } | ||
149 | |||
150 | static int | ||
151 | smime_opt_cipher(int argc, char **argv, int *argsused) | ||
152 | { | ||
153 | char *name = argv[0]; | ||
154 | |||
155 | if (*name++ != '-') | ||
156 | return (1); | ||
157 | |||
158 | if ((cfg.cipher = get_cipher_by_name(name)) == NULL) | ||
159 | if ((cfg.cipher = EVP_get_cipherbyname(name)) == NULL) | ||
160 | return (1); | ||
161 | |||
162 | *argsused = 1; | ||
163 | return (0); | ||
164 | } | ||
165 | |||
166 | static int | ||
167 | smime_opt_inkey(char *arg) | ||
168 | { | ||
169 | if (cfg.keyfile == NULL) { | ||
170 | cfg.keyfile = arg; | ||
171 | return (0); | ||
172 | } | ||
173 | |||
174 | if (cfg.signerfile == NULL) { | ||
175 | BIO_puts(bio_err, "Illegal -inkey without -signer\n"); | ||
176 | return (1); | ||
177 | } | ||
178 | |||
179 | if (cfg.sksigners == NULL) { | ||
180 | if ((cfg.sksigners = sk_OPENSSL_STRING_new_null()) == NULL) | ||
181 | return (1); | ||
182 | } | ||
183 | if (!sk_OPENSSL_STRING_push(cfg.sksigners, | ||
184 | cfg.signerfile)) | ||
185 | return (1); | ||
186 | |||
187 | cfg.signerfile = NULL; | ||
188 | |||
189 | if (cfg.skkeys == NULL) { | ||
190 | if ((cfg.skkeys = sk_OPENSSL_STRING_new_null()) == NULL) | ||
191 | return (1); | ||
192 | } | ||
193 | if (!sk_OPENSSL_STRING_push(cfg.skkeys, cfg.keyfile)) | ||
194 | return (1); | ||
195 | |||
196 | cfg.keyfile = arg; | ||
197 | return (0); | ||
198 | } | ||
199 | |||
200 | static int | ||
201 | smime_opt_md(char *arg) | ||
202 | { | ||
203 | if ((cfg.sign_md = EVP_get_digestbyname(arg)) == NULL) { | ||
204 | BIO_printf(bio_err, "Unknown digest %s\n", arg); | ||
205 | return (1); | ||
206 | } | ||
207 | return (0); | ||
208 | } | ||
209 | |||
210 | static int | ||
211 | smime_opt_signer(char *arg) | ||
212 | { | ||
213 | if (cfg.signerfile == NULL) { | ||
214 | cfg.signerfile = arg; | ||
215 | return (0); | ||
216 | } | ||
217 | |||
218 | if (cfg.sksigners == NULL) { | ||
219 | if ((cfg.sksigners = sk_OPENSSL_STRING_new_null()) == NULL) | ||
220 | return (1); | ||
221 | } | ||
222 | if (!sk_OPENSSL_STRING_push(cfg.sksigners, | ||
223 | cfg.signerfile)) | ||
224 | return (1); | ||
225 | |||
226 | if (cfg.keyfile == NULL) | ||
227 | cfg.keyfile = cfg.signerfile; | ||
228 | |||
229 | if (cfg.skkeys == NULL) { | ||
230 | if ((cfg.skkeys = sk_OPENSSL_STRING_new_null()) == NULL) | ||
231 | return (1); | ||
232 | } | ||
233 | if (!sk_OPENSSL_STRING_push(cfg.skkeys, cfg.keyfile)) | ||
234 | return (1); | ||
235 | |||
236 | cfg.keyfile = NULL; | ||
237 | |||
238 | cfg.signerfile = arg; | ||
239 | return (0); | ||
240 | } | ||
241 | |||
242 | static int | ||
243 | smime_opt_verify_param(int argc, char **argv, int *argsused) | ||
244 | { | ||
245 | int oargc = argc; | ||
246 | int badarg = 0; | ||
247 | |||
248 | if (!args_verify(&argv, &argc, &badarg, bio_err, &cfg.vpm)) | ||
249 | return (1); | ||
250 | if (badarg) | ||
251 | return (1); | ||
252 | |||
253 | *argsused = oargc - argc; | ||
254 | |||
255 | return (0); | ||
256 | } | ||
257 | |||
258 | static const struct option smime_options[] = { | ||
259 | #ifndef OPENSSL_NO_AES | ||
260 | { | ||
261 | .name = "aes128", | ||
262 | .desc = "Encrypt PEM output with CBC AES", | ||
263 | .type = OPTION_ARGV_FUNC, | ||
264 | .opt.argvfunc = smime_opt_cipher, | ||
265 | }, | ||
266 | { | ||
267 | .name = "aes192", | ||
268 | .desc = "Encrypt PEM output with CBC AES", | ||
269 | .type = OPTION_ARGV_FUNC, | ||
270 | .opt.argvfunc = smime_opt_cipher, | ||
271 | }, | ||
272 | { | ||
273 | .name = "aes256", | ||
274 | .desc = "Encrypt PEM output with CBC AES", | ||
275 | .type = OPTION_ARGV_FUNC, | ||
276 | .opt.argvfunc = smime_opt_cipher, | ||
277 | }, | ||
278 | #endif | ||
279 | #ifndef OPENSSL_NO_CAMELLIA | ||
280 | { | ||
281 | .name = "camellia128", | ||
282 | .desc = "Encrypt PEM output with CBC Camellia", | ||
283 | .type = OPTION_ARGV_FUNC, | ||
284 | .opt.argvfunc = smime_opt_cipher, | ||
285 | }, | ||
286 | { | ||
287 | .name = "camellia192", | ||
288 | .desc = "Encrypt PEM output with CBC Camellia", | ||
289 | .type = OPTION_ARGV_FUNC, | ||
290 | .opt.argvfunc = smime_opt_cipher, | ||
291 | }, | ||
292 | { | ||
293 | .name = "camellia256", | ||
294 | .desc = "Encrypt PEM output with CBC Camellia", | ||
295 | .type = OPTION_ARGV_FUNC, | ||
296 | .opt.argvfunc = smime_opt_cipher, | ||
297 | }, | ||
298 | #endif | ||
299 | #ifndef OPENSSL_NO_DES | ||
300 | { | ||
301 | .name = "des", | ||
302 | .desc = "Encrypt with DES", | ||
303 | .type = OPTION_ARGV_FUNC, | ||
304 | .opt.argvfunc = smime_opt_cipher, | ||
305 | }, | ||
306 | { | ||
307 | .name = "des3", | ||
308 | .desc = "Encrypt with triple DES", | ||
309 | .type = OPTION_ARGV_FUNC, | ||
310 | .opt.argvfunc = smime_opt_cipher, | ||
311 | }, | ||
312 | #endif | ||
313 | #ifndef OPENSSL_NO_RC2 | ||
314 | { | ||
315 | .name = "rc2-40", | ||
316 | .desc = "Encrypt with RC2-40 (default)", | ||
317 | .type = OPTION_ARGV_FUNC, | ||
318 | .opt.argvfunc = smime_opt_cipher, | ||
319 | }, | ||
320 | { | ||
321 | .name = "rc2-64", | ||
322 | .desc = "Encrypt with RC2-64", | ||
323 | .type = OPTION_ARGV_FUNC, | ||
324 | .opt.argvfunc = smime_opt_cipher, | ||
325 | }, | ||
326 | { | ||
327 | .name = "rc2-128", | ||
328 | .desc = "Encrypt with RC2-128", | ||
329 | .type = OPTION_ARGV_FUNC, | ||
330 | .opt.argvfunc = smime_opt_cipher, | ||
331 | }, | ||
332 | #endif | ||
333 | { | ||
334 | .name = "CAfile", | ||
335 | .argname = "file", | ||
336 | .desc = "Certificate Authority file", | ||
337 | .type = OPTION_ARG, | ||
338 | .opt.arg = &cfg.CAfile, | ||
339 | }, | ||
340 | { | ||
341 | .name = "CApath", | ||
342 | .argname = "path", | ||
343 | .desc = "Certificate Authority path", | ||
344 | .type = OPTION_ARG, | ||
345 | .opt.arg = &cfg.CApath, | ||
346 | }, | ||
347 | { | ||
348 | .name = "binary", | ||
349 | .desc = "Do not translate message to text", | ||
350 | .type = OPTION_VALUE_OR, | ||
351 | .opt.value = &cfg.flags, | ||
352 | .value = PKCS7_BINARY, | ||
353 | }, | ||
354 | { | ||
355 | .name = "certfile", | ||
356 | .argname = "file", | ||
357 | .desc = "Other certificates file", | ||
358 | .type = OPTION_ARG, | ||
359 | .opt.arg = &cfg.certfile, | ||
360 | }, | ||
361 | { | ||
362 | .name = "content", | ||
363 | .argname = "file", | ||
364 | .desc = "Supply or override content for detached signature", | ||
365 | .type = OPTION_ARG, | ||
366 | .opt.arg = &cfg.contfile, | ||
367 | }, | ||
368 | { | ||
369 | .name = "crlfeol", | ||
370 | .desc = "Use CRLF as EOL termination instead of CR only", | ||
371 | .type = OPTION_VALUE_OR, | ||
372 | .opt.value = &cfg.flags, | ||
373 | .value = PKCS7_CRLFEOL, | ||
374 | }, | ||
375 | { | ||
376 | .name = "decrypt", | ||
377 | .desc = "Decrypt encrypted message", | ||
378 | .type = OPTION_VALUE, | ||
379 | .opt.value = &cfg.operation, | ||
380 | .value = SMIME_DECRYPT, | ||
381 | }, | ||
382 | { | ||
383 | .name = "encrypt", | ||
384 | .desc = "Encrypt message", | ||
385 | .type = OPTION_VALUE, | ||
386 | .opt.value = &cfg.operation, | ||
387 | .value = SMIME_ENCRYPT, | ||
388 | }, | ||
389 | { | ||
390 | .name = "from", | ||
391 | .argname = "addr", | ||
392 | .desc = "From address", | ||
393 | .type = OPTION_ARG, | ||
394 | .opt.arg = &cfg.from, | ||
395 | }, | ||
396 | { | ||
397 | .name = "in", | ||
398 | .argname = "file", | ||
399 | .desc = "Input file", | ||
400 | .type = OPTION_ARG, | ||
401 | .opt.arg = &cfg.infile, | ||
402 | }, | ||
403 | { | ||
404 | .name = "indef", | ||
405 | .desc = "Same as -stream", | ||
406 | .type = OPTION_VALUE, | ||
407 | .opt.value = &cfg.indef, | ||
408 | .value = 1, | ||
409 | }, | ||
410 | { | ||
411 | .name = "inform", | ||
412 | .argname = "fmt", | ||
413 | .desc = "Input format (DER, PEM or SMIME (default))", | ||
414 | .type = OPTION_ARG_FORMAT, | ||
415 | .opt.value = &cfg.informat, | ||
416 | }, | ||
417 | { | ||
418 | .name = "inkey", | ||
419 | .argname = "file", | ||
420 | .desc = "Input key file", | ||
421 | .type = OPTION_ARG_FUNC, | ||
422 | .opt.argfunc = smime_opt_inkey, | ||
423 | }, | ||
424 | { | ||
425 | .name = "keyform", | ||
426 | .argname = "fmt", | ||
427 | .desc = "Input key format (DER or PEM (default))", | ||
428 | .type = OPTION_ARG_FORMAT, | ||
429 | .opt.value = &cfg.keyform, | ||
430 | }, | ||
431 | { | ||
432 | .name = "md", | ||
433 | .argname = "digest", | ||
434 | .desc = "Digest to use when signing or resigning", | ||
435 | .type = OPTION_ARG_FUNC, | ||
436 | .opt.argfunc = smime_opt_md, | ||
437 | }, | ||
438 | { | ||
439 | .name = "noattr", | ||
440 | .desc = "Do not include any signed attributes", | ||
441 | .type = OPTION_VALUE_OR, | ||
442 | .opt.value = &cfg.flags, | ||
443 | .value = PKCS7_NOATTR, | ||
444 | }, | ||
445 | { | ||
446 | .name = "nocerts", | ||
447 | .desc = "Do not include signer's certificate when signing", | ||
448 | .type = OPTION_VALUE_OR, | ||
449 | .opt.value = &cfg.flags, | ||
450 | .value = PKCS7_NOCERTS, | ||
451 | }, | ||
452 | { | ||
453 | .name = "nochain", | ||
454 | .desc = "Do not chain verification of signer's certificates", | ||
455 | .type = OPTION_VALUE_OR, | ||
456 | .opt.value = &cfg.flags, | ||
457 | .value = PKCS7_NOCHAIN, | ||
458 | }, | ||
459 | { | ||
460 | .name = "nodetach", | ||
461 | .desc = "Use opaque signing", | ||
462 | .type = OPTION_VALUE_AND, | ||
463 | .opt.value = &cfg.flags, | ||
464 | .value = ~PKCS7_DETACHED, | ||
465 | }, | ||
466 | { | ||
467 | .name = "noindef", | ||
468 | .desc = "Disable streaming I/O", | ||
469 | .type = OPTION_VALUE, | ||
470 | .opt.value = &cfg.indef, | ||
471 | .value = 0, | ||
472 | }, | ||
473 | { | ||
474 | .name = "nointern", | ||
475 | .desc = "Do not search certificates in message for signer", | ||
476 | .type = OPTION_VALUE_OR, | ||
477 | .opt.value = &cfg.flags, | ||
478 | .value = PKCS7_NOINTERN, | ||
479 | }, | ||
480 | { | ||
481 | .name = "nooldmime", | ||
482 | .desc = "Output old S/MIME content type", | ||
483 | .type = OPTION_VALUE_OR, | ||
484 | .opt.value = &cfg.flags, | ||
485 | .value = PKCS7_NOOLDMIMETYPE, | ||
486 | }, | ||
487 | { | ||
488 | .name = "nosigs", | ||
489 | .desc = "Do not verify message signature", | ||
490 | .type = OPTION_VALUE_OR, | ||
491 | .opt.value = &cfg.flags, | ||
492 | .value = PKCS7_NOSIGS, | ||
493 | }, | ||
494 | { | ||
495 | .name = "nosmimecap", | ||
496 | .desc = "Omit the SMIMECapabilities attribute", | ||
497 | .type = OPTION_VALUE_OR, | ||
498 | .opt.value = &cfg.flags, | ||
499 | .value = PKCS7_NOSMIMECAP, | ||
500 | }, | ||
501 | { | ||
502 | .name = "noverify", | ||
503 | .desc = "Do not verify signer's certificate", | ||
504 | .type = OPTION_VALUE_OR, | ||
505 | .opt.value = &cfg.flags, | ||
506 | .value = PKCS7_NOVERIFY, | ||
507 | }, | ||
508 | { | ||
509 | .name = "out", | ||
510 | .argname = "file", | ||
511 | .desc = "Output file", | ||
512 | .type = OPTION_ARG, | ||
513 | .opt.arg = &cfg.outfile, | ||
514 | }, | ||
515 | { | ||
516 | .name = "outform", | ||
517 | .argname = "fmt", | ||
518 | .desc = "Output format (DER, PEM or SMIME (default))", | ||
519 | .type = OPTION_ARG_FORMAT, | ||
520 | .opt.value = &cfg.outformat, | ||
521 | }, | ||
522 | { | ||
523 | .name = "passin", | ||
524 | .argname = "src", | ||
525 | .desc = "Private key password source", | ||
526 | .type = OPTION_ARG, | ||
527 | .opt.arg = &cfg.passargin, | ||
528 | }, | ||
529 | { | ||
530 | .name = "pk7out", | ||
531 | .desc = "Output PKCS#7 structure", | ||
532 | .type = OPTION_VALUE, | ||
533 | .opt.value = &cfg.operation, | ||
534 | .value = SMIME_PK7OUT, | ||
535 | }, | ||
536 | { | ||
537 | .name = "recip", | ||
538 | .argname = "file", | ||
539 | .desc = "Recipient certificate file for decryption", | ||
540 | .type = OPTION_ARG, | ||
541 | .opt.arg = &cfg.recipfile, | ||
542 | }, | ||
543 | { | ||
544 | .name = "resign", | ||
545 | .desc = "Resign a signed message", | ||
546 | .type = OPTION_VALUE, | ||
547 | .opt.value = &cfg.operation, | ||
548 | .value = SMIME_RESIGN, | ||
549 | }, | ||
550 | { | ||
551 | .name = "sign", | ||
552 | .desc = "Sign message", | ||
553 | .type = OPTION_VALUE, | ||
554 | .opt.value = &cfg.operation, | ||
555 | .value = SMIME_SIGN, | ||
556 | }, | ||
557 | { | ||
558 | .name = "signer", | ||
559 | .argname = "file", | ||
560 | .desc = "Signer certificate file", | ||
561 | .type = OPTION_ARG_FUNC, | ||
562 | .opt.argfunc = smime_opt_signer, | ||
563 | }, | ||
564 | { | ||
565 | .name = "stream", | ||
566 | .desc = "Enable streaming I/O", | ||
567 | .type = OPTION_VALUE, | ||
568 | .opt.value = &cfg.indef, | ||
569 | .value = 1, | ||
570 | }, | ||
571 | { | ||
572 | .name = "subject", | ||
573 | .argname = "s", | ||
574 | .desc = "Subject", | ||
575 | .type = OPTION_ARG, | ||
576 | .opt.arg = &cfg.subject, | ||
577 | }, | ||
578 | { | ||
579 | .name = "text", | ||
580 | .desc = "Include or delete text MIME headers", | ||
581 | .type = OPTION_VALUE_OR, | ||
582 | .opt.value = &cfg.flags, | ||
583 | .value = PKCS7_TEXT, | ||
584 | }, | ||
585 | { | ||
586 | .name = "to", | ||
587 | .argname = "addr", | ||
588 | .desc = "To address", | ||
589 | .type = OPTION_ARG, | ||
590 | .opt.arg = &cfg.to, | ||
591 | }, | ||
592 | { | ||
593 | .name = "verify", | ||
594 | .desc = "Verify signed message", | ||
595 | .type = OPTION_VALUE, | ||
596 | .opt.value = &cfg.operation, | ||
597 | .value = SMIME_VERIFY, | ||
598 | }, | ||
599 | { | ||
600 | .name = "check_ss_sig", | ||
601 | .type = OPTION_ARGV_FUNC, | ||
602 | .opt.argvfunc = smime_opt_verify_param, | ||
603 | }, | ||
604 | { | ||
605 | .name = "crl_check", | ||
606 | .type = OPTION_ARGV_FUNC, | ||
607 | .opt.argvfunc = smime_opt_verify_param, | ||
608 | }, | ||
609 | { | ||
610 | .name = "crl_check_all", | ||
611 | .type = OPTION_ARGV_FUNC, | ||
612 | .opt.argvfunc = smime_opt_verify_param, | ||
613 | }, | ||
614 | { | ||
615 | .name = "extended_crl", | ||
616 | .type = OPTION_ARGV_FUNC, | ||
617 | .opt.argvfunc = smime_opt_verify_param, | ||
618 | }, | ||
619 | { | ||
620 | .name = "ignore_critical", | ||
621 | .type = OPTION_ARGV_FUNC, | ||
622 | .opt.argvfunc = smime_opt_verify_param, | ||
623 | }, | ||
624 | { | ||
625 | .name = "issuer_checks", | ||
626 | .type = OPTION_ARGV_FUNC, | ||
627 | .opt.argvfunc = smime_opt_verify_param, | ||
628 | }, | ||
629 | { | ||
630 | .name = "policy_check", | ||
631 | .type = OPTION_ARGV_FUNC, | ||
632 | .opt.argvfunc = smime_opt_verify_param, | ||
633 | }, | ||
634 | { | ||
635 | .name = "x509_strict", | ||
636 | .type = OPTION_ARGV_FUNC, | ||
637 | .opt.argvfunc = smime_opt_verify_param, | ||
638 | }, | ||
639 | { | ||
640 | .name = NULL, | ||
641 | .type = OPTION_ARGV_FUNC, | ||
642 | .opt.argvfunc = smime_opt_cipher, | ||
643 | }, | ||
644 | { NULL }, | ||
645 | }; | ||
646 | |||
647 | static const struct option verify_shared_options[] = { | ||
648 | { | ||
649 | .name = "check_ss_sig", | ||
650 | .desc = "Check the root CA self-signed certificate signature", | ||
651 | }, | ||
652 | { | ||
653 | .name = "crl_check", | ||
654 | .desc = "Enable CRL checking for the leaf certificate", | ||
655 | }, | ||
656 | { | ||
657 | .name = "crl_check_all", | ||
658 | .desc = "Enable CRL checking for the entire certificate chain", | ||
659 | }, | ||
660 | { | ||
661 | .name = "extended_crl", | ||
662 | .desc = "Enable extended CRL support", | ||
663 | }, | ||
664 | { | ||
665 | .name = "ignore_critical", | ||
666 | .desc = "Disable critical extension checking", | ||
667 | }, | ||
668 | { | ||
669 | .name = "issuer_checks", | ||
670 | .desc = "Enable debugging of certificate issuer checks", | ||
671 | }, | ||
672 | { | ||
673 | .name = "policy_check", | ||
674 | .desc = "Enable certificate policy checking", | ||
675 | }, | ||
676 | { | ||
677 | .name = "x509_strict", | ||
678 | .desc = "Use strict X.509 rules (disables workarounds)", | ||
679 | }, | ||
680 | { NULL }, | ||
681 | }; | ||
682 | |||
683 | static void | ||
684 | smime_usage(void) | ||
685 | { | ||
686 | fprintf(stderr, "usage: smime " | ||
687 | "[-aes128 | -aes192 | -aes256 | -des |\n" | ||
688 | " -des3 | -rc2-40 | -rc2-64 | -rc2-128] [-binary]\n" | ||
689 | " [-CAfile file] [-CApath directory] [-certfile file]\n" | ||
690 | " [-content file]\n" | ||
691 | " [-decrypt] [-encrypt]\n" | ||
692 | " [-from addr] [-in file] [-indef]\n" | ||
693 | " [-inform der | pem | smime] [-inkey file]\n" | ||
694 | " [-keyform der | pem] [-md digest] [-noattr] [-nocerts]\n" | ||
695 | " [-nochain] [-nodetach] [-noindef] [-nointern] [-nosigs]\n" | ||
696 | " [-nosmimecap] [-noverify] [-out file]\n" | ||
697 | " [-outform der | pem | smime] [-passin arg] [-pk7out]\n" | ||
698 | " [-recip file] [-resign] [-sign]\n" | ||
699 | " [-signer file] [-stream] [-subject s] [-text] [-to addr]\n" | ||
700 | " [-verify] [cert.pem ...]\n\n"); | ||
701 | |||
702 | options_usage(smime_options); | ||
703 | |||
704 | fprintf(stderr, "\nVerification options:\n\n"); | ||
705 | options_usage(verify_shared_options); | ||
706 | } | ||
707 | |||
708 | int | ||
709 | smime_main(int argc, char **argv) | ||
710 | { | ||
711 | int ret = 0; | ||
712 | char **args; | ||
713 | int argsused = 0; | ||
714 | const char *inmode = "r", *outmode = "w"; | ||
715 | PKCS7 *p7 = NULL; | ||
716 | X509_STORE *store = NULL; | ||
717 | X509 *cert = NULL, *recip = NULL, *signer = NULL; | ||
718 | EVP_PKEY *key = NULL; | ||
719 | STACK_OF(X509) *encerts = NULL, *other = NULL; | ||
720 | BIO *in = NULL, *out = NULL, *indata = NULL; | ||
721 | int badarg = 0; | ||
722 | char *passin = NULL; | ||
723 | |||
724 | if (pledge("stdio cpath wpath rpath tty", NULL) == -1) { | ||
725 | perror("pledge"); | ||
726 | exit(1); | ||
727 | } | ||
728 | |||
729 | memset(&cfg, 0, sizeof(cfg)); | ||
730 | cfg.flags = PKCS7_DETACHED; | ||
731 | cfg.informat = FORMAT_SMIME; | ||
732 | cfg.outformat = FORMAT_SMIME; | ||
733 | cfg.keyform = FORMAT_PEM; | ||
734 | if (options_parse(argc, argv, smime_options, NULL, &argsused) != 0) { | ||
735 | goto argerr; | ||
736 | } | ||
737 | args = argv + argsused; | ||
738 | ret = 1; | ||
739 | |||
740 | if (!(cfg.operation & SMIME_SIGNERS) && | ||
741 | (cfg.skkeys != NULL || cfg.sksigners != NULL)) { | ||
742 | BIO_puts(bio_err, "Multiple signers or keys not allowed\n"); | ||
743 | goto argerr; | ||
744 | } | ||
745 | if (cfg.operation & SMIME_SIGNERS) { | ||
746 | /* Check to see if any final signer needs to be appended */ | ||
747 | if (cfg.keyfile != NULL && | ||
748 | cfg.signerfile == NULL) { | ||
749 | BIO_puts(bio_err, "Illegal -inkey without -signer\n"); | ||
750 | goto argerr; | ||
751 | } | ||
752 | if (cfg.signerfile != NULL) { | ||
753 | if (cfg.sksigners == NULL) { | ||
754 | if ((cfg.sksigners = | ||
755 | sk_OPENSSL_STRING_new_null()) == NULL) | ||
756 | goto end; | ||
757 | } | ||
758 | if (!sk_OPENSSL_STRING_push(cfg.sksigners, | ||
759 | cfg.signerfile)) | ||
760 | goto end; | ||
761 | if (cfg.skkeys == NULL) { | ||
762 | if ((cfg.skkeys = | ||
763 | sk_OPENSSL_STRING_new_null()) == NULL) | ||
764 | goto end; | ||
765 | } | ||
766 | if (cfg.keyfile == NULL) | ||
767 | cfg.keyfile = cfg.signerfile; | ||
768 | if (!sk_OPENSSL_STRING_push(cfg.skkeys, | ||
769 | cfg.keyfile)) | ||
770 | goto end; | ||
771 | } | ||
772 | if (cfg.sksigners == NULL) { | ||
773 | BIO_printf(bio_err, | ||
774 | "No signer certificate specified\n"); | ||
775 | badarg = 1; | ||
776 | } | ||
777 | cfg.signerfile = NULL; | ||
778 | cfg.keyfile = NULL; | ||
779 | } else if (cfg.operation == SMIME_DECRYPT) { | ||
780 | if (cfg.recipfile == NULL && | ||
781 | cfg.keyfile == NULL) { | ||
782 | BIO_printf(bio_err, | ||
783 | "No recipient certificate or key specified\n"); | ||
784 | badarg = 1; | ||
785 | } | ||
786 | } else if (cfg.operation == SMIME_ENCRYPT) { | ||
787 | if (*args == NULL) { | ||
788 | BIO_printf(bio_err, | ||
789 | "No recipient(s) certificate(s) specified\n"); | ||
790 | badarg = 1; | ||
791 | } | ||
792 | } else if (!cfg.operation) { | ||
793 | badarg = 1; | ||
794 | } | ||
795 | |||
796 | if (badarg) { | ||
797 | argerr: | ||
798 | smime_usage(); | ||
799 | goto end; | ||
800 | } | ||
801 | |||
802 | if (!app_passwd(bio_err, cfg.passargin, NULL, &passin, NULL)) { | ||
803 | BIO_printf(bio_err, "Error getting password\n"); | ||
804 | goto end; | ||
805 | } | ||
806 | ret = 2; | ||
807 | |||
808 | if (!(cfg.operation & SMIME_SIGNERS)) | ||
809 | cfg.flags &= ~PKCS7_DETACHED; | ||
810 | |||
811 | if (cfg.operation & SMIME_OP) { | ||
812 | if (cfg.outformat == FORMAT_ASN1) | ||
813 | outmode = "wb"; | ||
814 | } else { | ||
815 | if (cfg.flags & PKCS7_BINARY) | ||
816 | outmode = "wb"; | ||
817 | } | ||
818 | |||
819 | if (cfg.operation & SMIME_IP) { | ||
820 | if (cfg.informat == FORMAT_ASN1) | ||
821 | inmode = "rb"; | ||
822 | } else { | ||
823 | if (cfg.flags & PKCS7_BINARY) | ||
824 | inmode = "rb"; | ||
825 | } | ||
826 | |||
827 | if (cfg.operation == SMIME_ENCRYPT) { | ||
828 | if (cfg.cipher == NULL) { | ||
829 | #ifndef OPENSSL_NO_RC2 | ||
830 | cfg.cipher = EVP_rc2_40_cbc(); | ||
831 | #else | ||
832 | BIO_printf(bio_err, "No cipher selected\n"); | ||
833 | goto end; | ||
834 | #endif | ||
835 | } | ||
836 | if ((encerts = sk_X509_new_null()) == NULL) | ||
837 | goto end; | ||
838 | while (*args != NULL) { | ||
839 | if ((cert = load_cert(bio_err, *args, FORMAT_PEM, | ||
840 | NULL, "recipient certificate file")) == NULL) { | ||
841 | goto end; | ||
842 | } | ||
843 | if (!sk_X509_push(encerts, cert)) | ||
844 | goto end; | ||
845 | cert = NULL; | ||
846 | args++; | ||
847 | } | ||
848 | } | ||
849 | if (cfg.certfile != NULL) { | ||
850 | if ((other = load_certs(bio_err, cfg.certfile, | ||
851 | FORMAT_PEM, NULL, "certificate file")) == NULL) { | ||
852 | ERR_print_errors(bio_err); | ||
853 | goto end; | ||
854 | } | ||
855 | } | ||
856 | if (cfg.recipfile != NULL && | ||
857 | (cfg.operation == SMIME_DECRYPT)) { | ||
858 | if ((recip = load_cert(bio_err, cfg.recipfile, | ||
859 | FORMAT_PEM, NULL, "recipient certificate file")) == NULL) { | ||
860 | ERR_print_errors(bio_err); | ||
861 | goto end; | ||
862 | } | ||
863 | } | ||
864 | if (cfg.operation == SMIME_DECRYPT) { | ||
865 | if (cfg.keyfile == NULL) | ||
866 | cfg.keyfile = cfg.recipfile; | ||
867 | } else if (cfg.operation == SMIME_SIGN) { | ||
868 | if (cfg.keyfile == NULL) | ||
869 | cfg.keyfile = cfg.signerfile; | ||
870 | } else { | ||
871 | cfg.keyfile = NULL; | ||
872 | } | ||
873 | |||
874 | if (cfg.keyfile != NULL) { | ||
875 | key = load_key(bio_err, cfg.keyfile, | ||
876 | cfg.keyform, 0, passin, "signing key file"); | ||
877 | if (key == NULL) | ||
878 | goto end; | ||
879 | } | ||
880 | if (cfg.infile != NULL) { | ||
881 | if ((in = BIO_new_file(cfg.infile, inmode)) == NULL) { | ||
882 | BIO_printf(bio_err, | ||
883 | "Can't open input file %s\n", cfg.infile); | ||
884 | goto end; | ||
885 | } | ||
886 | } else { | ||
887 | if ((in = BIO_new_fp(stdin, BIO_NOCLOSE)) == NULL) | ||
888 | goto end; | ||
889 | } | ||
890 | |||
891 | if (cfg.operation & SMIME_IP) { | ||
892 | if (cfg.informat == FORMAT_SMIME) | ||
893 | p7 = SMIME_read_PKCS7(in, &indata); | ||
894 | else if (cfg.informat == FORMAT_PEM) | ||
895 | p7 = PEM_read_bio_PKCS7(in, NULL, NULL, NULL); | ||
896 | else if (cfg.informat == FORMAT_ASN1) | ||
897 | p7 = d2i_PKCS7_bio(in, NULL); | ||
898 | else { | ||
899 | BIO_printf(bio_err, | ||
900 | "Bad input format for PKCS#7 file\n"); | ||
901 | goto end; | ||
902 | } | ||
903 | |||
904 | if (p7 == NULL) { | ||
905 | BIO_printf(bio_err, "Error reading S/MIME message\n"); | ||
906 | goto end; | ||
907 | } | ||
908 | if (cfg.contfile != NULL) { | ||
909 | BIO_free(indata); | ||
910 | if ((indata = BIO_new_file(cfg.contfile, | ||
911 | "rb")) == NULL) { | ||
912 | BIO_printf(bio_err, | ||
913 | "Can't read content file %s\n", | ||
914 | cfg.contfile); | ||
915 | goto end; | ||
916 | } | ||
917 | } | ||
918 | } | ||
919 | if (cfg.outfile != NULL) { | ||
920 | if ((out = BIO_new_file(cfg.outfile, outmode)) == NULL) { | ||
921 | BIO_printf(bio_err, | ||
922 | "Can't open output file %s\n", | ||
923 | cfg.outfile); | ||
924 | goto end; | ||
925 | } | ||
926 | } else { | ||
927 | if ((out = BIO_new_fp(stdout, BIO_NOCLOSE)) == NULL) | ||
928 | goto end; | ||
929 | } | ||
930 | |||
931 | if (cfg.operation == SMIME_VERIFY) { | ||
932 | if ((store = setup_verify(bio_err, cfg.CAfile, | ||
933 | cfg.CApath)) == NULL) | ||
934 | goto end; | ||
935 | if (cfg.vpm != NULL) { | ||
936 | if (!X509_STORE_set1_param(store, cfg.vpm)) | ||
937 | goto end; | ||
938 | } | ||
939 | } | ||
940 | ret = 3; | ||
941 | |||
942 | if (cfg.operation == SMIME_ENCRYPT) { | ||
943 | if (cfg.indef) | ||
944 | cfg.flags |= PKCS7_STREAM; | ||
945 | p7 = PKCS7_encrypt(encerts, in, cfg.cipher, | ||
946 | cfg.flags); | ||
947 | } else if (cfg.operation & SMIME_SIGNERS) { | ||
948 | int i; | ||
949 | /* | ||
950 | * If detached data content we only enable streaming if | ||
951 | * S/MIME output format. | ||
952 | */ | ||
953 | if (cfg.operation == SMIME_SIGN) { | ||
954 | if (cfg.flags & PKCS7_DETACHED) { | ||
955 | if (cfg.outformat == FORMAT_SMIME) | ||
956 | cfg.flags |= PKCS7_STREAM; | ||
957 | } else if (cfg.indef) { | ||
958 | cfg.flags |= PKCS7_STREAM; | ||
959 | } | ||
960 | cfg.flags |= PKCS7_PARTIAL; | ||
961 | p7 = PKCS7_sign(NULL, NULL, other, in, | ||
962 | cfg.flags); | ||
963 | if (p7 == NULL) | ||
964 | goto end; | ||
965 | } else { | ||
966 | cfg.flags |= PKCS7_REUSE_DIGEST; | ||
967 | } | ||
968 | for (i = 0; i < sk_OPENSSL_STRING_num(cfg.sksigners); i++) { | ||
969 | cfg.signerfile = | ||
970 | sk_OPENSSL_STRING_value(cfg.sksigners, i); | ||
971 | cfg.keyfile = | ||
972 | sk_OPENSSL_STRING_value(cfg.skkeys, i); | ||
973 | signer = load_cert(bio_err, cfg.signerfile, | ||
974 | FORMAT_PEM, NULL, "signer certificate"); | ||
975 | if (signer == NULL) | ||
976 | goto end; | ||
977 | key = load_key(bio_err, cfg.keyfile, | ||
978 | cfg.keyform, 0, passin, | ||
979 | "signing key file"); | ||
980 | if (key == NULL) | ||
981 | goto end; | ||
982 | if (PKCS7_sign_add_signer(p7, signer, key, | ||
983 | cfg.sign_md, cfg.flags) == NULL) | ||
984 | goto end; | ||
985 | X509_free(signer); | ||
986 | signer = NULL; | ||
987 | EVP_PKEY_free(key); | ||
988 | key = NULL; | ||
989 | } | ||
990 | /* If not streaming or resigning finalize structure */ | ||
991 | if ((cfg.operation == SMIME_SIGN) && | ||
992 | !(cfg.flags & PKCS7_STREAM)) { | ||
993 | if (!PKCS7_final(p7, in, cfg.flags)) | ||
994 | goto end; | ||
995 | } | ||
996 | } | ||
997 | if (p7 == NULL) { | ||
998 | BIO_printf(bio_err, "Error creating PKCS#7 structure\n"); | ||
999 | goto end; | ||
1000 | } | ||
1001 | ret = 4; | ||
1002 | |||
1003 | if (cfg.operation == SMIME_DECRYPT) { | ||
1004 | if (!PKCS7_decrypt(p7, key, recip, out, cfg.flags)) { | ||
1005 | BIO_printf(bio_err, | ||
1006 | "Error decrypting PKCS#7 structure\n"); | ||
1007 | goto end; | ||
1008 | } | ||
1009 | } else if (cfg.operation == SMIME_VERIFY) { | ||
1010 | STACK_OF(X509) *signers; | ||
1011 | if (PKCS7_verify(p7, other, store, indata, out, | ||
1012 | cfg.flags)) { | ||
1013 | BIO_printf(bio_err, "Verification successful\n"); | ||
1014 | } else { | ||
1015 | BIO_printf(bio_err, "Verification failure\n"); | ||
1016 | goto end; | ||
1017 | } | ||
1018 | if ((signers = PKCS7_get0_signers(p7, other, | ||
1019 | cfg.flags)) == NULL) | ||
1020 | goto end; | ||
1021 | if (!save_certs(cfg.signerfile, signers)) { | ||
1022 | BIO_printf(bio_err, "Error writing signers to %s\n", | ||
1023 | cfg.signerfile); | ||
1024 | sk_X509_free(signers); | ||
1025 | ret = 5; | ||
1026 | goto end; | ||
1027 | } | ||
1028 | sk_X509_free(signers); | ||
1029 | } else if (cfg.operation == SMIME_PK7OUT) { | ||
1030 | PEM_write_bio_PKCS7(out, p7); | ||
1031 | } else { | ||
1032 | if (cfg.to != NULL) | ||
1033 | BIO_printf(out, "To: %s\n", cfg.to); | ||
1034 | if (cfg.from != NULL) | ||
1035 | BIO_printf(out, "From: %s\n", cfg.from); | ||
1036 | if (cfg.subject != NULL) | ||
1037 | BIO_printf(out, "Subject: %s\n", cfg.subject); | ||
1038 | if (cfg.outformat == FORMAT_SMIME) { | ||
1039 | if (cfg.operation == SMIME_RESIGN) { | ||
1040 | if (!SMIME_write_PKCS7(out, p7, indata, | ||
1041 | cfg.flags)) | ||
1042 | goto end; | ||
1043 | } else { | ||
1044 | if (!SMIME_write_PKCS7(out, p7, in, | ||
1045 | cfg.flags)) | ||
1046 | goto end; | ||
1047 | } | ||
1048 | } else if (cfg.outformat == FORMAT_PEM) { | ||
1049 | if (!PEM_write_bio_PKCS7_stream(out, p7, in, | ||
1050 | cfg.flags)) | ||
1051 | goto end; | ||
1052 | } else if (cfg.outformat == FORMAT_ASN1) { | ||
1053 | if (!i2d_PKCS7_bio_stream(out, p7, in, | ||
1054 | cfg.flags)) | ||
1055 | goto end; | ||
1056 | } else { | ||
1057 | BIO_printf(bio_err, | ||
1058 | "Bad output format for PKCS#7 file\n"); | ||
1059 | goto end; | ||
1060 | } | ||
1061 | } | ||
1062 | |||
1063 | ret = 0; | ||
1064 | |||
1065 | end: | ||
1066 | if (ret) | ||
1067 | ERR_print_errors(bio_err); | ||
1068 | sk_X509_pop_free(encerts, X509_free); | ||
1069 | sk_X509_pop_free(other, X509_free); | ||
1070 | X509_VERIFY_PARAM_free(cfg.vpm); | ||
1071 | sk_OPENSSL_STRING_free(cfg.sksigners); | ||
1072 | sk_OPENSSL_STRING_free(cfg.skkeys); | ||
1073 | X509_STORE_free(store); | ||
1074 | X509_free(cert); | ||
1075 | X509_free(recip); | ||
1076 | X509_free(signer); | ||
1077 | EVP_PKEY_free(key); | ||
1078 | PKCS7_free(p7); | ||
1079 | BIO_free(in); | ||
1080 | BIO_free(indata); | ||
1081 | BIO_free_all(out); | ||
1082 | free(passin); | ||
1083 | |||
1084 | return (ret); | ||
1085 | } | ||
1086 | |||
1087 | static int | ||
1088 | save_certs(char *signerfile, STACK_OF(X509) *signers) | ||
1089 | { | ||
1090 | int i; | ||
1091 | BIO *tmp; | ||
1092 | |||
1093 | if (signerfile == NULL) | ||
1094 | return 1; | ||
1095 | tmp = BIO_new_file(signerfile, "w"); | ||
1096 | if (tmp == NULL) | ||
1097 | return 0; | ||
1098 | for (i = 0; i < sk_X509_num(signers); i++) | ||
1099 | PEM_write_bio_X509(tmp, sk_X509_value(signers, i)); | ||
1100 | BIO_free(tmp); | ||
1101 | |||
1102 | return 1; | ||
1103 | } | ||