1635165faSspz /* 2*97e3c585Schristos * Copyright 1999-2024 The OpenSSL Project Authors. All Rights Reserved. 3a89c9211Schristos * 4b0d17251Schristos * Licensed under the Apache License 2.0 (the "License"). You may not use 5c7da899bSchristos * this file except in compliance with the License. You can obtain a copy 6c7da899bSchristos * in the file LICENSE in the source distribution or at 7c7da899bSchristos * https://www.openssl.org/source/license.html 8a89c9211Schristos */ 9a89c9211Schristos 10a89c9211Schristos /* S/MIME utility function */ 11a89c9211Schristos 12a89c9211Schristos #include <stdio.h> 13a89c9211Schristos #include <string.h> 14a89c9211Schristos #include "apps.h" 1513d40330Schristos #include "progs.h" 16a89c9211Schristos #include <openssl/crypto.h> 17a89c9211Schristos #include <openssl/pem.h> 18a89c9211Schristos #include <openssl/err.h> 19a89c9211Schristos #include <openssl/x509_vfy.h> 20a89c9211Schristos #include <openssl/x509v3.h> 21a89c9211Schristos 22a89c9211Schristos static int save_certs(char *signerfile, STACK_OF(X509) *signers); 23a89c9211Schristos static int smime_cb(int ok, X509_STORE_CTX *ctx); 24a89c9211Schristos 25a89c9211Schristos #define SMIME_OP 0x10 26a89c9211Schristos #define SMIME_IP 0x20 27a89c9211Schristos #define SMIME_SIGNERS 0x40 28a89c9211Schristos #define SMIME_ENCRYPT (1 | SMIME_OP) 29a89c9211Schristos #define SMIME_DECRYPT (2 | SMIME_IP) 30a89c9211Schristos #define SMIME_SIGN (3 | SMIME_OP | SMIME_SIGNERS) 31a89c9211Schristos #define SMIME_VERIFY (4 | SMIME_IP) 32a89c9211Schristos #define SMIME_PK7OUT (5 | SMIME_IP | SMIME_OP) 33a89c9211Schristos #define SMIME_RESIGN (6 | SMIME_IP | SMIME_OP | SMIME_SIGNERS) 34a89c9211Schristos 35c7da899bSchristos typedef enum OPTION_choice { 36b0d17251Schristos OPT_COMMON, 37c7da899bSchristos OPT_ENCRYPT, OPT_DECRYPT, OPT_SIGN, OPT_RESIGN, OPT_VERIFY, 38c7da899bSchristos OPT_PK7OUT, OPT_TEXT, OPT_NOINTERN, OPT_NOVERIFY, OPT_NOCHAIN, 39c7da899bSchristos OPT_NOCERTS, OPT_NOATTR, OPT_NODETACH, OPT_NOSMIMECAP, 40c7da899bSchristos OPT_BINARY, OPT_NOSIGS, OPT_STREAM, OPT_INDEF, OPT_NOINDEF, 4113d40330Schristos OPT_CRLFEOL, OPT_ENGINE, OPT_PASSIN, 42c7da899bSchristos OPT_TO, OPT_FROM, OPT_SUBJECT, OPT_SIGNER, OPT_RECIP, OPT_MD, 43c7da899bSchristos OPT_CIPHER, OPT_INKEY, OPT_KEYFORM, OPT_CERTFILE, OPT_CAFILE, 44b0d17251Schristos OPT_CAPATH, OPT_CASTORE, OPT_NOCAFILE, OPT_NOCAPATH, OPT_NOCASTORE, 45b0d17251Schristos OPT_R_ENUM, OPT_PROV_ENUM, OPT_CONFIG, 46c7da899bSchristos OPT_V_ENUM, 47b0d17251Schristos OPT_IN, OPT_INFORM, OPT_OUT, 48c7da899bSchristos OPT_OUTFORM, OPT_CONTENT 49c7da899bSchristos } OPTION_CHOICE; 50a89c9211Schristos 5113d40330Schristos const OPTIONS smime_options[] = { 52b0d17251Schristos {OPT_HELP_STR, 1, '-', "Usage: %s [options] [cert...]\n"}, 53b0d17251Schristos 54b0d17251Schristos OPT_SECTION("General"), 55c7da899bSchristos {"help", OPT_HELP, '-', "Display this summary"}, 56c7da899bSchristos {"in", OPT_IN, '<', "Input file"}, 57c7da899bSchristos {"inform", OPT_INFORM, 'c', "Input format SMIME (default), PEM or DER"}, 58c7da899bSchristos {"out", OPT_OUT, '>', "Output file"}, 59c7da899bSchristos {"outform", OPT_OUTFORM, 'c', 60c7da899bSchristos "Output format SMIME (default), PEM or DER"}, 61b0d17251Schristos {"inkey", OPT_INKEY, 's', 62b0d17251Schristos "Input private key (if not signer or recipient)"}, 63b0d17251Schristos {"keyform", OPT_KEYFORM, 'f', "Input private key format (ENGINE, other values ignored)"}, 64b0d17251Schristos #ifndef OPENSSL_NO_ENGINE 65b0d17251Schristos {"engine", OPT_ENGINE, 's', "Use engine, possibly a hardware device"}, 66b0d17251Schristos #endif 67b0d17251Schristos {"stream", OPT_STREAM, '-', "Enable CMS streaming" }, 68b0d17251Schristos {"indef", OPT_INDEF, '-', "Same as -stream" }, 69b0d17251Schristos {"noindef", OPT_NOINDEF, '-', "Disable CMS streaming"}, 70b0d17251Schristos OPT_CONFIG_OPTION, 71b0d17251Schristos 72b0d17251Schristos OPT_SECTION("Action"), 73b0d17251Schristos {"encrypt", OPT_ENCRYPT, '-', "Encrypt message"}, 74b0d17251Schristos {"decrypt", OPT_DECRYPT, '-', "Decrypt encrypted message"}, 75b0d17251Schristos {"sign", OPT_SIGN, '-', "Sign message"}, 76b0d17251Schristos {"resign", OPT_RESIGN, '-', "Resign a signed message"}, 77b0d17251Schristos {"verify", OPT_VERIFY, '-', "Verify signed message"}, 78b0d17251Schristos 79b0d17251Schristos OPT_SECTION("Signing/Encryption"), 80b0d17251Schristos {"passin", OPT_PASSIN, 's', "Input file pass phrase source"}, 81b0d17251Schristos {"md", OPT_MD, 's', "Digest algorithm to use when signing or resigning"}, 82b0d17251Schristos {"", OPT_CIPHER, '-', "Any supported cipher"}, 83b0d17251Schristos {"pk7out", OPT_PK7OUT, '-', "Output PKCS#7 structure"}, 84b0d17251Schristos {"nointern", OPT_NOINTERN, '-', 85b0d17251Schristos "Don't search certificates in message for signer"}, 86b0d17251Schristos {"nodetach", OPT_NODETACH, '-', "Use opaque signing"}, 87b0d17251Schristos {"noattr", OPT_NOATTR, '-', "Don't include any signed attributes"}, 88b0d17251Schristos {"binary", OPT_BINARY, '-', "Don't translate message to text"}, 89b0d17251Schristos {"signer", OPT_SIGNER, 's', "Signer certificate file"}, 90c7da899bSchristos {"content", OPT_CONTENT, '<', 91c7da899bSchristos "Supply or override content for detached signature"}, 92b0d17251Schristos {"nocerts", OPT_NOCERTS, '-', 93b0d17251Schristos "Don't include signers certificate when signing"}, 94b0d17251Schristos 95b0d17251Schristos OPT_SECTION("Verification/Decryption"), 96b0d17251Schristos {"nosigs", OPT_NOSIGS, '-', "Don't verify message signature"}, 97b0d17251Schristos {"noverify", OPT_NOVERIFY, '-', "Don't verify signers certificate"}, 98b0d17251Schristos 99b0d17251Schristos {"certfile", OPT_CERTFILE, '<', "Other certificates file"}, 100b0d17251Schristos {"recip", OPT_RECIP, '<', "Recipient certificate file for decryption"}, 101b0d17251Schristos 102b0d17251Schristos OPT_SECTION("Email"), 103c7da899bSchristos {"to", OPT_TO, 's', "To address"}, 104c7da899bSchristos {"from", OPT_FROM, 's', "From address"}, 105c7da899bSchristos {"subject", OPT_SUBJECT, 's', "Subject"}, 106c7da899bSchristos {"text", OPT_TEXT, '-', "Include or delete text MIME headers"}, 107b0d17251Schristos {"nosmimecap", OPT_NOSMIMECAP, '-', "Omit the SMIMECapabilities attribute"}, 108b0d17251Schristos 109b0d17251Schristos OPT_SECTION("Certificate chain"), 110c7da899bSchristos {"CApath", OPT_CAPATH, '/', "Trusted certificates directory"}, 111c7da899bSchristos {"CAfile", OPT_CAFILE, '<', "Trusted certificates file"}, 112b0d17251Schristos {"CAstore", OPT_CASTORE, ':', "Trusted certificates store URI"}, 113c7da899bSchristos {"no-CAfile", OPT_NOCAFILE, '-', 114c7da899bSchristos "Do not load the default certificates file"}, 115c7da899bSchristos {"no-CApath", OPT_NOCAPATH, '-', 116c7da899bSchristos "Do not load certificates from the default certificates directory"}, 117b0d17251Schristos {"no-CAstore", OPT_NOCASTORE, '-', 118b0d17251Schristos "Do not load certificates from the default certificates store"}, 119c7da899bSchristos {"nochain", OPT_NOCHAIN, '-', 120c7da899bSchristos "set PKCS7_NOCHAIN so certificates contained in the message are not used as untrusted CAs" }, 121*97e3c585Schristos {"crlfeol", OPT_CRLFEOL, '-', "Use CRLF as EOL termination instead of LF only"}, 122b0d17251Schristos 12313d40330Schristos OPT_R_OPTIONS, 124c7da899bSchristos OPT_V_OPTIONS, 125b0d17251Schristos OPT_PROV_OPTIONS, 126b0d17251Schristos 127b0d17251Schristos OPT_PARAMETERS(), 128b0d17251Schristos {"cert", 0, 0, "Recipient certs, used when encrypting"}, 129c7da899bSchristos {NULL} 130c7da899bSchristos }; 131a89c9211Schristos 132c7da899bSchristos int smime_main(int argc, char **argv) 133c7da899bSchristos { 134b0d17251Schristos CONF *conf = NULL; 135c7da899bSchristos BIO *in = NULL, *out = NULL, *indata = NULL; 136c7da899bSchristos EVP_PKEY *key = NULL; 137c7da899bSchristos PKCS7 *p7 = NULL; 138c7da899bSchristos STACK_OF(OPENSSL_STRING) *sksigners = NULL, *skkeys = NULL; 139c7da899bSchristos STACK_OF(X509) *encerts = NULL, *other = NULL; 140c7da899bSchristos X509 *cert = NULL, *recip = NULL, *signer = NULL; 141c7da899bSchristos X509_STORE *store = NULL; 142c7da899bSchristos X509_VERIFY_PARAM *vpm = NULL; 143b0d17251Schristos EVP_CIPHER *cipher = NULL; 144b0d17251Schristos EVP_MD *sign_md = NULL; 145b0d17251Schristos const char *CAfile = NULL, *CApath = NULL, *CAstore = NULL, *prog = NULL; 14613d40330Schristos char *certfile = NULL, *keyfile = NULL, *contfile = NULL; 14713d40330Schristos char *infile = NULL, *outfile = NULL, *signerfile = NULL, *recipfile = NULL; 148b0d17251Schristos char *passinarg = NULL, *passin = NULL, *to = NULL, *from = NULL; 149b0d17251Schristos char *subject = NULL, *digestname = NULL, *ciphername = NULL; 150c7da899bSchristos OPTION_CHOICE o; 151b0d17251Schristos int noCApath = 0, noCAfile = 0, noCAstore = 0; 15213d40330Schristos int flags = PKCS7_DETACHED, operation = 0, ret = 0, indef = 0; 153c7da899bSchristos int informat = FORMAT_SMIME, outformat = FORMAT_SMIME, keyform = 154b0d17251Schristos FORMAT_UNDEF; 155c7da899bSchristos int vpmtouched = 0, rv = 0; 156c7da899bSchristos ENGINE *e = NULL; 157c7da899bSchristos const char *mime_eol = "\n"; 158b0d17251Schristos OSSL_LIB_CTX *libctx = app_get0_libctx(); 159c7da899bSchristos 160c7da899bSchristos if ((vpm = X509_VERIFY_PARAM_new()) == NULL) 161c7da899bSchristos return 1; 162c7da899bSchristos 163c7da899bSchristos prog = opt_init(argc, argv, smime_options); 164c7da899bSchristos while ((o = opt_next()) != OPT_EOF) { 165c7da899bSchristos switch (o) { 166c7da899bSchristos case OPT_EOF: 167c7da899bSchristos case OPT_ERR: 168c7da899bSchristos opthelp: 169c7da899bSchristos BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); 170c7da899bSchristos goto end; 171c7da899bSchristos case OPT_HELP: 172c7da899bSchristos opt_help(smime_options); 173c7da899bSchristos ret = 0; 174c7da899bSchristos goto end; 175c7da899bSchristos case OPT_INFORM: 176c7da899bSchristos if (!opt_format(opt_arg(), OPT_FMT_PDS, &informat)) 177c7da899bSchristos goto opthelp; 178c7da899bSchristos break; 179c7da899bSchristos case OPT_IN: 180c7da899bSchristos infile = opt_arg(); 181c7da899bSchristos break; 182c7da899bSchristos case OPT_OUTFORM: 183c7da899bSchristos if (!opt_format(opt_arg(), OPT_FMT_PDS, &outformat)) 184c7da899bSchristos goto opthelp; 185c7da899bSchristos break; 186c7da899bSchristos case OPT_OUT: 187c7da899bSchristos outfile = opt_arg(); 188c7da899bSchristos break; 189c7da899bSchristos case OPT_ENCRYPT: 190c7da899bSchristos operation = SMIME_ENCRYPT; 191c7da899bSchristos break; 192c7da899bSchristos case OPT_DECRYPT: 193c7da899bSchristos operation = SMIME_DECRYPT; 194c7da899bSchristos break; 195c7da899bSchristos case OPT_SIGN: 196c7da899bSchristos operation = SMIME_SIGN; 197c7da899bSchristos break; 198c7da899bSchristos case OPT_RESIGN: 199c7da899bSchristos operation = SMIME_RESIGN; 200c7da899bSchristos break; 201c7da899bSchristos case OPT_VERIFY: 202c7da899bSchristos operation = SMIME_VERIFY; 203c7da899bSchristos break; 204c7da899bSchristos case OPT_PK7OUT: 205c7da899bSchristos operation = SMIME_PK7OUT; 206c7da899bSchristos break; 207c7da899bSchristos case OPT_TEXT: 208c7da899bSchristos flags |= PKCS7_TEXT; 209c7da899bSchristos break; 210c7da899bSchristos case OPT_NOINTERN: 211c7da899bSchristos flags |= PKCS7_NOINTERN; 212c7da899bSchristos break; 213c7da899bSchristos case OPT_NOVERIFY: 214c7da899bSchristos flags |= PKCS7_NOVERIFY; 215c7da899bSchristos break; 216c7da899bSchristos case OPT_NOCHAIN: 217c7da899bSchristos flags |= PKCS7_NOCHAIN; 218c7da899bSchristos break; 219c7da899bSchristos case OPT_NOCERTS: 220c7da899bSchristos flags |= PKCS7_NOCERTS; 221c7da899bSchristos break; 222c7da899bSchristos case OPT_NOATTR: 223c7da899bSchristos flags |= PKCS7_NOATTR; 224c7da899bSchristos break; 225c7da899bSchristos case OPT_NODETACH: 226c7da899bSchristos flags &= ~PKCS7_DETACHED; 227c7da899bSchristos break; 228c7da899bSchristos case OPT_NOSMIMECAP: 229c7da899bSchristos flags |= PKCS7_NOSMIMECAP; 230c7da899bSchristos break; 231c7da899bSchristos case OPT_BINARY: 232c7da899bSchristos flags |= PKCS7_BINARY; 233c7da899bSchristos break; 234c7da899bSchristos case OPT_NOSIGS: 235c7da899bSchristos flags |= PKCS7_NOSIGS; 236c7da899bSchristos break; 237c7da899bSchristos case OPT_STREAM: 238c7da899bSchristos case OPT_INDEF: 239c7da899bSchristos indef = 1; 240c7da899bSchristos break; 241c7da899bSchristos case OPT_NOINDEF: 242c7da899bSchristos indef = 0; 243c7da899bSchristos break; 244c7da899bSchristos case OPT_CRLFEOL: 245c7da899bSchristos flags |= PKCS7_CRLFEOL; 246c7da899bSchristos mime_eol = "\r\n"; 247c7da899bSchristos break; 24813d40330Schristos case OPT_R_CASES: 24913d40330Schristos if (!opt_rand(o)) 25013d40330Schristos goto end; 251c7da899bSchristos break; 252b0d17251Schristos case OPT_PROV_CASES: 253b0d17251Schristos if (!opt_provider(o)) 254b0d17251Schristos goto end; 255b0d17251Schristos break; 256b0d17251Schristos case OPT_CONFIG: 257b0d17251Schristos conf = app_load_config_modules(opt_arg()); 258b0d17251Schristos if (conf == NULL) 259b0d17251Schristos goto end; 260b0d17251Schristos break; 261c7da899bSchristos case OPT_ENGINE: 262c7da899bSchristos e = setup_engine(opt_arg(), 0); 263c7da899bSchristos break; 264c7da899bSchristos case OPT_PASSIN: 265c7da899bSchristos passinarg = opt_arg(); 266c7da899bSchristos break; 267c7da899bSchristos case OPT_TO: 268c7da899bSchristos to = opt_arg(); 269c7da899bSchristos break; 270c7da899bSchristos case OPT_FROM: 271c7da899bSchristos from = opt_arg(); 272c7da899bSchristos break; 273c7da899bSchristos case OPT_SUBJECT: 274c7da899bSchristos subject = opt_arg(); 275c7da899bSchristos break; 276c7da899bSchristos case OPT_SIGNER: 277c7da899bSchristos /* If previous -signer argument add signer to list */ 27813d40330Schristos if (signerfile != NULL) { 279c7da899bSchristos if (sksigners == NULL 280c7da899bSchristos && (sksigners = sk_OPENSSL_STRING_new_null()) == NULL) 281c7da899bSchristos goto end; 28286adef1bSchristos sk_OPENSSL_STRING_push(sksigners, signerfile); 283c7da899bSchristos if (keyfile == NULL) 284a89c9211Schristos keyfile = signerfile; 285c7da899bSchristos if (skkeys == NULL 286c7da899bSchristos && (skkeys = sk_OPENSSL_STRING_new_null()) == NULL) 287c7da899bSchristos goto end; 28886adef1bSchristos sk_OPENSSL_STRING_push(skkeys, keyfile); 289a89c9211Schristos keyfile = NULL; 290a89c9211Schristos } 291c7da899bSchristos signerfile = opt_arg(); 292c7da899bSchristos break; 293c7da899bSchristos case OPT_RECIP: 294c7da899bSchristos recipfile = opt_arg(); 295c7da899bSchristos break; 296c7da899bSchristos case OPT_MD: 297b0d17251Schristos digestname = opt_arg(); 298c7da899bSchristos break; 299c7da899bSchristos case OPT_CIPHER: 300b0d17251Schristos ciphername = opt_unknown(); 301c7da899bSchristos break; 302c7da899bSchristos case OPT_INKEY: 303c7da899bSchristos /* If previous -inkey argument add signer to list */ 30413d40330Schristos if (keyfile != NULL) { 305c7da899bSchristos if (signerfile == NULL) { 306c7da899bSchristos BIO_printf(bio_err, 307c7da899bSchristos "%s: Must have -signer before -inkey\n", prog); 308c7da899bSchristos goto opthelp; 309a89c9211Schristos } 310c7da899bSchristos if (sksigners == NULL 311c7da899bSchristos && (sksigners = sk_OPENSSL_STRING_new_null()) == NULL) 312c7da899bSchristos goto end; 31386adef1bSchristos sk_OPENSSL_STRING_push(sksigners, signerfile); 314a89c9211Schristos signerfile = NULL; 315c7da899bSchristos if (skkeys == NULL 316c7da899bSchristos && (skkeys = sk_OPENSSL_STRING_new_null()) == NULL) 317c7da899bSchristos goto end; 31886adef1bSchristos sk_OPENSSL_STRING_push(skkeys, keyfile); 319a89c9211Schristos } 320c7da899bSchristos keyfile = opt_arg(); 321c7da899bSchristos break; 322c7da899bSchristos case OPT_KEYFORM: 323c7da899bSchristos if (!opt_format(opt_arg(), OPT_FMT_ANY, &keyform)) 324c7da899bSchristos goto opthelp; 325c7da899bSchristos break; 326c7da899bSchristos case OPT_CERTFILE: 327c7da899bSchristos certfile = opt_arg(); 328c7da899bSchristos break; 329c7da899bSchristos case OPT_CAFILE: 330c7da899bSchristos CAfile = opt_arg(); 331c7da899bSchristos break; 332c7da899bSchristos case OPT_CAPATH: 333c7da899bSchristos CApath = opt_arg(); 334c7da899bSchristos break; 335b0d17251Schristos case OPT_CASTORE: 336b0d17251Schristos CAstore = opt_arg(); 337b0d17251Schristos break; 338c7da899bSchristos case OPT_NOCAFILE: 339c7da899bSchristos noCAfile = 1; 340c7da899bSchristos break; 341c7da899bSchristos case OPT_NOCAPATH: 342c7da899bSchristos noCApath = 1; 343c7da899bSchristos break; 344b0d17251Schristos case OPT_NOCASTORE: 345b0d17251Schristos noCAstore = 1; 346b0d17251Schristos break; 347c7da899bSchristos case OPT_CONTENT: 348c7da899bSchristos contfile = opt_arg(); 349c7da899bSchristos break; 350c7da899bSchristos case OPT_V_CASES: 351c7da899bSchristos if (!opt_verify(o, vpm)) 352c7da899bSchristos goto opthelp; 353c7da899bSchristos vpmtouched++; 354c7da899bSchristos break; 355a89c9211Schristos } 356c7da899bSchristos } 357b0d17251Schristos 358b0d17251Schristos /* Extra arguments are files with recipient keys. */ 359c7da899bSchristos argc = opt_num_rest(); 360c7da899bSchristos argv = opt_rest(); 361a89c9211Schristos 362b0d17251Schristos if (!app_RAND_load()) 363b0d17251Schristos goto end; 364b0d17251Schristos 365b0d17251Schristos if (digestname != NULL) { 366b0d17251Schristos if (!opt_md(digestname, &sign_md)) 367b0d17251Schristos goto opthelp; 368b0d17251Schristos } 369b0d17251Schristos if (ciphername != NULL) { 370b0d17251Schristos if (!opt_cipher_any(ciphername, &cipher)) 371b0d17251Schristos goto opthelp; 372b0d17251Schristos } 37313d40330Schristos if (!(operation & SMIME_SIGNERS) && (skkeys != NULL || sksigners != NULL)) { 374a89c9211Schristos BIO_puts(bio_err, "Multiple signers or keys not allowed\n"); 375c7da899bSchristos goto opthelp; 376a89c9211Schristos } 377b0d17251Schristos if (!operation) { 378b0d17251Schristos BIO_puts(bio_err, 379b0d17251Schristos "No operation (-encrypt|-sign|...) specified\n"); 380b0d17251Schristos goto opthelp; 381b0d17251Schristos } 382a89c9211Schristos 383635165faSspz if (operation & SMIME_SIGNERS) { 384a89c9211Schristos /* Check to see if any final signer needs to be appended */ 385635165faSspz if (keyfile && !signerfile) { 386a89c9211Schristos BIO_puts(bio_err, "Illegal -inkey without -signer\n"); 387c7da899bSchristos goto opthelp; 388a89c9211Schristos } 38913d40330Schristos if (signerfile != NULL) { 39013d40330Schristos if (sksigners == NULL 391c7da899bSchristos && (sksigners = sk_OPENSSL_STRING_new_null()) == NULL) 392c7da899bSchristos goto end; 39386adef1bSchristos sk_OPENSSL_STRING_push(sksigners, signerfile); 394c7da899bSchristos if (!skkeys && (skkeys = sk_OPENSSL_STRING_new_null()) == NULL) 395c7da899bSchristos goto end; 396a89c9211Schristos if (!keyfile) 397a89c9211Schristos keyfile = signerfile; 39886adef1bSchristos sk_OPENSSL_STRING_push(skkeys, keyfile); 399a89c9211Schristos } 40013d40330Schristos if (sksigners == NULL) { 401a89c9211Schristos BIO_printf(bio_err, "No signer certificate specified\n"); 402c7da899bSchristos goto opthelp; 403a89c9211Schristos } 404a89c9211Schristos signerfile = NULL; 405a89c9211Schristos keyfile = NULL; 406635165faSspz } else if (operation == SMIME_DECRYPT) { 40713d40330Schristos if (recipfile == NULL && keyfile == NULL) { 408635165faSspz BIO_printf(bio_err, 409635165faSspz "No recipient certificate or key specified\n"); 410c7da899bSchristos goto opthelp; 411a89c9211Schristos } 412635165faSspz } else if (operation == SMIME_ENCRYPT) { 413c7da899bSchristos if (argc == 0) { 414a89c9211Schristos BIO_printf(bio_err, "No recipient(s) certificate(s) specified\n"); 415c7da899bSchristos goto opthelp; 416a89c9211Schristos } 41713d40330Schristos } 418a89c9211Schristos 419c7da899bSchristos if (!app_passwd(passinarg, NULL, &passin, NULL)) { 420a89c9211Schristos BIO_printf(bio_err, "Error getting password\n"); 421a89c9211Schristos goto end; 422a89c9211Schristos } 423a89c9211Schristos 424a89c9211Schristos ret = 2; 425a89c9211Schristos 426a89c9211Schristos if (!(operation & SMIME_SIGNERS)) 427a89c9211Schristos flags &= ~PKCS7_DETACHED; 428a89c9211Schristos 429c7da899bSchristos if (!(operation & SMIME_OP)) { 430a89c9211Schristos if (flags & PKCS7_BINARY) 431c7da899bSchristos outformat = FORMAT_BINARY; 432a89c9211Schristos } 433a89c9211Schristos 434c7da899bSchristos if (!(operation & SMIME_IP)) { 435a89c9211Schristos if (flags & PKCS7_BINARY) 436c7da899bSchristos informat = FORMAT_BINARY; 437a89c9211Schristos } 438a89c9211Schristos 439635165faSspz if (operation == SMIME_ENCRYPT) { 44013d40330Schristos if (cipher == NULL) { 441e599299fSchristos #ifndef OPENSSL_NO_DES 442b0d17251Schristos cipher = (EVP_CIPHER *)EVP_des_ede3_cbc(); 443a89c9211Schristos #else 444a89c9211Schristos BIO_printf(bio_err, "No cipher selected\n"); 445a89c9211Schristos goto end; 446a89c9211Schristos #endif 447a89c9211Schristos } 448a89c9211Schristos encerts = sk_X509_new_null(); 44913d40330Schristos if (encerts == NULL) 450a89c9211Schristos goto end; 45113d40330Schristos while (*argv != NULL) { 452b0d17251Schristos cert = load_cert(*argv, FORMAT_UNDEF, 453c7da899bSchristos "recipient certificate file"); 454c7da899bSchristos if (cert == NULL) 455c7da899bSchristos goto end; 4560e2e28bcSchristos if (!sk_X509_push(encerts, cert)) 4570e2e28bcSchristos goto end; 458a89c9211Schristos cert = NULL; 459c7da899bSchristos argv++; 460a89c9211Schristos } 461a89c9211Schristos } 462a89c9211Schristos 46313d40330Schristos if (certfile != NULL) { 464b0d17251Schristos if (!load_certs(certfile, 0, &other, NULL, "certificates")) { 465a89c9211Schristos ERR_print_errors(bio_err); 466a89c9211Schristos goto end; 467a89c9211Schristos } 468a89c9211Schristos } 469a89c9211Schristos 47013d40330Schristos if (recipfile != NULL && (operation == SMIME_DECRYPT)) { 471b0d17251Schristos if ((recip = load_cert(recipfile, FORMAT_UNDEF, 472c7da899bSchristos "recipient certificate file")) == NULL) { 473a89c9211Schristos ERR_print_errors(bio_err); 474a89c9211Schristos goto end; 475a89c9211Schristos } 476a89c9211Schristos } 477a89c9211Schristos 478635165faSspz if (operation == SMIME_DECRYPT) { 47913d40330Schristos if (keyfile == NULL) 480a89c9211Schristos keyfile = recipfile; 481635165faSspz } else if (operation == SMIME_SIGN) { 48213d40330Schristos if (keyfile == NULL) 483a89c9211Schristos keyfile = signerfile; 48413d40330Schristos } else { 485635165faSspz keyfile = NULL; 48613d40330Schristos } 487a89c9211Schristos 48813d40330Schristos if (keyfile != NULL) { 489b0d17251Schristos key = load_key(keyfile, keyform, 0, passin, e, "signing key"); 49013d40330Schristos if (key == NULL) 491a89c9211Schristos goto end; 492a89c9211Schristos } 493a89c9211Schristos 494c7da899bSchristos in = bio_open_default(infile, 'r', informat); 495c7da899bSchristos if (in == NULL) 496a89c9211Schristos goto end; 497a89c9211Schristos 498635165faSspz if (operation & SMIME_IP) { 499b0d17251Schristos PKCS7 *p7_in = NULL; 500b0d17251Schristos 501b0d17251Schristos p7 = PKCS7_new_ex(libctx, app_get0_propq()); 502b0d17251Schristos if (p7 == NULL) { 503b0d17251Schristos BIO_printf(bio_err, "Error allocating PKCS7 object\n"); 504b0d17251Schristos goto end; 505b0d17251Schristos } 50613d40330Schristos if (informat == FORMAT_SMIME) { 507b0d17251Schristos p7_in = SMIME_read_PKCS7_ex(in, &indata, &p7); 50813d40330Schristos } else if (informat == FORMAT_PEM) { 509b0d17251Schristos p7_in = PEM_read_bio_PKCS7(in, &p7, NULL, NULL); 51013d40330Schristos } else if (informat == FORMAT_ASN1) { 511b0d17251Schristos p7_in = d2i_PKCS7_bio(in, &p7); 51213d40330Schristos } else { 513a89c9211Schristos BIO_printf(bio_err, "Bad input format for PKCS#7 file\n"); 514a89c9211Schristos goto end; 515a89c9211Schristos } 516a89c9211Schristos 517b0d17251Schristos if (p7_in == NULL) { 518a89c9211Schristos BIO_printf(bio_err, "Error reading S/MIME message\n"); 519a89c9211Schristos goto end; 520a89c9211Schristos } 52113d40330Schristos if (contfile != NULL) { 522a89c9211Schristos BIO_free(indata); 523c7da899bSchristos if ((indata = BIO_new_file(contfile, "rb")) == NULL) { 524a89c9211Schristos BIO_printf(bio_err, "Can't read content file %s\n", contfile); 525a89c9211Schristos goto end; 526a89c9211Schristos } 527a89c9211Schristos } 528a89c9211Schristos } 529a89c9211Schristos 530c7da899bSchristos out = bio_open_default(outfile, 'w', outformat); 531c7da899bSchristos if (out == NULL) 532a89c9211Schristos goto end; 533a89c9211Schristos 534635165faSspz if (operation == SMIME_VERIFY) { 535b0d17251Schristos if ((store = setup_verify(CAfile, noCAfile, CApath, noCApath, 536b0d17251Schristos CAstore, noCAstore)) == NULL) 537a89c9211Schristos goto end; 538cef2ee70Schristos X509_STORE_set_verify_cb(store, smime_cb); 539c7da899bSchristos if (vpmtouched) 540a89c9211Schristos X509_STORE_set1_param(store, vpm); 541a89c9211Schristos } 542a89c9211Schristos 543a89c9211Schristos ret = 3; 544a89c9211Schristos 545635165faSspz if (operation == SMIME_ENCRYPT) { 546a89c9211Schristos if (indef) 547a89c9211Schristos flags |= PKCS7_STREAM; 548b0d17251Schristos p7 = PKCS7_encrypt_ex(encerts, in, cipher, flags, libctx, app_get0_propq()); 549635165faSspz } else if (operation & SMIME_SIGNERS) { 550a89c9211Schristos int i; 551635165faSspz /* 552635165faSspz * If detached data content we only enable streaming if S/MIME output 553635165faSspz * format. 554a89c9211Schristos */ 555635165faSspz if (operation == SMIME_SIGN) { 556635165faSspz if (flags & PKCS7_DETACHED) { 557a89c9211Schristos if (outformat == FORMAT_SMIME) 558a89c9211Schristos flags |= PKCS7_STREAM; 55913d40330Schristos } else if (indef) { 560a89c9211Schristos flags |= PKCS7_STREAM; 56113d40330Schristos } 562a89c9211Schristos flags |= PKCS7_PARTIAL; 563b0d17251Schristos p7 = PKCS7_sign_ex(NULL, NULL, other, in, flags, libctx, app_get0_propq()); 56413d40330Schristos if (p7 == NULL) 565a89c9211Schristos goto end; 566218f7bfcSspz if (flags & PKCS7_NOCERTS) { 567218f7bfcSspz for (i = 0; i < sk_X509_num(other); i++) { 568218f7bfcSspz X509 *x = sk_X509_value(other, i); 569218f7bfcSspz PKCS7_add_certificate(p7, x); 570218f7bfcSspz } 571218f7bfcSspz } 57213d40330Schristos } else { 573a89c9211Schristos flags |= PKCS7_REUSE_DIGEST; 57413d40330Schristos } 575635165faSspz for (i = 0; i < sk_OPENSSL_STRING_num(sksigners); i++) { 57686adef1bSchristos signerfile = sk_OPENSSL_STRING_value(sksigners, i); 57786adef1bSchristos keyfile = sk_OPENSSL_STRING_value(skkeys, i); 578b0d17251Schristos signer = load_cert(signerfile, FORMAT_UNDEF, "signer certificate"); 57913d40330Schristos if (signer == NULL) 580a89c9211Schristos goto end; 581b0d17251Schristos key = load_key(keyfile, keyform, 0, passin, e, "signing key"); 58213d40330Schristos if (key == NULL) 583a89c9211Schristos goto end; 584b0d17251Schristos 585635165faSspz if (!PKCS7_sign_add_signer(p7, signer, key, sign_md, flags)) 586a89c9211Schristos goto end; 587a89c9211Schristos X509_free(signer); 588a89c9211Schristos signer = NULL; 589a89c9211Schristos EVP_PKEY_free(key); 590a89c9211Schristos key = NULL; 591a89c9211Schristos } 592a89c9211Schristos /* If not streaming or resigning finalize structure */ 593635165faSspz if ((operation == SMIME_SIGN) && !(flags & PKCS7_STREAM)) { 594a89c9211Schristos if (!PKCS7_final(p7, in, flags)) 595a89c9211Schristos goto end; 596a89c9211Schristos } 597a89c9211Schristos } 598a89c9211Schristos 59913d40330Schristos if (p7 == NULL) { 600a89c9211Schristos BIO_printf(bio_err, "Error creating PKCS#7 structure\n"); 601a89c9211Schristos goto end; 602a89c9211Schristos } 603a89c9211Schristos 604a89c9211Schristos ret = 4; 605635165faSspz if (operation == SMIME_DECRYPT) { 606635165faSspz if (!PKCS7_decrypt(p7, key, recip, out, flags)) { 607a89c9211Schristos BIO_printf(bio_err, "Error decrypting PKCS#7 structure\n"); 608a89c9211Schristos goto end; 609a89c9211Schristos } 610635165faSspz } else if (operation == SMIME_VERIFY) { 611a89c9211Schristos STACK_OF(X509) *signers; 612a89c9211Schristos if (PKCS7_verify(p7, other, store, indata, out, flags)) 613a89c9211Schristos BIO_printf(bio_err, "Verification successful\n"); 614635165faSspz else { 615a89c9211Schristos BIO_printf(bio_err, "Verification failure\n"); 616a89c9211Schristos goto end; 617a89c9211Schristos } 618a89c9211Schristos signers = PKCS7_get0_signers(p7, other, flags); 619635165faSspz if (!save_certs(signerfile, signers)) { 620635165faSspz BIO_printf(bio_err, "Error writing signers to %s\n", signerfile); 621a89c9211Schristos ret = 5; 622a89c9211Schristos goto end; 623a89c9211Schristos } 624a89c9211Schristos sk_X509_free(signers); 62513d40330Schristos } else if (operation == SMIME_PK7OUT) { 626a89c9211Schristos PEM_write_bio_PKCS7(out, p7); 62713d40330Schristos } else { 628a89c9211Schristos if (to) 629c7da899bSchristos BIO_printf(out, "To: %s%s", to, mime_eol); 630a89c9211Schristos if (from) 631c7da899bSchristos BIO_printf(out, "From: %s%s", from, mime_eol); 632a89c9211Schristos if (subject) 633c7da899bSchristos BIO_printf(out, "Subject: %s%s", subject, mime_eol); 634635165faSspz if (outformat == FORMAT_SMIME) { 635a89c9211Schristos if (operation == SMIME_RESIGN) 636c7da899bSchristos rv = SMIME_write_PKCS7(out, p7, indata, flags); 637a89c9211Schristos else 638c7da899bSchristos rv = SMIME_write_PKCS7(out, p7, in, flags); 63913d40330Schristos } else if (outformat == FORMAT_PEM) { 640c7da899bSchristos rv = PEM_write_bio_PKCS7_stream(out, p7, in, flags); 64113d40330Schristos } else if (outformat == FORMAT_ASN1) { 642c7da899bSchristos rv = i2d_PKCS7_bio_stream(out, p7, in, flags); 64313d40330Schristos } else { 644a89c9211Schristos BIO_printf(bio_err, "Bad output format for PKCS#7 file\n"); 645a89c9211Schristos goto end; 646a89c9211Schristos } 647c7da899bSchristos if (rv == 0) { 648c7da899bSchristos BIO_printf(bio_err, "Error writing output\n"); 649c7da899bSchristos ret = 3; 650c7da899bSchristos goto end; 651c7da899bSchristos } 652a89c9211Schristos } 653a89c9211Schristos ret = 0; 654a89c9211Schristos end: 655635165faSspz if (ret) 656635165faSspz ERR_print_errors(bio_err); 657a89c9211Schristos sk_X509_pop_free(encerts, X509_free); 658a89c9211Schristos sk_X509_pop_free(other, X509_free); 659a89c9211Schristos X509_VERIFY_PARAM_free(vpm); 66086adef1bSchristos sk_OPENSSL_STRING_free(sksigners); 66186adef1bSchristos sk_OPENSSL_STRING_free(skkeys); 662a89c9211Schristos X509_STORE_free(store); 663a89c9211Schristos X509_free(cert); 664a89c9211Schristos X509_free(recip); 665a89c9211Schristos X509_free(signer); 666a89c9211Schristos EVP_PKEY_free(key); 667b0d17251Schristos EVP_MD_free(sign_md); 668b0d17251Schristos EVP_CIPHER_free(cipher); 669a89c9211Schristos PKCS7_free(p7); 67034505c60Sspz release_engine(e); 671a89c9211Schristos BIO_free(in); 672a89c9211Schristos BIO_free(indata); 673a89c9211Schristos BIO_free_all(out); 674635165faSspz OPENSSL_free(passin); 675b0d17251Schristos NCONF_free(conf); 67613d40330Schristos return ret; 677a89c9211Schristos } 678a89c9211Schristos 679a89c9211Schristos static int save_certs(char *signerfile, STACK_OF(X509) *signers) 680a89c9211Schristos { 681a89c9211Schristos int i; 682a89c9211Schristos BIO *tmp; 68313d40330Schristos 68413d40330Schristos if (signerfile == NULL) 685a89c9211Schristos return 1; 686a89c9211Schristos tmp = BIO_new_file(signerfile, "w"); 68713d40330Schristos if (tmp == NULL) 688635165faSspz return 0; 689a89c9211Schristos for (i = 0; i < sk_X509_num(signers); i++) 690a89c9211Schristos PEM_write_bio_X509(tmp, sk_X509_value(signers, i)); 691a89c9211Schristos BIO_free(tmp); 692a89c9211Schristos return 1; 693a89c9211Schristos } 694a89c9211Schristos 695a89c9211Schristos /* Minimal callback just to output policy info (if any) */ 696a89c9211Schristos 697a89c9211Schristos static int smime_cb(int ok, X509_STORE_CTX *ctx) 698a89c9211Schristos { 699a89c9211Schristos int error; 700a89c9211Schristos 701a89c9211Schristos error = X509_STORE_CTX_get_error(ctx); 702a89c9211Schristos 703a89c9211Schristos if ((error != X509_V_ERR_NO_EXPLICIT_POLICY) 704a89c9211Schristos && ((error != X509_V_OK) || (ok != 2))) 705a89c9211Schristos return ok; 706a89c9211Schristos 707c7da899bSchristos policies_print(ctx); 708a89c9211Schristos 709a89c9211Schristos return ok; 710a89c9211Schristos } 711