summaryrefslogtreecommitdiffstats
path: root/sm/gpgsm.c
diff options
context:
space:
mode:
Diffstat (limited to 'sm/gpgsm.c')
-rw-r--r--sm/gpgsm.c2262
1 files changed, 2262 insertions, 0 deletions
diff --git a/sm/gpgsm.c b/sm/gpgsm.c
new file mode 100644
index 0000000..2716890
--- /dev/null
+++ b/sm/gpgsm.c
@@ -0,0 +1,2262 @@
+/* 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 <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#define INCLUDED_BY_MAIN_MODULE 1
+
+#include "gpgsm.h"
+#include <gcrypt.h>
+#include <assuan.h> /* 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,
+ oMinRSALength,
+
+ 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,
+ oKeyServer_deprecated,
+ oEncryptTo,
+ oNoEncryptTo,
+ oLoggerFD,
+ oDisableCipherAlgo,
+ oDisablePubkeyAlgo,
+ oIgnoreTimeConflict,
+ oNoRandomSeedFile,
+ oNoCommonCertsImport,
+ oIgnoreCertExtension,
+ oIgnoreCertWithOID,
+ oRequireCompliance,
+ oCompatibilityFlags,
+ 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_header ("Monitor", N_("Options controlling the diagnostic output")),
+
+ ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
+ ARGPARSE_s_n (oNoVerbose, "no-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_n (oNoGreeting, "no-greeting", "@"),
+ 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_s (oLogFile, "log-file",
+ N_("|FILE|write server mode logs to FILE")),
+ ARGPARSE_s_n (oNoLogFile, "no-log-file", "@"),
+ ARGPARSE_s_i (oLoggerFD, "logger-fd", "@"),
+ ARGPARSE_s_n (oNoSecmemWarn, "no-secmem-warning", "@"),
+
+
+ ARGPARSE_header ("Configuration",
+ N_("Options controlling the configuration")),
+
+ ARGPARSE_s_s (oHomedir, "homedir", "@"),
+ ARGPARSE_s_s (oFakedSystemTime, "faked-system-time", "@"),
+ ARGPARSE_s_n (oPreferSystemDirmngr,"prefer-system-dirmngr", "@"),
+ 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_s (oCompliance, "compliance", "@"),
+ ARGPARSE_p_u (oMinRSALength, "min-rsa-length", "@"),
+ ARGPARSE_s_n (oNoCommonCertsImport, "no-common-certs-import", "@"),
+ ARGPARSE_s_s (oIgnoreCertExtension, "ignore-cert-extension", "@"),
+ ARGPARSE_s_s (oIgnoreCertWithOID, "ignore-cert-with-oid", "@"),
+ ARGPARSE_s_n (oNoAutostart, "no-autostart", "@"),
+ ARGPARSE_s_s (oAgentProgram, "agent-program", "@"),
+ ARGPARSE_s_s (oDirmngrProgram, "dirmngr-program", "@"),
+ ARGPARSE_s_s (oProtectToolProgram, "protect-tool-program", "@"),
+
+
+ ARGPARSE_header ("Input", N_("Options controlling the input")),
+
+ 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_header ("Output", N_("Options controlling the output")),
+
+ ARGPARSE_s_n (oArmor, "armor", N_("create ascii armored output")),
+ ARGPARSE_s_n (oArmor, "armour", "@"),
+ ARGPARSE_s_n (oNoArmor, "no-armor", "@"),
+ ARGPARSE_s_n (oNoArmor, "no-armour", "@"),
+ ARGPARSE_s_n (oBase64, "base64", N_("create base-64 encoded output")),
+ ARGPARSE_s_s (oOutput, "output", N_("|FILE|write output to FILE")),
+
+
+ ARGPARSE_header (NULL, N_("Options to specify keys")),
+
+ ARGPARSE_s_s (oRecipient, "recipient", N_("|USER-ID|encrypt for USER-ID")),
+ ARGPARSE_s_s (oUser, "local-user",
+ N_("|USER-ID|use USER-ID to sign or decrypt")),
+ ARGPARSE_s_s (oDefaultKey, "default-key",
+ N_("|USER-ID|use USER-ID as default secret key")),
+ ARGPARSE_s_s (oEncryptTo, "encrypt-to",
+ N_("|NAME|encrypt to user ID NAME as well")),
+ ARGPARSE_s_n (oNoEncryptTo, "no-encrypt-to", "@"),
+ /* 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 (oKeyring, "keyring",
+ N_("|FILE|add keyring to the list of keyrings")),
+ ARGPARSE_s_n (oNoDefKeyring, "no-default-keyring", "@"),
+ ARGPARSE_s_s (oKeyServer_deprecated, "ldapserver", "@"),
+ ARGPARSE_s_s (oKeyServer, "keyserver", "@"),
+
+ ARGPARSE_header ("ImportExport",
+ N_("Options controlling key import and export")),
+
+ ARGPARSE_s_n (oDisableDirmngr, "disable-dirmngr",
+ N_("disable all access to the dirmngr")),
+ ARGPARSE_s_n (oAutoIssuerKeyRetrieve, "auto-issuer-key-retrieve",
+ N_("fetch missing issuer certificates")),
+ ARGPARSE_s_s (oP12Charset, "p12-charset",
+ N_("|NAME|use encoding NAME for PKCS#12 passphrases")),
+
+
+ ARGPARSE_header ("Keylist", N_("Options controlling key listings")),
+
+ 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_header ("Security", N_("Options controlling the security")),
+
+ 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",
+ N_("do not check CRLs for root certificates")),
+ ARGPARSE_s_n (oEnableTrustedCertCRLCheck,
+ "enable-trusted-cert-crl-check", "@"),
+ ARGPARSE_s_n (oDisableOCSP, "disable-ocsp", "@"),
+ ARGPARSE_s_n (oEnableOCSP, "enable-ocsp", N_("check validity using OCSP")),
+ ARGPARSE_s_n (oDisablePolicyChecks, "disable-policy-checks",
+ N_("do not check certificate policies")),
+ ARGPARSE_s_n (oEnablePolicyChecks, "enable-policy-checks", "@"),
+ 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_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 (oRequireCompliance, "require-compliance", "@"),
+
+
+ ARGPARSE_header (NULL, N_("Options for unattended use")),
+
+ ARGPARSE_s_n (oBatch, "batch", N_("batch mode: never ask")),
+ ARGPARSE_s_n (oNoBatch, "no-batch", "@"),
+ ARGPARSE_s_n (oAnswerYes, "yes", N_("assume yes on most questions")),
+ ARGPARSE_s_n (oAnswerNo, "no", N_("assume no on most questions")),
+ ARGPARSE_s_i (oStatusFD, "status-fd", N_("|FD|write status info to this FD")),
+ ARGPARSE_s_n (oEnableSpecialFilenames, "enable-special-filenames", "@"),
+ ARGPARSE_s_i (oPassphraseFD, "passphrase-fd", "@"),
+ ARGPARSE_s_s (oPinentryMode, "pinentry-mode", "@"),
+
+
+ ARGPARSE_header (NULL, N_("Other options")),
+
+ ARGPARSE_conffile (oOptions, "options", N_("|FILE|read options from FILE")),
+ ARGPARSE_noconffile (oNoOptions, "no-options", "@"),
+ ARGPARSE_s_n (oDryRun, "dry-run", N_("do not make any changes")),
+ ARGPARSE_s_s (oRequestOrigin, "request-origin", "@"),
+ ARGPARSE_s_n (oForceCRLRefresh, "force-crl-refresh", "@"),
+ ARGPARSE_s_n (oEnableIssuerBasedCRLCheck, "enable-issuer-based-crl-check",
+ "@"),
+ ARGPARSE_s_s (oAuditLog, "audit-log",
+ N_("|FILE|write an audit log to FILE")),
+ ARGPARSE_s_s (oHtmlAuditLog, "html-audit-log", "@"),
+ 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 (oCompatibilityFlags, "compatibility-flags", "@"),
+
+ ARGPARSE_header (NULL, ""), /* Stop the header group. */
+
+
+ /* 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_group (302, N_(
+ "@\n(See the man page for a complete listing of all commands and options)\n"
+ )),
+
+ 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 }
+ };
+
+
+/* The list of compatibility flags. */
+static struct compatibility_flags_s compatibility_flags [] =
+ {
+ { COMPAT_ALLOW_KA_TO_ENCR, "allow-ka-to-encr" },
+ { 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|GPGRT_LOG_NO_REGISTRY);
+
+ /* 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 oCompatibilityFlags:
+ if (parse_compatibility_flags (pargs.r.ret_str, &opt.compat_flags,
+ compatibility_flags))
+ {
+ pargs.r_opt = ARGPARSE_INVALID_ARG;
+ pargs.err = ARGPARSE_PRINT_ERROR;
+ }
+ 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 oKeyServer_deprecated:
+ obsolete_option (configname, pargs.lineno, "ldapserver");
+ break;
+
+ case oIgnoreCertExtension:
+ add_to_strlist (&opt.ignored_cert_extensions, pargs.r.ret_str);
+ break;
+
+ case oIgnoreCertWithOID:
+ add_to_strlist (&opt.ignore_cert_with_oid, 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;
+
+ case oMinRSALength: opt.min_rsa_length = pargs.r.ret_ulong; break;
+
+ case oRequireCompliance: opt.require_compliance = 1; 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 ();
+ if (opt.verbose) /* Print the compatibility flags. */
+ parse_compatibility_flags (NULL, &opt.compat_flags, compatibility_flags);
+ gnupg_set_compliance_extra_info (opt.min_rsa_length);
+
+ /* 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. */
+
+ es_printf ("debug-level:%lu:\"none:\n", GC_OPT_FLAG_DEFAULT);
+ es_printf ("include-certs:%lu:%d:\n", GC_OPT_FLAG_DEFAULT,
+ DEFAULT_INCLUDE_CERTS);
+ 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);
+
+ /* 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");
+ }
+ 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 <command> {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)
+ err = gpgsm_decrypt (&ctrl, 0, fp); /* from stdin */
+ else if (argc == 1)
+ err = gpgsm_decrypt (&ctrl, open_read (*argv), fp); /* from file */
+ else
+ wrong_args ("--decrypt [filename]");
+
+#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 <key-Id>");
+ 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);
+}