diff --git a/common/compliance.c b/common/compliance.c index f62f2f77d..1cda1ec16 100644 --- a/common/compliance.c +++ b/common/compliance.c @@ -1,684 +1,685 @@ /* compliance.c - Functions for compliance modi * Copyright (C) 2017 g10 Code GmbH * Copyright (C) 2017 Bundesamt für Sicherheit in der Informationstechnik * * This file is part of GnuPG. * * This file is free software; you can redistribute it and/or modify * it under the terms of either * * - the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at * your option) any later version. * * or * * - the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at * your option) any later version. * * or both in parallel, as here. * * This file is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include "openpgpdefs.h" #include "logging.h" #include "util.h" #include "i18n.h" #include "compliance.h" static int initialized; static int module; /* Return the address of a compliance cache variable for COMPLIANCE. * If no such variable exists NULL is returned. FOR_RNG returns the * cache variable for the RNG compliance check. */ static int * get_compliance_cache (enum gnupg_compliance_mode compliance, int for_rng) { static int r_gnupg = -1, s_gnupg = -1; static int r_rfc4880 = -1, s_rfc4880 = -1; static int r_rfc2440 = -1, s_rfc2440 = -1; static int r_pgp6 = -1, s_pgp6 = -1; static int r_pgp7 = -1, s_pgp7 = -1; static int r_pgp8 = -1, s_pgp8 = -1; static int r_de_vs = -1, s_de_vs = -1; int *ptr = NULL; switch (compliance) { case CO_GNUPG: ptr = for_rng? &r_gnupg : &s_gnupg ; break; case CO_RFC4880: ptr = for_rng? &r_rfc4880 : &s_rfc4880; break; case CO_RFC2440: ptr = for_rng? &r_rfc2440 : &s_rfc2440; break; case CO_PGP6: ptr = for_rng? &r_pgp6 : &s_pgp6 ; break; case CO_PGP7: ptr = for_rng? &r_pgp7 : &s_pgp7 ; break; case CO_PGP8: ptr = for_rng? &r_pgp8 : &s_pgp8 ; break; case CO_DE_VS: ptr = for_rng? &r_de_vs : &s_de_vs ; break; } return ptr; } /* Initializes the module. Must be called with the current * GNUPG_MODULE_NAME. Checks a few invariants, and tunes the policies * for the given module. */ void gnupg_initialize_compliance (int gnupg_module_name) { log_assert (! initialized); /* We accept both OpenPGP-style and gcrypt-style algorithm ids. * Assert that they are compatible. */ log_assert ((int) GCRY_PK_RSA == (int) PUBKEY_ALGO_RSA); log_assert ((int) GCRY_PK_RSA_E == (int) PUBKEY_ALGO_RSA_E); log_assert ((int) GCRY_PK_RSA_S == (int) PUBKEY_ALGO_RSA_S); log_assert ((int) GCRY_PK_ELG_E == (int) PUBKEY_ALGO_ELGAMAL_E); log_assert ((int) GCRY_PK_DSA == (int) PUBKEY_ALGO_DSA); log_assert ((int) GCRY_PK_ECC == (int) PUBKEY_ALGO_ECDH); log_assert ((int) GCRY_PK_ELG == (int) PUBKEY_ALGO_ELGAMAL); log_assert ((int) GCRY_CIPHER_NONE == (int) CIPHER_ALGO_NONE); log_assert ((int) GCRY_CIPHER_IDEA == (int) CIPHER_ALGO_IDEA); log_assert ((int) GCRY_CIPHER_3DES == (int) CIPHER_ALGO_3DES); log_assert ((int) GCRY_CIPHER_CAST5 == (int) CIPHER_ALGO_CAST5); log_assert ((int) GCRY_CIPHER_BLOWFISH == (int) CIPHER_ALGO_BLOWFISH); log_assert ((int) GCRY_CIPHER_AES == (int) CIPHER_ALGO_AES); log_assert ((int) GCRY_CIPHER_AES192 == (int) CIPHER_ALGO_AES192); log_assert ((int) GCRY_CIPHER_AES256 == (int) CIPHER_ALGO_AES256); log_assert ((int) GCRY_CIPHER_TWOFISH == (int) CIPHER_ALGO_TWOFISH); log_assert ((int) GCRY_MD_MD5 == (int) DIGEST_ALGO_MD5); log_assert ((int) GCRY_MD_SHA1 == (int) DIGEST_ALGO_SHA1); log_assert ((int) GCRY_MD_RMD160 == (int) DIGEST_ALGO_RMD160); log_assert ((int) GCRY_MD_SHA256 == (int) DIGEST_ALGO_SHA256); log_assert ((int) GCRY_MD_SHA384 == (int) DIGEST_ALGO_SHA384); log_assert ((int) GCRY_MD_SHA512 == (int) DIGEST_ALGO_SHA512); log_assert ((int) GCRY_MD_SHA224 == (int) DIGEST_ALGO_SHA224); switch (gnupg_module_name) { case GNUPG_MODULE_NAME_GPGSM: case GNUPG_MODULE_NAME_GPG: break; default: log_assert (!"no policies for this module"); } module = gnupg_module_name; initialized = 1; } /* Return true if ALGO with a key of KEYLENGTH is compliant to the * given COMPLIANCE mode. If KEY is not NULL, various bits of * information will be extracted from it. If CURVENAME is not NULL, it * is assumed to be the already computed. ALGO may be either an * OpenPGP-style pubkey_algo_t, or a gcrypt-style enum gcry_pk_algos, * both are compatible from the point of view of this function. */ int gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo, unsigned int algo_flags, gcry_mpi_t key[], unsigned int keylength, const char *curvename) { enum { is_rsa, is_dsa, is_elg, is_ecc } algotype; int result = 0; if (! initialized) return 0; switch (algo) { case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_RSA_S: algotype = is_rsa; break; case PUBKEY_ALGO_DSA: algotype = is_dsa; break; case PUBKEY_ALGO_ELGAMAL_E: algotype = is_elg; break; case PUBKEY_ALGO_ECDH: case PUBKEY_ALGO_ECDSA: case PUBKEY_ALGO_EDDSA: algotype = is_ecc; break; case PUBKEY_ALGO_ELGAMAL: return 0; /* Signing with Elgamal is not at all supported. */ default: /* Unknown. */ return 0; } if (compliance == CO_DE_VS) { char *curve = NULL; switch (algotype) { case is_elg: result = 0; break; case is_rsa: result = (keylength == 2048 || keylength == 3072 || keylength == 4096); /* Although rsaPSS was not part of the original evaluation * we got word that we can claim compliance. */ (void)algo_flags; break; case is_dsa: if (key) { size_t P = gcry_mpi_get_nbits (key[0]); size_t Q = gcry_mpi_get_nbits (key[1]); result = (Q == 256 && (P == 2048 || P == 3072)); } break; case is_ecc: if (!curvename && key) { curve = openpgp_oid_to_str (key[0]); curvename = openpgp_oid_to_curve (curve, 0); if (!curvename) curvename = curve; } result = (curvename && (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA) && (!strcmp (curvename, "brainpoolP256r1") || !strcmp (curvename, "brainpoolP384r1") || !strcmp (curvename, "brainpoolP512r1"))); break; default: result = 0; } xfree (curve); } else { result = 1; /* Assume compliance. */ } return result; } /* Return true if ALGO with the given KEYLENGTH is allowed in the * given COMPLIANCE mode. USE specifies for which use case the * predicate is evaluated. This way policies can be strict in what * they produce, and liberal in what they accept. */ int gnupg_pk_is_allowed (enum gnupg_compliance_mode compliance, enum pk_use_case use, int algo, unsigned int algo_flags, gcry_mpi_t key[], unsigned int keylength, const char *curvename) { int result = 0; if (! initialized) return 1; switch (compliance) { case CO_DE_VS: switch (algo) { case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_RSA_S: switch (use) { case PK_USE_DECRYPTION: case PK_USE_VERIFICATION: result = 1; break; case PK_USE_ENCRYPTION: case PK_USE_SIGNING: result = (keylength == 2048 || keylength == 3072 || keylength == 4096); break; default: log_assert (!"reached"); } (void)algo_flags; break; case PUBKEY_ALGO_DSA: if (use == PK_USE_VERIFICATION) result = 1; else if (use == PK_USE_SIGNING && key) { size_t P = gcry_mpi_get_nbits (key[0]); size_t Q = gcry_mpi_get_nbits (key[1]); result = (Q == 256 && (P == 2048 || P == 3072)); } break; case PUBKEY_ALGO_ELGAMAL: case PUBKEY_ALGO_ELGAMAL_E: result = (use == PK_USE_DECRYPTION); break; case PUBKEY_ALGO_ECDH: if (use == PK_USE_DECRYPTION) result = 1; else if (use == PK_USE_ENCRYPTION) { char *curve = NULL; if (!curvename && key) { curve = openpgp_oid_to_str (key[0]); curvename = openpgp_oid_to_curve (curve, 0); if (!curvename) curvename = curve; } result = (curvename && (!strcmp (curvename, "brainpoolP256r1") || !strcmp (curvename, "brainpoolP384r1") || !strcmp (curvename, "brainpoolP512r1"))); xfree (curve); } break; case PUBKEY_ALGO_ECDSA: if (use == PK_USE_VERIFICATION) result = 1; else { char *curve = NULL; if (! curvename && key) { curve = openpgp_oid_to_str (key[0]); curvename = openpgp_oid_to_curve (curve, 0); if (!curvename) curvename = curve; } result = (use == PK_USE_SIGNING && curvename && (!strcmp (curvename, "brainpoolP256r1") || !strcmp (curvename, "brainpoolP384r1") || !strcmp (curvename, "brainpoolP512r1"))); xfree (curve); } break; case PUBKEY_ALGO_EDDSA: break; default: break; } break; default: /* The default policy is to allow all algorithms. */ result = 1; } return result; } /* Return true if (CIPHER, MODE) is compliant to the given COMPLIANCE mode. */ int gnupg_cipher_is_compliant (enum gnupg_compliance_mode compliance, cipher_algo_t cipher, enum gcry_cipher_modes mode) { if (! initialized) return 0; switch (compliance) { case CO_DE_VS: switch (cipher) { case CIPHER_ALGO_AES: case CIPHER_ALGO_AES192: case CIPHER_ALGO_AES256: case CIPHER_ALGO_3DES: switch (module) { case GNUPG_MODULE_NAME_GPG: return mode == GCRY_CIPHER_MODE_CFB; case GNUPG_MODULE_NAME_GPGSM: return mode == GCRY_CIPHER_MODE_CBC; } log_assert (!"reached"); default: return 0; } log_assert (!"reached"); default: return 0; } log_assert (!"reached"); } /* Return true if CIPHER is allowed in the given COMPLIANCE mode. If * PRODUCER is true, the predicate is evaluated for the producer, if * false for the consumer. This way policies can be strict in what * they produce, and liberal in what they accept. */ int gnupg_cipher_is_allowed (enum gnupg_compliance_mode compliance, int producer, cipher_algo_t cipher, enum gcry_cipher_modes mode) { if (! initialized) return 1; switch (compliance) { case CO_DE_VS: switch (cipher) { case CIPHER_ALGO_AES: case CIPHER_ALGO_AES192: case CIPHER_ALGO_AES256: case CIPHER_ALGO_3DES: switch (module) { case GNUPG_MODULE_NAME_GPG: return (mode == GCRY_CIPHER_MODE_NONE || mode == GCRY_CIPHER_MODE_CFB); case GNUPG_MODULE_NAME_GPGSM: return (mode == GCRY_CIPHER_MODE_NONE - || mode == GCRY_CIPHER_MODE_CBC); + || mode == GCRY_CIPHER_MODE_CBC + || (mode == GCRY_CIPHER_MODE_GCM && !producer)); } log_assert (!"reached"); case CIPHER_ALGO_BLOWFISH: case CIPHER_ALGO_CAMELLIA128: case CIPHER_ALGO_CAMELLIA192: case CIPHER_ALGO_CAMELLIA256: case CIPHER_ALGO_CAST5: case CIPHER_ALGO_IDEA: case CIPHER_ALGO_TWOFISH: return (module == GNUPG_MODULE_NAME_GPG && (mode == GCRY_CIPHER_MODE_NONE || mode == GCRY_CIPHER_MODE_CFB) && ! producer); default: return 0; } log_assert (!"reached"); default: /* The default policy is to allow all algorithms. */ return 1; } log_assert (!"reached"); } /* Return true if DIGEST is compliant to the given COMPLIANCE mode. */ int gnupg_digest_is_compliant (enum gnupg_compliance_mode compliance, digest_algo_t digest) { if (! initialized) return 0; switch (compliance) { case CO_DE_VS: switch (digest) { case DIGEST_ALGO_SHA256: case DIGEST_ALGO_SHA384: case DIGEST_ALGO_SHA512: return 1; default: return 0; } log_assert (!"reached"); default: return 0; } log_assert (!"reached"); } /* Return true if DIGEST is allowed in the given COMPLIANCE mode. If * PRODUCER is true, the predicate is evaluated for the producer, if * false for the consumer. This way policies can be strict in what * they produce, and liberal in what they accept. */ int gnupg_digest_is_allowed (enum gnupg_compliance_mode compliance, int producer, digest_algo_t digest) { if (! initialized) return 1; switch (compliance) { case CO_DE_VS: switch (digest) { case DIGEST_ALGO_SHA256: case DIGEST_ALGO_SHA384: case DIGEST_ALGO_SHA512: return 1; case DIGEST_ALGO_SHA1: case DIGEST_ALGO_SHA224: case DIGEST_ALGO_RMD160: return ! producer; case DIGEST_ALGO_MD5: return ! producer && module == GNUPG_MODULE_NAME_GPGSM; default: return 0; } log_assert (!"reached"); default: /* The default policy is to allow all algorithms. */ return 1; } log_assert (!"reached"); } /* Return True if the random number generator is compliant in * COMPLIANCE mode. */ int gnupg_rng_is_compliant (enum gnupg_compliance_mode compliance) { int *result; int res; result = get_compliance_cache (compliance, 1); if (result && *result != -1) res = *result; /* Use cached result. */ else if (compliance == CO_DE_VS) { /* We also check whether the library is at all compliant. */ res = gnupg_gcrypt_is_compliant (compliance); /* In DE_VS mode under Windows we also require that the JENT RNG * is active. Check it here. */ #ifdef HAVE_W32_SYSTEM if (res == 1) { char *buf; char *fields[5]; buf = gcry_get_config (0, "rng-type"); if (buf && split_fields_colon (buf, fields, DIM (fields)) >= 5 && atoi (fields[4]) > 0) ; /* Field 5 > 0 := Jent is active. */ else result = 0; /* Force non-compliance. */ gcry_free (buf); } #endif /*HAVE_W32_SYSTEM*/ } else res = 1; if (result) *result = res; return res; } /* Return true if the used Libgcrypt is compliant in COMPLIANCE * mode. */ int gnupg_gcrypt_is_compliant (enum gnupg_compliance_mode compliance) { int *result; int res; result = get_compliance_cache (compliance, 0); if (result && *result != -1) res = *result; /* Use cached result. */ else if (compliance == CO_DE_VS) { int is19orlater = !!gcry_check_version ("1.9.0"); /* A compliant version of GnuPG requires Libgcrypt >= 1.8.1 and * less than 1.9.0. Version 1.9.0 requires a re-evaluation and * can thus not be used for de-vs. */ if (gcry_check_version ("1.8.1") && !is19orlater) res = 1; /* Compliant version of Libgcrypt. */ else if (is19orlater) { /* Libgcrypt might be nice enough to tell us whether it is * compliant. */ char *buf; char *fields[3]; buf = gcry_get_config (0, "compliance"); if (buf && split_fields_colon (buf, fields, DIM (fields)) >= 2 && strstr (fields[1], "de-vs")) res = 1; /* Compliant. */ else res = 0; /* Non-compliant. */ gcry_free (buf); } else res = 0; /* Non-compliant version of Libgcrypt. */ } else res = 1; if (result) *result = res; return res; } const char * gnupg_status_compliance_flag (enum gnupg_compliance_mode compliance) { switch (compliance) { case CO_GNUPG: return "8"; case CO_RFC4880: case CO_RFC2440: case CO_PGP6: case CO_PGP7: case CO_PGP8: log_assert (!"no status code assigned for this compliance mode"); case CO_DE_VS: return "23"; } log_assert (!"invalid compliance mode"); } /* Parse the value of --compliance. Returns the value corresponding * to the given STRING according to OPTIONS of size LENGTH, or -1 * indicating that the lookup was unsuccessful, or the list of options * was printed. If quiet is false, an additional hint to use 'help' * is printed on unsuccessful lookups. */ int gnupg_parse_compliance_option (const char *string, struct gnupg_compliance_option options[], size_t length, int quiet) { size_t i; if (! ascii_strcasecmp (string, "help")) { log_info (_("valid values for option '%s':\n"), "--compliance"); for (i = 0; i < length; i++) log_info (" %s\n", options[i].keyword); return -1; } for (i = 0; i < length; i++) if (! ascii_strcasecmp (string, options[i].keyword)) return options[i].value; log_error (_("invalid value for option '%s'\n"), "--compliance"); if (! quiet) log_info (_("(use \"help\" to list choices)\n")); return -1; } /* Return the command line option for the given COMPLIANCE mode. */ const char * gnupg_compliance_option_string (enum gnupg_compliance_mode compliance) { switch (compliance) { case CO_GNUPG: return "--compliance=gnupg"; case CO_RFC4880: return "--compliance=openpgp"; case CO_RFC2440: return "--compliance=rfc2440"; case CO_PGP6: return "--compliance=pgp6"; case CO_PGP7: return "--compliance=pgp7"; case CO_PGP8: return "--compliance=pgp8"; case CO_DE_VS: return "--compliance=de-vs"; } log_assert (!"invalid compliance mode"); } diff --git a/sm/decrypt.c b/sm/decrypt.c index beb236ad8..38aa83210 100644 --- a/sm/decrypt.c +++ b/sm/decrypt.c @@ -1,1086 +1,1160 @@ /* decrypt.c - Decrypt a message * Copyright (C) 2001, 2003, 2010 Free Software Foundation, Inc. * Copyright (C) 2001-2019 Werner Koch * Copyright (C) 2015-2021 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * SPDX-License-Identifier: GPL-3.0-or-later */ #include #include #include #include #include #include #include #include #include "gpgsm.h" #include #include #include "keydb.h" #include "../common/i18n.h" #include "../common/compliance.h" #include "../common/tlv.h" +/* We can provide an enum value which is only availabale with KSBA + * 1.6.0 so that we can compile even against older versions. Some + * calls will of course return an error in this case. This value is + * currently not used because the cipher mode is sufficient here. */ +/* #if KSBA_VERSION_NUMBER < 0x010600 /\* 1.6.0 *\/ */ +/* # define KSBA_CT_AUTHENVELOPED_DATA 10 */ +/* #endif */ + + struct decrypt_filter_parm_s { int algo; int mode; int blklen; gcry_cipher_hd_t hd; char iv[16]; size_t ivlen; int any_data; /* did we push anything through the filter at all? */ unsigned char lastblock[16]; /* to strip the padding we have to keep this one */ char helpblock[16]; /* needed because there is no block buffering in libgcrypt (yet) */ int helpblocklen; int is_de_vs; /* Helper to track CO_DE_VS state. */ }; /* Return the hash algorithm's algo id from its name given in the * non-null termnated string in (buffer,buflen). Returns 0 on failure * or if the algo is not known. */ static char * string_from_gcry_buffer (gcry_buffer_t *buffer) { char *string; string = xtrymalloc (buffer->len + 1); if (!string) return NULL; memcpy (string, buffer->data, buffer->len); string[buffer->len] = 0; return string; } /* Helper for pwri_decrypt to parse the derive info. * Example data for (DER,DERLEN): * SEQUENCE { * OCTET STRING * 60 76 4B E9 5E DF 3C F8 B2 F9 B6 C2 7D 5A FB 90 * 23 B6 47 DF * INTEGER 10000 * SEQUENCE { * OBJECT IDENTIFIER * hmacWithSHA512 (1 2 840 113549 2 11) * NULL * } * } */ static gpg_error_t pwri_parse_pbkdf2 (const unsigned char *der, size_t derlen, unsigned char const **r_salt, unsigned int *r_saltlen, unsigned long *r_iterations, enum gcry_md_algos *r_digest) { gpg_error_t err; size_t objlen, hdrlen; int class, tag, constructed, ndef; char *oidstr; err = parse_ber_header (&der, &derlen, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > derlen || tag != TAG_SEQUENCE || !constructed || ndef)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) return err; derlen = objlen; err = parse_ber_header (&der, &derlen, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > derlen || tag != TAG_OCTET_STRING || constructed || ndef)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) return err; *r_salt = der; *r_saltlen = objlen; der += objlen; derlen -= objlen; err = parse_ber_header (&der, &derlen, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > derlen || tag != TAG_INTEGER || constructed || ndef)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) return err; *r_iterations = 0; for (; objlen; objlen--) { *r_iterations <<= 8; *r_iterations |= (*der++) & 0xff; derlen--; } err = parse_ber_header (&der, &derlen, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > derlen || tag != TAG_SEQUENCE || !constructed || ndef)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) return err; derlen = objlen; err = parse_ber_header (&der, &derlen, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > derlen || tag != TAG_OBJECT_ID || constructed || ndef)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) return err; oidstr = ksba_oid_to_str (der, objlen); if (!oidstr) return gpg_error_from_syserror (); *r_digest = gcry_md_map_name (oidstr); if (*r_digest) ; else if (!strcmp (oidstr, "1.2.840.113549.2.7")) *r_digest = GCRY_MD_SHA1; else if (!strcmp (oidstr, "1.2.840.113549.2.8")) *r_digest = GCRY_MD_SHA224; else if (!strcmp (oidstr, "1.2.840.113549.2.9")) *r_digest = GCRY_MD_SHA256; else if (!strcmp (oidstr, "1.2.840.113549.2.10")) *r_digest = GCRY_MD_SHA384; else if (!strcmp (oidstr, "1.2.840.113549.2.11")) *r_digest = GCRY_MD_SHA512; else err = gpg_error (GPG_ERR_DIGEST_ALGO); ksba_free (oidstr); return err; } /* Password based decryption. * ENC_VAL has the form: * (enc-val * (pwri * (derive-algo ) --| both are optional * (derive-parm ) --| * (encr-algo ) * (encr-parm ) * (encr-key ))) -- this is the encrypted session key * */ static gpg_error_t pwri_decrypt (ctrl_t ctrl, gcry_sexp_t enc_val, unsigned char **r_result, unsigned int *r_resultlen, struct decrypt_filter_parm_s *parm) { gpg_error_t err; gcry_buffer_t ioarray[5] = { {0} }; char *derive_algo_str = NULL; char *encr_algo_str = NULL; const unsigned char *dparm; /* Alias for ioarray[1]. */ unsigned int dparmlen; const unsigned char *eparm; /* Alias for ioarray[3]. */ unsigned int eparmlen; const unsigned char *ekey; /* Alias for ioarray[4]. */ unsigned int ekeylen; unsigned char kek[32]; unsigned int keklen; enum gcry_cipher_algos encr_algo; enum gcry_cipher_modes encr_mode; gcry_cipher_hd_t encr_hd = NULL; unsigned char *result = NULL; unsigned int resultlen; unsigned int blklen; const unsigned char *salt; /* Points int dparm. */ unsigned int saltlen; unsigned long iterations; enum gcry_md_algos digest_algo; char *passphrase = NULL; *r_resultlen = 0; *r_result = NULL; err = gcry_sexp_extract_param (enc_val, "enc-val!pwri", "&'derive-algo'?'derive-parm'?" "'encr-algo''encr-parm''encr-key'", ioarray+0, ioarray+1, ioarray+2, ioarray+3, ioarray+4, NULL); if (err) { /* If this is not pwri element, it is likly a kekri element * which we do not yet support. Change the error back to the * original as returned by ksba_cms_get_issuer. */ if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) err = gpg_error (GPG_ERR_UNSUPPORTED_CMS_OBJ); else log_error ("extracting PWRI parameter failed: %s\n", gpg_strerror (err)); goto leave; } if (ioarray[0].data) { derive_algo_str = string_from_gcry_buffer (ioarray+0); if (!derive_algo_str) { err = gpg_error_from_syserror (); goto leave; } } dparm = ioarray[1].data; dparmlen = ioarray[1].len; encr_algo_str = string_from_gcry_buffer (ioarray+2); if (!encr_algo_str) { err = gpg_error_from_syserror (); goto leave; } eparm = ioarray[3].data; eparmlen = ioarray[3].len; ekey = ioarray[4].data; ekeylen = ioarray[4].len; /* Check parameters. */ if (DBG_CRYPTO) { if (derive_algo_str) { log_debug ("derive algo: %s\n", derive_algo_str); log_printhex (dparm, dparmlen, "derive parm:"); } log_debug ("encr algo .: %s\n", encr_algo_str); log_printhex (eparm, eparmlen, "encr parm .:"); log_printhex (ekey, ekeylen, "encr key .:"); } if (!derive_algo_str) { err = gpg_error (GPG_ERR_NOT_SUPPORTED); log_info ("PWRI with no key derivation detected\n"); goto leave; } if (strcmp (derive_algo_str, "1.2.840.113549.1.5.12")) { err = gpg_error (GPG_ERR_NOT_SUPPORTED); log_info ("PWRI does not use PBKDF2 (but %s)\n", derive_algo_str); goto leave; } digest_algo = 0; /*(silence cc warning)*/ err = pwri_parse_pbkdf2 (dparm, dparmlen, &salt, &saltlen, &iterations, &digest_algo); if (err) { log_error ("parsing PWRI parameter failed: %s\n", gpg_strerror (err)); goto leave; } parm->is_de_vs = (parm->is_de_vs && gnupg_digest_is_compliant (CO_DE_VS, digest_algo)); encr_algo = gcry_cipher_map_name (encr_algo_str); encr_mode = gcry_cipher_mode_from_oid (encr_algo_str); if (!encr_algo || !encr_mode) { log_error ("PWRI uses unknown algorithm %s\n", encr_algo_str); err = gpg_error (GPG_ERR_CIPHER_ALGO); goto leave; } parm->is_de_vs = (parm->is_de_vs && gnupg_cipher_is_compliant (CO_DE_VS, encr_algo, encr_mode)); keklen = gcry_cipher_get_algo_keylen (encr_algo); blklen = gcry_cipher_get_algo_blklen (encr_algo); if (!keklen || keklen > sizeof kek || blklen != 16 ) { log_error ("PWRI algorithm %s cannot be used\n", encr_algo_str); err = gpg_error (GPG_ERR_INV_KEYLEN); goto leave; } if ((ekeylen % blklen) || (ekeylen / blklen < 2)) { /* Note that we need at least two full blocks. */ log_error ("PWRI uses a wrong length of encrypted key\n"); err = gpg_error (GPG_ERR_INV_KEYLEN); goto leave; } err = gpgsm_agent_ask_passphrase (ctrl, i18n_utf8 (N_("Please enter the passphrase for decryption.")), 0, &passphrase); if (err) goto leave; err = gcry_kdf_derive (passphrase, strlen (passphrase), GCRY_KDF_PBKDF2, digest_algo, salt, saltlen, iterations, keklen, kek); if (passphrase) { wipememory (passphrase, strlen (passphrase)); xfree (passphrase); passphrase = NULL; } if (err) { log_error ("deriving key from passphrase failed: %s\n", gpg_strerror (err)); goto leave; } if (DBG_CRYPTO) log_printhex (kek, keklen, "KEK .......:"); /* Unwrap the key. */ resultlen = ekeylen; result = xtrymalloc_secure (resultlen); if (!result) { err = gpg_error_from_syserror (); goto leave; } err = gcry_cipher_open (&encr_hd, encr_algo, encr_mode, 0); if (err) { log_error ("PWRI failed to open cipher: %s\n", gpg_strerror (err)); goto leave; } err = gcry_cipher_setkey (encr_hd, kek, keklen); wipememory (kek, sizeof kek); if (!err) err = gcry_cipher_setiv (encr_hd, ekey + ekeylen - 2 * blklen, blklen); if (!err) err = gcry_cipher_decrypt (encr_hd, result + ekeylen - blklen, blklen, ekey + ekeylen - blklen, blklen); if (!err) err = gcry_cipher_setiv (encr_hd, result + ekeylen - blklen, blklen); if (!err) err = gcry_cipher_decrypt (encr_hd, result, ekeylen - blklen, ekey, ekeylen - blklen); /* (We assume that that eparm is the octet string with the IV) */ if (!err) err = gcry_cipher_setiv (encr_hd, eparm, eparmlen); if (!err) err = gcry_cipher_decrypt (encr_hd, result, resultlen, NULL, 0); if (err) { log_error ("KEK decryption failed for PWRI: %s\n", gpg_strerror (err)); goto leave; } if (DBG_CRYPTO) log_printhex (result, resultlen, "Frame .....:"); if (result[0] < 8 /* At least 64 bits */ || (result[0] % 8) /* Multiple of 64 bits */ || result[0] > resultlen - 4 /* Not more than the size of the input */ || ( (result[1] ^ result[4]) /* Matching check bytes. */ & (result[2] ^ result[5]) & (result[3] ^ result[6]) ) != 0xff) { err = gpg_error (GPG_ERR_BAD_PASSPHRASE); goto leave; } *r_resultlen = result[0]; *r_result = memmove (result, result + 4, result[0]); result = NULL; leave: if (result) { wipememory (result, resultlen); xfree (result); } if (passphrase) { wipememory (passphrase, strlen (passphrase)); xfree (passphrase); } gcry_cipher_close (encr_hd); xfree (derive_algo_str); xfree (encr_algo_str); xfree (ioarray[0].data); xfree (ioarray[1].data); xfree (ioarray[2].data); xfree (ioarray[3].data); xfree (ioarray[4].data); return err; } /* Decrypt the session key and fill in the parm structure. The algo and the IV is expected to be already in PARM. */ static int prepare_decryption (ctrl_t ctrl, const char *hexkeygrip, const char *desc, ksba_const_sexp_t enc_val, struct decrypt_filter_parm_s *parm) { char *seskey = NULL; size_t n, seskeylen; int pwri = !hexkeygrip; int rc; if (DBG_CRYPTO) log_printcanon ("decrypting:", enc_val, 0); if (!pwri) { rc = gpgsm_agent_pkdecrypt (ctrl, hexkeygrip, desc, enc_val, &seskey, &seskeylen); if (rc) { log_error ("error decrypting session key: %s\n", gpg_strerror (rc)); goto leave; } } n=0; if (pwri) /* Password based encryption. */ { gcry_sexp_t s_enc_val; unsigned char *decrypted; unsigned int decryptedlen; rc = gcry_sexp_sscan (&s_enc_val, NULL, enc_val, gcry_sexp_canon_len (enc_val, 0, NULL, NULL)); if (rc) goto leave; rc = pwri_decrypt (ctrl, s_enc_val, &decrypted, &decryptedlen, parm); gcry_sexp_release (s_enc_val); if (rc) goto leave; xfree (seskey); seskey = decrypted; seskeylen = decryptedlen; } else if (seskeylen == 32 || seskeylen == 24 || seskeylen == 16) { /* Smells like an AES-128, 3-DES, or AES-256 key. This might * happen because a SC has already done the unpacking. A better * solution would be to test for this only after we triggered * the GPG_ERR_INV_SESSION_KEY. */ } else { if (n + 7 > seskeylen ) { rc = gpg_error (GPG_ERR_INV_SESSION_KEY); goto leave; } /* FIXME: Actually the leading zero is required but due to the way we encode the output in libgcrypt as an MPI we are not able to encode that leading zero. However, when using a Smartcard we are doing it the right way and therefore we have to skip the zero. This should be fixed in gpg-agent of course. */ if (!seskey[n]) n++; if (seskey[n] != 2 ) /* Wrong block type version. */ { rc = gpg_error (GPG_ERR_INV_SESSION_KEY); goto leave; } for (n++; n < seskeylen && seskey[n]; n++) /* Skip the random bytes. */ ; n++; /* and the zero byte */ if (n >= seskeylen ) { rc = gpg_error (GPG_ERR_INV_SESSION_KEY); goto leave; } } if (DBG_CRYPTO) log_printhex (seskey+n, seskeylen-n, "session key:"); if (opt.verbose) log_info (_("%s.%s encrypted data\n"), gcry_cipher_algo_name (parm->algo), cipher_mode_to_string (parm->mode)); rc = gcry_cipher_open (&parm->hd, parm->algo, parm->mode, 0); if (rc) { log_error ("error creating decryptor: %s\n", gpg_strerror (rc)); goto leave; } rc = gcry_cipher_setkey (parm->hd, seskey+n, seskeylen-n); if (gpg_err_code (rc) == GPG_ERR_WEAK_KEY) { log_info (_("WARNING: message was encrypted with " "a weak key in the symmetric cipher.\n")); rc = 0; } if (rc) { log_error("key setup failed: %s\n", gpg_strerror(rc) ); goto leave; } rc = gcry_cipher_setiv (parm->hd, parm->iv, parm->ivlen); if (rc) { log_error("IV setup failed: %s\n", gpg_strerror(rc) ); goto leave; } + if (parm->mode == GCRY_CIPHER_MODE_GCM) + { + /* GCM mode really sucks in CMS. We need to know the AAD before + * we start decrypting but CMS puts the AAD after the content. + * Thus temporary files are required. Let's hope that no real + * messages with actual AAD are ever used. OCB Rules! */ + } + leave: xfree (seskey); return rc; } /* This function is called by the KSBA writer just before the actual write is done. The function must take INLEN bytes from INBUF, decrypt it and store it inoutbuf which has a maximum size of maxoutlen. The valid bytes in outbuf should be return in outlen. Due to different buffer sizes or different length of input and output, it may happen that fewer bytes are processed or fewer bytes are written. */ static gpg_error_t decrypt_filter (void *arg, const void *inbuf, size_t inlen, size_t *inused, void *outbuf, size_t maxoutlen, size_t *outlen) { struct decrypt_filter_parm_s *parm = arg; int blklen = parm->blklen; size_t orig_inlen = inlen; /* fixme: Should we issue an error when we have not seen one full block? */ if (!inlen) return gpg_error (GPG_ERR_BUG); if (maxoutlen < 2*parm->blklen) return gpg_error (GPG_ERR_BUG); /* Make some space because we will later need an extra block at the end. */ maxoutlen -= blklen; if (parm->helpblocklen) { int i, j; for (i=parm->helpblocklen,j=0; i < blklen && j < inlen; i++, j++) parm->helpblock[i] = ((const char*)inbuf)[j]; inlen -= j; if (blklen > maxoutlen) return gpg_error (GPG_ERR_BUG); if (i < blklen) { parm->helpblocklen = i; *outlen = 0; } else { parm->helpblocklen = 0; if (parm->any_data) { memcpy (outbuf, parm->lastblock, blklen); *outlen =blklen; } else *outlen = 0; gcry_cipher_decrypt (parm->hd, parm->lastblock, blklen, parm->helpblock, blklen); parm->any_data = 1; } *inused = orig_inlen - inlen; return 0; } if (inlen > maxoutlen) inlen = maxoutlen; if (inlen % blklen) { /* store the remainder away */ parm->helpblocklen = inlen%blklen; inlen = inlen/blklen*blklen; memcpy (parm->helpblock, (const char*)inbuf+inlen, parm->helpblocklen); } *inused = inlen + parm->helpblocklen; if (inlen) { assert (inlen >= blklen); if (parm->any_data) { gcry_cipher_decrypt (parm->hd, (char*)outbuf+blklen, inlen, inbuf, inlen); memcpy (outbuf, parm->lastblock, blklen); memcpy (parm->lastblock,(char*)outbuf+inlen, blklen); *outlen = inlen; } else { gcry_cipher_decrypt (parm->hd, outbuf, inlen, inbuf, inlen); memcpy (parm->lastblock, (char*)outbuf+inlen-blklen, blklen); *outlen = inlen - blklen; parm->any_data = 1; } } else *outlen = 0; return 0; } +/* This is the GCM version of decrypt_filter. */ +static gpg_error_t +decrypt_gcm_filter (void *arg, + const void *inbuf, size_t inlen, size_t *inused, + void *outbuf, size_t maxoutlen, size_t *outlen) +{ + struct decrypt_filter_parm_s *parm = arg; + + if (!inlen) + return gpg_error (GPG_ERR_BUG); + + if (maxoutlen < parm->blklen) + return gpg_error (GPG_ERR_BUG); + + if (inlen > maxoutlen) + inlen = maxoutlen; + + *inused = inlen; + if (inlen) + { + gcry_cipher_decrypt (parm->hd, outbuf, inlen, inbuf, inlen); + *outlen = inlen; + parm->any_data = 1; + } + else + *outlen = 0; + return 0; +} + + /* Perform a decrypt operation. */ int gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) { int rc; gnupg_ksba_io_t b64reader = NULL; gnupg_ksba_io_t b64writer = NULL; ksba_reader_t reader; ksba_writer_t writer; ksba_cms_t cms = NULL; ksba_stop_reason_t stopreason; KEYDB_HANDLE kh; int recp; estream_t in_fp = NULL; struct decrypt_filter_parm_s dfparm; memset (&dfparm, 0, sizeof dfparm); audit_set_type (ctrl->audit, AUDIT_TYPE_DECRYPT); kh = keydb_new (); if (!kh) { log_error (_("failed to allocate keyDB handle\n")); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } in_fp = es_fdopen_nc (in_fd, "rb"); if (!in_fp) { rc = gpg_error_from_syserror (); log_error ("fdopen() failed: %s\n", strerror (errno)); goto leave; } rc = gnupg_ksba_create_reader (&b64reader, ((ctrl->is_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->is_base64? GNUPG_KSBA_IO_BASE64 : 0) | (ctrl->autodetect_encoding? GNUPG_KSBA_IO_AUTODETECT : 0)), in_fp, &reader); if (rc) { log_error ("can't create reader: %s\n", gpg_strerror (rc)); goto leave; } rc = gnupg_ksba_create_writer (&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)), ctrl->pem_name, out_fp, &writer); if (rc) { log_error ("can't create writer: %s\n", gpg_strerror (rc)); goto leave; } rc = ksba_cms_new (&cms); if (rc) goto leave; rc = ksba_cms_set_reader_writer (cms, reader, writer); if (rc) { log_debug ("ksba_cms_set_reader_writer failed: %s\n", gpg_strerror (rc)); goto leave; } audit_log (ctrl->audit, AUDIT_SETUP_READY); /* Parser loop. */ do { rc = ksba_cms_parse (cms, &stopreason); if (rc) { log_debug ("ksba_cms_parse failed: %s\n", gpg_strerror (rc)); goto leave; } if (stopreason == KSBA_SR_BEGIN_DATA || stopreason == KSBA_SR_DETACHED_DATA) { int algo, mode; const char *algoid; int any_key = 0; audit_log (ctrl->audit, AUDIT_GOT_DATA); algoid = ksba_cms_get_content_oid (cms, 2/* encryption algo*/); algo = gcry_cipher_map_name (algoid); mode = gcry_cipher_mode_from_oid (algoid); if (!algo || !mode) { rc = gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); log_error ("unsupported algorithm '%s'\n", algoid? algoid:"?"); if (algoid && !strcmp (algoid, "1.2.840.113549.3.2")) log_info (_("(this is the RC2 algorithm)\n")); else if (!algoid) log_info (_("(this does not seem to be an encrypted" " message)\n")); { char numbuf[50]; sprintf (numbuf, "%d", rc); gpgsm_status2 (ctrl, STATUS_ERROR, "decrypt.algorithm", numbuf, algoid?algoid:"?", NULL); audit_log_s (ctrl->audit, AUDIT_BAD_DATA_CIPHER_ALGO, algoid); } /* If it seems that this is not an encrypted message we return a more sensible error code. */ if (!algoid) rc = gpg_error (GPG_ERR_NO_DATA); goto leave; } /* Check compliance. */ if (! gnupg_cipher_is_allowed (opt.compliance, 0, algo, mode)) { log_error (_("cipher algorithm '%s'" " may not be used in %s mode\n"), gcry_cipher_algo_name (algo), gnupg_compliance_option_string (opt.compliance)); rc = gpg_error (GPG_ERR_CIPHER_ALGO); goto leave; } /* For CMS, CO_DE_VS demands CBC mode. */ dfparm.is_de_vs = gnupg_cipher_is_compliant (CO_DE_VS, algo, mode); audit_log_i (ctrl->audit, AUDIT_DATA_CIPHER_ALGO, algo); dfparm.algo = algo; dfparm.mode = mode; dfparm.blklen = gcry_cipher_get_algo_blklen (algo); if (dfparm.blklen > sizeof (dfparm.helpblock)) return gpg_error (GPG_ERR_BUG); rc = ksba_cms_get_content_enc_iv (cms, dfparm.iv, sizeof (dfparm.iv), &dfparm.ivlen); if (rc) { log_error ("error getting IV: %s\n", gpg_strerror (rc)); goto leave; } for (recp=0; !any_key; recp++) { char *issuer; ksba_sexp_t serial; ksba_sexp_t enc_val; char *hexkeygrip = NULL; char *pkalgostr = NULL; char *pkfpr = NULL; char *desc = NULL; char kidbuf[16+1]; int tmp_rc; ksba_cert_t cert = NULL; unsigned int nbits; int pk_algo = 0; int maybe_pwri = 0; *kidbuf = 0; tmp_rc = ksba_cms_get_issuer_serial (cms, recp, &issuer, &serial); if (tmp_rc == -1 && recp) break; /* no more recipients */ audit_log_i (ctrl->audit, AUDIT_NEW_RECP, recp); if (gpg_err_code (tmp_rc) == GPG_ERR_UNSUPPORTED_CMS_OBJ) { maybe_pwri = 1; } else if (tmp_rc) { log_error ("recp %d - error getting info: %s\n", recp, gpg_strerror (tmp_rc)); } else { if (opt.verbose) { log_debug ("recp %d - issuer: '%s'\n", recp, issuer? issuer:"[NONE]"); log_debug ("recp %d - serial: ", recp); gpgsm_dump_serial (serial); log_printf ("\n"); } if (ctrl->audit) { char *tmpstr = gpgsm_format_sn_issuer (serial, issuer); audit_log_s (ctrl->audit, AUDIT_RECP_NAME, tmpstr); xfree (tmpstr); } keydb_search_reset (kh); rc = keydb_search_issuer_sn (ctrl, kh, issuer, serial); if (rc) { log_error ("failed to find the certificate: %s\n", gpg_strerror(rc)); goto oops; } rc = keydb_get_cert (kh, &cert); if (rc) { log_error ("failed to get cert: %s\n", gpg_strerror (rc)); goto oops; } /* Print the ENC_TO status line. Note that we can do so only if we have the certificate. This is in contrast to gpg where the keyID is commonly included in the encrypted messages. It is too cumbersome to retrieve the used algorithm, thus we don't print it for now. We also record the keyid for later use. */ { unsigned long kid[2]; kid[0] = gpgsm_get_short_fingerprint (cert, kid+1); snprintf (kidbuf, sizeof kidbuf, "%08lX%08lX", kid[1], kid[0]); gpgsm_status2 (ctrl, STATUS_ENC_TO, kidbuf, "0", "0", NULL); } /* Put the certificate into the audit log. */ audit_log_cert (ctrl->audit, AUDIT_SAVE_CERT, cert, 0); /* Just in case there is a problem with the own certificate we print this message - should never happen of course */ rc = gpgsm_cert_use_decrypt_p (cert); if (rc) { char numbuf[50]; sprintf (numbuf, "%d", rc); gpgsm_status2 (ctrl, STATUS_ERROR, "decrypt.keyusage", numbuf, NULL); rc = 0; } hexkeygrip = gpgsm_get_keygrip_hexstring (cert); desc = gpgsm_format_keydesc (cert); pkfpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); pkalgostr = gpgsm_pubkey_algo_string (cert, NULL); pk_algo = gpgsm_get_key_algo_info (cert, &nbits); if (!opt.quiet) log_info (_("encrypted to %s key %s\n"), pkalgostr, pkfpr); /* Check compliance. */ if (!gnupg_pk_is_allowed (opt.compliance, PK_USE_DECRYPTION, pk_algo, 0, NULL, nbits, NULL)) { char kidstr[10+1]; snprintf (kidstr, sizeof kidstr, "0x%08lX", gpgsm_get_short_fingerprint (cert, NULL)); log_info (_("key %s is not suitable for decryption" " in %s mode\n"), kidstr, gnupg_compliance_option_string(opt.compliance)); rc = gpg_error (GPG_ERR_PUBKEY_ALGO); goto oops; } /* Check that all certs are compliant with CO_DE_VS. */ dfparm.is_de_vs = (dfparm.is_de_vs && gnupg_pk_is_compliant (CO_DE_VS, pk_algo, 0, NULL, nbits, NULL)); oops: if (rc) { /* We cannot check compliance of certs that we * don't have. */ dfparm.is_de_vs = 0; } xfree (issuer); xfree (serial); ksba_cert_release (cert); } if ((!hexkeygrip || !pk_algo) && !maybe_pwri) ; else if (!(enc_val = ksba_cms_get_enc_val (cms, recp))) { log_error ("recp %d - error getting encrypted session key\n", recp); if (maybe_pwri) log_info ("(possibly unsupported KEK info)\n"); } else { if (maybe_pwri && opt.verbose) log_info ("recp %d - KEKRI or PWRI\n", recp); rc = prepare_decryption (ctrl, hexkeygrip, desc, enc_val, &dfparm); xfree (enc_val); if (rc) { log_info ("decrypting session key failed: %s\n", gpg_strerror (rc)); if (gpg_err_code (rc) == GPG_ERR_NO_SECKEY && *kidbuf) gpgsm_status2 (ctrl, STATUS_NO_SECKEY, kidbuf, NULL); } else { /* setup the bulk decrypter */ any_key = 1; - ksba_writer_set_filter (writer, - decrypt_filter, - &dfparm); + ksba_writer_set_filter + (writer, + dfparm.mode == GCRY_CIPHER_MODE_GCM? + decrypt_gcm_filter : decrypt_filter, + &dfparm); if (dfparm.is_de_vs && gnupg_gcrypt_is_compliant (CO_DE_VS)) gpgsm_status (ctrl, STATUS_DECRYPTION_COMPLIANCE_MODE, gnupg_status_compliance_flag (CO_DE_VS)); } audit_log_ok (ctrl->audit, AUDIT_RECP_RESULT, rc); } xfree (pkalgostr); xfree (pkfpr); xfree (hexkeygrip); xfree (desc); } /* If we write an audit log add the unused recipients to the log as well. */ if (ctrl->audit && any_key) { for (;; recp++) { char *issuer; ksba_sexp_t serial; int tmp_rc; tmp_rc = ksba_cms_get_issuer_serial (cms, recp, &issuer, &serial); if (tmp_rc == -1) break; /* no more recipients */ audit_log_i (ctrl->audit, AUDIT_NEW_RECP, recp); if (tmp_rc) log_error ("recp %d - error getting info: %s\n", recp, gpg_strerror (rc)); else { char *tmpstr = gpgsm_format_sn_issuer (serial, issuer); audit_log_s (ctrl->audit, AUDIT_RECP_NAME, tmpstr); xfree (tmpstr); xfree (issuer); xfree (serial); } } } if (!any_key) { rc = gpg_error (GPG_ERR_NO_SECKEY); goto leave; } } else if (stopreason == KSBA_SR_END_DATA) { ksba_writer_set_filter (writer, NULL, NULL); - if (dfparm.any_data) + if (dfparm.mode == GCRY_CIPHER_MODE_GCM) + { + /* Nothing yet to do. We wait for the ready event. */ + } + else if (dfparm.any_data ) { /* write the last block with padding removed */ int i, npadding = dfparm.lastblock[dfparm.blklen-1]; if (!npadding || npadding > dfparm.blklen) { log_error ("invalid padding with value %d\n", npadding); rc = gpg_error (GPG_ERR_INV_DATA); goto leave; } rc = ksba_writer_write (writer, dfparm.lastblock, dfparm.blklen - npadding); if (rc) goto leave; for (i=dfparm.blklen - npadding; i < dfparm.blklen; i++) { if (dfparm.lastblock[i] != npadding) { log_error ("inconsistent padding\n"); rc = gpg_error (GPG_ERR_INV_DATA); goto leave; } } } } + else if (stopreason == KSBA_SR_READY) + { + if (dfparm.mode == GCRY_CIPHER_MODE_GCM) + { + char *authtag; + size_t authtaglen; + rc = ksba_cms_get_message_digest (cms, 0, &authtag, &authtaglen); + if (rc) + { + log_error ("error getting authtag: %s\n", gpg_strerror (rc)); + goto leave; + } + if (DBG_CRYPTO) + log_printhex (authtag, authtaglen, "Authtag ...:"); + rc = gcry_cipher_checktag (dfparm.hd, authtag, authtaglen); + xfree (authtag); + if (rc) + log_error ("data is not authentic: %s\n", gpg_strerror (rc)); + goto leave; + } + } } while (stopreason != KSBA_SR_READY); rc = gnupg_ksba_finish_writer (b64writer); if (rc) { log_error ("write failed: %s\n", gpg_strerror (rc)); goto leave; } gpgsm_status (ctrl, STATUS_DECRYPTION_OKAY, NULL); leave: audit_log_ok (ctrl->audit, AUDIT_DECRYPTION_RESULT, rc); if (rc) { gpgsm_status (ctrl, STATUS_DECRYPTION_FAILED, NULL); log_error ("message decryption failed: %s <%s>\n", gpg_strerror (rc), gpg_strsource (rc)); } ksba_cms_release (cms); gnupg_ksba_destroy_reader (b64reader); gnupg_ksba_destroy_writer (b64writer); keydb_release (kh); es_fclose (in_fp); if (dfparm.hd) gcry_cipher_close (dfparm.hd); return rc; } diff --git a/sm/gpgsm.c b/sm/gpgsm.c index d91379da2..3e4f67f4d 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -1,2219 +1,2225 @@ /* gpgsm.c - GnuPG for S/MIME * Copyright (C) 2001-2020 Free Software Foundation, Inc. * Copyright (C) 2001-2019 Werner Koch * Copyright (C) 2015-2020 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * SPDX-License-Identifier: GPL-3.0-or-later */ #include #include #include #include #include #include #include #include #define INCLUDED_BY_MAIN_MODULE 1 #include "gpgsm.h" #include #include /* malloc hooks */ #include "passphrase.h" #include "../common/shareddefs.h" #include "../kbx/keybox.h" /* malloc hooks */ #include "../common/i18n.h" #include "keydb.h" #include "../common/sysutils.h" #include "../common/gc-opt-flags.h" #include "../common/asshelp.h" #include "../common/init.h" #include "../common/compliance.h" #include "minip12.h" #ifndef O_BINARY #define O_BINARY 0 #endif enum cmd_and_opt_values { aNull = 0, oArmor = 'a', aDetachedSign = 'b', aSym = 'c', aDecrypt = 'd', aEncr = 'e', aListKeys = 'k', aListSecretKeys = 'K', oDryRun = 'n', oOutput = 'o', oQuiet = 'q', oRecipient = 'r', aSign = 's', oUser = 'u', oVerbose = 'v', oBatch = 500, aClearsign, aKeygen, aSignEncr, aDeleteKey, aImport, aVerify, aListExternalKeys, aListChain, aSendKeys, aRecvKeys, aExport, aExportSecretKeyP12, aExportSecretKeyP8, aExportSecretKeyRaw, aServer, aLearnCard, aCallDirmngr, aCallProtectTool, aPasswd, aGPGConfList, aGPGConfTest, aDumpKeys, aDumpChain, aDumpSecretKeys, aDumpExternalKeys, aKeydbClearSomeCertFlags, aFingerprint, oOptions, oDebug, oDebugLevel, oDebugAll, oDebugNone, oDebugWait, oDebugAllowCoreDump, oDebugNoChainValidation, oDebugIgnoreExpiration, oLogFile, oNoLogFile, oAuditLog, oHtmlAuditLog, oEnableSpecialFilenames, oAgentProgram, oDisplay, oTTYname, oTTYtype, oLCctype, oLCmessages, oXauthority, oPreferSystemDirmngr, oDirmngrProgram, oDisableDirmngr, oProtectToolProgram, oFakedSystemTime, oPassphraseFD, oPinentryMode, oRequestOrigin, oAssumeArmor, oAssumeBase64, oAssumeBinary, oBase64, oNoArmor, oP12Charset, oCompliance, oDisableCRLChecks, oEnableCRLChecks, oDisableTrustedCertCRLCheck, oEnableTrustedCertCRLCheck, oForceCRLRefresh, oEnableIssuerBasedCRLCheck, oDisableOCSP, oEnableOCSP, oIncludeCerts, oPolicyFile, oDisablePolicyChecks, oEnablePolicyChecks, oAutoIssuerKeyRetrieve, oWithFingerprint, oWithMD5Fingerprint, oWithKeygrip, oWithSecret, oAnswerYes, oAnswerNo, oKeyring, oDefaultKey, oDefRecipient, oDefRecipientSelf, oNoDefRecipient, oStatusFD, oCipherAlgo, oDigestAlgo, oExtraDigestAlgo, oNoVerbose, oNoSecmemWarn, oNoDefKeyring, oNoGreeting, oNoTTY, oNoOptions, oNoBatch, oHomedir, oWithColons, oWithKeyData, oWithValidation, oWithEphemeralKeys, oSkipVerify, oValidationModel, oKeyServer, oEncryptTo, oNoEncryptTo, oLoggerFD, oDisableCipherAlgo, oDisablePubkeyAlgo, oIgnoreTimeConflict, oNoRandomSeedFile, oNoCommonCertsImport, oIgnoreCertExtension, oNoAutostart }; static ARGPARSE_OPTS opts[] = { ARGPARSE_group (300, N_("@Commands:\n ")), ARGPARSE_c (aSign, "sign", N_("make a signature")), /*ARGPARSE_c (aClearsign, "clearsign", N_("make a clear text signature") ),*/ ARGPARSE_c (aDetachedSign, "detach-sign", N_("make a detached signature")), ARGPARSE_c (aEncr, "encrypt", N_("encrypt data")), /*ARGPARSE_c (aSym, "symmetric", N_("encryption only with symmetric cipher")),*/ ARGPARSE_c (aDecrypt, "decrypt", N_("decrypt data (default)")), ARGPARSE_c (aVerify, "verify", N_("verify a signature")), ARGPARSE_c (aListKeys, "list-keys", N_("list keys")), ARGPARSE_c (aListExternalKeys, "list-external-keys", N_("list external keys")), ARGPARSE_c (aListSecretKeys, "list-secret-keys", N_("list secret keys")), ARGPARSE_c (aListChain, "list-chain", N_("list certificate chain")), ARGPARSE_c (aFingerprint, "fingerprint", N_("list keys and fingerprints")), ARGPARSE_c (aKeygen, "generate-key", N_("generate a new key pair")), ARGPARSE_c (aKeygen, "gen-key", "@"), ARGPARSE_c (aDeleteKey, "delete-keys", N_("remove keys from the public keyring")), /*ARGPARSE_c (aSendKeys, "send-keys", N_("export keys to a keyserver")),*/ /*ARGPARSE_c (aRecvKeys, "recv-keys", N_("import keys from a keyserver")),*/ ARGPARSE_c (aImport, "import", N_("import certificates")), ARGPARSE_c (aExport, "export", N_("export certificates")), /* We use -raw and not -p1 for pkcs#1 secret key export so that it won't accidentally be used in case -p12 was intended. */ ARGPARSE_c (aExportSecretKeyP12, "export-secret-key-p12", "@"), ARGPARSE_c (aExportSecretKeyP8, "export-secret-key-p8", "@"), ARGPARSE_c (aExportSecretKeyRaw, "export-secret-key-raw", "@"), ARGPARSE_c (aLearnCard, "learn-card", N_("register a smartcard")), ARGPARSE_c (aServer, "server", N_("run in server mode")), ARGPARSE_c (aCallDirmngr, "call-dirmngr", N_("pass a command to the dirmngr")), ARGPARSE_c (aCallProtectTool, "call-protect-tool", N_("invoke gpg-protect-tool")), ARGPARSE_c (aPasswd, "change-passphrase", N_("change a passphrase")), ARGPARSE_c (aPasswd, "passwd", "@"), ARGPARSE_c (aGPGConfList, "gpgconf-list", "@"), ARGPARSE_c (aGPGConfTest, "gpgconf-test", "@"), ARGPARSE_c (aDumpKeys, "dump-cert", "@"), ARGPARSE_c (aDumpKeys, "dump-keys", "@"), ARGPARSE_c (aDumpChain, "dump-chain", "@"), ARGPARSE_c (aDumpExternalKeys, "dump-external-keys", "@"), ARGPARSE_c (aDumpSecretKeys, "dump-secret-keys", "@"), ARGPARSE_c (aKeydbClearSomeCertFlags, "keydb-clear-some-cert-flags", "@"), ARGPARSE_group (301, N_("@\nOptions:\n ")), ARGPARSE_s_n (oArmor, "armor", N_("create ascii armored output")), ARGPARSE_s_n (oArmor, "armour", "@"), ARGPARSE_s_n (oBase64, "base64", N_("create base-64 encoded output")), ARGPARSE_s_s (oP12Charset, "p12-charset", "@"), ARGPARSE_s_i (oPassphraseFD, "passphrase-fd", "@"), ARGPARSE_s_s (oPinentryMode, "pinentry-mode", "@"), ARGPARSE_s_s (oRequestOrigin, "request-origin", "@"), ARGPARSE_s_n (oAssumeArmor, "assume-armor", N_("assume input is in PEM format")), ARGPARSE_s_n (oAssumeBase64, "assume-base64", N_("assume input is in base-64 format")), ARGPARSE_s_n (oAssumeBinary, "assume-binary", N_("assume input is in binary format")), ARGPARSE_s_s (oRecipient, "recipient", N_("|USER-ID|encrypt for USER-ID")), ARGPARSE_s_n (oPreferSystemDirmngr,"prefer-system-dirmngr", "@"), ARGPARSE_s_n (oDisableCRLChecks, "disable-crl-checks", N_("never consult a CRL")), ARGPARSE_s_n (oEnableCRLChecks, "enable-crl-checks", "@"), ARGPARSE_s_n (oDisableTrustedCertCRLCheck, "disable-trusted-cert-crl-check", "@"), ARGPARSE_s_n (oEnableTrustedCertCRLCheck, "enable-trusted-cert-crl-check", "@"), ARGPARSE_s_n (oForceCRLRefresh, "force-crl-refresh", "@"), ARGPARSE_s_n (oDisableOCSP, "disable-ocsp", "@"), ARGPARSE_s_n (oEnableOCSP, "enable-ocsp", N_("check validity using OCSP")), ARGPARSE_s_s (oValidationModel, "validation-model", "@"), ARGPARSE_s_i (oIncludeCerts, "include-certs", N_("|N|number of certificates to include") ), ARGPARSE_s_s (oPolicyFile, "policy-file", N_("|FILE|take policy information from FILE")), ARGPARSE_s_n (oDisablePolicyChecks, "disable-policy-checks", N_("do not check certificate policies")), ARGPARSE_s_n (oEnablePolicyChecks, "enable-policy-checks", "@"), ARGPARSE_s_n (oAutoIssuerKeyRetrieve, "auto-issuer-key-retrieve", N_("fetch missing issuer certificates")), ARGPARSE_s_s (oEncryptTo, "encrypt-to", "@"), ARGPARSE_s_n (oNoEncryptTo, "no-encrypt-to", "@"), ARGPARSE_s_s (oUser, "local-user", N_("|USER-ID|use USER-ID to sign or decrypt")), ARGPARSE_s_s (oOutput, "output", N_("|FILE|write output to FILE")), ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")), ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")), ARGPARSE_s_n (oNoTTY, "no-tty", N_("don't use the terminal at all")), ARGPARSE_s_s (oLogFile, "log-file", N_("|FILE|write a server mode log to FILE")), ARGPARSE_s_n (oNoLogFile, "no-log-file", "@"), ARGPARSE_s_i (oLoggerFD, "logger-fd", "@"), ARGPARSE_s_s (oAuditLog, "audit-log", N_("|FILE|write an audit log to FILE")), ARGPARSE_s_s (oHtmlAuditLog, "html-audit-log", "@"), ARGPARSE_s_n (oDryRun, "dry-run", N_("do not make any changes")), ARGPARSE_s_n (oBatch, "batch", N_("batch mode: never ask")), ARGPARSE_s_n (oAnswerYes, "yes", N_("assume yes on most questions")), ARGPARSE_s_n (oAnswerNo, "no", N_("assume no on most questions")), ARGPARSE_s_s (oKeyring, "keyring", N_("|FILE|add keyring to the list of keyrings")), ARGPARSE_s_s (oDefaultKey, "default-key", N_("|USER-ID|use USER-ID as default secret key")), /* Not yet used: */ /* ARGPARSE_s_s (oDefRecipient, "default-recipient", */ /* N_("|NAME|use NAME as default recipient")), */ /* ARGPARSE_s_n (oDefRecipientSelf, "default-recipient-self", */ /* N_("use the default key as default recipient")), */ /* ARGPARSE_s_n (oNoDefRecipient, "no-default-recipient", "@"), */ ARGPARSE_s_s (oKeyServer, "keyserver", N_("|SPEC|use this keyserver to lookup keys")), ARGPARSE_conffile (oOptions, "options", N_("|FILE|read options from FILE")), ARGPARSE_s_s (oDebug, "debug", "@"), ARGPARSE_s_s (oDebugLevel, "debug-level", N_("|LEVEL|set the debugging level to LEVEL")), ARGPARSE_s_n (oDebugAll, "debug-all", "@"), ARGPARSE_s_n (oDebugNone, "debug-none", "@"), ARGPARSE_s_i (oDebugWait, "debug-wait", "@"), ARGPARSE_s_n (oDebugAllowCoreDump, "debug-allow-core-dump", "@"), ARGPARSE_s_n (oDebugNoChainValidation, "debug-no-chain-validation", "@"), ARGPARSE_s_n (oDebugIgnoreExpiration, "debug-ignore-expiration", "@"), ARGPARSE_s_i (oStatusFD, "status-fd", N_("|FD|write status info to this FD")), ARGPARSE_s_s (oCipherAlgo, "cipher-algo", N_("|NAME|use cipher algorithm NAME")), ARGPARSE_s_s (oDigestAlgo, "digest-algo", N_("|NAME|use message digest algorithm NAME")), ARGPARSE_s_s (oExtraDigestAlgo, "extra-digest-algo", "@"), ARGPARSE_group (302, N_( "@\n(See the man page for a complete listing of all commands and options)\n" )), /* Hidden options. */ ARGPARSE_s_s (oCompliance, "compliance", "@"), ARGPARSE_s_n (oNoVerbose, "no-verbose", "@"), ARGPARSE_s_n (oEnableSpecialFilenames, "enable-special-filenames", "@"), ARGPARSE_s_n (oNoSecmemWarn, "no-secmem-warning", "@"), ARGPARSE_s_n (oNoArmor, "no-armor", "@"), ARGPARSE_s_n (oNoArmor, "no-armour", "@"), ARGPARSE_s_n (oNoDefKeyring, "no-default-keyring", "@"), ARGPARSE_s_n (oNoGreeting, "no-greeting", "@"), ARGPARSE_noconffile (oNoOptions, "no-options", "@"), ARGPARSE_s_s (oHomedir, "homedir", "@"), ARGPARSE_s_s (oAgentProgram, "agent-program", "@"), ARGPARSE_s_s (oDisplay, "display", "@"), ARGPARSE_s_s (oTTYname, "ttyname", "@"), ARGPARSE_s_s (oTTYtype, "ttytype", "@"), ARGPARSE_s_s (oLCctype, "lc-ctype", "@"), ARGPARSE_s_s (oLCmessages, "lc-messages", "@"), ARGPARSE_s_s (oXauthority, "xauthority", "@"), ARGPARSE_s_s (oDirmngrProgram, "dirmngr-program", "@"), ARGPARSE_s_n (oDisableDirmngr, "disable-dirmngr", "@"), ARGPARSE_s_s (oProtectToolProgram, "protect-tool-program", "@"), ARGPARSE_s_s (oFakedSystemTime, "faked-system-time", "@"), ARGPARSE_s_n (oNoBatch, "no-batch", "@"), ARGPARSE_s_n (oWithColons, "with-colons", "@"), ARGPARSE_s_n (oWithKeyData,"with-key-data", "@"), ARGPARSE_s_n (oWithValidation, "with-validation", "@"), ARGPARSE_s_n (oWithMD5Fingerprint, "with-md5-fingerprint", "@"), ARGPARSE_s_n (oWithEphemeralKeys, "with-ephemeral-keys", "@"), ARGPARSE_s_n (oSkipVerify, "skip-verify", "@"), ARGPARSE_s_n (oWithFingerprint, "with-fingerprint", "@"), ARGPARSE_s_n (oWithKeygrip, "with-keygrip", "@"), ARGPARSE_s_n (oWithSecret, "with-secret", "@"), ARGPARSE_s_s (oDisableCipherAlgo, "disable-cipher-algo", "@"), ARGPARSE_s_s (oDisablePubkeyAlgo, "disable-pubkey-algo", "@"), ARGPARSE_s_n (oIgnoreTimeConflict, "ignore-time-conflict", "@"), ARGPARSE_s_n (oNoRandomSeedFile, "no-random-seed-file", "@"), ARGPARSE_s_n (oNoCommonCertsImport, "no-common-certs-import", "@"), ARGPARSE_s_s (oIgnoreCertExtension, "ignore-cert-extension", "@"), ARGPARSE_s_n (oNoAutostart, "no-autostart", "@"), ARGPARSE_s_n (oEnableIssuerBasedCRLCheck, "enable-issuer-based-crl-check", "@"), /* Command aliases. */ ARGPARSE_c (aListKeys, "list-key", "@"), ARGPARSE_c (aListChain, "list-signatures", "@"), ARGPARSE_c (aListChain, "list-sigs", "@"), ARGPARSE_c (aListChain, "check-signatures", "@"), ARGPARSE_c (aListChain, "check-sigs", "@"), ARGPARSE_c (aDeleteKey, "delete-key", "@"), ARGPARSE_end () }; /* The list of supported debug flags. */ static struct debug_flags_s debug_flags [] = { { DBG_X509_VALUE , "x509" }, { DBG_MPI_VALUE , "mpi" }, { DBG_CRYPTO_VALUE , "crypto" }, { DBG_MEMORY_VALUE , "memory" }, { DBG_CACHE_VALUE , "cache" }, { DBG_MEMSTAT_VALUE, "memstat" }, { DBG_HASHING_VALUE, "hashing" }, { DBG_IPC_VALUE , "ipc" }, { 0, NULL } }; /* Global variable to keep an error count. */ int gpgsm_errors_seen = 0; /* It is possible that we are currentlu running under setuid permissions */ static int maybe_setuid = 1; /* Helper to implement --debug-level and --debug*/ static const char *debug_level; static unsigned int debug_value; /* Default value for include-certs. We need an extra macro for gpgconf-list because the variable will be changed by the command line option. It is often cumbersome to locate intermediate certificates, thus by default we include all certificates in the chain. However we leave out the root certificate because that would make it too easy for the recipient to import that root certificate. A root certificate should be installed only after due checks and thus it won't help to send it along with each message. */ #define DEFAULT_INCLUDE_CERTS -2 /* Include all certs but root. */ static int default_include_certs = DEFAULT_INCLUDE_CERTS; /* Whether the chain mode shall be used for validation. */ static int default_validation_model; /* The default cipher algo. */ #define DEFAULT_CIPHER_ALGO "AES" static char *build_list (const char *text, const char *(*mapf)(int), int (*chkf)(int)); static void set_cmd (enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd ); static void emergency_cleanup (void); static int open_read (const char *filename); static estream_t open_es_fread (const char *filename, const char *mode); static estream_t open_es_fwrite (const char *filename); static void run_protect_tool (int argc, char **argv); static int our_pk_test_algo (int algo) { switch (algo) { case GCRY_PK_RSA: case GCRY_PK_ECDSA: return gcry_pk_test_algo (algo); default: return 1; } } static int our_cipher_test_algo (int algo) { switch (algo) { case GCRY_CIPHER_3DES: case GCRY_CIPHER_AES128: case GCRY_CIPHER_AES192: case GCRY_CIPHER_AES256: case GCRY_CIPHER_SERPENT128: case GCRY_CIPHER_SERPENT192: case GCRY_CIPHER_SERPENT256: case GCRY_CIPHER_SEED: case GCRY_CIPHER_CAMELLIA128: case GCRY_CIPHER_CAMELLIA192: case GCRY_CIPHER_CAMELLIA256: return gcry_cipher_test_algo (algo); default: return 1; } } static int our_md_test_algo (int algo) { switch (algo) { case GCRY_MD_MD5: case GCRY_MD_SHA1: case GCRY_MD_RMD160: case GCRY_MD_SHA224: case GCRY_MD_SHA256: case GCRY_MD_SHA384: case GCRY_MD_SHA512: case GCRY_MD_WHIRLPOOL: return gcry_md_test_algo (algo); default: return 1; } } static char * make_libversion (const char *libname, const char *(*getfnc)(const char*)) { const char *s; char *result; if (maybe_setuid) { gcry_control (GCRYCTL_INIT_SECMEM, 0, 0); /* Drop setuid. */ maybe_setuid = 0; } s = getfnc (NULL); result = xmalloc (strlen (libname) + 1 + strlen (s) + 1); strcpy (stpcpy (stpcpy (result, libname), " "), s); return result; } static const char * my_strusage( int level ) { static char *digests, *pubkeys, *ciphers; static char *ver_gcry, *ver_ksba; const char *p; switch (level) { case 9: p = "GPL-3.0-or-later"; break; case 11: p = "@GPGSM@ (@GNUPG@)"; break; case 13: p = VERSION; break; case 14: p = GNUPG_DEF_COPYRIGHT_LINE; break; case 17: p = PRINTABLE_OS_NAME; break; case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break; case 1: case 40: p = _("Usage: @GPGSM@ [options] [files] (-h for help)"); break; case 41: p = _("Syntax: @GPGSM@ [options] [files]\n" "Sign, check, encrypt or decrypt using the S/MIME protocol\n" "Default operation depends on the input data\n"); break; case 20: if (!ver_gcry) ver_gcry = make_libversion ("libgcrypt", gcry_check_version); p = ver_gcry; break; case 21: if (!ver_ksba) ver_ksba = make_libversion ("libksba", ksba_check_version); p = ver_ksba; break; case 31: p = "\nHome: "; break; case 32: p = gnupg_homedir (); break; case 33: p = _("\nSupported algorithms:\n"); break; case 34: if (!ciphers) ciphers = build_list ("Cipher: ", gnupg_cipher_algo_name, our_cipher_test_algo ); p = ciphers; break; case 35: if (!pubkeys) pubkeys = build_list ("Pubkey: ", gcry_pk_algo_name, our_pk_test_algo ); p = pubkeys; break; case 36: if (!digests) digests = build_list("Hash: ", gcry_md_algo_name, our_md_test_algo ); p = digests; break; default: p = NULL; break; } return p; } static char * build_list (const char *text, const char * (*mapf)(int), int (*chkf)(int)) { int i; size_t n=strlen(text)+2; char *list, *p; if (maybe_setuid) { gcry_control (GCRYCTL_DROP_PRIVS); /* drop setuid */ } for (i=1; i < 400; i++ ) if (!chkf(i)) n += strlen(mapf(i)) + 2; list = xmalloc (21 + n); *list = 0; for (p=NULL, i=1; i < 400; i++) { if (!chkf(i)) { if( !p ) p = stpcpy (list, text ); else p = stpcpy (p, ", "); p = stpcpy (p, mapf(i) ); } } if (p) strcpy (p, "\n" ); return list; } /* Set the file pointer into binary mode if required. */ static void set_binary (FILE *fp) { #ifdef HAVE_DOSISH_SYSTEM setmode (fileno (fp), O_BINARY); #else (void)fp; #endif } static void wrong_args (const char *text) { fprintf (stderr, _("usage: %s [options] %s\n"), GPGSM_NAME, text); gpgsm_exit (2); } static void set_opt_session_env (const char *name, const char *value) { gpg_error_t err; err = session_env_setenv (opt.session_env, name, value); if (err) log_fatal ("error setting session environment: %s\n", gpg_strerror (err)); } /* Setup the debugging. With a DEBUG_LEVEL of NULL only the active debug flags are propagated to the subsystems. With DEBUG_LEVEL set, a specific set of debug flags is set; and individual debugging flags will be added on top. */ static void set_debug (void) { int numok = (debug_level && digitp (debug_level)); int numlvl = numok? atoi (debug_level) : 0; if (!debug_level) ; else if (!strcmp (debug_level, "none") || (numok && numlvl < 1)) opt.debug = 0; else if (!strcmp (debug_level, "basic") || (numok && numlvl <= 2)) opt.debug = DBG_IPC_VALUE; else if (!strcmp (debug_level, "advanced") || (numok && numlvl <= 5)) opt.debug = DBG_IPC_VALUE|DBG_X509_VALUE; else if (!strcmp (debug_level, "expert") || (numok && numlvl <= 8)) opt.debug = (DBG_IPC_VALUE|DBG_X509_VALUE |DBG_CACHE_VALUE|DBG_CRYPTO_VALUE); else if (!strcmp (debug_level, "guru") || numok) { opt.debug = ~0; /* Unless the "guru" string has been used we don't want to allow hashing debugging. The rationale is that people tend to select the highest debug value and would then clutter their disk with debug files which may reveal confidential data. */ if (numok) opt.debug &= ~(DBG_HASHING_VALUE); } else { log_error (_("invalid debug-level '%s' given\n"), debug_level); gpgsm_exit (2); } opt.debug |= debug_value; if (opt.debug && !opt.verbose) opt.verbose = 1; if (opt.debug) opt.quiet = 0; if (opt.debug & DBG_MPI_VALUE) gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 2); if (opt.debug & DBG_CRYPTO_VALUE ) gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1); gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose); if (opt.debug) parse_debug_flag (NULL, &opt.debug, debug_flags); /* minip12.c may be used outside of GnuPG, thus we don't have the * opt structure over there. */ p12_set_verbosity (opt.verbose); } static void set_cmd (enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd) { enum cmd_and_opt_values cmd = *ret_cmd; if (!cmd || cmd == new_cmd) cmd = new_cmd; else if ( cmd == aSign && new_cmd == aEncr ) cmd = aSignEncr; else if ( cmd == aEncr && new_cmd == aSign ) cmd = aSignEncr; else if ( (cmd == aSign && new_cmd == aClearsign) || (cmd == aClearsign && new_cmd == aSign) ) cmd = aClearsign; else { log_error(_("conflicting commands\n")); gpgsm_exit(2); } *ret_cmd = cmd; } /* Helper to add recipients to a list. */ static void do_add_recipient (ctrl_t ctrl, const char *name, certlist_t *recplist, int is_encrypt_to, int recp_required) { int rc = gpgsm_add_to_certlist (ctrl, name, 0, recplist, is_encrypt_to); if (rc) { if (recp_required) { log_error ("can't encrypt to '%s': %s\n", name, gpg_strerror (rc)); gpgsm_status2 (ctrl, STATUS_INV_RECP, get_inv_recpsgnr_code (rc), name, NULL); } else log_info (_("Note: won't be able to encrypt to '%s': %s\n"), name, gpg_strerror (rc)); } } static void parse_validation_model (const char *model) { int i = gpgsm_parse_validation_model (model); if (i == -1) log_error (_("unknown validation model '%s'\n"), model); else default_validation_model = i; } int main ( int argc, char **argv) { ARGPARSE_ARGS pargs; int orig_argc; char **orig_argv; /* char *username;*/ int may_coredump; strlist_t sl, remusr= NULL, locusr=NULL; strlist_t nrings=NULL; int detached_sig = 0; char *last_configname = NULL; const char *configname = NULL; /* NULL or points to last_configname. * NULL also indicates that we are * processing options from the cmdline. */ int debug_argparser = 0; int no_more_options = 0; int default_keyring = 1; char *logfile = NULL; char *auditlog = NULL; char *htmlauditlog = NULL; int greeting = 0; int nogreeting = 0; int debug_wait = 0; int use_random_seed = 1; int no_common_certs_import = 0; int with_fpr = 0; const char *forced_digest_algo = NULL; const char *extra_digest_algo = NULL; enum cmd_and_opt_values cmd = 0; struct server_control_s ctrl; certlist_t recplist = NULL; certlist_t signerlist = NULL; int do_not_setup_keys = 0; int recp_required = 0; estream_t auditfp = NULL; estream_t htmlauditfp = NULL; struct assuan_malloc_hooks malloc_hooks; int pwfd = -1; static const char *homedirvalue; early_system_init (); gnupg_reopen_std (GPGSM_NAME); /* trap_unaligned ();*/ gnupg_rl_initialize (); set_strusage (my_strusage); gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); /* Please note that we may running SUID(ROOT), so be very CAREFUL when adding any stuff between here and the call to secmem_init() somewhere after the option parsing */ log_set_prefix (GPGSM_NAME, GPGRT_LOG_WITH_PREFIX); /* Make sure that our subsystems are ready. */ i18n_init (); init_common_subsystems (&argc, &argv); /* Check that the libraries are suitable. Do it here because the option parse may need services of the library */ if (!ksba_check_version (NEED_KSBA_VERSION) ) log_fatal (_("%s is too old (need %s, have %s)\n"), "libksba", NEED_KSBA_VERSION, ksba_check_version (NULL) ); gcry_control (GCRYCTL_USE_SECURE_RNDPOOL); may_coredump = disable_core_dumps (); gnupg_init_signals (0, emergency_cleanup); dotlock_create (NULL, 0); /* Register lockfile cleanup. */ /* Tell the compliance module who we are. */ gnupg_initialize_compliance (GNUPG_MODULE_NAME_GPGSM); opt.autostart = 1; opt.session_env = session_env_new (); if (!opt.session_env) log_fatal ("error allocating session environment block: %s\n", strerror (errno)); /* Note: If you change this default cipher algorithm , please remember to update the Gpgconflist entry as well. */ opt.def_cipher_algoid = DEFAULT_CIPHER_ALGO; /* First check whether we have a config file on the commandline */ orig_argc = argc; orig_argv = argv; pargs.argc = &argc; pargs.argv = &argv; pargs.flags= (ARGPARSE_FLAG_KEEP | ARGPARSE_FLAG_NOVERSION); while (gnupg_argparse (NULL, &pargs, opts)) { switch (pargs.r_opt) { case oDebug: case oDebugAll: debug_argparser++; break; case oNoOptions: /* Set here here because the homedir would otherwise be * created before main option parsing starts. */ opt.no_homedir_creation = 1; break; case oHomedir: homedirvalue = pargs.r.ret_str; break; case aCallProtectTool: /* Make sure that --version and --help are passed to the * protect-tool. */ goto leave_cmdline_parser; } } leave_cmdline_parser: /* Reset the flags. */ pargs.flags &= ~(ARGPARSE_FLAG_KEEP | ARGPARSE_FLAG_NOVERSION); /* Initialize the secure memory. */ gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0); maybe_setuid = 0; /* * Now we are now working under our real uid */ ksba_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free ); malloc_hooks.malloc = gcry_malloc; malloc_hooks.realloc = gcry_realloc; malloc_hooks.free = gcry_free; assuan_set_malloc_hooks (&malloc_hooks); assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT); setup_libassuan_logging (&opt.debug, NULL); /* Set homedir. */ gnupg_set_homedir (homedirvalue); /* Setup a default control structure for command line mode */ memset (&ctrl, 0, sizeof ctrl); gpgsm_init_default_ctrl (&ctrl); ctrl.no_server = 1; ctrl.status_fd = -1; /* No status output. */ ctrl.autodetect_encoding = 1; /* Set the default policy file */ opt.policy_file = make_filename (gnupg_homedir (), "policies.txt", NULL); /* The configuraton directories for use by gpgrt_argparser. */ gnupg_set_confdir (GNUPG_CONFDIR_SYS, gnupg_sysconfdir ()); gnupg_set_confdir (GNUPG_CONFDIR_USER, gnupg_homedir ()); /* We are re-using the struct, thus the reset flag. We OR the * flags so that the internal intialized flag won't be cleared. */ argc = orig_argc; argv = orig_argv; pargs.argc = &argc; pargs.argv = &argv; pargs.flags |= (ARGPARSE_FLAG_RESET | ARGPARSE_FLAG_KEEP | ARGPARSE_FLAG_SYS | ARGPARSE_FLAG_USER); while (!no_more_options && gnupg_argparser (&pargs, opts, GPGSM_NAME EXTSEP_S "conf")) { switch (pargs.r_opt) { case ARGPARSE_CONFFILE: if (debug_argparser) log_info (_("reading options from '%s'\n"), pargs.r_type? pargs.r.ret_str: "[cmdline]"); if (pargs.r_type) { xfree (last_configname); last_configname = xstrdup (pargs.r.ret_str); configname = last_configname; } else configname = NULL; break; case aGPGConfList: case aGPGConfTest: set_cmd (&cmd, pargs.r_opt); do_not_setup_keys = 1; default_keyring = 0; nogreeting = 1; break; case aServer: opt.batch = 1; set_cmd (&cmd, aServer); break; case aCallDirmngr: opt.batch = 1; set_cmd (&cmd, aCallDirmngr); do_not_setup_keys = 1; break; case aCallProtectTool: opt.batch = 1; set_cmd (&cmd, aCallProtectTool); no_more_options = 1; /* Stop parsing. */ do_not_setup_keys = 1; break; case aDeleteKey: set_cmd (&cmd, aDeleteKey); /*greeting=1;*/ do_not_setup_keys = 1; break; case aDetachedSign: detached_sig = 1; set_cmd (&cmd, aSign ); break; case aKeygen: set_cmd (&cmd, aKeygen); greeting=1; do_not_setup_keys = 1; break; case aImport: case aSendKeys: case aRecvKeys: case aExport: case aExportSecretKeyP12: case aExportSecretKeyP8: case aExportSecretKeyRaw: case aDumpKeys: case aDumpChain: case aDumpExternalKeys: case aDumpSecretKeys: case aListKeys: case aListExternalKeys: case aListSecretKeys: case aListChain: case aLearnCard: case aPasswd: case aKeydbClearSomeCertFlags: do_not_setup_keys = 1; set_cmd (&cmd, pargs.r_opt); break; case aEncr: recp_required = 1; set_cmd (&cmd, pargs.r_opt); break; case aSym: case aDecrypt: case aSign: case aClearsign: case aVerify: set_cmd (&cmd, pargs.r_opt); break; /* Output encoding selection. */ case oArmor: ctrl.create_pem = 1; break; case oBase64: ctrl.create_pem = 0; ctrl.create_base64 = 1; break; case oNoArmor: ctrl.create_pem = 0; ctrl.create_base64 = 0; break; case oP12Charset: opt.p12_charset = pargs.r.ret_str; break; case oPassphraseFD: pwfd = translate_sys2libc_fd_int (pargs.r.ret_int, 0); break; case oPinentryMode: opt.pinentry_mode = parse_pinentry_mode (pargs.r.ret_str); if (opt.pinentry_mode == -1) log_error (_("invalid pinentry mode '%s'\n"), pargs.r.ret_str); break; case oRequestOrigin: opt.request_origin = parse_request_origin (pargs.r.ret_str); if (opt.request_origin == -1) log_error (_("invalid request origin '%s'\n"), pargs.r.ret_str); break; /* Input encoding selection. */ case oAssumeArmor: ctrl.autodetect_encoding = 0; ctrl.is_pem = 1; ctrl.is_base64 = 0; break; case oAssumeBase64: ctrl.autodetect_encoding = 0; ctrl.is_pem = 0; ctrl.is_base64 = 1; break; case oAssumeBinary: ctrl.autodetect_encoding = 0; ctrl.is_pem = 0; ctrl.is_base64 = 0; break; case oDisableCRLChecks: opt.no_crl_check = 1; break; case oEnableCRLChecks: opt.no_crl_check = 0; break; case oDisableTrustedCertCRLCheck: opt.no_trusted_cert_crl_check = 1; break; case oEnableTrustedCertCRLCheck: opt.no_trusted_cert_crl_check = 0; break; case oForceCRLRefresh: opt.force_crl_refresh = 1; break; case oEnableIssuerBasedCRLCheck: opt.enable_issuer_based_crl_check = 1; break; case oDisableOCSP: ctrl.use_ocsp = opt.enable_ocsp = 0; break; case oEnableOCSP: ctrl.use_ocsp = opt.enable_ocsp = 1; break; case oIncludeCerts: ctrl.include_certs = default_include_certs = pargs.r.ret_int; break; case oPolicyFile: xfree (opt.policy_file); if (*pargs.r.ret_str) opt.policy_file = xstrdup (pargs.r.ret_str); else opt.policy_file = NULL; break; case oDisablePolicyChecks: opt.no_policy_check = 1; break; case oEnablePolicyChecks: opt.no_policy_check = 0; break; case oAutoIssuerKeyRetrieve: opt.auto_issuer_key_retrieve = 1; break; case oOutput: opt.outfile = pargs.r.ret_str; break; case oQuiet: opt.quiet = 1; break; case oNoTTY: /* fixme:tty_no_terminal(1);*/ break; case oDryRun: opt.dry_run = 1; break; case oVerbose: opt.verbose++; gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose); break; case oNoVerbose: opt.verbose = 0; gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose); break; case oLogFile: logfile = pargs.r.ret_str; break; case oNoLogFile: logfile = NULL; break; case oAuditLog: auditlog = pargs.r.ret_str; break; case oHtmlAuditLog: htmlauditlog = pargs.r.ret_str; break; case oBatch: opt.batch = 1; greeting = 0; break; case oNoBatch: opt.batch = 0; break; case oAnswerYes: opt.answer_yes = 1; break; case oAnswerNo: opt.answer_no = 1; break; case oKeyring: append_to_strlist (&nrings, pargs.r.ret_str); break; case oDebug: if (parse_debug_flag (pargs.r.ret_str, &debug_value, debug_flags)) { pargs.r_opt = ARGPARSE_INVALID_ARG; pargs.err = ARGPARSE_PRINT_ERROR; } break; case oDebugAll: debug_value = ~0; break; case oDebugNone: debug_value = 0; break; case oDebugLevel: debug_level = pargs.r.ret_str; break; case oDebugWait: debug_wait = pargs.r.ret_int; break; case oDebugAllowCoreDump: may_coredump = enable_core_dumps (); break; case oDebugNoChainValidation: opt.no_chain_validation = 1; break; case oDebugIgnoreExpiration: opt.ignore_expiration = 1; break; case oStatusFD: ctrl.status_fd = translate_sys2libc_fd_int (pargs.r.ret_int, 1); break; case oLoggerFD: log_set_fd (translate_sys2libc_fd_int (pargs.r.ret_int, 1)); break; case oWithMD5Fingerprint: opt.with_md5_fingerprint=1; /*fall through*/ case oWithFingerprint: with_fpr=1; /*fall through*/ case aFingerprint: opt.fingerprint++; break; case oWithKeygrip: opt.with_keygrip = 1; break; case oHomedir: gnupg_set_homedir (pargs.r.ret_str); break; case oAgentProgram: opt.agent_program = pargs.r.ret_str; break; case oDisplay: set_opt_session_env ("DISPLAY", pargs.r.ret_str); break; case oTTYname: set_opt_session_env ("GPG_TTY", pargs.r.ret_str); break; case oTTYtype: set_opt_session_env ("TERM", pargs.r.ret_str); break; case oXauthority: set_opt_session_env ("XAUTHORITY", pargs.r.ret_str); break; case oLCctype: opt.lc_ctype = xstrdup (pargs.r.ret_str); break; case oLCmessages: opt.lc_messages = xstrdup (pargs.r.ret_str); break; case oDirmngrProgram: opt.dirmngr_program = pargs.r.ret_str; break; case oDisableDirmngr: opt.disable_dirmngr = 1; break; case oPreferSystemDirmngr: /* Obsolete */; break; case oProtectToolProgram: opt.protect_tool_program = pargs.r.ret_str; break; case oFakedSystemTime: { time_t faked_time = isotime2epoch (pargs.r.ret_str); if (faked_time == (time_t)(-1)) faked_time = (time_t)strtoul (pargs.r.ret_str, NULL, 10); gnupg_set_time (faked_time, 0); } break; case oNoDefKeyring: default_keyring = 0; break; case oNoGreeting: nogreeting = 1; break; case oDefaultKey: if (*pargs.r.ret_str) { xfree (opt.local_user); opt.local_user = xstrdup (pargs.r.ret_str); } break; case oDefRecipient: if (*pargs.r.ret_str) opt.def_recipient = xstrdup (pargs.r.ret_str); break; case oDefRecipientSelf: xfree (opt.def_recipient); opt.def_recipient = NULL; opt.def_recipient_self = 1; break; case oNoDefRecipient: xfree (opt.def_recipient); opt.def_recipient = NULL; opt.def_recipient_self = 0; break; case oWithKeyData: opt.with_key_data=1; /* fall through */ case oWithColons: ctrl.with_colons = 1; break; case oWithSecret: ctrl.with_secret = 1; break; case oWithValidation: ctrl.with_validation=1; break; case oWithEphemeralKeys: ctrl.with_ephemeral_keys=1; break; case oSkipVerify: opt.skip_verify=1; break; case oNoEncryptTo: opt.no_encrypt_to = 1; break; case oEncryptTo: /* Store the recipient in the second list */ sl = add_to_strlist (&remusr, pargs.r.ret_str); sl->flags = 1; break; case oRecipient: /* store the recipient */ add_to_strlist ( &remusr, pargs.r.ret_str); break; case oUser: /* Store the local users, the first one is the default */ if (!opt.local_user) opt.local_user = xstrdup (pargs.r.ret_str); add_to_strlist (&locusr, pargs.r.ret_str); break; case oNoSecmemWarn: gcry_control (GCRYCTL_DISABLE_SECMEM_WARN); break; case oCipherAlgo: opt.def_cipher_algoid = pargs.r.ret_str; break; case oDisableCipherAlgo: { int algo = gcry_cipher_map_name (pargs.r.ret_str); gcry_cipher_ctl (NULL, GCRYCTL_DISABLE_ALGO, &algo, sizeof algo); } break; case oDisablePubkeyAlgo: { int algo = gcry_pk_map_name (pargs.r.ret_str); gcry_pk_ctl (GCRYCTL_DISABLE_ALGO,&algo, sizeof algo ); } break; case oDigestAlgo: forced_digest_algo = pargs.r.ret_str; break; case oExtraDigestAlgo: extra_digest_algo = pargs.r.ret_str; break; case oIgnoreTimeConflict: opt.ignore_time_conflict = 1; break; case oNoRandomSeedFile: use_random_seed = 0; break; case oNoCommonCertsImport: no_common_certs_import = 1; break; case oEnableSpecialFilenames: enable_special_filenames (); break; case oValidationModel: parse_validation_model (pargs.r.ret_str); break; case oKeyServer: append_to_strlist (&opt.keyserver, pargs.r.ret_str); break; case oIgnoreCertExtension: add_to_strlist (&opt.ignored_cert_extensions, pargs.r.ret_str); break; case oNoAutostart: opt.autostart = 0; break; case oCompliance: { struct gnupg_compliance_option compliance_options[] = { { "gnupg", CO_GNUPG }, { "de-vs", CO_DE_VS } }; int compliance = gnupg_parse_compliance_option (pargs.r.ret_str, compliance_options, DIM (compliance_options), opt.quiet); if (compliance < 0) log_inc_errorcount (); /* Force later termination. */ opt.compliance = compliance; } break; default: if (configname) pargs.err = ARGPARSE_PRINT_WARNING; else { pargs.err = ARGPARSE_PRINT_ERROR; /* The argparse function calls a plain exit and thus we * need to print a status here. */ gpgsm_status_with_error (&ctrl, STATUS_FAILURE, "option-parser", gpg_error (GPG_ERR_GENERAL)); } break; } } gnupg_argparse (NULL, &pargs, NULL); /* Release internal state. */ if (!last_configname) opt.config_filename = make_filename (gnupg_homedir (), GPGSM_NAME EXTSEP_S "conf", NULL); else opt.config_filename = last_configname; if (log_get_errorcount(0)) { gpgsm_status_with_error (&ctrl, STATUS_FAILURE, "option-parser", gpg_error (GPG_ERR_GENERAL)); gpgsm_exit(2); } if (pwfd != -1) /* Read the passphrase now. */ read_passphrase_from_fd (pwfd); /* Now that we have the options parsed we need to update the default control structure. */ gpgsm_init_default_ctrl (&ctrl); if (nogreeting) greeting = 0; if (greeting) { es_fprintf (es_stderr, "%s %s; %s\n", strusage(11), strusage(13), strusage(14) ); es_fprintf (es_stderr, "%s\n", strusage(15) ); } # ifdef IS_DEVELOPMENT_VERSION if (!opt.batch) { log_info ("NOTE: THIS IS A DEVELOPMENT VERSION!\n"); log_info ("It is only intended for test purposes and should NOT be\n"); log_info ("used in a production environment or with production keys!\n"); } # endif if (may_coredump && !opt.quiet) log_info (_("WARNING: program may create a core file!\n")); /* if (opt.qualsig_approval && !opt.quiet) */ /* log_info (_("This software has officially been approved to " */ /* "create and verify\n" */ /* "qualified signatures according to German law.\n")); */ if (logfile && cmd == aServer) { log_set_file (logfile); log_set_prefix (NULL, GPGRT_LOG_WITH_PREFIX | GPGRT_LOG_WITH_TIME | GPGRT_LOG_WITH_PID); } if (gnupg_faked_time_p ()) { gnupg_isotime_t tbuf; log_info (_("WARNING: running with faked system time: ")); gnupg_get_isotime (tbuf); dump_isotime (tbuf); log_printf ("\n"); } /* Print a warning if an argument looks like an option. */ if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN)) { int i; for (i=0; i < argc; i++) if (argv[i][0] == '-' && argv[i][1] == '-') log_info (_("Note: '%s' is not considered an option\n"), argv[i]); } /*FIXME if (opt.batch) */ /* tty_batchmode (1); */ gcry_control (GCRYCTL_RESUME_SECMEM_WARN); set_debug (); /* Although we always use gpgsm_exit, we better install a regualr exit handler so that at least the secure memory gets wiped out. */ if (atexit (emergency_cleanup)) { log_error ("atexit failed\n"); gpgsm_exit (2); } /* Must do this after dropping setuid, because the mapping functions may try to load an module and we may have disabled an algorithm. We remap the commonly used algorithms to the OIDs for convenience. We need to work with the OIDs because they are used to check whether the encryption mode is actually available. */ if (!strcmp (opt.def_cipher_algoid, "3DES") ) opt.def_cipher_algoid = "1.2.840.113549.3.7"; else if (!strcmp (opt.def_cipher_algoid, "AES") || !strcmp (opt.def_cipher_algoid, "AES128")) opt.def_cipher_algoid = "2.16.840.1.101.3.4.1.2"; else if (!strcmp (opt.def_cipher_algoid, "AES192") ) opt.def_cipher_algoid = "2.16.840.1.101.3.4.1.22"; else if (!strcmp (opt.def_cipher_algoid, "AES256") ) opt.def_cipher_algoid = "2.16.840.1.101.3.4.1.42"; else if (!strcmp (opt.def_cipher_algoid, "SERPENT") || !strcmp (opt.def_cipher_algoid, "SERPENT128") ) opt.def_cipher_algoid = "1.3.6.1.4.1.11591.13.2.2"; else if (!strcmp (opt.def_cipher_algoid, "SERPENT192") ) opt.def_cipher_algoid = "1.3.6.1.4.1.11591.13.2.22"; else if (!strcmp (opt.def_cipher_algoid, "SERPENT256") ) opt.def_cipher_algoid = "1.3.6.1.4.1.11591.13.2.42"; else if (!strcmp (opt.def_cipher_algoid, "SEED") ) opt.def_cipher_algoid = "1.2.410.200004.1.4"; else if (!strcmp (opt.def_cipher_algoid, "CAMELLIA") || !strcmp (opt.def_cipher_algoid, "CAMELLIA128") ) opt.def_cipher_algoid = "1.2.392.200011.61.1.1.1.2"; else if (!strcmp (opt.def_cipher_algoid, "CAMELLIA192") ) opt.def_cipher_algoid = "1.2.392.200011.61.1.1.1.3"; else if (!strcmp (opt.def_cipher_algoid, "CAMELLIA256") ) opt.def_cipher_algoid = "1.2.392.200011.61.1.1.1.4"; if (cmd != aGPGConfList) { if ( !gcry_cipher_map_name (opt.def_cipher_algoid) || !gcry_cipher_mode_from_oid (opt.def_cipher_algoid)) log_error (_("selected cipher algorithm is invalid\n")); if (forced_digest_algo) { opt.forced_digest_algo = gcry_md_map_name (forced_digest_algo); if (our_md_test_algo(opt.forced_digest_algo) ) log_error (_("selected digest algorithm is invalid\n")); } if (extra_digest_algo) { opt.extra_digest_algo = gcry_md_map_name (extra_digest_algo); if (our_md_test_algo (opt.extra_digest_algo) ) log_error (_("selected digest algorithm is invalid\n")); } } /* Check our chosen algorithms against the list of allowed * algorithms in the current compliance mode, and fail hard if it is * not. This is us being nice to the user informing her early that * the chosen algorithms are not available. We also check and * enforce this right before the actual operation. */ if (! gnupg_cipher_is_allowed (opt.compliance, cmd == aEncr || cmd == aSignEncr, gcry_cipher_map_name (opt.def_cipher_algoid), GCRY_CIPHER_MODE_NONE) && ! gnupg_cipher_is_allowed (opt.compliance, cmd == aEncr || cmd == aSignEncr, gcry_cipher_mode_from_oid (opt.def_cipher_algoid), GCRY_CIPHER_MODE_NONE)) log_error (_("cipher algorithm '%s' may not be used in %s mode\n"), opt.def_cipher_algoid, gnupg_compliance_option_string (opt.compliance)); if (forced_digest_algo && ! gnupg_digest_is_allowed (opt.compliance, cmd == aSign || cmd == aSignEncr || cmd == aClearsign, opt.forced_digest_algo)) log_error (_("digest algorithm '%s' may not be used in %s mode\n"), forced_digest_algo, gnupg_compliance_option_string (opt.compliance)); if (extra_digest_algo && ! gnupg_digest_is_allowed (opt.compliance, cmd == aSign || cmd == aSignEncr || cmd == aClearsign, opt.extra_digest_algo)) log_error (_("digest algorithm '%s' may not be used in %s mode\n"), extra_digest_algo, gnupg_compliance_option_string (opt.compliance)); if (log_get_errorcount(0)) { gpgsm_status_with_error (&ctrl, STATUS_FAILURE, "option-postprocessing", gpg_error (GPG_ERR_GENERAL)); gpgsm_exit (2); } /* Set the random seed file. */ if (use_random_seed) { char *p = make_filename (gnupg_homedir (), "random_seed", NULL); gcry_control (GCRYCTL_SET_RANDOM_SEED_FILE, p); xfree(p); } if (!cmd && opt.fingerprint && !with_fpr) set_cmd (&cmd, aListKeys); /* Add default keybox. */ if (!nrings && default_keyring) { int created; keydb_add_resource (&ctrl, "pubring.kbx", 0, &created); if (created && !no_common_certs_import) { /* Import the standard certificates for a new default keybox. */ char *filelist[2]; filelist[0] = make_filename (gnupg_datadir (),"com-certs.pem", NULL); filelist[1] = NULL; if (!gnupg_access (filelist[0], F_OK)) { log_info (_("importing common certificates '%s'\n"), filelist[0]); gpgsm_import_files (&ctrl, 1, filelist, open_read); } xfree (filelist[0]); } } for (sl = nrings; sl; sl = sl->next) keydb_add_resource (&ctrl, sl->d, 0, NULL); FREE_STRLIST(nrings); /* Prepare the audit log feature for certain commands. */ if (auditlog || htmlauditlog) { switch (cmd) { case aEncr: case aSign: case aDecrypt: case aVerify: audit_release (ctrl.audit); ctrl.audit = audit_new (); if (auditlog) auditfp = open_es_fwrite (auditlog); if (htmlauditlog) htmlauditfp = open_es_fwrite (htmlauditlog); break; default: break; } } if (!do_not_setup_keys) { int errcount = log_get_errorcount (0); for (sl = locusr; sl ; sl = sl->next) { int rc = gpgsm_add_to_certlist (&ctrl, sl->d, 1, &signerlist, 0); if (rc) { log_error (_("can't sign using '%s': %s\n"), sl->d, gpg_strerror (rc)); gpgsm_status2 (&ctrl, STATUS_INV_SGNR, get_inv_recpsgnr_code (rc), sl->d, NULL); gpgsm_status2 (&ctrl, STATUS_INV_RECP, get_inv_recpsgnr_code (rc), sl->d, NULL); } } /* Build the recipient list. We first add the regular ones and then the encrypt-to ones because the underlying function will silently ignore duplicates and we can't allow keeping a duplicate which is flagged as encrypt-to as the actually encrypt function would then complain about no (regular) recipients. */ for (sl = remusr; sl; sl = sl->next) if (!(sl->flags & 1)) do_add_recipient (&ctrl, sl->d, &recplist, 0, recp_required); if (!opt.no_encrypt_to) { for (sl = remusr; sl; sl = sl->next) if ((sl->flags & 1)) do_add_recipient (&ctrl, sl->d, &recplist, 1, recp_required); } /* We do not require a recipient for decryption but because * recipients and signers are always checked and log_error is * sometimes used (for failed signing keys or due to a failed * CRL checking) that would have bumbed up the error counter. * We clear the counter in the decryption case because there is * no reason to force decryption to fail. */ if (cmd == aDecrypt && !errcount) log_get_errorcount (1); /* clear counter */ } if (log_get_errorcount(0)) gpgsm_exit(1); /* Must stop for invalid recipients. */ /* Dispatch command. */ switch (cmd) { case aGPGConfList: { /* List options and default values in the GPG Conf format. */ char *config_filename_esc = percent_escape (opt.config_filename, NULL); es_printf ("%s-%s.conf:%lu:\"%s\n", GPGCONF_NAME, GPGSM_NAME, GC_OPT_FLAG_DEFAULT, config_filename_esc); xfree (config_filename_esc); es_printf ("verbose:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("quiet:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("debug-level:%lu:\"none:\n", GC_OPT_FLAG_DEFAULT); es_printf ("log-file:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("disable-crl-checks:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("enable-crl-checks:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("disable-trusted-cert-crl-check:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("enable-ocsp:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("include-certs:%lu:%d:\n", GC_OPT_FLAG_DEFAULT, DEFAULT_INCLUDE_CERTS); es_printf ("disable-policy-checks:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("auto-issuer-key-retrieve:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("disable-dirmngr:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("cipher-algo:%lu:\"%s:\n", GC_OPT_FLAG_DEFAULT, DEFAULT_CIPHER_ALGO); es_printf ("p12-charset:%lu:\n", GC_OPT_FLAG_DEFAULT); es_printf ("default-key:%lu:\n", GC_OPT_FLAG_DEFAULT); es_printf ("encrypt-to:%lu:\n", GC_OPT_FLAG_DEFAULT); es_printf ("keyserver:%lu:\n", GC_OPT_FLAG_NONE); /* The next one is an info only item and should match what proc_parameters actually implements. */ es_printf ("default_pubkey_algo:%lu:\"%s:\n", GC_OPT_FLAG_DEFAULT, "RSA-3072"); es_printf ("compliance:%lu:\"%s:\n", GC_OPT_FLAG_DEFAULT, "gnupg"); } break; case aGPGConfTest: /* This is merely a dummy command to test whether the configuration file is valid. */ break; case aServer: if (debug_wait) { log_debug ("waiting for debugger - my pid is %u .....\n", (unsigned int)getpid()); gnupg_sleep (debug_wait); log_debug ("... okay\n"); } gpgsm_server (recplist); break; case aCallDirmngr: if (!argc) wrong_args ("--call-dirmngr {args}"); else if (gpgsm_dirmngr_run_command (&ctrl, *argv, argc-1, argv+1)) gpgsm_exit (1); break; case aCallProtectTool: run_protect_tool (argc, argv); break; case aEncr: /* Encrypt the given file. */ { estream_t fp = open_es_fwrite (opt.outfile?opt.outfile:"-"); set_binary (stdin); if (!argc) /* Source is stdin. */ gpgsm_encrypt (&ctrl, recplist, 0, fp); else if (argc == 1) /* Source is the given file. */ gpgsm_encrypt (&ctrl, recplist, open_read (*argv), fp); else wrong_args ("--encrypt [datafile]"); es_fclose (fp); } break; case aSign: /* Sign the given file. */ { estream_t fp = open_es_fwrite (opt.outfile?opt.outfile:"-"); /* Fixme: We should also allow concatenation of multiple files for signing because that is what gpg does.*/ set_binary (stdin); if (!argc) /* Create from stdin. */ gpgsm_sign (&ctrl, signerlist, 0, detached_sig, fp); else if (argc == 1) /* From file. */ gpgsm_sign (&ctrl, signerlist, open_read (*argv), detached_sig, fp); else wrong_args ("--sign [datafile]"); es_fclose (fp); } break; case aSignEncr: /* sign and encrypt the given file */ log_error ("this command has not yet been implemented\n"); break; case aClearsign: /* make a clearsig */ log_error ("this command has not yet been implemented\n"); break; case aVerify: { estream_t fp = NULL; set_binary (stdin); if (argc == 2 && opt.outfile) log_info ("option --output ignored for a detached signature\n"); else if (opt.outfile) fp = open_es_fwrite (opt.outfile); if (!argc) gpgsm_verify (&ctrl, 0, -1, fp); /* normal signature from stdin */ else if (argc == 1) gpgsm_verify (&ctrl, open_read (*argv), -1, fp); /* std signature */ else if (argc == 2) /* detached signature (sig, detached) */ gpgsm_verify (&ctrl, open_read (*argv), open_read (argv[1]), NULL); else wrong_args ("--verify [signature [detached_data]]"); es_fclose (fp); } break; case aDecrypt: { estream_t fp = open_es_fwrite (opt.outfile?opt.outfile:"-"); + gpg_error_t err; set_binary (stdin); if (!argc) - gpgsm_decrypt (&ctrl, 0, fp); /* from stdin */ + err = gpgsm_decrypt (&ctrl, 0, fp); /* from stdin */ else if (argc == 1) - gpgsm_decrypt (&ctrl, open_read (*argv), fp); /* from file */ + err = gpgsm_decrypt (&ctrl, open_read (*argv), fp); /* from file */ else wrong_args ("--decrypt [filename]"); - es_fclose (fp); +#if GPGRT_VERSION_NUMBER >= 0x012700 /* 1.39 */ + if (err) + gpgrt_fcancel (fp); + else +#endif + es_fclose (fp); } break; case aDeleteKey: for (sl=NULL; argc; argc--, argv++) add_to_strlist (&sl, *argv); gpgsm_delete (&ctrl, sl); free_strlist(sl); break; case aListChain: case aDumpChain: ctrl.with_chain = 1; /* fall through */ case aListKeys: case aDumpKeys: case aListExternalKeys: case aDumpExternalKeys: case aListSecretKeys: case aDumpSecretKeys: { unsigned int mode; estream_t fp; switch (cmd) { case aListChain: case aListKeys: mode = (0 | 0 | (1<<6)); break; case aDumpChain: case aDumpKeys: mode = (256 | 0 | (1<<6)); break; case aListExternalKeys: mode = (0 | 0 | (1<<7)); break; case aDumpExternalKeys: mode = (256 | 0 | (1<<7)); break; case aListSecretKeys: mode = (0 | 2 | (1<<6)); break; case aDumpSecretKeys: mode = (256 | 2 | (1<<6)); break; default: BUG(); } fp = open_es_fwrite (opt.outfile?opt.outfile:"-"); for (sl=NULL; argc; argc--, argv++) add_to_strlist (&sl, *argv); gpgsm_list_keys (&ctrl, sl, fp, mode); free_strlist(sl); es_fclose (fp); } break; case aKeygen: /* Generate a key; well kind of. */ { estream_t fpin = NULL; estream_t fpout; if (opt.batch) { if (!argc) /* Create from stdin. */ fpin = open_es_fread ("-", "r"); else if (argc == 1) /* From file. */ fpin = open_es_fread (*argv, "r"); else wrong_args ("--generate-key --batch [parmfile]"); } fpout = open_es_fwrite (opt.outfile?opt.outfile:"-"); if (fpin) gpgsm_genkey (&ctrl, fpin, fpout); else gpgsm_gencertreq_tty (&ctrl, fpout); es_fclose (fpout); } break; case aImport: gpgsm_import_files (&ctrl, argc, argv, open_read); break; case aExport: { estream_t fp; fp = open_es_fwrite (opt.outfile?opt.outfile:"-"); for (sl=NULL; argc; argc--, argv++) add_to_strlist (&sl, *argv); gpgsm_export (&ctrl, sl, fp); free_strlist(sl); es_fclose (fp); } break; case aExportSecretKeyP12: { estream_t fp = open_es_fwrite (opt.outfile?opt.outfile:"-"); if (argc == 1) gpgsm_p12_export (&ctrl, *argv, fp, 0); else wrong_args ("--export-secret-key-p12 KEY-ID"); if (fp != es_stdout) es_fclose (fp); } break; case aExportSecretKeyP8: { estream_t fp = open_es_fwrite (opt.outfile?opt.outfile:"-"); if (argc == 1) gpgsm_p12_export (&ctrl, *argv, fp, 1); else wrong_args ("--export-secret-key-p8 KEY-ID"); if (fp != es_stdout) es_fclose (fp); } break; case aExportSecretKeyRaw: { estream_t fp = open_es_fwrite (opt.outfile?opt.outfile:"-"); if (argc == 1) gpgsm_p12_export (&ctrl, *argv, fp, 2); else wrong_args ("--export-secret-key-raw KEY-ID"); if (fp != es_stdout) es_fclose (fp); } break; case aSendKeys: case aRecvKeys: log_error ("this command has not yet been implemented\n"); break; case aLearnCard: if (argc) wrong_args ("--learn-card"); else { int rc = gpgsm_agent_learn (&ctrl); if (rc) log_error ("error learning card: %s\n", gpg_strerror (rc)); } break; case aPasswd: if (argc != 1) wrong_args ("--change-passphrase "); else { int rc; ksba_cert_t cert = NULL; char *grip = NULL; rc = gpgsm_find_cert (&ctrl, *argv, NULL, &cert, 0); if (rc) ; else if (!(grip = gpgsm_get_keygrip_hexstring (cert))) rc = gpg_error (GPG_ERR_BUG); else { char *desc = gpgsm_format_keydesc (cert); rc = gpgsm_agent_passwd (&ctrl, grip, desc); xfree (desc); } if (rc) log_error ("error changing passphrase: %s\n", gpg_strerror (rc)); xfree (grip); ksba_cert_release (cert); } break; case aKeydbClearSomeCertFlags: for (sl=NULL; argc; argc--, argv++) add_to_strlist (&sl, *argv); keydb_clear_some_cert_flags (&ctrl, sl); free_strlist(sl); break; default: log_error (_("invalid command (there is no implicit command)\n")); break; } /* Print the audit result if needed. */ if ((auditlog && auditfp) || (htmlauditlog && htmlauditfp)) { if (auditlog && auditfp) audit_print_result (ctrl.audit, auditfp, 0); if (htmlauditlog && htmlauditfp) audit_print_result (ctrl.audit, htmlauditfp, 1); audit_release (ctrl.audit); ctrl.audit = NULL; es_fclose (auditfp); es_fclose (htmlauditfp); } /* cleanup */ free_strlist (opt.keyserver); opt.keyserver = NULL; gpgsm_release_certlist (recplist); gpgsm_release_certlist (signerlist); FREE_STRLIST (remusr); FREE_STRLIST (locusr); gpgsm_exit(0); return 8; /*NOTREACHED*/ } /* Note: This function is used by signal handlers!. */ static void emergency_cleanup (void) { gcry_control (GCRYCTL_TERM_SECMEM ); } void gpgsm_exit (int rc) { gcry_control (GCRYCTL_UPDATE_RANDOM_SEED_FILE); if (opt.debug & DBG_MEMSTAT_VALUE) { gcry_control( GCRYCTL_DUMP_MEMORY_STATS ); gcry_control( GCRYCTL_DUMP_RANDOM_STATS ); } if (opt.debug) gcry_control (GCRYCTL_DUMP_SECMEM_STATS ); emergency_cleanup (); rc = rc? rc : log_get_errorcount(0)? 2 : gpgsm_errors_seen? 1 : 0; exit (rc); } void gpgsm_init_default_ctrl (struct server_control_s *ctrl) { ctrl->include_certs = default_include_certs; ctrl->use_ocsp = opt.enable_ocsp; ctrl->validation_model = default_validation_model; ctrl->offline = opt.disable_dirmngr; } int gpgsm_parse_validation_model (const char *model) { if (!ascii_strcasecmp (model, "shell") ) return 0; else if ( !ascii_strcasecmp (model, "chain") ) return 1; else if ( !ascii_strcasecmp (model, "steed") ) return 2; else return -1; } /* Open the FILENAME for read and return the file descriptor. Stop with an error message in case of problems. "-" denotes stdin and if special filenames are allowed the given fd is opened instead. */ static int open_read (const char *filename) { int fd; if (filename[0] == '-' && !filename[1]) { set_binary (stdin); return 0; /* stdin */ } fd = check_special_filename (filename, 0, 0); if (fd != -1) return fd; fd = gnupg_open (filename, O_RDONLY | O_BINARY, 0); if (fd == -1) { log_error (_("can't open '%s': %s\n"), filename, strerror (errno)); gpgsm_exit (2); } return fd; } /* Same as open_read but return an estream_t. */ static estream_t open_es_fread (const char *filename, const char *mode) { int fd; estream_t fp; if (filename[0] == '-' && !filename[1]) fd = fileno (stdin); else fd = check_special_filename (filename, 0, 0); if (fd != -1) { fp = es_fdopen_nc (fd, mode); if (!fp) { log_error ("es_fdopen(%d) failed: %s\n", fd, strerror (errno)); gpgsm_exit (2); } return fp; } fp = es_fopen (filename, mode); if (!fp) { log_error (_("can't open '%s': %s\n"), filename, strerror (errno)); gpgsm_exit (2); } return fp; } /* Open FILENAME for fwrite and return an extended stream. Stop with an error message in case of problems. "-" denotes stdout and if special filenames are allowed the given fd is opened instead. Caller must close the returned stream. */ static estream_t open_es_fwrite (const char *filename) { int fd; estream_t fp; if (filename[0] == '-' && !filename[1]) { fflush (stdout); fp = es_fdopen_nc (fileno(stdout), "wb"); return fp; } fd = check_special_filename (filename, 1, 0); if (fd != -1) { fp = es_fdopen_nc (fd, "wb"); if (!fp) { log_error ("es_fdopen(%d) failed: %s\n", fd, strerror (errno)); gpgsm_exit (2); } return fp; } fp = es_fopen (filename, "wb"); if (!fp) { log_error (_("can't open '%s': %s\n"), filename, strerror (errno)); gpgsm_exit (2); } return fp; } static void run_protect_tool (int argc, char **argv) { #ifdef HAVE_W32_SYSTEM (void)argc; (void)argv; #else const char *pgm; char **av; int i; if (!opt.protect_tool_program || !*opt.protect_tool_program) pgm = gnupg_module_name (GNUPG_MODULE_NAME_PROTECT_TOOL); else pgm = opt.protect_tool_program; av = xcalloc (argc+2, sizeof *av); av[0] = strrchr (pgm, '/'); if (!av[0]) av[0] = xstrdup (pgm); for (i=1; argc; i++, argc--, argv++) av[i] = *argv; av[i] = NULL; execv (pgm, av); log_error ("error executing '%s': %s\n", pgm, strerror (errno)); #endif /*!HAVE_W32_SYSTEM*/ gpgsm_exit (2); }