diff --git a/g10/gpg.c b/g10/gpg.c
index 97975fb23..3a7dc3802 100644
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -1,4502 +1,4501 @@
/* gpg.c - The GnuPG utility (main for gpg)
* Copyright (C) 1998-2011 Free Software Foundation, Inc.
* Copyright (C) 1997-2015 Werner Koch
* Copyright (C) 2015 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 .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef HAVE_STAT
#include /* for stat() */
#endif
#include
#ifdef HAVE_W32_SYSTEM
# ifdef HAVE_WINSOCK2_H
# include
# endif
# include
#endif
#define INCLUDED_BY_MAIN_MODULE 1
#include "gpg.h"
#include
#include "packet.h"
#include "../common/iobuf.h"
#include "util.h"
#include "membuf.h"
#include "main.h"
#include "options.h"
#include "keydb.h"
#include "trustdb.h"
#include "cipher.h"
#include "filter.h"
#include "ttyio.h"
#include "i18n.h"
#include "sysutils.h"
#include "status.h"
#include "keyserver-internal.h"
#include "exec.h"
#include "gc-opt-flags.h"
#if defined(HAVE_DOSISH_SYSTEM) || defined(__CYGWIN__)
#define MY_O_BINARY O_BINARY
#ifndef S_IRGRP
# define S_IRGRP 0
# define S_IWGRP 0
#endif
#else
#define MY_O_BINARY 0
#endif
enum cmd_and_opt_values
{
aNull = 0,
oArmor = 'a',
aDetachedSign = 'b',
aSym = 'c',
aDecrypt = 'd',
aEncr = 'e',
oInteractive = 'i',
aListKeys = 'k',
oDryRun = 'n',
oOutput = 'o',
oQuiet = 'q',
oRecipient = 'r',
oHiddenRecipient = 'R',
aSign = 's',
oTextmodeShort= 't',
oLocalUser = 'u',
oVerbose = 'v',
oCompress = 'z',
oSetNotation = 'N',
aListSecretKeys = 'K',
oBatch = 500,
oMaxOutput,
oSigNotation,
oCertNotation,
oShowNotation,
oNoShowNotation,
aEncrFiles,
aEncrSym,
aDecryptFiles,
aClearsign,
aStore,
aKeygen,
aSignEncr,
aSignEncrSym,
aSignSym,
aSignKey,
aLSignKey,
aListConfig,
aListGcryptConfig,
aGPGConfList,
aGPGConfTest,
aListPackets,
aEditKey,
aDeleteKeys,
aDeleteSecretKeys,
aDeleteSecretAndPublicKeys,
aImport,
aFastImport,
aVerify,
aVerifyFiles,
aListSigs,
aSendKeys,
aRecvKeys,
aLocateKeys,
aSearchKeys,
aRefreshKeys,
aFetchKeys,
aExport,
aExportSecret,
aExportSecretSub,
aCheckKeys,
aGenRevoke,
aDesigRevoke,
aPrimegen,
aPrintMD,
aPrintMDs,
aCheckTrustDB,
aUpdateTrustDB,
aFixTrustDB,
aListTrustDB,
aListTrustPath,
aExportOwnerTrust,
aImportOwnerTrust,
aDeArmor,
aEnArmor,
aGenRandom,
aRebuildKeydbCaches,
aCardStatus,
aCardEdit,
aChangePIN,
aPasswd,
aServer,
oTextmode,
oNoTextmode,
oExpert,
oNoExpert,
oDefSigExpire,
oAskSigExpire,
oNoAskSigExpire,
oDefCertExpire,
oAskCertExpire,
oNoAskCertExpire,
oDefCertLevel,
oMinCertLevel,
oAskCertLevel,
oNoAskCertLevel,
oFingerprint,
oWithFingerprint,
oAnswerYes,
oAnswerNo,
oKeyring,
oPrimaryKeyring,
oSecretKeyring,
oShowKeyring,
oDefaultKey,
oDefRecipient,
oDefRecipientSelf,
oNoDefRecipient,
oOptions,
oDebug,
oDebugLevel,
oDebugAll,
oDebugCCIDDriver,
oStatusFD,
oStatusFile,
oAttributeFD,
oAttributeFile,
oEmitVersion,
oNoEmitVersion,
oCompletesNeeded,
oMarginalsNeeded,
oMaxCertDepth,
oLoadExtension,
oGnuPG,
oRFC1991,
oRFC2440,
oRFC4880,
oOpenPGP,
oPGP2,
oPGP6,
oPGP7,
oPGP8,
oRFC2440Text,
oNoRFC2440Text,
oCipherAlgo,
oDigestAlgo,
oCertDigestAlgo,
oCompressAlgo,
oCompressLevel,
oBZ2CompressLevel,
oBZ2DecompressLowmem,
oPassphrase,
oPassphraseFD,
oPassphraseFile,
oPassphraseRepeat,
oCommandFD,
oCommandFile,
oQuickRandom,
oNoVerbose,
oTrustDBName,
oNoSecmemWarn,
oRequireSecmem,
oNoRequireSecmem,
oNoPermissionWarn,
oNoMDCWarn,
oNoArmor,
oNoDefKeyring,
oNoGreeting,
oNoTTY,
oNoOptions,
oNoBatch,
oHomedir,
oWithColons,
oWithKeyData,
oWithSigList,
oWithSigCheck,
oSkipVerify,
oSkipHiddenRecipients,
oNoSkipHiddenRecipients,
oCompressKeys,
oCompressSigs,
oAlwaysTrust,
oTrustModel,
oForceOwnertrust,
oSetFilename,
oForYourEyesOnly,
oNoForYourEyesOnly,
oSetPolicyURL,
oSigPolicyURL,
oCertPolicyURL,
oShowPolicyURL,
oNoShowPolicyURL,
oSigKeyserverURL,
oUseEmbeddedFilename,
oNoUseEmbeddedFilename,
oComment,
oDefaultComment,
oNoComments,
oThrowKeyids,
oNoThrowKeyids,
oShowPhotos,
oNoShowPhotos,
oPhotoViewer,
oForceV3Sigs,
oNoForceV3Sigs,
oForceV4Certs,
oNoForceV4Certs,
oForceMDC,
oNoForceMDC,
oDisableMDC,
oNoDisableMDC,
oS2KMode,
oS2KDigest,
oS2KCipher,
oS2KCount,
oSimpleSKChecksum,
oDisplayCharset,
oNotDashEscaped,
oEscapeFrom,
oNoEscapeFrom,
oLockOnce,
oLockMultiple,
oLockNever,
oKeyServer,
oKeyServerOptions,
oImportOptions,
oExportOptions,
oListOptions,
oVerifyOptions,
oTempDir,
oExecPath,
oEncryptTo,
oHiddenEncryptTo,
oNoEncryptTo,
oLoggerFD,
oLoggerFile,
oUtf8Strings,
oNoUtf8Strings,
oDisableCipherAlgo,
oDisablePubkeyAlgo,
oAllowNonSelfsignedUID,
oNoAllowNonSelfsignedUID,
oAllowFreeformUID,
oNoAllowFreeformUID,
oAllowSecretKeyImport,
oEnableSpecialFilenames,
oNoLiteral,
oSetFilesize,
oHonorHttpProxy,
oFastListMode,
oListOnly,
oIgnoreTimeConflict,
oIgnoreValidFrom,
oIgnoreCrcError,
oIgnoreMDCError,
oShowSessionKey,
oOverrideSessionKey,
oNoRandomSeedFile,
oAutoKeyRetrieve,
oNoAutoKeyRetrieve,
oUseAgent,
oNoUseAgent,
oGpgAgentInfo,
oMergeOnly,
oTryAllSecrets,
oTrustedKey,
oNoExpensiveTrustChecks,
oFixedListMode,
oNoSigCache,
oAutoCheckTrustDB,
oNoAutoCheckTrustDB,
oPreservePermissions,
oDefaultPreferenceList,
oDefaultKeyserverURL,
oPersonalCipherPreferences,
oPersonalDigestPreferences,
oPersonalCompressPreferences,
oAgentProgram,
oDisplay,
oTTYname,
oTTYtype,
oLCctype,
oLCmessages,
oXauthority,
oGroup,
oUnGroup,
oNoGroups,
oStrict,
oNoStrict,
oMangleDosFilenames,
oNoMangleDosFilenames,
oEnableProgressFilter,
oMultifile,
oKeyidFormat,
oExitOnStatusWriteError,
oLimitCardInsertTries,
oReaderPort,
octapiDriver,
opcscDriver,
oDisableCCID,
oRequireCrossCert,
oNoRequireCrossCert,
oAutoKeyLocate,
oNoAutoKeyLocate,
oAllowMultisigVerification,
oEnableLargeRSA,
oDisableLargeRSA,
oEnableDSA2,
oDisableDSA2,
oAllowMultipleMessages,
oNoAllowMultipleMessages,
oAllowWeakDigestAlgos,
oNoop
};
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 (aEncrFiles, "encrypt-files", "@"),
ARGPARSE_c (aSym, "symmetric", N_("encryption only with symmetric cipher")),
ARGPARSE_c (aStore, "store", "@"),
ARGPARSE_c (aDecrypt, "decrypt", N_("decrypt data (default)")),
ARGPARSE_c (aDecryptFiles, "decrypt-files", "@"),
ARGPARSE_c (aVerify, "verify" , N_("verify a signature")),
ARGPARSE_c (aVerifyFiles, "verify-files" , "@" ),
ARGPARSE_c (aListKeys, "list-keys", N_("list keys")),
ARGPARSE_c (aListKeys, "list-public-keys", "@" ),
ARGPARSE_c (aListSigs, "list-sigs", N_("list keys and signatures")),
ARGPARSE_c (aCheckKeys, "check-sigs",N_("list and check key signatures")),
ARGPARSE_c (oFingerprint, "fingerprint", N_("list keys and fingerprints")),
ARGPARSE_c (aListSecretKeys, "list-secret-keys", N_("list secret keys")),
ARGPARSE_c (aKeygen, "gen-key", N_("generate a new key pair")),
ARGPARSE_c (aGenRevoke, "gen-revoke",N_("generate a revocation certificate")),
ARGPARSE_c (aDeleteKeys,"delete-keys",
N_("remove keys from the public keyring")),
ARGPARSE_c (aDeleteSecretKeys, "delete-secret-keys",
N_("remove keys from the secret keyring")),
ARGPARSE_c (aSignKey, "sign-key" ,N_("sign a key")),
ARGPARSE_c (aLSignKey, "lsign-key" ,N_("sign a key locally")),
ARGPARSE_c (aEditKey, "edit-key" ,N_("sign or edit a key")),
ARGPARSE_c (aEditKey, "key-edit" ,"@"),
ARGPARSE_c (aPasswd, "passwd", N_("change a passphrase")),
ARGPARSE_c (aDesigRevoke, "desig-revoke","@" ),
ARGPARSE_c (aExport, "export" , N_("export keys") ),
ARGPARSE_c (aSendKeys, "send-keys" , N_("export keys to a key server") ),
ARGPARSE_c (aRecvKeys, "recv-keys" , N_("import keys from a key server") ),
ARGPARSE_c (aSearchKeys, "search-keys" ,
N_("search for keys on a key server") ),
ARGPARSE_c (aRefreshKeys, "refresh-keys",
N_("update all keys from a keyserver")),
ARGPARSE_c (aLocateKeys, "locate-keys", "@"),
ARGPARSE_c (aFetchKeys, "fetch-keys" , "@" ),
ARGPARSE_c (aExportSecret, "export-secret-keys" , "@" ),
ARGPARSE_c (aExportSecretSub, "export-secret-subkeys" , "@" ),
ARGPARSE_c (aImport, "import", N_("import/merge keys")),
ARGPARSE_c (aFastImport, "fast-import", "@"),
#ifdef ENABLE_CARD_SUPPORT
ARGPARSE_c (aCardStatus, "card-status", N_("print the card status")),
ARGPARSE_c (aCardEdit, "card-edit", N_("change data on a card")),
ARGPARSE_c (aChangePIN, "change-pin", N_("change a card's PIN")),
#endif
ARGPARSE_c (aListConfig, "list-config", "@"),
ARGPARSE_c (aListGcryptConfig, "list-gcrypt-config", "@"),
ARGPARSE_c (aGPGConfList, "gpgconf-list", "@" ),
ARGPARSE_c (aGPGConfTest, "gpgconf-test", "@" ),
ARGPARSE_c (aListPackets, "list-packets","@"),
ARGPARSE_c (aExportOwnerTrust, "export-ownertrust", "@"),
ARGPARSE_c (aImportOwnerTrust, "import-ownertrust", "@"),
ARGPARSE_c (aUpdateTrustDB,"update-trustdb",
N_("update the trust database")),
ARGPARSE_c (aCheckTrustDB, "check-trustdb", "@"),
ARGPARSE_c (aFixTrustDB, "fix-trustdb", "@"),
ARGPARSE_c (aDeArmor, "dearmor", "@"),
ARGPARSE_c (aDeArmor, "dearmour", "@"),
ARGPARSE_c (aEnArmor, "enarmor", "@"),
ARGPARSE_c (aEnArmor, "enarmour", "@"),
ARGPARSE_c (aPrintMD, "print-md", N_("print message digests")),
ARGPARSE_c (aPrimegen, "gen-prime", "@" ),
ARGPARSE_c (aGenRandom,"gen-random", "@" ),
ARGPARSE_c (aServer, "server", N_("run in server mode")),
ARGPARSE_group (301, N_("@\nOptions:\n ")),
ARGPARSE_s_n (oArmor, "armor", N_("create ascii armored output")),
ARGPARSE_s_n (oArmor, "armour", "@"),
ARGPARSE_s_s (oRecipient, "recipient", N_("|USER-ID|encrypt for USER-ID")),
ARGPARSE_s_s (oHiddenRecipient, "hidden-recipient", "@"),
ARGPARSE_s_s (oRecipient, "remote-user", "@"), /* (old option name) */
ARGPARSE_s_s (oDefRecipient, "default-recipient", "@"),
ARGPARSE_s_n (oDefRecipientSelf, "default-recipient-self", "@"),
ARGPARSE_s_n (oNoDefRecipient, "no-default-recipient", "@"),
ARGPARSE_s_s (oTempDir, "temp-directory", "@"),
ARGPARSE_s_s (oExecPath, "exec-path", "@"),
ARGPARSE_s_s (oEncryptTo, "encrypt-to", "@"),
ARGPARSE_s_n (oNoEncryptTo, "no-encrypt-to", "@"),
ARGPARSE_s_s (oHiddenEncryptTo, "hidden-encrypt-to", "@"),
ARGPARSE_s_s (oLocalUser, "local-user",
N_("|USER-ID|use USER-ID to sign or decrypt")),
ARGPARSE_s_i (oCompress, NULL,
N_("|N|set compress level to N (0 disables)")),
ARGPARSE_s_i (oCompressLevel, "compress-level", "@"),
ARGPARSE_s_i (oBZ2CompressLevel, "bzip2-compress-level", "@"),
ARGPARSE_s_n (oBZ2DecompressLowmem, "bzip2-decompress-lowmem", "@"),
ARGPARSE_s_n (oTextmodeShort, NULL, "@"),
ARGPARSE_s_n (oTextmode, "textmode", N_("use canonical text mode")),
ARGPARSE_s_n (oNoTextmode, "no-textmode", "@"),
ARGPARSE_s_n (oExpert, "expert", "@"),
ARGPARSE_s_n (oNoExpert, "no-expert", "@"),
ARGPARSE_s_s (oDefSigExpire, "default-sig-expire", "@"),
ARGPARSE_s_n (oAskSigExpire, "ask-sig-expire", "@"),
ARGPARSE_s_n (oNoAskSigExpire, "no-ask-sig-expire", "@"),
ARGPARSE_s_s (oDefCertExpire, "default-cert-expire", "@"),
ARGPARSE_s_n (oAskCertExpire, "ask-cert-expire", "@"),
ARGPARSE_s_n (oNoAskCertExpire, "no-ask-cert-expire", "@"),
ARGPARSE_s_i (oDefCertLevel, "default-cert-level", "@"),
ARGPARSE_s_i (oMinCertLevel, "min-cert-level", "@"),
ARGPARSE_s_n (oAskCertLevel, "ask-cert-level", "@"),
ARGPARSE_s_n (oNoAskCertLevel, "no-ask-cert-level", "@"),
ARGPARSE_s_s (oOutput, "output", N_("|FILE|write output to FILE")),
ARGPARSE_p_u (oMaxOutput, "max-output", "@"),
ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
ARGPARSE_s_n (oQuiet, "quiet", "@"),
ARGPARSE_s_n (oNoTTY, "no-tty", "@"),
ARGPARSE_s_n (oForceV3Sigs, "force-v3-sigs", "@"),
ARGPARSE_s_n (oNoForceV3Sigs, "no-force-v3-sigs", "@"),
ARGPARSE_s_n (oForceV4Certs, "force-v4-certs", "@"),
ARGPARSE_s_n (oNoForceV4Certs, "no-force-v4-certs", "@"),
ARGPARSE_s_n (oForceMDC, "force-mdc", "@"),
ARGPARSE_s_n (oNoForceMDC, "no-force-mdc", "@"),
ARGPARSE_s_n (oDisableMDC, "disable-mdc", "@"),
ARGPARSE_s_n (oNoDisableMDC, "no-disable-mdc", "@"),
ARGPARSE_s_n (oDryRun, "dry-run", N_("do not make any changes")),
ARGPARSE_s_n (oInteractive, "interactive", N_("prompt before overwriting")),
ARGPARSE_s_n (oBatch, "batch", "@"),
ARGPARSE_s_n (oAnswerYes, "yes", "@"),
ARGPARSE_s_n (oAnswerNo, "no", "@"),
ARGPARSE_s_s (oKeyring, "keyring", "@"),
ARGPARSE_s_s (oPrimaryKeyring, "primary-keyring", "@"),
ARGPARSE_s_s (oSecretKeyring, "secret-keyring", "@"),
ARGPARSE_s_n (oShowKeyring, "show-keyring", "@"),
ARGPARSE_s_s (oDefaultKey, "default-key", "@"),
ARGPARSE_s_s (oKeyServer, "keyserver", "@"),
ARGPARSE_s_s (oKeyServerOptions, "keyserver-options", "@"),
ARGPARSE_s_s (oImportOptions, "import-options", "@"),
ARGPARSE_s_s (oExportOptions, "export-options", "@"),
ARGPARSE_s_s (oListOptions, "list-options", "@"),
ARGPARSE_s_s (oVerifyOptions, "verify-options", "@"),
ARGPARSE_s_s (oDisplayCharset, "display-charset", "@"),
ARGPARSE_s_s (oDisplayCharset, "charset", "@"),
ARGPARSE_s_s (oOptions, "options", "@"),
ARGPARSE_p_u (oDebug, "debug", "@"),
ARGPARSE_s_s (oDebugLevel, "debug-level", "@"),
ARGPARSE_s_n (oDebugAll, "debug-all", "@"),
ARGPARSE_s_i (oStatusFD, "status-fd", "@"),
ARGPARSE_s_s (oStatusFile, "status-file", "@"),
ARGPARSE_s_i (oAttributeFD, "attribute-fd", "@"),
ARGPARSE_s_s (oAttributeFile, "attribute-file", "@"),
ARGPARSE_s_i (oCompletesNeeded, "completes-needed", "@"),
ARGPARSE_s_i (oMarginalsNeeded, "marginals-needed", "@"),
ARGPARSE_s_i (oMaxCertDepth, "max-cert-depth", "@" ),
ARGPARSE_s_s (oTrustedKey, "trusted-key", "@"),
ARGPARSE_s_s (oLoadExtension, "load-extension", "@"), /* Dummy. */
ARGPARSE_s_n (oGnuPG, "gnupg", "@"),
ARGPARSE_s_n (oGnuPG, "no-pgp2", "@"),
ARGPARSE_s_n (oGnuPG, "no-pgp6", "@"),
ARGPARSE_s_n (oGnuPG, "no-pgp7", "@"),
ARGPARSE_s_n (oGnuPG, "no-pgp8", "@"),
ARGPARSE_s_n (oRFC1991, "rfc1991", "@"),
ARGPARSE_s_n (oRFC2440, "rfc2440", "@"),
ARGPARSE_s_n (oRFC4880, "rfc4880", "@"),
ARGPARSE_s_n (oOpenPGP, "openpgp", N_("use strict OpenPGP behavior")),
ARGPARSE_s_n (oPGP2, "pgp2", "@"),
ARGPARSE_s_n (oPGP6, "pgp6", "@"),
ARGPARSE_s_n (oPGP7, "pgp7", "@"),
ARGPARSE_s_n (oPGP8, "pgp8", "@"),
ARGPARSE_s_n (oRFC2440Text, "rfc2440-text", "@"),
ARGPARSE_s_n (oNoRFC2440Text, "no-rfc2440-text", "@"),
ARGPARSE_s_i (oS2KMode, "s2k-mode", "@"),
ARGPARSE_s_s (oS2KDigest, "s2k-digest-algo", "@"),
ARGPARSE_s_s (oS2KCipher, "s2k-cipher-algo", "@"),
ARGPARSE_s_i (oS2KCount, "s2k-count", "@"),
ARGPARSE_s_n (oSimpleSKChecksum, "simple-sk-checksum", "@"),
ARGPARSE_s_s (oCipherAlgo, "cipher-algo", "@"),
ARGPARSE_s_s (oDigestAlgo, "digest-algo", "@"),
ARGPARSE_s_s (oCertDigestAlgo, "cert-digest-algo", "@"),
ARGPARSE_s_s (oCompressAlgo,"compress-algo", "@"),
ARGPARSE_s_s (oCompressAlgo, "compression-algo", "@"), /* Alias */
ARGPARSE_s_n (oThrowKeyids, "throw-keyid", "@"),
ARGPARSE_s_n (oThrowKeyids, "throw-keyids", "@"),
ARGPARSE_s_n (oNoThrowKeyids, "no-throw-keyid", "@"),
ARGPARSE_s_n (oNoThrowKeyids, "no-throw-keyids", "@"),
ARGPARSE_s_n (oShowPhotos, "show-photos", "@"),
ARGPARSE_s_n (oNoShowPhotos, "no-show-photos", "@"),
ARGPARSE_s_s (oPhotoViewer, "photo-viewer", "@"),
ARGPARSE_s_s (oSetNotation, "set-notation", "@"),
ARGPARSE_s_s (oSetNotation, "notation-data", "@"), /* Alias */
ARGPARSE_s_s (oSigNotation, "sig-notation", "@"),
ARGPARSE_s_s (oCertNotation, "cert-notation", "@"),
ARGPARSE_group (302, N_(
"@\n(See the man page for a complete listing of all commands and options)\n"
)),
ARGPARSE_group (303, N_("@\nExamples:\n\n"
" -se -r Bob [file] sign and encrypt for user Bob\n"
" --clearsign [file] make a clear text signature\n"
" --detach-sign [file] make a detached signature\n"
" --list-keys [names] show keys\n"
" --fingerprint [names] show fingerprints\n")),
/* More hidden commands and options. */
ARGPARSE_c (aPrintMDs, "print-mds", "@"), /* old */
ARGPARSE_c (aListTrustDB, "list-trustdb", "@"),
/* Not yet used:
ARGPARSE_c (aListTrustPath, "list-trust-path", "@"), */
ARGPARSE_c (aDeleteSecretAndPublicKeys,
"delete-secret-and-public-keys", "@"),
ARGPARSE_c (aRebuildKeydbCaches, "rebuild-keydb-caches", "@"),
ARGPARSE_s_s (oPassphrase, "passphrase", "@"),
ARGPARSE_s_i (oPassphraseFD, "passphrase-fd", "@"),
ARGPARSE_s_s (oPassphraseFile, "passphrase-file", "@"),
ARGPARSE_s_i (oPassphraseRepeat,"passphrase-repeat", "@"),
ARGPARSE_s_i (oCommandFD, "command-fd", "@"),
ARGPARSE_s_s (oCommandFile, "command-file", "@"),
ARGPARSE_s_n (oQuickRandom, "debug-quick-random", "@"),
ARGPARSE_s_n (oNoVerbose, "no-verbose", "@"),
ARGPARSE_s_s (oTrustDBName, "trustdb-name", "@"),
ARGPARSE_s_n (oNoSecmemWarn, "no-secmem-warning", "@"),
ARGPARSE_s_n (oRequireSecmem, "require-secmem", "@"),
ARGPARSE_s_n (oNoRequireSecmem, "no-require-secmem", "@"),
ARGPARSE_s_n (oNoPermissionWarn, "no-permission-warning", "@"),
ARGPARSE_s_n (oNoMDCWarn, "no-mdc-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_s_n (oNoOptions, "no-options", "@"),
ARGPARSE_s_s (oHomedir, "homedir", "@"),
ARGPARSE_s_n (oNoBatch, "no-batch", "@"),
ARGPARSE_s_n (oWithColons, "with-colons", "@"),
ARGPARSE_s_n (oWithKeyData,"with-key-data", "@"),
ARGPARSE_s_n (oWithSigList,"with-sig-list", "@"),
ARGPARSE_s_n (oWithSigCheck,"with-sig-check", "@"),
ARGPARSE_s_n (aListKeys, "list-key", "@"), /* alias */
ARGPARSE_s_n (aListSigs, "list-sig", "@"), /* alias */
ARGPARSE_s_n (aCheckKeys, "check-sig", "@"), /* alias */
ARGPARSE_s_n (oSkipVerify, "skip-verify", "@"),
ARGPARSE_s_n (oSkipHiddenRecipients, "skip-hidden-recipients", "@"),
ARGPARSE_s_n (oNoSkipHiddenRecipients, "no-skip-hidden-recipients", "@"),
ARGPARSE_s_n (oCompressKeys, "compress-keys", "@"),
ARGPARSE_s_n (oCompressSigs, "compress-sigs", "@"),
ARGPARSE_s_i (oDefCertLevel, "default-cert-check-level", "@"), /* old */
ARGPARSE_s_n (oAlwaysTrust, "always-trust", "@"),
ARGPARSE_s_s (oTrustModel, "trust-model", "@"),
ARGPARSE_s_s (oForceOwnertrust, "force-ownertrust", "@"),
ARGPARSE_s_s (oSetFilename, "set-filename", "@"),
ARGPARSE_s_n (oForYourEyesOnly, "for-your-eyes-only", "@"),
ARGPARSE_s_n (oNoForYourEyesOnly, "no-for-your-eyes-only", "@"),
ARGPARSE_s_s (oSetPolicyURL, "set-policy-url", "@"),
ARGPARSE_s_s (oSigPolicyURL, "sig-policy-url", "@"),
ARGPARSE_s_s (oCertPolicyURL, "cert-policy-url", "@"),
ARGPARSE_s_n (oShowPolicyURL, "show-policy-url", "@"),
ARGPARSE_s_n (oNoShowPolicyURL, "no-show-policy-url", "@"),
ARGPARSE_s_s (oSigKeyserverURL, "sig-keyserver-url", "@"),
ARGPARSE_s_n (oShowNotation, "show-notation", "@"),
ARGPARSE_s_n (oNoShowNotation, "no-show-notation", "@"),
ARGPARSE_s_s (oComment, "comment", "@"),
ARGPARSE_s_n (oDefaultComment, "default-comment", "@"),
ARGPARSE_s_n (oNoComments, "no-comments", "@"),
ARGPARSE_s_n (oEmitVersion, "emit-version", "@"),
ARGPARSE_s_n (oNoEmitVersion, "no-emit-version", "@"),
ARGPARSE_s_n (oNoEmitVersion, "no-version", "@"), /* alias */
ARGPARSE_s_n (oNotDashEscaped, "not-dash-escaped", "@"),
ARGPARSE_s_n (oEscapeFrom, "escape-from-lines", "@"),
ARGPARSE_s_n (oNoEscapeFrom, "no-escape-from-lines", "@"),
ARGPARSE_s_n (oLockOnce, "lock-once", "@"),
ARGPARSE_s_n (oLockMultiple, "lock-multiple", "@"),
ARGPARSE_s_n (oLockNever, "lock-never", "@"),
ARGPARSE_s_i (oLoggerFD, "logger-fd", "@"),
ARGPARSE_s_s (oLoggerFile, "log-file", "@"),
ARGPARSE_s_s (oLoggerFile, "logger-file", "@"), /* 1.4 compatibility. */
ARGPARSE_s_n (oUseEmbeddedFilename, "use-embedded-filename", "@"),
ARGPARSE_s_n (oNoUseEmbeddedFilename, "no-use-embedded-filename", "@"),
ARGPARSE_s_n (oUtf8Strings, "utf8-strings", "@"),
ARGPARSE_s_n (oNoUtf8Strings, "no-utf8-strings", "@"),
ARGPARSE_s_n (oWithFingerprint, "with-fingerprint", "@"),
ARGPARSE_s_s (oDisableCipherAlgo, "disable-cipher-algo", "@"),
ARGPARSE_s_s (oDisablePubkeyAlgo, "disable-pubkey-algo", "@"),
ARGPARSE_s_n (oAllowNonSelfsignedUID, "allow-non-selfsigned-uid", "@"),
ARGPARSE_s_n (oNoAllowNonSelfsignedUID, "no-allow-non-selfsigned-uid", "@"),
ARGPARSE_s_n (oAllowFreeformUID, "allow-freeform-uid", "@"),
ARGPARSE_s_n (oNoAllowFreeformUID, "no-allow-freeform-uid", "@"),
ARGPARSE_s_n (oNoLiteral, "no-literal", "@"),
ARGPARSE_p_u (oSetFilesize, "set-filesize", "@"),
ARGPARSE_s_n (oHonorHttpProxy, "honor-http-proxy", "@"),
ARGPARSE_s_n (oFastListMode, "fast-list-mode", "@"),
ARGPARSE_s_n (oFixedListMode, "fixed-list-mode", "@"),
ARGPARSE_s_n (oListOnly, "list-only", "@"),
ARGPARSE_s_n (oIgnoreTimeConflict, "ignore-time-conflict", "@"),
ARGPARSE_s_n (oIgnoreValidFrom, "ignore-valid-from", "@"),
ARGPARSE_s_n (oIgnoreCrcError, "ignore-crc-error", "@"),
ARGPARSE_s_n (oIgnoreMDCError, "ignore-mdc-error", "@"),
ARGPARSE_s_n (oShowSessionKey, "show-session-key", "@"),
ARGPARSE_s_s (oOverrideSessionKey, "override-session-key", "@"),
ARGPARSE_s_n (oNoRandomSeedFile, "no-random-seed-file", "@"),
ARGPARSE_s_n (oAutoKeyRetrieve, "auto-key-retrieve", "@"),
ARGPARSE_s_n (oNoAutoKeyRetrieve, "no-auto-key-retrieve", "@"),
ARGPARSE_s_n (oNoSigCache, "no-sig-cache", "@"),
ARGPARSE_s_n (oAutoCheckTrustDB, "auto-check-trustdb", "@"),
ARGPARSE_s_n (oNoAutoCheckTrustDB, "no-auto-check-trustdb", "@"),
ARGPARSE_s_n (oMergeOnly, "merge-only", "@" ),
ARGPARSE_s_n (oAllowSecretKeyImport, "allow-secret-key-import", "@"),
ARGPARSE_s_n (oTryAllSecrets, "try-all-secrets", "@"),
ARGPARSE_s_n (oEnableSpecialFilenames, "enable-special-filenames", "@"),
ARGPARSE_s_n (oNoExpensiveTrustChecks, "no-expensive-trust-checks", "@"),
ARGPARSE_s_n (oPreservePermissions, "preserve-permissions", "@"),
ARGPARSE_s_s (oDefaultPreferenceList, "default-preference-list", "@"),
ARGPARSE_s_s (oDefaultKeyserverURL, "default-keyserver-url", "@"),
ARGPARSE_s_s (oPersonalCipherPreferences, "personal-cipher-preferences","@"),
ARGPARSE_s_s (oPersonalDigestPreferences, "personal-digest-preferences","@"),
ARGPARSE_s_s (oPersonalCompressPreferences,
"personal-compress-preferences", "@"),
/* Aliases. I constantly mistype these, and assume other people do
as well. */
ARGPARSE_s_s (oPersonalCipherPreferences, "personal-cipher-prefs", "@"),
ARGPARSE_s_s (oPersonalDigestPreferences, "personal-digest-prefs", "@"),
ARGPARSE_s_s (oPersonalCompressPreferences, "personal-compress-prefs", "@"),
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 (oGroup, "group", "@"),
ARGPARSE_s_s (oUnGroup, "ungroup", "@"),
ARGPARSE_s_n (oNoGroups, "no-groups", "@"),
ARGPARSE_s_n (oStrict, "strict", "@"),
ARGPARSE_s_n (oNoStrict, "no-strict", "@"),
ARGPARSE_s_n (oMangleDosFilenames, "mangle-dos-filenames", "@"),
ARGPARSE_s_n (oNoMangleDosFilenames, "no-mangle-dos-filenames", "@"),
ARGPARSE_s_n (oEnableProgressFilter, "enable-progress-filter", "@"),
ARGPARSE_s_n (oMultifile, "multifile", "@"),
ARGPARSE_s_s (oKeyidFormat, "keyid-format", "@"),
ARGPARSE_s_n (oExitOnStatusWriteError, "exit-on-status-write-error", "@"),
ARGPARSE_s_i (oLimitCardInsertTries, "limit-card-insert-tries", "@"),
ARGPARSE_s_n (oAllowMultisigVerification,
"allow-multisig-verification", "@"),
ARGPARSE_s_n (oEnableLargeRSA, "enable-large-rsa", "@"),
ARGPARSE_s_n (oDisableLargeRSA, "disable-large-rsa", "@"),
ARGPARSE_s_n (oEnableDSA2, "enable-dsa2", "@"),
ARGPARSE_s_n (oDisableDSA2, "disable-dsa2", "@"),
ARGPARSE_s_n (oAllowMultipleMessages, "allow-multiple-messages", "@"),
ARGPARSE_s_n (oNoAllowMultipleMessages, "no-allow-multiple-messages", "@"),
ARGPARSE_s_n (oAllowWeakDigestAlgos, "allow-weak-digest-algos", "@"),
/* These two are aliases to help users of the PGP command line
product use gpg with minimal pain. Many commands are common
already as they seem to have borrowed commands from us. Now I'm
returning the favor. */
ARGPARSE_s_s (oLocalUser, "sign-with", "@"),
ARGPARSE_s_s (oRecipient, "user", "@"),
ARGPARSE_s_n (oRequireCrossCert, "require-backsigs", "@"),
ARGPARSE_s_n (oRequireCrossCert, "require-cross-certification", "@"),
ARGPARSE_s_n (oNoRequireCrossCert, "no-require-backsigs", "@"),
ARGPARSE_s_n (oNoRequireCrossCert, "no-require-cross-certification", "@"),
/* New options. Fixme: Should go more to the top. */
ARGPARSE_s_s (oAutoKeyLocate, "auto-key-locate", "@"),
ARGPARSE_s_n (oNoAutoKeyLocate, "no-auto-key-locate", "@"),
/* Dummy options with warnings. */
ARGPARSE_s_n (oUseAgent, "use-agent", "@"),
ARGPARSE_s_n (oNoUseAgent, "no-use-agent", "@"),
ARGPARSE_s_s (oGpgAgentInfo, "gpg-agent-info", "@"),
ARGPARSE_s_s (oReaderPort, "reader-port", "@"),
ARGPARSE_s_s (octapiDriver, "ctapi-driver", "@"),
ARGPARSE_s_s (opcscDriver, "pcsc-driver", "@"),
ARGPARSE_s_n (oDisableCCID, "disable-ccid", "@"),
/* Dummy options. */
ARGPARSE_s_n (oNoop, "sk-comments", "@"),
ARGPARSE_s_n (oNoop, "no-sk-comments", "@"),
ARGPARSE_s_n (oNoop, "no-sig-create-check", "@"),
ARGPARSE_end ()
};
#ifdef ENABLE_SELINUX_HACKS
#define ALWAYS_ADD_KEYRINGS 1
#else
#define ALWAYS_ADD_KEYRINGS 0
#endif
int g10_errors_seen = 0;
static int utf8_strings = 0;
static int maybe_setuid = 1;
static char *build_list( const char *text, char letter,
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 print_mds( const char *fname, int algo );
static void add_notation_data( const char *string, int which );
static void add_policy_url( const char *string, int which );
static void add_keyserver_url( const char *string, int which );
static void emergency_cleanup (void);
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, *zips, *ver_gcry;
const char *p;
switch( level ) {
case 11: p = "gpg (GnuPG)";
break;
case 13: p = VERSION; break;
case 17: p = PRINTABLE_OS_NAME; break;
case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
case 20:
if (!ver_gcry)
ver_gcry = make_libversion ("libgcrypt", gcry_check_version);
p = ver_gcry;
break;
#ifdef IS_DEVELOPMENT_VERSION
case 25:
p="NOTE: THIS IS A DEVELOPMENT VERSION!";
break;
case 26:
p="It is only intended for test purposes and should NOT be";
break;
case 27:
p="used in a production environment or with production keys!";
break;
#endif
case 1:
case 40: p =
_("Usage: gpg [options] [files] (-h for help)");
break;
case 41: p =
_("Syntax: gpg [options] [files]\n"
"Sign, check, encrypt or decrypt\n"
"Default operation depends on the input data\n");
break;
case 31: p = "\nHome: "; break;
#ifndef __riscos__
case 32: p = opt.homedir; break;
#else /* __riscos__ */
case 32: p = make_filename(opt.homedir, NULL); break;
#endif /* __riscos__ */
case 33: p = _("\nSupported algorithms:\n"); break;
case 34:
if (!pubkeys)
pubkeys = build_list (_("Pubkey: "), 'P',
openpgp_pk_algo_name,
openpgp_pk_test_algo );
p = pubkeys;
break;
case 35:
if( !ciphers )
ciphers = build_list(_("Cipher: "), 'S',
openpgp_cipher_algo_name,
openpgp_cipher_test_algo );
p = ciphers;
break;
case 36:
if( !digests )
digests = build_list(_("Hash: "), 'H',
gcry_md_algo_name,
openpgp_md_test_algo );
p = digests;
break;
case 37:
if( !zips )
zips = build_list(_("Compression: "),'Z',
compress_algo_to_string,
check_compress_algo);
p = zips;
break;
default: p = NULL;
}
return p;
}
static char *
build_list (const char *text, char letter,
const char * (*mapf)(int), int (*chkf)(int))
{
membuf_t mb;
int indent;
int i, j, len;
const char *s;
char *string;
if (maybe_setuid)
gcry_control (GCRYCTL_INIT_SECMEM, 0, 0); /* Drop setuid. */
indent = utf8_charcount (text);
len = 0;
init_membuf (&mb, 512);
for (i=0; i <= 110; i++ )
{
if (letter == 'P' && i == 19 )
continue; /* No need to print a second "ECC" string. */
if (!chkf (i) && (s = mapf (i)))
{
if (mb.len - len > 60)
{
put_membuf_str (&mb, ",\n");
len = mb.len;
for (j=0; j < indent; j++)
put_membuf_str (&mb, " ");
}
else if (mb.len)
put_membuf_str (&mb, ", ");
else
put_membuf_str (&mb, text);
put_membuf_str (&mb, s);
if (opt.verbose && letter && letter != 'P')
{
char num[20];
snprintf (num, sizeof num, " (%c%d)", letter, i);
put_membuf_str (&mb, num);
}
}
}
if (mb.len)
put_membuf_str (&mb, "\n");
put_membuf (&mb, "", 1);
string = get_membuf (&mb, NULL);
return xrealloc (string, strlen (string)+1);
}
static void
wrong_args( const char *text)
{
fputs(_("usage: gpg [options] "),stderr);
fputs(text,stderr);
putc('\n',stderr);
g10_exit(2);
}
static char *
make_username( const char *string )
{
char *p;
if( utf8_strings )
p = xstrdup(string);
else
p = native_to_utf8( string );
return p;
}
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 LEVEL of NULL only the active debug
flags are propagated to the subsystems. With LEVEL set, a specific
set of debug flags is set; thus overriding all flags already
set. */
static void
set_debug (const char *level)
{
int numok = (level && digitp (level));
int numlvl = numok? atoi (level) : 0;
if (!level)
;
else if (!strcmp (level, "none") || (numok && numlvl < 1))
opt.debug = 0;
else if (!strcmp (level, "basic") || (numok && numlvl <= 2))
opt.debug = DBG_MEMSTAT_VALUE;
else if (!strcmp (level, "advanced") || (numok && numlvl <= 5))
opt.debug = DBG_MEMSTAT_VALUE|DBG_TRUST_VALUE|DBG_EXTPROG_VALUE;
else if (!strcmp (level, "expert") || (numok && numlvl <= 8))
opt.debug = (DBG_MEMSTAT_VALUE|DBG_TRUST_VALUE|DBG_EXTPROG_VALUE
|DBG_CACHE_VALUE|DBG_FILTER_VALUE|DBG_PACKET_VALUE);
else if (!strcmp (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"), level);
g10_exit (2);
}
if (opt.debug & DBG_MEMORY_VALUE )
memory_debug_mode = 1;
if (opt.debug & DBG_MEMSTAT_VALUE )
memory_stat_debug_mode = 1;
if (opt.debug & DBG_MPI_VALUE)
gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 2);
if (opt.debug & DBG_CIPHER_VALUE )
gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1);
if (opt.debug & DBG_IOBUF_VALUE )
iobuf_debug_mode = 1;
gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
if (opt.debug)
log_info ("enabled debug flags:%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
(opt.debug & DBG_PACKET_VALUE )? " packet":"",
(opt.debug & DBG_MPI_VALUE )? " mpi":"",
(opt.debug & DBG_CIPHER_VALUE )? " cipher":"",
(opt.debug & DBG_FILTER_VALUE )? " filter":"",
(opt.debug & DBG_IOBUF_VALUE )? " iobuf":"",
(opt.debug & DBG_MEMORY_VALUE )? " memory":"",
(opt.debug & DBG_CACHE_VALUE )? " cache":"",
(opt.debug & DBG_MEMSTAT_VALUE)? " memstat":"",
(opt.debug & DBG_TRUST_VALUE )? " trust":"",
(opt.debug & DBG_HASHING_VALUE)? " hashing":"",
(opt.debug & DBG_EXTPROG_VALUE)? " extprog":"",
(opt.debug & DBG_CARD_IO_VALUE)? " cardio":"",
(opt.debug & DBG_ASSUAN_VALUE )? " assuan":"");
}
/* We need the home directory also in some other directories, so make
sure that both variables are always in sync. */
static void
set_homedir (const char *dir)
{
if (!dir)
dir = "";
opt.homedir = dir;
}
/* We set the screen dimensions for UI purposes. Do not allow screens
smaller than 80x24 for the sake of simplicity. */
static void
set_screen_dimensions(void)
{
#ifndef HAVE_W32_SYSTEM
char *str;
str=getenv("COLUMNS");
if(str)
opt.screen_columns=atoi(str);
str=getenv("LINES");
if(str)
opt.screen_lines=atoi(str);
#endif
if(opt.screen_columns<80 || opt.screen_columns>255)
opt.screen_columns=80;
if(opt.screen_lines<24 || opt.screen_lines>255)
opt.screen_lines=24;
}
/* Helper to open a file FNAME either for reading or writing to be
used with --status-file etc functions. Not generally useful but it
avoids the riscos specific functions and well some Windows people
might like it too. Prints an error message and returns -1 on
error. On success the file descriptor is returned. */
static int
open_info_file (const char *fname, int for_write, int binary)
{
#ifdef __riscos__
return riscos_fdopenfile (fname, for_write);
#elif defined (ENABLE_SELINUX_HACKS)
/* We can't allow these even when testing for a secured filename
because files to be secured might not yet been secured. This is
similar to the option file but in that case it is unlikely that
sensitive information may be retrieved by means of error
messages. */
(void)fname;
(void)for_write;
(void)binary;
return -1;
#else
int fd;
if (binary)
binary = MY_O_BINARY;
/* if (is_secured_filename (fname)) */
/* { */
/* fd = -1; */
/* errno = EPERM; */
/* } */
/* else */
/* { */
do
{
if (for_write)
fd = open (fname, O_CREAT | O_TRUNC | O_WRONLY | binary,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
else
fd = open (fname, O_RDONLY | binary);
}
while (fd == -1 && errno == EINTR);
/* } */
if ( fd == -1)
log_error ( for_write? _("can't create `%s': %s\n")
: _("can't open `%s': %s\n"), fname, strerror(errno));
return fd;
#endif
}
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 == aSym )
cmd = aSignSym;
else if( cmd == aSym && new_cmd == aSign )
cmd = aSignSym;
else if( cmd == aSym && new_cmd == aEncr )
cmd = aEncrSym;
else if( cmd == aEncr && new_cmd == aSym )
cmd = aEncrSym;
else if (cmd == aSignEncr && new_cmd == aSym)
cmd = aSignEncrSym;
else if (cmd == aSignSym && new_cmd == aEncr)
cmd = aSignEncrSym;
else if (cmd == aEncrSym && new_cmd == aSign)
cmd = aSignEncrSym;
else if( ( cmd == aSign && new_cmd == aClearsign )
|| ( cmd == aClearsign && new_cmd == aSign ) )
cmd = aClearsign;
else {
log_error(_("conflicting commands\n"));
g10_exit(2);
}
*ret_cmd = cmd;
}
static void
add_group(char *string)
{
char *name,*value;
struct groupitem *item;
/* Break off the group name */
name=strsep(&string,"=");
if(string==NULL)
{
log_error(_("no = sign found in group definition `%s'\n"),name);
return;
}
trim_trailing_ws(name,strlen(name));
/* Does this group already exist? */
for(item=opt.grouplist;item;item=item->next)
if(strcasecmp(item->name,name)==0)
break;
if(!item)
{
item=xmalloc(sizeof(struct groupitem));
item->name=name;
item->next=opt.grouplist;
item->values=NULL;
opt.grouplist=item;
}
/* Break apart the values */
while ((value= strsep(&string," \t")))
{
if (*value)
add_to_strlist2(&item->values,value,utf8_strings);
}
}
static void
rm_group(char *name)
{
struct groupitem *item,*last=NULL;
trim_trailing_ws(name,strlen(name));
for(item=opt.grouplist;item;last=item,item=item->next)
{
if(strcasecmp(item->name,name)==0)
{
if(last)
last->next=item->next;
else
opt.grouplist=item->next;
free_strlist(item->values);
xfree(item);
break;
}
}
}
/* We need to check three things.
0) The homedir. It must be x00, a directory, and owned by the
user.
1) The options/gpg.conf file. Okay unless it or its containing
directory is group or other writable or not owned by us. Disable
exec in this case.
2) Extensions. Same as #1.
Returns true if the item is unsafe. */
static int
check_permissions(const char *path,int item)
{
#if defined(HAVE_STAT) && !defined(HAVE_DOSISH_SYSTEM)
static int homedir_cache=-1;
char *tmppath,*dir;
struct stat statbuf,dirbuf;
int homedir=0,ret=0,checkonly=0;
int perm=0,own=0,enc_dir_perm=0,enc_dir_own=0;
if(opt.no_perm_warn)
return 0;
assert(item==0 || item==1 || item==2);
/* extensions may attach a path */
if(item==2 && path[0]!=DIRSEP_C)
{
if(strchr(path,DIRSEP_C))
tmppath=make_filename(path,NULL);
else
tmppath=make_filename(gnupg_libdir (),path,NULL);
}
else
tmppath=xstrdup(path);
/* If the item is located in the homedir, but isn't the homedir,
don't continue if we already checked the homedir itself. This is
to avoid user confusion with an extra options file warning which
could be rectified if the homedir itself had proper
permissions. */
if(item!=0 && homedir_cache>-1
&& ascii_strncasecmp(opt.homedir,tmppath,strlen(opt.homedir))==0)
{
ret=homedir_cache;
goto end;
}
/* It's okay if the file or directory doesn't exist */
if(stat(tmppath,&statbuf)!=0)
{
ret=0;
goto end;
}
/* Now check the enclosing directory. Theoretically, we could walk
this test up to the root directory /, but for the sake of sanity,
I'm stopping at one level down. */
dir=make_dirname(tmppath);
if(stat(dir,&dirbuf)!=0 || !S_ISDIR(dirbuf.st_mode))
{
/* Weird error */
ret=1;
goto end;
}
xfree(dir);
/* Assume failure */
ret=1;
if(item==0)
{
/* The homedir must be x00, a directory, and owned by the user. */
if(S_ISDIR(statbuf.st_mode))
{
if(statbuf.st_uid==getuid())
{
if((statbuf.st_mode & (S_IRWXG|S_IRWXO))==0)
ret=0;
else
perm=1;
}
else
own=1;
homedir_cache=ret;
}
}
else if(item==1 || item==2)
{
/* The options or extension file. Okay unless it or its
containing directory is group or other writable or not owned
by us or root. */
if(S_ISREG(statbuf.st_mode))
{
if(statbuf.st_uid==getuid() || statbuf.st_uid==0)
{
if((statbuf.st_mode & (S_IWGRP|S_IWOTH))==0)
{
/* it's not writable, so make sure the enclosing
directory is also not writable */
if(dirbuf.st_uid==getuid() || dirbuf.st_uid==0)
{
if((dirbuf.st_mode & (S_IWGRP|S_IWOTH))==0)
ret=0;
else
enc_dir_perm=1;
}
else
enc_dir_own=1;
}
else
{
/* it's writable, so the enclosing directory had
better not let people get to it. */
if(dirbuf.st_uid==getuid() || dirbuf.st_uid==0)
{
if((dirbuf.st_mode & (S_IRWXG|S_IRWXO))==0)
ret=0;
else
perm=enc_dir_perm=1; /* unclear which one to fix! */
}
else
enc_dir_own=1;
}
}
else
own=1;
}
}
else
BUG();
if(!checkonly)
{
if(own)
{
if(item==0)
log_info(_("WARNING: unsafe ownership on"
" homedir `%s'\n"),tmppath);
else if(item==1)
log_info(_("WARNING: unsafe ownership on"
" configuration file `%s'\n"),tmppath);
else
log_info(_("WARNING: unsafe ownership on"
" extension `%s'\n"),tmppath);
}
if(perm)
{
if(item==0)
log_info(_("WARNING: unsafe permissions on"
" homedir `%s'\n"),tmppath);
else if(item==1)
log_info(_("WARNING: unsafe permissions on"
" configuration file `%s'\n"),tmppath);
else
log_info(_("WARNING: unsafe permissions on"
" extension `%s'\n"),tmppath);
}
if(enc_dir_own)
{
if(item==0)
log_info(_("WARNING: unsafe enclosing directory ownership on"
" homedir `%s'\n"),tmppath);
else if(item==1)
log_info(_("WARNING: unsafe enclosing directory ownership on"
" configuration file `%s'\n"),tmppath);
else
log_info(_("WARNING: unsafe enclosing directory ownership on"
" extension `%s'\n"),tmppath);
}
if(enc_dir_perm)
{
if(item==0)
log_info(_("WARNING: unsafe enclosing directory permissions on"
" homedir `%s'\n"),tmppath);
else if(item==1)
log_info(_("WARNING: unsafe enclosing directory permissions on"
" configuration file `%s'\n"),tmppath);
else
log_info(_("WARNING: unsafe enclosing directory permissions on"
" extension `%s'\n"),tmppath);
}
}
end:
xfree(tmppath);
if(homedir)
homedir_cache=ret;
return ret;
#endif /* HAVE_STAT && !HAVE_DOSISH_SYSTEM */
return 0;
}
/* Print the OpenPGP defined algo numbers. */
static void
print_algo_numbers(int (*checker)(int))
{
int i,first=1;
for(i=0;i<=110;i++)
{
if(!checker(i))
{
if(first)
first=0;
else
printf(";");
printf("%d",i);
}
}
}
static void
print_algo_names(int (*checker)(int),const char *(*mapper)(int))
{
int i,first=1;
for(i=0;i<=110;i++)
{
if(!checker(i))
{
if(first)
first=0;
else
printf(";");
printf("%s",mapper(i));
}
}
}
/* In the future, we can do all sorts of interesting configuration
output here. For now, just give "group" as the Enigmail folks need
it, and pubkey, cipher, hash, and compress as they may be useful
for frontends. */
static void
list_config(char *items)
{
int show_all=(items==NULL);
char *name=NULL;
if(!opt.with_colons)
return;
while(show_all || (name=strsep(&items," ")))
{
int any=0;
if(show_all || ascii_strcasecmp(name,"group")==0)
{
struct groupitem *iter;
for(iter=opt.grouplist;iter;iter=iter->next)
{
strlist_t sl;
printf("cfg:group:");
print_string(stdout,iter->name,strlen(iter->name),':');
printf(":");
for(sl=iter->values;sl;sl=sl->next)
{
print_sanitized_string2 (stdout, sl->d, ':',';');
if(sl->next)
printf(";");
}
printf("\n");
}
any=1;
}
if(show_all || ascii_strcasecmp(name,"version")==0)
{
printf("cfg:version:");
print_string(stdout,VERSION,strlen(VERSION),':');
printf("\n");
any=1;
}
if(show_all || ascii_strcasecmp(name,"pubkey")==0)
{
printf("cfg:pubkey:");
print_algo_numbers (openpgp_pk_test_algo);
printf("\n");
any=1;
}
if(show_all || ascii_strcasecmp(name,"cipher")==0)
{
printf("cfg:cipher:");
print_algo_numbers(openpgp_cipher_test_algo);
printf("\n");
any=1;
}
if (show_all || !ascii_strcasecmp (name,"ciphername"))
{
printf ("cfg:ciphername:");
print_algo_names (openpgp_cipher_test_algo,openpgp_cipher_algo_name);
printf ("\n");
any = 1;
}
if(show_all
|| ascii_strcasecmp(name,"digest")==0
|| ascii_strcasecmp(name,"hash")==0)
{
printf("cfg:digest:");
print_algo_numbers(openpgp_md_test_algo);
printf("\n");
any=1;
}
if (show_all
|| !ascii_strcasecmp(name,"digestname")
|| !ascii_strcasecmp(name,"hashname"))
{
printf ("cfg:digestname:");
print_algo_names (openpgp_md_test_algo, gcry_md_algo_name);
printf("\n");
any=1;
}
if(show_all || ascii_strcasecmp(name,"compress")==0)
{
printf("cfg:compress:");
print_algo_numbers(check_compress_algo);
printf("\n");
any=1;
}
if(show_all || ascii_strcasecmp(name,"ccid-reader-id")==0)
{
#if defined(ENABLE_CARD_SUPPORT) && defined(HAVE_LIBUSB) \
&& GNUPG_MAJOR_VERSION == 1
char *p, *p2, *list = ccid_get_reader_list ();
for (p=list; p && (p2 = strchr (p, '\n')); p = p2+1)
{
*p2 = 0;
printf("cfg:ccid-reader-id:%s\n", p);
}
free (list);
#endif
any=1;
}
if(show_all)
break;
if(!any)
log_error(_("unknown configuration item `%s'\n"),name);
}
}
/* List options and default values in the GPG Conf format. This is a
new tool distributed with gnupg 1.9.x but we also want some limited
support in older gpg versions. The output is the name of the
configuration file and a list of options available for editing by
gpgconf. */
static void
gpgconf_list (const char *configfile)
{
char *configfile_esc = percent_escape (configfile, NULL);
printf ("gpgconf-gpg.conf:%lu:\"%s\n",
GC_OPT_FLAG_DEFAULT, configfile_esc ? configfile_esc : "/dev/null");
printf ("verbose:%lu:\n", GC_OPT_FLAG_NONE);
printf ("quiet:%lu:\n", GC_OPT_FLAG_NONE);
printf ("keyserver:%lu:\n", GC_OPT_FLAG_NONE);
printf ("reader-port:%lu:\n", GC_OPT_FLAG_NONE);
printf ("default-key:%lu:\n", GC_OPT_FLAG_NONE);
printf ("encrypt-to:%lu:\n", GC_OPT_FLAG_NONE);
printf ("auto-key-locate:%lu:\n", GC_OPT_FLAG_NONE);
printf ("log-file:%lu:\n", GC_OPT_FLAG_NONE);
printf ("debug-level:%lu:\"none:\n", GC_OPT_FLAG_DEFAULT);
printf ("group:%lu:\n", GC_OPT_FLAG_NONE);
/* The next one is an info only item and should match the macros at
the top of keygen.c. */
printf ("default_pubkey_algo:%lu:\"%s:\n", GC_OPT_FLAG_DEFAULT,
"RSA-2048");
xfree (configfile_esc);
}
static int
parse_subpacket_list(char *list)
{
char *tok;
byte subpackets[128],i;
int count=0;
if(!list)
{
/* No arguments means all subpackets */
memset(subpackets+1,1,sizeof(subpackets)-1);
count=127;
}
else
{
memset(subpackets,0,sizeof(subpackets));
/* Merge with earlier copy */
if(opt.show_subpackets)
{
byte *in;
for(in=opt.show_subpackets;*in;in++)
{
if(*in>127 || *in<1)
BUG();
if(!subpackets[*in])
count++;
subpackets[*in]=1;
}
}
while((tok=strsep(&list," ,")))
{
if(!*tok)
continue;
i=atoi(tok);
if(i>127 || i<1)
return 0;
if(!subpackets[i])
count++;
subpackets[i]=1;
}
}
xfree(opt.show_subpackets);
opt.show_subpackets=xmalloc(count+1);
opt.show_subpackets[count--]=0;
for(i=1;i<128 && count>=0;i++)
if(subpackets[i])
opt.show_subpackets[count--]=i;
return 1;
}
static int
parse_list_options(char *str)
{
char *subpackets=""; /* something that isn't NULL */
struct parse_options lopts[]=
{
{"show-photos",LIST_SHOW_PHOTOS,NULL,
N_("display photo IDs during key listings")},
{"show-policy-urls",LIST_SHOW_POLICY_URLS,NULL,
N_("show policy URLs during signature listings")},
{"show-notations",LIST_SHOW_NOTATIONS,NULL,
N_("show all notations during signature listings")},
{"show-std-notations",LIST_SHOW_STD_NOTATIONS,NULL,
N_("show IETF standard notations during signature listings")},
{"show-standard-notations",LIST_SHOW_STD_NOTATIONS,NULL,
NULL},
{"show-user-notations",LIST_SHOW_USER_NOTATIONS,NULL,
N_("show user-supplied notations during signature listings")},
{"show-keyserver-urls",LIST_SHOW_KEYSERVER_URLS,NULL,
N_("show preferred keyserver URLs during signature listings")},
{"show-uid-validity",LIST_SHOW_UID_VALIDITY,NULL,
N_("show user ID validity during key listings")},
{"show-unusable-uids",LIST_SHOW_UNUSABLE_UIDS,NULL,
N_("show revoked and expired user IDs in key listings")},
{"show-unusable-subkeys",LIST_SHOW_UNUSABLE_SUBKEYS,NULL,
N_("show revoked and expired subkeys in key listings")},
{"show-keyring",LIST_SHOW_KEYRING,NULL,
N_("show the keyring name in key listings")},
{"show-sig-expire",LIST_SHOW_SIG_EXPIRE,NULL,
N_("show expiration dates during signature listings")},
{"show-sig-subpackets",LIST_SHOW_SIG_SUBPACKETS,NULL,
NULL},
{NULL,0,NULL,NULL}
};
/* C99 allows for non-constant initializers, but we'd like to
compile everywhere, so fill in the show-sig-subpackets argument
here. Note that if the parse_options array changes, we'll have
to change the subscript here. */
lopts[12].value=&subpackets;
if(parse_options(str,&opt.list_options,lopts,1))
{
if(opt.list_options&LIST_SHOW_SIG_SUBPACKETS)
{
/* Unset so users can pass multiple lists in. */
opt.list_options&=~LIST_SHOW_SIG_SUBPACKETS;
if(!parse_subpacket_list(subpackets))
return 0;
}
else if(subpackets==NULL && opt.show_subpackets)
{
/* User did 'no-show-subpackets' */
xfree(opt.show_subpackets);
opt.show_subpackets=NULL;
}
return 1;
}
else
return 0;
}
/* Collapses argc/argv into a single string that must be freed */
static char *
collapse_args(int argc,char *argv[])
{
char *str=NULL;
int i,first=1,len=0;
for(i=0;iflags=2;
break;
case oShowKeyring:
deprecated_warning(configname,configlineno,"--show-keyring",
"--list-options ","show-keyring");
opt.list_options|=LIST_SHOW_KEYRING;
break;
case oDebug: opt.debug |= pargs.r.ret_ulong; break;
case oDebugAll: opt.debug = ~0; break;
case oDebugLevel: debug_level = pargs.r.ret_str; break;
case oStatusFD:
set_status_fd ( translate_sys2libc_fd_int (pargs.r.ret_int, 1) );
break;
case oStatusFile:
set_status_fd ( open_info_file (pargs.r.ret_str, 1, 0) );
break;
case oAttributeFD:
set_attrib_fd ( translate_sys2libc_fd_int (pargs.r.ret_int, 1) );
break;
case oAttributeFile:
set_attrib_fd ( open_info_file (pargs.r.ret_str, 1, 1) );
break;
case oLoggerFD:
log_set_fd (translate_sys2libc_fd_int (pargs.r.ret_int, 1));
break;
case oLoggerFile:
logfile = pargs.r.ret_str;
break;
case oWithFingerprint:
opt.with_fingerprint = 1;
opt.fingerprint++;
break;
case oFingerprint:
opt.fingerprint++;
fpr_maybe_cmd = 1;
break;
case oSecretKeyring:
append_to_strlist( &sec_nrings, pargs.r.ret_str);
break;
case oOptions:
/* config files may not be nested (silently ignore them) */
if( !configfp ) {
xfree(configname);
configname = xstrdup(pargs.r.ret_str);
goto next_pass;
}
break;
case oNoArmor: opt.no_armor=1; opt.armor=0; break;
case oNoDefKeyring: default_keyring = 0; break;
case oNoGreeting: nogreeting = 1; break;
case oNoVerbose:
opt.verbose = 0;
gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
opt.list_sigs=0;
break;
case oQuickRandom:
gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
break;
case oEmitVersion: opt.emit_version++; break;
case oNoEmitVersion: opt.emit_version=0; break;
case oCompletesNeeded: opt.completes_needed = pargs.r.ret_int; break;
case oMarginalsNeeded: opt.marginals_needed = pargs.r.ret_int; break;
case oMaxCertDepth: opt.max_cert_depth = pargs.r.ret_int; break;
case oTrustDBName: trustdb_name = pargs.r.ret_str; break;
case oDefaultKey: opt.def_secret_key = pargs.r.ret_str; break;
case oDefRecipient:
if( *pargs.r.ret_str )
opt.def_recipient = make_username(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 oNoOptions: opt.no_homedir_creation = 1; break; /* no-options */
case oHomedir: break;
case oNoBatch: opt.batch = 0; break;
case oWithKeyData: opt.with_key_data=1; /*FALLTHRU*/
case oWithColons: opt.with_colons=':'; break;
case oWithSigCheck: opt.check_sigs = 1; /*FALLTHRU*/
case oWithSigList: opt.list_sigs = 1; break;
case oSkipVerify: opt.skip_verify=1; break;
case oSkipHiddenRecipients:
case oNoSkipHiddenRecipients:
/* Dummies for options to be used in 2.1. */
break;
case oCompressKeys: opt.compress_keys = 1; break;
case aListSecretKeys: set_cmd( &cmd, aListSecretKeys); break;
/* There are many programs (like mutt) that call gpg with
--always-trust so keep this option around for a long
time. */
case oAlwaysTrust: opt.trust_model=TM_ALWAYS; break;
case oTrustModel:
parse_trust_model(pargs.r.ret_str);
break;
case oForceOwnertrust:
log_info(_("NOTE: %s is not for normal use!\n"),
"--force-ownertrust");
opt.force_ownertrust=string_to_trust_value(pargs.r.ret_str);
if(opt.force_ownertrust==-1)
{
log_error("invalid ownertrust `%s'\n",pargs.r.ret_str);
opt.force_ownertrust=0;
}
break;
case oLoadExtension:
/* Dummy so that gpg 1.4 conf files can work. Should
eventually be removed. */
break;
case oRFC1991:
opt.compliance = CO_RFC1991;
opt.force_v4_certs = 0;
opt.escape_from = 1;
break;
case oOpenPGP:
case oRFC4880:
/* This is effectively the same as RFC2440, but with
"--enable-dsa2 --no-rfc2440-text --escape-from-lines
--require-cross-certification". */
opt.compliance = CO_RFC4880;
opt.flags.dsa2 = 1;
opt.flags.require_cross_cert = 1;
opt.rfc2440_text = 0;
opt.allow_non_selfsigned_uid = 1;
opt.allow_freeform_uid = 1;
opt.pgp2_workarounds = 0;
opt.escape_from = 1;
opt.force_v3_sigs = 0;
opt.compress_keys = 0; /* not mandated, but we do it */
opt.compress_sigs = 0; /* ditto. */
opt.not_dash_escaped = 0;
opt.def_cipher_algo = 0;
opt.def_digest_algo = 0;
opt.cert_digest_algo = 0;
opt.compress_algo = -1;
opt.s2k_mode = 3; /* iterated+salted */
opt.s2k_digest_algo = DIGEST_ALGO_SHA1;
opt.s2k_cipher_algo = CIPHER_ALGO_3DES;
break;
case oRFC2440:
opt.compliance = CO_RFC2440;
opt.flags.dsa2 = 0;
opt.rfc2440_text = 1;
opt.allow_non_selfsigned_uid = 1;
opt.allow_freeform_uid = 1;
opt.pgp2_workarounds = 0;
opt.escape_from = 0;
opt.force_v3_sigs = 0;
opt.compress_keys = 0; /* not mandated, but we do it */
opt.compress_sigs = 0; /* ditto. */
opt.not_dash_escaped = 0;
opt.def_cipher_algo = 0;
opt.def_digest_algo = 0;
opt.cert_digest_algo = 0;
opt.compress_algo = -1;
opt.s2k_mode = 3; /* iterated+salted */
opt.s2k_digest_algo = DIGEST_ALGO_SHA1;
opt.s2k_cipher_algo = CIPHER_ALGO_3DES;
break;
case oPGP2: opt.compliance = CO_PGP2; break;
case oPGP6: opt.compliance = CO_PGP6; break;
case oPGP7: opt.compliance = CO_PGP7; break;
case oPGP8: opt.compliance = CO_PGP8; break;
case oGnuPG: opt.compliance = CO_GNUPG; break;
case oCompressSigs: opt.compress_sigs = 1; break;
case oRFC2440Text: opt.rfc2440_text=1; break;
case oNoRFC2440Text: opt.rfc2440_text=0; break;
case oSetFilename:
if(utf8_strings)
opt.set_filename = pargs.r.ret_str;
else
opt.set_filename = native_to_utf8(pargs.r.ret_str);
break;
case oForYourEyesOnly: eyes_only = 1; break;
case oNoForYourEyesOnly: eyes_only = 0; break;
case oSetPolicyURL:
add_policy_url(pargs.r.ret_str,0);
add_policy_url(pargs.r.ret_str,1);
break;
case oSigPolicyURL: add_policy_url(pargs.r.ret_str,0); break;
case oCertPolicyURL: add_policy_url(pargs.r.ret_str,1); break;
case oShowPolicyURL:
deprecated_warning(configname,configlineno,"--show-policy-url",
"--list-options ","show-policy-urls");
deprecated_warning(configname,configlineno,"--show-policy-url",
"--verify-options ","show-policy-urls");
opt.list_options|=LIST_SHOW_POLICY_URLS;
opt.verify_options|=VERIFY_SHOW_POLICY_URLS;
break;
case oNoShowPolicyURL:
deprecated_warning(configname,configlineno,"--no-show-policy-url",
"--list-options ","no-show-policy-urls");
deprecated_warning(configname,configlineno,"--no-show-policy-url",
"--verify-options ","no-show-policy-urls");
opt.list_options&=~LIST_SHOW_POLICY_URLS;
opt.verify_options&=~VERIFY_SHOW_POLICY_URLS;
break;
case oSigKeyserverURL: add_keyserver_url(pargs.r.ret_str,0); break;
case oUseEmbeddedFilename:
opt.flags.use_embedded_filename=1;
break;
case oNoUseEmbeddedFilename:
opt.flags.use_embedded_filename=0;
break;
case oComment:
if(pargs.r.ret_str[0])
append_to_strlist(&opt.comments,pargs.r.ret_str);
break;
case oDefaultComment:
deprecated_warning(configname,configlineno,
"--default-comment","--no-comments","");
/* fall through */
case oNoComments:
free_strlist(opt.comments);
opt.comments=NULL;
break;
case oThrowKeyids: opt.throw_keyid = 1; break;
case oNoThrowKeyids: opt.throw_keyid = 0; break;
case oShowPhotos:
deprecated_warning(configname,configlineno,"--show-photos",
"--list-options ","show-photos");
deprecated_warning(configname,configlineno,"--show-photos",
"--verify-options ","show-photos");
opt.list_options|=LIST_SHOW_PHOTOS;
opt.verify_options|=VERIFY_SHOW_PHOTOS;
break;
case oNoShowPhotos:
deprecated_warning(configname,configlineno,"--no-show-photos",
"--list-options ","no-show-photos");
deprecated_warning(configname,configlineno,"--no-show-photos",
"--verify-options ","no-show-photos");
opt.list_options&=~LIST_SHOW_PHOTOS;
opt.verify_options&=~VERIFY_SHOW_PHOTOS;
break;
case oPhotoViewer: opt.photo_viewer = pargs.r.ret_str; break;
case oForceV3Sigs: opt.force_v3_sigs = 1; break;
case oNoForceV3Sigs: opt.force_v3_sigs = 0; break;
case oForceV4Certs: opt.force_v4_certs = 1; break;
case oNoForceV4Certs: opt.force_v4_certs = 0; break;
case oForceMDC: opt.force_mdc = 1; break;
case oNoForceMDC: opt.force_mdc = 0; break;
case oDisableMDC: opt.disable_mdc = 1; break;
case oNoDisableMDC: opt.disable_mdc = 0; break;
case oS2KMode: opt.s2k_mode = pargs.r.ret_int; break;
case oS2KDigest: s2k_digest_string = xstrdup(pargs.r.ret_str); break;
case oS2KCipher: s2k_cipher_string = xstrdup(pargs.r.ret_str); break;
case oS2KCount:
if (pargs.r.ret_int)
opt.s2k_count = encode_s2k_iterations (pargs.r.ret_int);
else
opt.s2k_count = 0; /* Auto-calibrate when needed. */
break;
case oSimpleSKChecksum: opt.simple_sk_checksum = 1; break;
case oNoEncryptTo: opt.no_encrypt_to = 1; break;
case oEncryptTo: /* store the recipient in the second list */
sl = add_to_strlist2( &remusr, pargs.r.ret_str, utf8_strings );
sl->flags = 1;
break;
case oHiddenEncryptTo: /* store the recipient in the second list */
sl = add_to_strlist2( &remusr, pargs.r.ret_str, utf8_strings );
sl->flags = 1|2;
break;
case oRecipient: /* store the recipient */
add_to_strlist2( &remusr, pargs.r.ret_str, utf8_strings );
any_explicit_recipient = 1;
break;
case oHiddenRecipient: /* store the recipient with a flag */
sl = add_to_strlist2( &remusr, pargs.r.ret_str, utf8_strings );
sl->flags = 2;
any_explicit_recipient = 1;
break;
case oTextmodeShort: opt.textmode = 2; break;
case oTextmode: opt.textmode=1; break;
case oNoTextmode: opt.textmode=0; break;
case oExpert: opt.expert = 1; break;
case oNoExpert: opt.expert = 0; break;
case oDefSigExpire:
if(*pargs.r.ret_str!='\0')
{
if(parse_expire_string(pargs.r.ret_str)==(u32)-1)
log_error(_("`%s' is not a valid signature expiration\n"),
pargs.r.ret_str);
else
opt.def_sig_expire=pargs.r.ret_str;
}
break;
case oAskSigExpire: opt.ask_sig_expire = 1; break;
case oNoAskSigExpire: opt.ask_sig_expire = 0; break;
case oDefCertExpire:
if(*pargs.r.ret_str!='\0')
{
if(parse_expire_string(pargs.r.ret_str)==(u32)-1)
log_error(_("`%s' is not a valid signature expiration\n"),
pargs.r.ret_str);
else
opt.def_cert_expire=pargs.r.ret_str;
}
break;
case oAskCertExpire: opt.ask_cert_expire = 1; break;
case oNoAskCertExpire: opt.ask_cert_expire = 0; break;
case oDefCertLevel: opt.def_cert_level=pargs.r.ret_int; break;
case oMinCertLevel: opt.min_cert_level=pargs.r.ret_int; break;
case oAskCertLevel: opt.ask_cert_level = 1; break;
case oNoAskCertLevel: opt.ask_cert_level = 0; break;
case oLocalUser: /* store the local users */
add_to_strlist2( &locusr, pargs.r.ret_str, utf8_strings );
break;
case oCompress:
/* this is the -z command line option */
opt.compress_level = opt.bz2_compress_level = pargs.r.ret_int;
break;
case oCompressLevel: opt.compress_level = pargs.r.ret_int; break;
case oBZ2CompressLevel: opt.bz2_compress_level = pargs.r.ret_int; break;
case oBZ2DecompressLowmem: opt.bz2_decompress_lowmem=1; break;
case oPassphrase:
set_passphrase_from_string(pargs.r.ret_str);
break;
case oPassphraseFD:
pwfd = translate_sys2libc_fd_int (pargs.r.ret_int, 0);
break;
case oPassphraseFile:
pwfd = open_info_file (pargs.r.ret_str, 0, 1);
break;
case oPassphraseRepeat: opt.passphrase_repeat=pargs.r.ret_int; break;
case oCommandFD:
opt.command_fd = translate_sys2libc_fd_int (pargs.r.ret_int, 0);
break;
case oCommandFile:
opt.command_fd = open_info_file (pargs.r.ret_str, 0, 1);
break;
case oCipherAlgo:
def_cipher_string = xstrdup(pargs.r.ret_str);
break;
case oDigestAlgo:
def_digest_string = xstrdup(pargs.r.ret_str);
break;
case oCompressAlgo:
/* If it is all digits, stick a Z in front of it for
later. This is for backwards compatibility with
versions that took the compress algorithm number. */
{
char *pt=pargs.r.ret_str;
while(*pt)
{
if (!isascii (*pt) || !isdigit (*pt))
break;
pt++;
}
if(*pt=='\0')
{
compress_algo_string=xmalloc(strlen(pargs.r.ret_str)+2);
strcpy(compress_algo_string,"Z");
strcat(compress_algo_string,pargs.r.ret_str);
}
else
compress_algo_string = xstrdup(pargs.r.ret_str);
}
break;
case oCertDigestAlgo:
cert_digest_string = xstrdup(pargs.r.ret_str);
break;
case oNoSecmemWarn:
gcry_control (GCRYCTL_DISABLE_SECMEM_WARN);
break;
case oRequireSecmem: require_secmem=1; break;
case oNoRequireSecmem: require_secmem=0; break;
case oNoPermissionWarn: opt.no_perm_warn=1; break;
case oNoMDCWarn: opt.no_mdc_warn=1; break;
case oDisplayCharset:
if( set_native_charset( pargs.r.ret_str ) )
log_error(_("`%s' is not a valid character set\n"),
pargs.r.ret_str);
break;
case oNotDashEscaped: opt.not_dash_escaped = 1; break;
case oEscapeFrom: opt.escape_from = 1; break;
case oNoEscapeFrom: opt.escape_from = 0; break;
case oLockOnce: opt.lock_once = 1; break;
case oLockNever:
disable_dotlock ();
break;
case oLockMultiple:
#ifndef __riscos__
opt.lock_once = 0;
#else /* __riscos__ */
riscos_not_implemented("lock-multiple");
#endif /* __riscos__ */
break;
case oKeyServer:
{
struct keyserver_spec *keyserver;
keyserver=parse_keyserver_uri(pargs.r.ret_str,0,
configname,configlineno);
if(!keyserver)
log_error(_("could not parse keyserver URL\n"));
else
{
keyserver->next=opt.keyserver;
opt.keyserver=keyserver;
}
}
break;
case oKeyServerOptions:
if(!parse_keyserver_options(pargs.r.ret_str))
{
if(configname)
log_error(_("%s:%d: invalid keyserver options\n"),
configname,configlineno);
else
log_error(_("invalid keyserver options\n"));
}
break;
case oImportOptions:
if(!parse_import_options(pargs.r.ret_str,&opt.import_options,1))
{
if(configname)
log_error(_("%s:%d: invalid import options\n"),
configname,configlineno);
else
log_error(_("invalid import options\n"));
}
break;
case oExportOptions:
if(!parse_export_options(pargs.r.ret_str,&opt.export_options,1))
{
if(configname)
log_error(_("%s:%d: invalid export options\n"),
configname,configlineno);
else
log_error(_("invalid export options\n"));
}
break;
case oListOptions:
if(!parse_list_options(pargs.r.ret_str))
{
if(configname)
log_error(_("%s:%d: invalid list options\n"),
configname,configlineno);
else
log_error(_("invalid list options\n"));
}
break;
case oVerifyOptions:
{
struct parse_options vopts[]=
{
{"show-photos",VERIFY_SHOW_PHOTOS,NULL,
N_("display photo IDs during signature verification")},
{"show-policy-urls",VERIFY_SHOW_POLICY_URLS,NULL,
N_("show policy URLs during signature verification")},
{"show-notations",VERIFY_SHOW_NOTATIONS,NULL,
N_("show all notations during signature verification")},
{"show-std-notations",VERIFY_SHOW_STD_NOTATIONS,NULL,
N_("show IETF standard notations during signature verification")},
{"show-standard-notations",VERIFY_SHOW_STD_NOTATIONS,NULL,
NULL},
{"show-user-notations",VERIFY_SHOW_USER_NOTATIONS,NULL,
N_("show user-supplied notations during signature verification")},
{"show-keyserver-urls",VERIFY_SHOW_KEYSERVER_URLS,NULL,
N_("show preferred keyserver URLs during signature verification")},
{"show-uid-validity",VERIFY_SHOW_UID_VALIDITY,NULL,
N_("show user ID validity during signature verification")},
{"show-unusable-uids",VERIFY_SHOW_UNUSABLE_UIDS,NULL,
N_("show revoked and expired user IDs in signature verification")},
{"show-primary-uid-only",VERIFY_SHOW_PRIMARY_UID_ONLY,NULL,
N_("show only the primary user ID in signature verification")},
{"pka-lookups",VERIFY_PKA_LOOKUPS,NULL,
N_("validate signatures with PKA data")},
{"pka-trust-increase",VERIFY_PKA_TRUST_INCREASE,NULL,
N_("elevate the trust of signatures with valid PKA data")},
{NULL,0,NULL,NULL}
};
if(!parse_options(pargs.r.ret_str,&opt.verify_options,vopts,1))
{
if(configname)
log_error(_("%s:%d: invalid verify options\n"),
configname,configlineno);
else
log_error(_("invalid verify options\n"));
}
}
break;
case oTempDir: opt.temp_dir=pargs.r.ret_str; break;
case oExecPath:
if(set_exec_path(pargs.r.ret_str))
log_error(_("unable to set exec-path to %s\n"),pargs.r.ret_str);
else
opt.exec_path_set=1;
break;
case oSetNotation:
add_notation_data( pargs.r.ret_str, 0 );
add_notation_data( pargs.r.ret_str, 1 );
break;
case oSigNotation: add_notation_data( pargs.r.ret_str, 0 ); break;
case oCertNotation: add_notation_data( pargs.r.ret_str, 1 ); break;
case oShowNotation:
deprecated_warning(configname,configlineno,"--show-notation",
"--list-options ","show-notations");
deprecated_warning(configname,configlineno,"--show-notation",
"--verify-options ","show-notations");
opt.list_options|=LIST_SHOW_NOTATIONS;
opt.verify_options|=VERIFY_SHOW_NOTATIONS;
break;
case oNoShowNotation:
deprecated_warning(configname,configlineno,"--no-show-notation",
"--list-options ","no-show-notations");
deprecated_warning(configname,configlineno,"--no-show-notation",
"--verify-options ","no-show-notations");
opt.list_options&=~LIST_SHOW_NOTATIONS;
opt.verify_options&=~VERIFY_SHOW_NOTATIONS;
break;
case oUtf8Strings: utf8_strings = 1; break;
case oNoUtf8Strings: utf8_strings = 0; break;
case oDisableCipherAlgo:
{
int algo = string_to_cipher_algo (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 oNoSigCache: opt.no_sig_cache = 1; break;
case oAllowNonSelfsignedUID: opt.allow_non_selfsigned_uid = 1; break;
case oNoAllowNonSelfsignedUID: opt.allow_non_selfsigned_uid=0; break;
case oAllowFreeformUID: opt.allow_freeform_uid = 1; break;
case oNoAllowFreeformUID: opt.allow_freeform_uid = 0; break;
case oNoLiteral: opt.no_literal = 1; break;
case oSetFilesize: opt.set_filesize = pargs.r.ret_ulong; break;
case oHonorHttpProxy:
add_to_strlist(&opt.keyserver_options.other,"http-proxy");
deprecated_warning(configname,configlineno,
"--honor-http-proxy",
"--keyserver-options ","http-proxy");
break;
case oFastListMode: opt.fast_list_mode = 1; break;
case oFixedListMode: /* Dummy */ break;
case oListOnly: opt.list_only=1; break;
case oIgnoreTimeConflict: opt.ignore_time_conflict = 1; break;
case oIgnoreValidFrom: opt.ignore_valid_from = 1; break;
case oIgnoreCrcError: opt.ignore_crc_error = 1; break;
case oIgnoreMDCError: opt.ignore_mdc_error = 1; break;
case oNoRandomSeedFile: use_random_seed = 0; break;
case oAutoKeyRetrieve:
case oNoAutoKeyRetrieve:
if(pargs.r_opt==oAutoKeyRetrieve)
opt.keyserver_options.options|=KEYSERVER_AUTO_KEY_RETRIEVE;
else
opt.keyserver_options.options&=~KEYSERVER_AUTO_KEY_RETRIEVE;
deprecated_warning(configname,configlineno,
pargs.r_opt==oAutoKeyRetrieve?"--auto-key-retrieve":
"--no-auto-key-retrieve","--keyserver-options ",
pargs.r_opt==oAutoKeyRetrieve?"auto-key-retrieve":
"no-auto-key-retrieve");
break;
case oShowSessionKey: opt.show_session_key = 1; break;
case oOverrideSessionKey:
opt.override_session_key = pargs.r.ret_str;
break;
case oMergeOnly:
deprecated_warning(configname,configlineno,"--merge-only",
"--import-options ","merge-only");
opt.import_options|=IMPORT_MERGE_ONLY;
break;
case oAllowSecretKeyImport: /* obsolete */ break;
case oTryAllSecrets: opt.try_all_secrets = 1; break;
case oTrustedKey: register_trusted_key( pargs.r.ret_str ); break;
case oEnableSpecialFilenames:
iobuf_enable_special_filenames (1);
break;
case oNoExpensiveTrustChecks: opt.no_expensive_trust_checks=1; break;
case oAutoCheckTrustDB: opt.no_auto_check_trustdb=0; break;
case oNoAutoCheckTrustDB: opt.no_auto_check_trustdb=1; break;
case oPreservePermissions: opt.preserve_permissions=1; break;
case oDefaultPreferenceList:
opt.def_preference_list = pargs.r.ret_str;
break;
case oDefaultKeyserverURL:
{
struct keyserver_spec *keyserver;
keyserver=parse_keyserver_uri(pargs.r.ret_str,1,
configname,configlineno);
if(!keyserver)
log_error(_("could not parse keyserver URL\n"));
else
free_keyserver_spec(keyserver);
opt.def_keyserver_url = pargs.r.ret_str;
}
break;
case oPersonalCipherPreferences:
pers_cipher_list=pargs.r.ret_str;
break;
case oPersonalDigestPreferences:
pers_digest_list=pargs.r.ret_str;
break;
case oPersonalCompressPreferences:
pers_compress_list=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 = pargs.r.ret_str; break;
case oLCmessages: opt.lc_messages = pargs.r.ret_str; break;
case oGroup: add_group(pargs.r.ret_str); break;
case oUnGroup: rm_group(pargs.r.ret_str); break;
case oNoGroups:
while(opt.grouplist)
{
struct groupitem *iter=opt.grouplist;
free_strlist(iter->values);
opt.grouplist=opt.grouplist->next;
xfree(iter);
}
break;
case oStrict:
case oNoStrict:
/* Not used */
break;
case oMangleDosFilenames: opt.mangle_dos_filenames = 1; break;
case oNoMangleDosFilenames: opt.mangle_dos_filenames = 0; break;
case oEnableProgressFilter: opt.enable_progress_filter = 1; break;
case oMultifile: multifile=1; break;
case oKeyidFormat:
if(ascii_strcasecmp(pargs.r.ret_str,"short")==0)
opt.keyid_format=KF_SHORT;
else if(ascii_strcasecmp(pargs.r.ret_str,"long")==0)
opt.keyid_format=KF_LONG;
else if(ascii_strcasecmp(pargs.r.ret_str,"0xshort")==0)
opt.keyid_format=KF_0xSHORT;
else if(ascii_strcasecmp(pargs.r.ret_str,"0xlong")==0)
opt.keyid_format=KF_0xLONG;
else
log_error("unknown keyid-format `%s'\n",pargs.r.ret_str);
break;
case oExitOnStatusWriteError:
opt.exit_on_status_write_error = 1;
break;
case oLimitCardInsertTries:
opt.limit_card_insert_tries = pargs.r.ret_int;
break;
case oRequireCrossCert: opt.flags.require_cross_cert=1; break;
case oNoRequireCrossCert: opt.flags.require_cross_cert=0; break;
case oAutoKeyLocate:
if(!parse_auto_key_locate(pargs.r.ret_str))
{
if(configname)
log_error(_("%s:%d: invalid auto-key-locate list\n"),
configname,configlineno);
else
log_error(_("invalid auto-key-locate list\n"));
}
break;
case oNoAutoKeyLocate:
release_akl();
break;
case oEnableLargeRSA:
#if SECMEM_BUFFER_SIZE >= 65536
opt.flags.large_rsa=1;
#else
if (configname)
log_info("%s:%d: WARNING: gpg not built with large secure "
"memory buffer. Ignoring enable-large-rsa\n",
configname,configlineno);
else
log_info("WARNING: gpg not built with large secure "
"memory buffer. Ignoring --enable-large-rsa\n");
#endif /* SECMEM_BUFFER_SIZE >= 65536 */
break;
case oDisableLargeRSA: opt.flags.large_rsa=0;
break;
case oEnableDSA2: opt.flags.dsa2=1; break;
case oDisableDSA2: opt.flags.dsa2=0; break;
case oAllowMultisigVerification:
case oAllowMultipleMessages:
opt.flags.allow_multiple_messages=1;
break;
case oNoAllowMultipleMessages:
opt.flags.allow_multiple_messages=0;
break;
case oAllowWeakDigestAlgos:
opt.flags.allow_weak_digest_algos = 1;
break;
case oNoop: break;
default:
pargs.err = configfp? ARGPARSE_PRINT_WARNING:ARGPARSE_PRINT_ERROR;
break;
}
}
if( configfp ) {
fclose( configfp );
configfp = NULL;
/* Remember the first config file name. */
if (!save_configname)
save_configname = configname;
else
xfree(configname);
configname = NULL;
goto next_pass;
}
xfree( configname ); configname = NULL;
if( log_get_errorcount(0) )
g10_exit(2);
/* The command --gpgconf-list is pretty simple and may be called
directly after the option parsing. */
if (cmd == aGPGConfList)
{
gpgconf_list (save_configname ? save_configname : default_configname);
g10_exit (0);
}
xfree (save_configname);
xfree (default_configname);
if( nogreeting )
greeting = 0;
if( greeting ) {
fprintf(stderr, "%s %s; %s\n",
strusage(11), strusage(13), strusage(14) );
fprintf(stderr, "%s\n", strusage(15) );
}
#ifdef IS_DEVELOPMENT_VERSION
if (!opt.batch)
{
const char *s;
if((s=strusage(25)))
log_info("%s\n",s);
if((s=strusage(26)))
log_info("%s\n",s);
if((s=strusage(27)))
log_info("%s\n",s);
}
#endif
/* FIXME: We should use logging to a file only in server mode;
however we have not yet implemetyed that. Thus we try to get
away with --batch as indication for logging to file
required. */
if (logfile && opt.batch)
{
log_set_file (logfile);
log_set_prefix (NULL, 1|2|4);
}
/* Older Libgcrypts fail with an assertion during DSA key
generation. Better disable DSA2 entirely. */
if (opt.flags.dsa2 && !gcry_check_version ("1.4.0") )
{
log_info ("WARNING: "
"DSA2 is only available with Libgcrypt 1.4 and later\n");
opt.flags.dsa2 = 0;
}
if (opt.verbose > 2)
log_info ("using character set `%s'\n", get_native_charset ());
if( may_coredump && !opt.quiet )
log_info(_("WARNING: program may create a core file!\n"));
if (eyes_only) {
if (opt.set_filename)
log_info(_("WARNING: %s overrides %s\n"),
"--for-your-eyes-only","--set-filename");
opt.set_filename="_CONSOLE";
}
if (opt.no_literal) {
log_info(_("NOTE: %s is not for normal use!\n"), "--no-literal");
if (opt.textmode)
log_error(_("%s not allowed with %s!\n"),
"--textmode", "--no-literal" );
if (opt.set_filename)
log_error(_("%s makes no sense with %s!\n"),
eyes_only?"--for-your-eyes-only":"--set-filename",
"--no-literal" );
}
if (opt.set_filesize)
log_info(_("NOTE: %s is not for normal use!\n"), "--set-filesize");
if( opt.batch )
tty_batchmode( 1 );
gcry_control (GCRYCTL_RESUME_SECMEM_WARN);
if(require_secmem && !got_secmem)
{
log_info(_("will not run with insecure memory due to %s\n"),
"--require-secmem");
g10_exit(2);
}
set_debug (debug_level);
/* Do these after the switch(), so they can override settings. */
if(PGP2)
{
int unusable=0;
if(cmd==aSign && !detached_sig)
{
log_info(_("you can only make detached or clear signatures "
"while in --pgp2 mode\n"));
unusable=1;
}
else if(cmd==aSignEncr || cmd==aSignSym)
{
log_info(_("you can't sign and encrypt at the "
"same time while in --pgp2 mode\n"));
unusable=1;
}
else if(argc==0 && (cmd==aSign || cmd==aEncr || cmd==aSym))
{
log_info(_("you must use files (and not a pipe) when "
"working with --pgp2 enabled.\n"));
unusable=1;
}
else if(cmd==aEncr || cmd==aSym)
{
/* Everything else should work without IDEA (except using
a secret key encrypted with IDEA and setting an IDEA
preference, but those have their own error
messages). */
if (openpgp_cipher_test_algo(CIPHER_ALGO_IDEA))
{
log_info(_("encrypting a message in --pgp2 mode requires "
"the IDEA cipher\n"));
idea_cipher_warn(1);
unusable=1;
}
else if(cmd==aSym)
{
/* This only sets IDEA for symmetric encryption
since it is set via select_algo_from_prefs for
pk encryption. */
xfree(def_cipher_string);
def_cipher_string = xstrdup("idea");
}
/* PGP2 can't handle the output from the textmode
filter, so we disable it for anything that could
create a literal packet (only encryption and
symmetric encryption, since we disable signing
above). */
if(!unusable)
opt.textmode=0;
}
if(unusable)
compliance_failure();
else
{
opt.force_v4_certs = 0;
opt.escape_from = 1;
opt.force_v3_sigs = 1;
opt.pgp2_workarounds = 1;
opt.ask_sig_expire = 0;
opt.ask_cert_expire = 0;
opt.flags.allow_weak_digest_algos = 1;
xfree(def_digest_string);
def_digest_string = xstrdup("md5");
xfree(s2k_digest_string);
s2k_digest_string = xstrdup("md5");
opt.compress_algo = COMPRESS_ALGO_ZIP;
}
}
else if(PGP6)
{
opt.disable_mdc=1;
opt.escape_from=1;
opt.force_v3_sigs=1;
opt.ask_sig_expire=0;
}
else if(PGP7)
{
opt.escape_from=1;
opt.force_v3_sigs=1;
opt.ask_sig_expire=0;
}
else if(PGP8)
{
opt.escape_from=1;
}
if( def_cipher_string ) {
opt.def_cipher_algo = string_to_cipher_algo (def_cipher_string);
if(opt.def_cipher_algo==0 &&
(ascii_strcasecmp(def_cipher_string,"idea")==0
|| ascii_strcasecmp(def_cipher_string,"s1")==0))
idea_cipher_warn(1);
xfree(def_cipher_string); def_cipher_string = NULL;
if ( openpgp_cipher_test_algo (opt.def_cipher_algo) )
log_error(_("selected cipher algorithm is invalid\n"));
}
if( def_digest_string ) {
opt.def_digest_algo = string_to_digest_algo (def_digest_string);
xfree(def_digest_string); def_digest_string = NULL;
if ( openpgp_md_test_algo (opt.def_digest_algo) )
log_error(_("selected digest algorithm is invalid\n"));
}
if( compress_algo_string ) {
opt.compress_algo = string_to_compress_algo(compress_algo_string);
xfree(compress_algo_string); compress_algo_string = NULL;
if( check_compress_algo(opt.compress_algo) )
log_error(_("selected compression algorithm is invalid\n"));
}
if( cert_digest_string ) {
opt.cert_digest_algo = string_to_digest_algo (cert_digest_string);
xfree(cert_digest_string); cert_digest_string = NULL;
if (openpgp_md_test_algo(opt.cert_digest_algo))
log_error(_("selected certification digest algorithm is invalid\n"));
}
if( s2k_cipher_string ) {
opt.s2k_cipher_algo = string_to_cipher_algo (s2k_cipher_string);
xfree(s2k_cipher_string); s2k_cipher_string = NULL;
if (openpgp_cipher_test_algo (opt.s2k_cipher_algo))
log_error(_("selected cipher algorithm is invalid\n"));
}
if( s2k_digest_string ) {
opt.s2k_digest_algo = string_to_digest_algo (s2k_digest_string);
xfree(s2k_digest_string); s2k_digest_string = NULL;
if (openpgp_md_test_algo(opt.s2k_digest_algo))
log_error(_("selected digest algorithm is invalid\n"));
}
if( opt.completes_needed < 1 )
log_error(_("completes-needed must be greater than 0\n"));
if( opt.marginals_needed < 2 )
log_error(_("marginals-needed must be greater than 1\n"));
if( opt.max_cert_depth < 1 || opt.max_cert_depth > 255 )
log_error(_("max-cert-depth must be in the range from 1 to 255\n"));
if(opt.def_cert_level<0 || opt.def_cert_level>3)
log_error(_("invalid default-cert-level; must be 0, 1, 2, or 3\n"));
if( opt.min_cert_level < 1 || opt.min_cert_level > 3 )
log_error(_("invalid min-cert-level; must be 1, 2, or 3\n"));
switch( opt.s2k_mode ) {
case 0:
log_info(_("NOTE: simple S2K mode (0) is strongly discouraged\n"));
break;
case 1: case 3: break;
default:
log_error(_("invalid S2K mode; must be 0, 1 or 3\n"));
}
/* This isn't actually needed, but does serve to error out if the
string is invalid. */
if(opt.def_preference_list &&
keygen_set_std_prefs(opt.def_preference_list,0))
log_error(_("invalid default preferences\n"));
if(pers_cipher_list &&
keygen_set_std_prefs(pers_cipher_list,PREFTYPE_SYM))
log_error(_("invalid personal cipher preferences\n"));
if(pers_digest_list &&
keygen_set_std_prefs(pers_digest_list,PREFTYPE_HASH))
log_error(_("invalid personal digest preferences\n"));
if(pers_compress_list &&
keygen_set_std_prefs(pers_compress_list,PREFTYPE_ZIP))
log_error(_("invalid personal compress preferences\n"));
/* We don't support all possible commands with multifile yet */
if(multifile)
{
char *cmdname;
switch(cmd)
{
case aSign:
cmdname="--sign";
break;
case aClearsign:
cmdname="--clearsign";
break;
case aDetachedSign:
cmdname="--detach-sign";
break;
case aSym:
cmdname="--symmetric";
break;
case aEncrSym:
cmdname="--symmetric --encrypt";
break;
case aStore:
cmdname="--store";
break;
default:
cmdname=NULL;
break;
}
if(cmdname)
log_error(_("%s does not yet work with %s\n"),cmdname,"--multifile");
}
if( log_get_errorcount(0) )
g10_exit(2);
if(opt.compress_level==0)
opt.compress_algo=COMPRESS_ALGO_NONE;
/* Check our chosen algorithms against the list of legal
algorithms. */
if(!GNUPG)
{
const char *badalg=NULL;
preftype_t badtype=PREFTYPE_NONE;
if(opt.def_cipher_algo
&& !algo_available(PREFTYPE_SYM,opt.def_cipher_algo,NULL))
{
badalg = openpgp_cipher_algo_name (opt.def_cipher_algo);
badtype = PREFTYPE_SYM;
}
else if(opt.def_digest_algo
&& !algo_available(PREFTYPE_HASH,opt.def_digest_algo,NULL))
{
badalg = gcry_md_algo_name (opt.def_digest_algo);
badtype = PREFTYPE_HASH;
}
else if(opt.cert_digest_algo
&& !algo_available(PREFTYPE_HASH,opt.cert_digest_algo,NULL))
{
badalg = gcry_md_algo_name (opt.cert_digest_algo);
badtype = PREFTYPE_HASH;
}
else if(opt.compress_algo!=-1
&& !algo_available(PREFTYPE_ZIP,opt.compress_algo,NULL))
{
badalg = compress_algo_to_string(opt.compress_algo);
badtype = PREFTYPE_ZIP;
}
if(badalg)
{
switch(badtype)
{
case PREFTYPE_SYM:
log_info(_("you may not use cipher algorithm `%s'"
" while in %s mode\n"),
badalg,compliance_option_string());
break;
case PREFTYPE_HASH:
log_info(_("you may not use digest algorithm `%s'"
" while in %s mode\n"),
badalg,compliance_option_string());
break;
case PREFTYPE_ZIP:
log_info(_("you may not use compression algorithm `%s'"
" while in %s mode\n"),
badalg,compliance_option_string());
break;
default:
BUG();
}
compliance_failure();
}
}
/* Set the random seed file. */
if( use_random_seed ) {
char *p = make_filename(opt.homedir, "random_seed", NULL );
gcry_control (GCRYCTL_SET_RANDOM_SEED_FILE, p);
if (!access (p, F_OK))
register_secured_file (p);
xfree(p);
}
/* If there is no command but the --fingerprint is given, default
to the --list-keys command. */
if (!cmd && fpr_maybe_cmd)
{
set_cmd (&cmd, aListKeys);
}
if( opt.verbose > 1 )
set_packet_list_mode(1);
/* Add the keyrings, but not for some special commands. Also
avoid adding the secret keyring for a couple of commands to
avoid unneeded access in case the secrings are stored on a
floppy.
We always need to add the keyrings if we are running under
SELinux, this is so that the rings are added to the list of
secured files. */
if( ALWAYS_ADD_KEYRINGS
|| (cmd != aDeArmor && cmd != aEnArmor && cmd != aGPGConfTest) )
{
if (ALWAYS_ADD_KEYRINGS
|| (cmd != aCheckKeys && cmd != aListSigs && cmd != aListKeys
&& cmd != aVerify && cmd != aSym && cmd != aLocateKeys))
{
if (!sec_nrings || default_keyring) /* add default secret rings */
keydb_add_resource ("secring" EXTSEP_S "gpg", 4, 1);
for (sl = sec_nrings; sl; sl = sl->next)
keydb_add_resource ( sl->d, 0, 1 );
}
if( !nrings || default_keyring ) /* add default ring */
keydb_add_resource ("pubring" EXTSEP_S "gpg", 4, 0);
for(sl = nrings; sl; sl = sl->next )
keydb_add_resource ( sl->d, sl->flags, 0 );
}
FREE_STRLIST(nrings);
FREE_STRLIST(sec_nrings);
if (cmd == aGPGConfTest)
g10_exit(0);
if( pwfd != -1 ) /* Read the passphrase now. */
read_passphrase_from_fd( pwfd );
fname = argc? *argv : NULL;
if(fname && utf8_strings)
opt.flags.utf8_filename=1;
switch (cmd)
{
case aPrimegen:
case aPrintMD:
case aPrintMDs:
case aGenRandom:
case aDeArmor:
case aEnArmor:
break;
case aFixTrustDB:
case aExportOwnerTrust:
rc = setup_trustdb (0, trustdb_name);
break;
case aListTrustDB:
rc = setup_trustdb (argc? 1:0, trustdb_name);
break;
default:
/* If we are using TM_ALWAYS, we do not need to create the
trustdb. */
rc = setup_trustdb (opt.trust_model != TM_ALWAYS, trustdb_name);
break;
}
if (rc)
log_error (_("failed to initialize the TrustDB: %s\n"), g10_errstr(rc));
switch (cmd)
{
case aStore:
case aSym:
case aSign:
case aSignSym:
case aClearsign:
if (!opt.quiet && any_explicit_recipient)
log_info (_("WARNING: recipients (-r) given "
"without using public key encryption\n"));
break;
default:
break;
}
switch( cmd )
{
case aServer:
{
ctrl_t ctrl = xtrycalloc (1, sizeof *ctrl);
gpg_init_default_ctrl (ctrl);
gpg_server (ctrl);
gpg_deinit_default_ctrl (ctrl);
xfree (ctrl);
}
break;
case aStore: /* only store the file */
if( argc > 1 )
wrong_args(_("--store [filename]"));
if( (rc = encode_store(fname)) )
{
write_status_failure ("store", rc);
log_error ("storing `%s' failed: %s\n",
print_fname_stdin(fname),g10_errstr(rc) );
}
break;
case aSym: /* encrypt the given file only with the symmetric cipher */
if( argc > 1 )
wrong_args(_("--symmetric [filename]"));
if( (rc = encode_symmetric(fname)) )
{
write_status_failure ("symencrypt", rc);
log_error (_("symmetric encryption of `%s' failed: %s\n"),
print_fname_stdin(fname),g10_errstr(rc) );
}
break;
case aEncr: /* encrypt the given file */
if(multifile)
encode_crypt_files(argc, argv, remusr);
else
{
if( argc > 1 )
wrong_args(_("--encrypt [filename]"));
if( (rc = encode_crypt(fname,remusr,0)) )
{
write_status_failure ("encrypt", rc);
log_error("%s: encryption failed: %s\n",
print_fname_stdin(fname), g10_errstr(rc) );
}
}
break;
case aEncrSym:
/* This works with PGP 8 in the sense that it acts just like a
symmetric message. It doesn't work at all with 2 or 6. It
might work with 7, but alas, I don't have a copy to test
with right now. */
if( argc > 1 )
wrong_args(_("--symmetric --encrypt [filename]"));
else if(opt.s2k_mode==0)
log_error(_("you cannot use --symmetric --encrypt"
" with --s2k-mode 0\n"));
else if(PGP2 || PGP6 || PGP7 || RFC1991)
log_error(_("you cannot use --symmetric --encrypt"
" while in %s mode\n"),compliance_option_string());
else
{
if( (rc = encode_crypt(fname,remusr,1)) )
{
write_status_failure ("encrypt", rc);
log_error("%s: encryption failed: %s\n",
print_fname_stdin(fname), g10_errstr(rc) );
}
}
break;
case aSign: /* sign the given file */
sl = NULL;
if( detached_sig ) { /* sign all files */
for( ; argc; argc--, argv++ )
add_to_strlist( &sl, *argv );
}
else {
if( argc > 1 )
wrong_args(_("--sign [filename]"));
if( argc ) {
sl = xmalloc_clear( sizeof *sl + strlen(fname));
strcpy(sl->d, fname);
}
}
if( (rc = sign_file( sl, detached_sig, locusr, 0, NULL, NULL)) )
{
write_status_failure ("sign", rc);
log_error("signing failed: %s\n", g10_errstr(rc) );
}
free_strlist(sl);
break;
case aSignEncr: /* sign and encrypt the given file */
if( argc > 1 )
wrong_args(_("--sign --encrypt [filename]"));
if( argc ) {
sl = xmalloc_clear( sizeof *sl + strlen(fname));
strcpy(sl->d, fname);
}
else
sl = NULL;
if( (rc = sign_file(sl, detached_sig, locusr, 1, remusr, NULL)) )
{
write_status_failure ("sign-encrypt", rc);
log_error("%s: sign+encrypt failed: %s\n",
print_fname_stdin(fname), g10_errstr(rc) );
}
free_strlist(sl);
break;
case aSignEncrSym: /* sign and encrypt the given file */
if( argc > 1 )
wrong_args(_("--symmetric --sign --encrypt [filename]"));
else if(opt.s2k_mode==0)
log_error(_("you cannot use --symmetric --sign --encrypt"
" with --s2k-mode 0\n"));
else if(PGP2 || PGP6 || PGP7 || RFC1991)
log_error(_("you cannot use --symmetric --sign --encrypt"
" while in %s mode\n"),compliance_option_string());
else
{
if( argc )
{
sl = xmalloc_clear( sizeof *sl + strlen(fname));
strcpy(sl->d, fname);
}
else
sl = NULL;
if( (rc = sign_file(sl, detached_sig, locusr, 2, remusr, NULL)) )
{
write_status_failure ("sign-encrypt", rc);
log_error("%s: symmetric+sign+encrypt failed: %s\n",
print_fname_stdin(fname), g10_errstr(rc) );
}
free_strlist(sl);
}
break;
case aSignSym: /* sign and conventionally encrypt the given file */
if (argc > 1)
wrong_args(_("--sign --symmetric [filename]"));
rc = sign_symencrypt_file (fname, locusr);
if (rc)
{
write_status_failure ("sign-symencrypt", rc);
log_error("%s: sign+symmetric failed: %s\n",
print_fname_stdin(fname), g10_errstr(rc) );
}
break;
case aClearsign: /* make a clearsig */
if( argc > 1 )
wrong_args(_("--clearsign [filename]"));
if( (rc = clearsign_file(fname, locusr, NULL)) )
{
write_status_failure ("sign", rc);
log_error("%s: clearsign failed: %s\n",
print_fname_stdin(fname), g10_errstr(rc) );
}
break;
case aVerify:
if(multifile)
{
if( (rc = verify_files( argc, argv ) ))
log_error("verify files failed: %s\n", g10_errstr(rc) );
}
else
{
if( (rc = verify_signatures( argc, argv ) ))
log_error("verify signatures failed: %s\n", g10_errstr(rc) );
}
if (rc)
write_status_failure ("verify", rc);
break;
case aDecrypt:
if(multifile)
decrypt_messages(argc, argv);
else
{
if( argc > 1 )
wrong_args(_("--decrypt [filename]"));
if( (rc = decrypt_message( fname ) ))
{
write_status_failure ("decrypt", rc);
log_error("decrypt_message failed: %s\n", g10_errstr(rc));
}
}
break;
case aSignKey:
if( argc != 1 )
wrong_args(_("--sign-key user-id"));
/* fall through */
case aLSignKey:
if( argc != 1 )
wrong_args(_("--lsign-key user-id"));
/* fall through */
sl=NULL;
if(cmd==aSignKey)
append_to_strlist(&sl,"sign");
else if(cmd==aLSignKey)
append_to_strlist(&sl,"lsign");
else
BUG();
append_to_strlist( &sl, "save" );
username = make_username( fname );
keyedit_menu (username, locusr, sl, 0, 0 );
xfree(username);
free_strlist(sl);
break;
case aEditKey: /* Edit a key signature */
if( !argc )
wrong_args(_("--edit-key user-id [commands]"));
username = make_username( fname );
if( argc > 1 ) {
sl = NULL;
for( argc--, argv++ ; argc; argc--, argv++ )
append_to_strlist( &sl, *argv );
keyedit_menu( username, locusr, sl, 0, 1 );
free_strlist(sl);
}
else
keyedit_menu(username, locusr, NULL, 0, 1 );
xfree(username);
break;
case aPasswd:
if (argc != 1)
wrong_args (_("--passwd "));
else
{
username = make_username (fname);
keyedit_passwd (username);
xfree (username);
}
break;
case aDeleteKeys:
case aDeleteSecretKeys:
case aDeleteSecretAndPublicKeys:
sl = NULL;
/* I'm adding these in reverse order as add_to_strlist2
reverses them again, and it's easier to understand in the
proper order :) */
for( ; argc; argc-- )
add_to_strlist2( &sl, argv[argc-1], utf8_strings );
delete_keys(sl,cmd==aDeleteSecretKeys,cmd==aDeleteSecretAndPublicKeys);
free_strlist(sl);
break;
case aCheckKeys:
opt.check_sigs = 1;
case aListSigs:
opt.list_sigs = 1;
case aListKeys:
sl = NULL;
for( ; argc; argc--, argv++ )
add_to_strlist2( &sl, *argv, utf8_strings );
public_key_list( sl, 0 );
free_strlist(sl);
break;
case aListSecretKeys:
sl = NULL;
for( ; argc; argc--, argv++ )
add_to_strlist2( &sl, *argv, utf8_strings );
secret_key_list( sl );
free_strlist(sl);
break;
case aLocateKeys:
sl = NULL;
for (; argc; argc--, argv++)
add_to_strlist2( &sl, *argv, utf8_strings );
public_key_list (sl, 1);
free_strlist (sl);
break;
case aKeygen: /* generate a key */
if( opt.batch ) {
if( argc > 1 )
wrong_args("--gen-key [parameterfile]");
generate_keypair( argc? *argv : NULL, NULL, NULL );
}
else {
if( argc )
wrong_args("--gen-key");
generate_keypair(NULL, NULL, NULL);
}
break;
case aFastImport:
opt.import_options |= IMPORT_FAST;
case aImport:
import_keys( argc? argv:NULL, argc, NULL, opt.import_options );
break;
/* TODO: There are a number of command that use this same
"make strlist, call function, report error, free strlist"
pattern. Join them together here and avoid all that
duplicated code. */
case aExport:
case aSendKeys:
case aRecvKeys:
sl = NULL;
for( ; argc; argc--, argv++ )
append_to_strlist2( &sl, *argv, utf8_strings );
if( cmd == aSendKeys )
rc=keyserver_export( sl );
else if( cmd == aRecvKeys )
rc=keyserver_import( sl );
else
rc=export_pubkeys( sl, opt.export_options );
if(rc)
{
if(cmd==aSendKeys)
{
write_status_failure ("send-keys", rc);
log_error(_("keyserver send failed: %s\n"),g10_errstr(rc));
}
else if(cmd==aRecvKeys)
{
write_status_failure ("recv-keys", rc);
log_error(_("keyserver receive failed: %s\n"),g10_errstr(rc));
}
else
{
write_status_failure ("export", rc);
log_error(_("key export failed: %s\n"),g10_errstr(rc));
}
}
free_strlist(sl);
break;
case aSearchKeys:
sl = NULL;
for( ; argc; argc--, argv++ )
append_to_strlist2( &sl, *argv, utf8_strings );
rc=keyserver_search( sl );
if(rc)
{
write_status_failure ("search-keys", rc);
log_error(_("keyserver search failed: %s\n"), g10_errstr(rc));
}
free_strlist(sl);
break;
case aRefreshKeys:
sl = NULL;
for( ; argc; argc--, argv++ )
append_to_strlist2( &sl, *argv, utf8_strings );
rc=keyserver_refresh(sl);
if(rc)
{
write_status_failure ("refresh-keys", rc);
log_error(_("keyserver refresh failed: %s\n"),g10_errstr(rc));
}
free_strlist(sl);
break;
case aFetchKeys:
sl = NULL;
for( ; argc; argc--, argv++ )
append_to_strlist2( &sl, *argv, utf8_strings );
rc=keyserver_fetch(sl);
if(rc)
{
write_status_failure ("fetch-keys", rc);
log_error("key fetch failed: %s\n",g10_errstr(rc));
}
free_strlist(sl);
break;
case aExportSecret:
sl = NULL;
for( ; argc; argc--, argv++ )
add_to_strlist2( &sl, *argv, utf8_strings );
export_seckeys( sl );
free_strlist(sl);
break;
case aExportSecretSub:
sl = NULL;
for( ; argc; argc--, argv++ )
add_to_strlist2( &sl, *argv, utf8_strings );
export_secsubkeys( sl );
free_strlist(sl);
break;
case aGenRevoke:
if( argc != 1 )
wrong_args("--gen-revoke user-id");
username = make_username(*argv);
gen_revoke( username );
xfree( username );
break;
case aDesigRevoke:
if( argc != 1 )
wrong_args("--desig-revoke user-id");
username = make_username(*argv);
gen_desig_revoke( username, locusr );
xfree( username );
break;
case aDeArmor:
if( argc > 1 )
wrong_args("--dearmor [file]");
rc = dearmor_file( argc? *argv: NULL );
if( rc )
{
write_status_failure ("dearmor", rc);
log_error(_("dearmoring failed: %s\n"), g10_errstr(rc));
}
break;
case aEnArmor:
if( argc > 1 )
wrong_args("--enarmor [file]");
rc = enarmor_file( argc? *argv: NULL );
if( rc )
{
write_status_failure ("enarmor", rc);
log_error(_("enarmoring failed: %s\n"), g10_errstr(rc));
}
break;
case aPrimegen:
#if 0 /*FIXME*/
{ int mode = argc < 2 ? 0 : atoi(*argv);
if( mode == 1 && argc == 2 ) {
mpi_print( stdout, generate_public_prime( atoi(argv[1]) ), 1);
}
else if( mode == 2 && argc == 3 ) {
mpi_print( stdout, generate_elg_prime(
0, atoi(argv[1]),
atoi(argv[2]), NULL,NULL ), 1);
}
else if( mode == 3 && argc == 3 ) {
MPI *factors;
mpi_print( stdout, generate_elg_prime(
1, atoi(argv[1]),
atoi(argv[2]), NULL,&factors ), 1);
putchar('\n');
mpi_print( stdout, factors[0], 1 ); /* print q */
}
else if( mode == 4 && argc == 3 ) {
MPI g = mpi_alloc(1);
mpi_print( stdout, generate_elg_prime(
0, atoi(argv[1]),
atoi(argv[2]), g, NULL ), 1);
putchar('\n');
mpi_print( stdout, g, 1 );
mpi_free(g);
}
else
wrong_args("--gen-prime mode bits [qbits] ");
putchar('\n');
}
#endif
wrong_args("--gen-prime not yet supported ");
break;
case aGenRandom:
{
int level = argc ? atoi(*argv):0;
int count = argc > 1 ? atoi(argv[1]): 0;
int endless = !count;
if( argc < 1 || argc > 2 || level < 0 || level > 2 || count < 0 )
wrong_args("--gen-random 0|1|2 [count]");
while( endless || count ) {
byte *p;
/* Wee need a multiple of 3, so that in case of
armored output we get a correct string. No
linefolding is done, as it is best to levae this to
other tools */
size_t n = !endless && count < 99? count : 99;
p = gcry_random_bytes (n, level);
#ifdef HAVE_DOSISH_SYSTEM
setmode ( fileno(stdout), O_BINARY );
#endif
if (opt.armor) {
char *tmp = make_radix64_string (p, n);
fputs (tmp, stdout);
xfree (tmp);
if (n%3 == 1)
putchar ('=');
if (n%3)
putchar ('=');
} else {
fwrite( p, n, 1, stdout );
}
xfree(p);
if( !endless )
count -= n;
}
if (opt.armor)
putchar ('\n');
}
break;
case aPrintMD:
if( argc < 1)
wrong_args("--print-md algo [files]");
{
int all_algos = (**argv=='*' && !(*argv)[1]);
int algo = all_algos? 0 : gcry_md_map_name (*argv);
if( !algo && !all_algos )
log_error(_("invalid hash algorithm `%s'\n"), *argv );
else {
argc--; argv++;
if( !argc )
print_mds(NULL, algo);
else {
for(; argc; argc--, argv++ )
print_mds(*argv, algo);
}
}
}
break;
case aPrintMDs: /* old option */
if( !argc )
print_mds(NULL,0);
else {
for(; argc; argc--, argv++ )
print_mds(*argv,0);
}
break;
case aListTrustDB:
if( !argc )
list_trustdb(NULL);
else {
for( ; argc; argc--, argv++ )
list_trustdb( *argv );
}
break;
case aUpdateTrustDB:
if( argc )
wrong_args("--update-trustdb");
update_trustdb();
break;
case aCheckTrustDB:
/* Old versions allowed for arguments - ignore them */
check_trustdb();
break;
case aFixTrustDB:
how_to_fix_the_trustdb ();
break;
case aListTrustPath:
if( !argc )
wrong_args("--list-trust-path ");
for( ; argc; argc--, argv++ ) {
username = make_username( *argv );
list_trust_path( username );
xfree(username);
}
break;
case aExportOwnerTrust:
if( argc )
wrong_args("--export-ownertrust");
export_ownertrust();
break;
case aImportOwnerTrust:
if( argc > 1 )
wrong_args("--import-ownertrust [file]");
import_ownertrust( argc? *argv:NULL );
break;
case aRebuildKeydbCaches:
if (argc)
wrong_args ("--rebuild-keydb-caches");
keydb_rebuild_caches (1);
break;
#ifdef ENABLE_CARD_SUPPORT
case aCardStatus:
if (argc)
wrong_args ("--card-status");
card_status (stdout, NULL, 0);
break;
case aCardEdit:
if (argc) {
sl = NULL;
for (argc--, argv++ ; argc; argc--, argv++)
append_to_strlist (&sl, *argv);
card_edit (sl);
free_strlist (sl);
}
else
card_edit (NULL);
break;
case aChangePIN:
if (!argc)
change_pin (0,1);
else if (argc == 1)
change_pin (atoi (*argv),1);
else
wrong_args ("--change-pin [no]");
break;
#endif /* ENABLE_CARD_SUPPORT*/
case aListConfig:
{
char *str=collapse_args(argc,argv);
list_config(str);
xfree(str);
}
break;
case aListGcryptConfig:
/* Fixme: It would be nice to integrate that with
--list-config but unfortunately there is no way yet to have
libgcrypt print it to an estream for further parsing. */
gcry_control (GCRYCTL_PRINT_CONFIG, stdout);
break;
case aListPackets:
- opt.list_packets=2;
default:
if( argc > 1 )
wrong_args(_("[filename]"));
/* Issue some output for the unix newbie */
if( !fname && !opt.outfile && isatty( fileno(stdin) )
&& isatty( fileno(stdout) ) && isatty( fileno(stderr) ) )
log_info(_("Go ahead and type your message ...\n"));
a = iobuf_open(fname);
if (a && is_secured_file (iobuf_get_fd (a)))
{
iobuf_close (a);
a = NULL;
errno = EPERM;
}
if( !a )
log_error(_("can't open `%s'\n"), print_fname_stdin(fname));
else {
if( !opt.no_armor ) {
if( use_armor_filter( a ) ) {
afx = new_armor_context ();
push_armor_filter (afx, a);
}
}
if( cmd == aListPackets ) {
- set_packet_list_mode(1);
opt.list_packets=1;
+ set_packet_list_mode(1);
}
rc = proc_packets(NULL, a );
if( rc )
{
write_status_failure ("-", rc);
log_error("processing message failed: %s\n", g10_errstr(rc) );
}
iobuf_close(a);
}
break;
}
/* cleanup */
release_armor_context (afx);
FREE_STRLIST(remusr);
FREE_STRLIST(locusr);
g10_exit(0);
return 8; /*NEVER REACHED*/
}
/* Note: This function is used by signal handlers!. */
static void
emergency_cleanup (void)
{
gcry_control (GCRYCTL_TERM_SECMEM );
}
void
g10_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 : g10_errors_seen? 1 : 0;
exit (rc);
}
/* Pretty-print hex hashes. This assumes at least an 80-character
display, but there are a few other similar assumptions in the
display code. */
static void
print_hex( gcry_md_hd_t md, int algo, const char *fname )
{
int i,n,count,indent=0;
const byte *p;
if(fname)
indent=printf("%s: ",fname);
if(indent>40)
{
printf("\n");
indent=0;
}
if(algo==DIGEST_ALGO_RMD160)
indent+=printf("RMD160 = ");
else if(algo>0)
indent+=printf("%6s = ", gcry_md_algo_name (algo));
else
algo=abs(algo);
count=indent;
p = gcry_md_read (md, algo);
n = gcry_md_get_algo_dlen (algo);
count += printf ("%02X",*p++);
for(i=1;i79)
{
printf("\n%*s",indent," ");
count=indent;
}
else
count+=printf(" ");
if(!(i%8))
count+=printf(" ");
}
else if (n==20)
{
if(!(i%2))
{
if(count+4>79)
{
printf("\n%*s",indent," ");
count=indent;
}
else
count+=printf(" ");
}
if(!(i%10))
count+=printf(" ");
}
else
{
if(!(i%4))
{
if(count+8>79)
{
printf("\n%*s",indent," ");
count=indent;
}
else
count+=printf(" ");
}
}
count+=printf("%02X",*p);
}
printf("\n");
}
static void
print_hashline( gcry_md_hd_t md, int algo, const char *fname )
{
int i, n;
const byte *p;
if ( fname ) {
for (p = fname; *p; p++ ) {
if ( *p <= 32 || *p > 127 || *p == ':' || *p == '%' )
printf("%%%02X", *p );
else
putchar( *p );
}
}
putchar(':');
printf("%d:", algo );
p = gcry_md_read (md, algo);
n = gcry_md_get_algo_dlen (algo);
for(i=0; i < n ; i++, p++ )
printf("%02X", *p );
putchar(':');
putchar('\n');
}
static void
print_mds( const char *fname, int algo )
{
FILE *fp;
char buf[1024];
size_t n;
gcry_md_hd_t md;
if( !fname ) {
fp = stdin;
#ifdef HAVE_DOSISH_SYSTEM
setmode ( fileno(fp) , O_BINARY );
#endif
}
else {
fp = fopen( fname, "rb" );
if (fp && is_secured_file (fileno (fp)))
{
fclose (fp);
fp = NULL;
errno = EPERM;
}
}
if( !fp ) {
log_error("%s: %s\n", fname?fname:"[stdin]", strerror(errno) );
return;
}
gcry_md_open (&md, 0, 0);
if( algo )
gcry_md_enable (md, algo);
else {
gcry_md_enable (md, GCRY_MD_MD5);
gcry_md_enable (md, GCRY_MD_SHA1);
gcry_md_enable (md, GCRY_MD_RMD160);
if (!openpgp_md_test_algo (GCRY_MD_SHA224))
gcry_md_enable (md, GCRY_MD_SHA224);
if (!openpgp_md_test_algo (GCRY_MD_SHA256))
gcry_md_enable (md, GCRY_MD_SHA256);
if (!openpgp_md_test_algo (GCRY_MD_SHA384))
gcry_md_enable (md, GCRY_MD_SHA384);
if (!openpgp_md_test_algo (GCRY_MD_SHA512))
gcry_md_enable (md, GCRY_MD_SHA512);
}
while( (n=fread( buf, 1, DIM(buf), fp )) )
gcry_md_write (md, buf, n);
if( ferror(fp) )
log_error("%s: %s\n", fname?fname:"[stdin]", strerror(errno) );
else {
gcry_md_final (md);
if ( opt.with_colons ) {
if ( algo )
print_hashline( md, algo, fname );
else {
print_hashline( md, GCRY_MD_MD5, fname );
print_hashline( md, GCRY_MD_SHA1, fname );
if (!gcry_md_test_algo (GCRY_MD_RMD160))
print_hashline( md, GCRY_MD_RMD160, fname );
if (!gcry_md_test_algo (GCRY_MD_SHA224))
print_hashline (md, GCRY_MD_SHA224, fname);
if (!gcry_md_test_algo (GCRY_MD_SHA256))
print_hashline( md, GCRY_MD_SHA256, fname );
if (!gcry_md_test_algo (GCRY_MD_SHA384))
print_hashline ( md, GCRY_MD_SHA384, fname );
if (!gcry_md_test_algo (GCRY_MD_SHA512))
print_hashline ( md, GCRY_MD_SHA512, fname );
}
}
else {
if( algo )
print_hex(md,-algo,fname);
else {
print_hex( md, GCRY_MD_MD5, fname );
print_hex( md, GCRY_MD_SHA1, fname );
if (!gcry_md_test_algo (GCRY_MD_RMD160))
print_hex( md, GCRY_MD_RMD160, fname );
if (!gcry_md_test_algo (GCRY_MD_SHA224))
print_hex (md, GCRY_MD_SHA224, fname);
if (!gcry_md_test_algo (GCRY_MD_SHA256))
print_hex( md, GCRY_MD_SHA256, fname );
if (!gcry_md_test_algo (GCRY_MD_SHA384))
print_hex( md, GCRY_MD_SHA384, fname );
if (!gcry_md_test_algo (GCRY_MD_SHA512))
print_hex( md, GCRY_MD_SHA512, fname );
}
}
}
gcry_md_close(md);
if( fp != stdin )
fclose(fp);
}
/****************
* Check the supplied name,value string and add it to the notation
* data to be used for signatures. which==0 for sig notations, and 1
* for cert notations.
*/
static void
add_notation_data( const char *string, int which )
{
struct notation *notation;
notation=string_to_notation(string,utf8_strings);
if(notation)
{
if(which)
{
notation->next=opt.cert_notations;
opt.cert_notations=notation;
}
else
{
notation->next=opt.sig_notations;
opt.sig_notations=notation;
}
}
}
static void
add_policy_url( const char *string, int which )
{
unsigned int i,critical=0;
strlist_t sl;
if(*string=='!')
{
string++;
critical=1;
}
for(i=0;iflags |= 1;
}
static void
add_keyserver_url( const char *string, int which )
{
unsigned int i,critical=0;
strlist_t sl;
if(*string=='!')
{
string++;
critical=1;
}
for(i=0;iflags |= 1;
}
diff --git a/g10/mainproc.c b/g10/mainproc.c
index 17d40def9..8c2d2e19d 100644
--- a/g10/mainproc.c
+++ b/g10/mainproc.c
@@ -1,2238 +1,2238 @@
/* mainproc.c - handle packets
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
* 2008, 2009 Free Software Foundation, Inc.
* Copyright (C) 2013 Werner Koch
*
* 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 .
*/
#include
#include
#include
#include
#include
#include
#include "gpg.h"
#include "packet.h"
#include "iobuf.h"
#include "options.h"
#include "util.h"
#include "cipher.h"
#include "keydb.h"
#include "filter.h"
#include "main.h"
#include "status.h"
#include "i18n.h"
#include "trustdb.h"
#include "keyserver-internal.h"
#include "photoid.h"
#include "pka.h"
/* Put an upper limit on nested packets. The 32 is an arbitrary
value, a much lower should actually be sufficient. */
#define MAX_NESTING_DEPTH 32
struct kidlist_item {
struct kidlist_item *next;
u32 kid[2];
int pubkey_algo;
int reason;
};
/****************
* Structure to hold the context
*/
typedef struct mainproc_context *CTX;
struct mainproc_context
{
struct mainproc_context *anchor; /* May be useful in the future. */
PKT_public_key *last_pubkey;
PKT_secret_key *last_seckey;
PKT_user_id *last_user_id;
md_filter_context_t mfx;
int sigs_only; /* Process only signatures and reject all other stuff. */
int encrypt_only; /* Process only encryption messages. */
/* Name of the file with the complete signature or the file with the
detached signature. This is currently only used to deduce the
file name of the data file if that has not been given. */
const char *sigfilename;
/* A structure to describe the signed data in case of a detached
signature. */
struct
{
/* A file descriptor of the the signed data. Only used if not -1. */
int data_fd;
/* A list of filenames with the data files or NULL. This is only
used if DATA_FD is -1. */
strlist_t data_names;
/* Flag to indicated that either one of the next previous fieldss
is used. This is only needed for better readability. */
int used;
} signed_data;
DEK *dek;
int last_was_session_key;
KBNODE list; /* The current list of packets. */
IOBUF iobuf; /* Used to get the filename etc. */
int trustletter; /* Temporary usage in list_node. */
ulong symkeys;
struct kidlist_item *pkenc_list; /* List of encryption packets. */
struct {
unsigned int sig_seen:1; /* Set to true if a signature packet
has been seen. */
unsigned int data:1; /* Any data packet seen */
unsigned int uncompress_failed:1;
} any;
};
static int do_proc_packets( CTX c, IOBUF a );
static void list_node( CTX c, KBNODE node );
static void proc_tree( CTX c, KBNODE node );
static int literals_seen;
void
reset_literals_seen(void)
{
literals_seen=0;
}
static void
release_list( CTX c )
{
if( !c->list )
return;
proc_tree(c, c->list );
release_kbnode( c->list );
while( c->pkenc_list ) {
struct kidlist_item *tmp = c->pkenc_list->next;
xfree( c->pkenc_list );
c->pkenc_list = tmp;
}
c->pkenc_list = NULL;
c->list = NULL;
c->any.data = 0;
c->any.uncompress_failed = 0;
c->last_was_session_key = 0;
xfree(c->dek); c->dek = NULL;
}
static int
add_onepass_sig( CTX c, PACKET *pkt )
{
KBNODE node;
if ( c->list ) /* add another packet */
add_kbnode( c->list, new_kbnode( pkt ));
else /* insert the first one */
c->list = node = new_kbnode( pkt );
return 1;
}
static int
add_gpg_control( CTX c, PACKET *pkt )
{
if ( pkt->pkt.gpg_control->control == CTRLPKT_CLEARSIGN_START ) {
/* New clear text signature.
* Process the last one and reset everything */
release_list(c);
}
if( c->list ) /* add another packet */
add_kbnode( c->list, new_kbnode( pkt ));
else /* insert the first one */
c->list = new_kbnode( pkt );
return 1;
}
static int
add_user_id( CTX c, PACKET *pkt )
{
if( !c->list ) {
log_error("orphaned user ID\n" );
return 0;
}
add_kbnode( c->list, new_kbnode( pkt ) );
return 1;
}
static int
add_subkey( CTX c, PACKET *pkt )
{
if( !c->list ) {
log_error("subkey w/o mainkey\n" );
return 0;
}
add_kbnode( c->list, new_kbnode( pkt ) );
return 1;
}
static int
add_ring_trust( CTX c, PACKET *pkt )
{
if( !c->list ) {
log_error("ring trust w/o key\n" );
return 0;
}
add_kbnode( c->list, new_kbnode( pkt ) );
return 1;
}
static int
add_signature( CTX c, PACKET *pkt )
{
KBNODE node;
c->any.sig_seen = 1;
if( pkt->pkttype == PKT_SIGNATURE && !c->list ) {
/* This is the first signature for the following datafile.
* GPG does not write such packets; instead it always uses
* onepass-sig packets. The drawback of PGP's method
* of prepending the signature to the data is
* that it is not possible to make a signature from data read
* from stdin. (GPG is able to read PGP stuff anyway.) */
node = new_kbnode( pkt );
c->list = node;
return 1;
}
else if( !c->list )
return 0; /* oops (invalid packet sequence)*/
else if( !c->list->pkt )
BUG(); /* so nicht */
/* add a new signature node id at the end */
node = new_kbnode( pkt );
add_kbnode( c->list, node );
return 1;
}
static int
symkey_decrypt_seskey( DEK *dek, byte *seskey, size_t slen )
{
gcry_cipher_hd_t hd;
if(slen < 17 || slen > 33)
{
log_error ( _("weird size for an encrypted session key (%d)\n"),
(int)slen);
return G10ERR_BAD_KEY;
}
if (openpgp_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1))
BUG ();
if (gcry_cipher_setkey ( hd, dek->key, dek->keylen ))
BUG ();
gcry_cipher_setiv ( hd, NULL, 0 );
gcry_cipher_decrypt ( hd, seskey, slen, NULL, 0 );
gcry_cipher_close ( hd );
/* Now we replace the dek components with the real session key to
decrypt the contents of the sequencing packet. */
dek->keylen=slen-1;
dek->algo=seskey[0];
if(dek->keylen > DIM(dek->key))
BUG ();
/* This is not completely accurate, since a bad passphrase may have
resulted in a garbage algorithm byte, but it's close enough since
a bogus byte here will fail later. */
if(dek->algo==CIPHER_ALGO_IDEA)
idea_cipher_warn(0);
memcpy(dek->key, seskey + 1, dek->keylen);
/*log_hexdump( "thekey", dek->key, dek->keylen );*/
return 0;
}
static void
proc_symkey_enc( CTX c, PACKET *pkt )
{
PKT_symkey_enc *enc;
enc = pkt->pkt.symkey_enc;
if (!enc)
log_error ("invalid symkey encrypted packet\n");
else if(!c->dek)
{
int algo = enc->cipher_algo;
const char *s = openpgp_cipher_algo_name (algo);
if (!openpgp_cipher_test_algo (algo))
{
if(!opt.quiet)
{
if(enc->seskeylen)
log_info(_("%s encrypted session key\n"), s );
else
log_info(_("%s encrypted data\n"), s );
}
}
else
log_error(_("encrypted with unknown algorithm %d\n"), algo );
if(openpgp_md_test_algo (enc->s2k.hash_algo))
{
log_error(_("passphrase generated with unknown digest"
" algorithm %d\n"),enc->s2k.hash_algo);
s=NULL;
}
c->last_was_session_key = 2;
if(!s || opt.list_only)
goto leave;
if(opt.override_session_key)
{
c->dek = xmalloc_clear( sizeof *c->dek );
if(get_override_session_key(c->dek, opt.override_session_key))
{
xfree(c->dek);
c->dek = NULL;
}
}
else
{
c->dek = passphrase_to_dek (NULL, 0, algo, &enc->s2k, 3,
NULL, NULL);
if(c->dek)
{
c->dek->symmetric=1;
/* FIXME: This doesn't work perfectly if a symmetric
key comes before a public key in the message - if
the user doesn't know the passphrase, then there is
a chance that the "decrypted" algorithm will happen
to be a valid one, which will make the returned dek
appear valid, so we won't try any public keys that
come later. */
if(enc->seskeylen)
{
if(symkey_decrypt_seskey(c->dek, enc->seskey,
enc->seskeylen))
{
xfree(c->dek);
c->dek=NULL;
}
}
else
c->dek->algo_info_printed = 1;
}
}
}
leave:
c->symkeys++;
free_packet(pkt);
}
static void
proc_pubkey_enc( CTX c, PACKET *pkt )
{
PKT_pubkey_enc *enc;
int result = 0;
/* check whether the secret key is available and store in this case */
c->last_was_session_key = 1;
enc = pkt->pkt.pubkey_enc;
/*printf("enc: encrypted by a pubkey with keyid %08lX\n", enc->keyid[1] );*/
/* Hmmm: why do I have this algo check here - anyway there is
* function to check it. */
if( opt.verbose )
log_info(_("public key is %s\n"), keystr(enc->keyid) );
if( is_status_enabled() ) {
char buf[50];
sprintf(buf, "%08lX%08lX %d 0",
(ulong)enc->keyid[0], (ulong)enc->keyid[1], enc->pubkey_algo );
write_status_text( STATUS_ENC_TO, buf );
}
if( !opt.list_only && opt.override_session_key ) {
/* It does not make much sense to store the session key in
* secure memory because it has already been passed on the
* command line and the GCHQ knows about it. */
c->dek = xmalloc_clear( sizeof *c->dek );
result = get_override_session_key ( c->dek, opt.override_session_key );
if ( result ) {
xfree(c->dek); c->dek = NULL;
}
}
else if( enc->pubkey_algo == PUBKEY_ALGO_ELGAMAL_E
|| enc->pubkey_algo == PUBKEY_ALGO_RSA
|| enc->pubkey_algo == PUBKEY_ALGO_RSA_E
|| enc->pubkey_algo == PUBKEY_ALGO_ELGAMAL) {
/* Note that we also allow type 20 Elgamal keys for decryption.
There are still a couple of those keys in active use as a
subkey. */
/* FIXME: Store this all in a list and process it later so that
we can prioritize what key to use. This gives a better user
experience if wildcard keyids are used. */
if ( !c->dek && ((!enc->keyid[0] && !enc->keyid[1])
|| opt.try_all_secrets
|| !seckey_available( enc->keyid )) ) {
if( opt.list_only )
result = -1;
else {
c->dek = xmalloc_secure_clear( sizeof *c->dek );
if( (result = get_session_key( enc, c->dek )) ) {
/* error: delete the DEK */
xfree(c->dek); c->dek = NULL;
}
}
}
else
result = G10ERR_NO_SECKEY;
}
else
result = G10ERR_PUBKEY_ALGO;
if( result == -1 )
;
else
{
/* store it for later display */
struct kidlist_item *x = xmalloc( sizeof *x );
x->kid[0] = enc->keyid[0];
x->kid[1] = enc->keyid[1];
x->pubkey_algo = enc->pubkey_algo;
x->reason = result;
x->next = c->pkenc_list;
c->pkenc_list = x;
if( !result && opt.verbose > 1 )
log_info( _("public key encrypted data: good DEK\n") );
}
free_packet(pkt);
}
/****************
* Print the list of public key encrypted packets which we could
* not decrypt.
*/
static void
print_pkenc_list( struct kidlist_item *list, int failed )
{
for( ; list; list = list->next ) {
PKT_public_key *pk;
const char *algstr;
if ( failed && !list->reason )
continue;
if ( !failed && list->reason )
continue;
algstr = openpgp_pk_algo_name ( list->pubkey_algo );
pk = xmalloc_clear( sizeof *pk );
if( !algstr )
algstr = "[?]";
pk->pubkey_algo = list->pubkey_algo;
if( !get_pubkey( pk, list->kid ) )
{
char *p;
log_info( _("encrypted with %u-bit %s key, ID %s, created %s\n"),
nbits_from_pk( pk ), algstr, keystr_from_pk(pk),
strtimestamp(pk->timestamp) );
p=get_user_id_native(list->kid);
log_printf (_(" \"%s\"\n"),p);
xfree(p);
}
else
log_info(_("encrypted with %s key, ID %s\n"),
algstr,keystr(list->kid));
free_public_key( pk );
if( list->reason == G10ERR_NO_SECKEY ) {
if( is_status_enabled() ) {
char buf[20];
snprintf (buf, sizeof buf, "%08lX%08lX",
(ulong)list->kid[0], (ulong)list->kid[1]);
write_status_text( STATUS_NO_SECKEY, buf );
}
}
else if (list->reason)
{
log_info(_("public key decryption failed: %s\n"),
g10_errstr(list->reason));
write_status_error ("pkdecrypt_failed", list->reason);
}
}
}
static void
proc_encrypted( CTX c, PACKET *pkt )
{
int result = 0;
if (!opt.quiet)
{
if(c->symkeys>1)
log_info(_("encrypted with %lu passphrases\n"),c->symkeys);
else if(c->symkeys==1)
log_info(_("encrypted with 1 passphrase\n"));
print_pkenc_list ( c->pkenc_list, 1 );
print_pkenc_list ( c->pkenc_list, 0 );
}
/* FIXME: Figure out the session key by looking at all pkenc packets. */
write_status( STATUS_BEGIN_DECRYPTION );
/*log_debug("dat: %sencrypted data\n", c->dek?"":"conventional ");*/
if( opt.list_only )
result = -1;
else if( !c->dek && !c->last_was_session_key ) {
int algo;
STRING2KEY s2kbuf;
STRING2KEY *s2k = NULL;
int canceled;
if(opt.override_session_key)
{
c->dek = xmalloc_clear( sizeof *c->dek );
result=get_override_session_key(c->dek, opt.override_session_key);
if(result)
{
xfree(c->dek);
c->dek = NULL;
}
}
else
{
/* Assume this is old style conventional encrypted data. */
algo = opt.def_cipher_algo;
if ( algo )
log_info (_("assuming %s encrypted data\n"),
openpgp_cipher_algo_name (algo));
else if ( openpgp_cipher_test_algo (CIPHER_ALGO_IDEA) )
{
algo = opt.def_cipher_algo;
if (!algo)
algo = opt.s2k_cipher_algo;
idea_cipher_warn(1);
log_info (_("IDEA cipher unavailable, "
"optimistically attempting to use %s instead\n"),
openpgp_cipher_algo_name (algo));
}
else
{
algo = CIPHER_ALGO_IDEA;
if (!opt.s2k_digest_algo)
{
/* If no digest is given we assume MD5 */
s2kbuf.mode = 0;
s2kbuf.hash_algo = DIGEST_ALGO_MD5;
s2k = &s2kbuf;
}
log_info (_("assuming %s encrypted data\n"), "IDEA");
}
c->dek = passphrase_to_dek ( NULL, 0, algo, s2k, 3, NULL,&canceled);
if (c->dek)
c->dek->algo_info_printed = 1;
else if (canceled)
result = gpg_error (GPG_ERR_CANCELED);
else
result = gpg_error (GPG_ERR_INV_PASSPHRASE);
}
}
else if( !c->dek )
result = G10ERR_NO_SECKEY;
if( !result )
result = decrypt_data( c, pkt->pkt.encrypted, c->dek );
if( result == -1 )
;
else if( !result || (gpg_err_code (result) == GPG_ERR_BAD_SIGNATURE
&& opt.ignore_mdc_error)) {
write_status( STATUS_DECRYPTION_OKAY );
if( opt.verbose > 1 )
log_info(_("decryption okay\n"));
if( pkt->pkt.encrypted->mdc_method && !result )
write_status( STATUS_GOODMDC );
else if(!opt.no_mdc_warn)
log_info (_("WARNING: message was not integrity protected\n"));
}
else if( gpg_err_code (result) == G10ERR_BAD_SIGN ) {
log_error(_("WARNING: encrypted message has been manipulated!\n"));
write_status( STATUS_BADMDC );
write_status( STATUS_DECRYPTION_FAILED );
}
else {
if (gpg_err_code (result) == GPG_ERR_BAD_KEY
&& *c->dek->s2k_cacheid != '\0')
{
log_debug(_("cleared passphrase cached with ID: %s\n"),
c->dek->s2k_cacheid);
passphrase_clear_cache (NULL, c->dek->s2k_cacheid, 0);
}
write_status( STATUS_DECRYPTION_FAILED );
log_error(_("decryption failed: %s\n"), g10_errstr(result));
/* Hmmm: does this work when we have encrypted using multiple
* ways to specify the session key (symmmetric and PK)*/
}
xfree(c->dek); c->dek = NULL;
free_packet(pkt);
c->last_was_session_key = 0;
write_status( STATUS_END_DECRYPTION );
}
static void
proc_plaintext( CTX c, PACKET *pkt )
{
PKT_plaintext *pt = pkt->pkt.plaintext;
int any, clearsig, only_md5, rc;
KBNODE n;
literals_seen++;
if( pt->namelen == 8 && !memcmp( pt->name, "_CONSOLE", 8 ) )
log_info(_("NOTE: sender requested \"for-your-eyes-only\"\n"));
else if( opt.verbose )
log_info(_("original file name='%.*s'\n"), pt->namelen, pt->name);
free_md_filter_context( &c->mfx );
if (gcry_md_open (&c->mfx.md, 0, 0))
BUG ();
/* fixme: we may need to push the textfilter if we have sigclass 1
* and no armoring - Not yet tested
* Hmmm, why don't we need it at all if we have sigclass 1
* Should we assume that plaintext in mode 't' has always sigclass 1??
* See: Russ Allbery's mail 1999-02-09
*/
any = clearsig = only_md5 = 0;
for(n=c->list; n; n = n->next )
{
if( n->pkt->pkttype == PKT_ONEPASS_SIG )
{
/* For the onepass signature case */
if( n->pkt->pkt.onepass_sig->digest_algo )
{
gcry_md_enable (c->mfx.md,
n->pkt->pkt.onepass_sig->digest_algo);
if( !any && n->pkt->pkt.onepass_sig->digest_algo
== DIGEST_ALGO_MD5 )
only_md5 = 1;
else
only_md5 = 0;
any = 1;
}
if( n->pkt->pkt.onepass_sig->sig_class != 0x01 )
only_md5 = 0;
}
else if( n->pkt->pkttype == PKT_GPG_CONTROL
&& n->pkt->pkt.gpg_control->control
== CTRLPKT_CLEARSIGN_START )
{
/* For the clearsigned message case */
size_t datalen = n->pkt->pkt.gpg_control->datalen;
const byte *data = n->pkt->pkt.gpg_control->data;
/* check that we have at least the sigclass and one hash */
if ( datalen < 2 )
log_fatal("invalid control packet CTRLPKT_CLEARSIGN_START\n");
/* Note that we don't set the clearsig flag for not-dash-escaped
* documents */
clearsig = (*data == 0x01);
for( data++, datalen--; datalen; datalen--, data++ )
gcry_md_enable (c->mfx.md, *data);
any = 1;
break; /* Stop here as one-pass signature packets are not
expected. */
}
else if(n->pkt->pkttype==PKT_SIGNATURE)
{
/* For the SIG+LITERAL case that PGP used to use. */
gcry_md_enable ( c->mfx.md, n->pkt->pkt.signature->digest_algo );
any=1;
}
}
if( !any && !opt.skip_verify )
{
/* This is for the old GPG LITERAL+SIG case. It's not legal
according to 2440, so hopefully it won't come up that
often. There is no good way to specify what algorithms to
use in that case, so these three are the historical
answer. */
gcry_md_enable( c->mfx.md, DIGEST_ALGO_RMD160 );
gcry_md_enable( c->mfx.md, DIGEST_ALGO_SHA1 );
gcry_md_enable( c->mfx.md, DIGEST_ALGO_MD5 );
}
if( opt.pgp2_workarounds && only_md5 && !opt.skip_verify ) {
/* This is a kludge to work around a bug in pgp2. It does only
* catch those mails which are armored. To catch the non-armored
* pgp mails we could see whether there is the signature packet
* in front of the plaintext. If someone needs this, send me a patch.
*/
if ( gcry_md_open (&c->mfx.md2, DIGEST_ALGO_MD5, 0) )
BUG ();
}
if ( DBG_HASHING ) {
gcry_md_debug ( c->mfx.md, "verify" );
if ( c->mfx.md2 )
gcry_md_debug ( c->mfx.md2, "verify2" );
}
rc=0;
if (literals_seen>1)
{
log_info (_("WARNING: multiple plaintexts seen\n"));
if (!opt.flags.allow_multiple_messages)
{
write_status_text (STATUS_ERROR, "proc_pkt.plaintext 89_BAD_DATA");
log_inc_errorcount ();
rc = gpg_error (GPG_ERR_UNEXPECTED);
}
}
if(!rc)
{
rc = handle_plaintext( pt, &c->mfx, c->sigs_only, clearsig );
if ( gpg_err_code (rc) == GPG_ERR_EACCES && !c->sigs_only )
{
/* Can't write output but we hash it anyway to check the
signature. */
rc = handle_plaintext( pt, &c->mfx, 1, clearsig );
}
}
if( rc )
log_error( "handle plaintext failed: %s\n", g10_errstr(rc));
free_packet(pkt);
c->last_was_session_key = 0;
/* We add a marker control packet instead of the plaintext packet.
* This is so that we can later detect invalid packet sequences.
*/
n = new_kbnode (create_gpg_control (CTRLPKT_PLAINTEXT_MARK, NULL, 0));
if (c->list)
add_kbnode (c->list, n);
else
c->list = n;
}
static int
proc_compressed_cb( IOBUF a, void *info )
{
if ( ((CTX)info)->signed_data.used
&& ((CTX)info)->signed_data.data_fd != -1)
return proc_signature_packets_by_fd (info, a,
((CTX)info)->signed_data.data_fd);
else
return proc_signature_packets (info, a,
((CTX)info)->signed_data.data_names,
((CTX)info)->sigfilename );
}
static int
proc_encrypt_cb( IOBUF a, void *info )
{
return proc_encryption_packets( info, a );
}
static int
proc_compressed( CTX c, PACKET *pkt )
{
PKT_compressed *zd = pkt->pkt.compressed;
int rc;
/*printf("zip: compressed data packet\n");*/
if (c->sigs_only)
rc = handle_compressed (c, zd, proc_compressed_cb, c);
else if (c->encrypt_only)
rc = handle_compressed (c, zd, proc_encrypt_cb, c);
else
rc = handle_compressed (c, zd, NULL, NULL);
if (gpg_err_code (rc) == GPG_ERR_BAD_DATA)
{
if (!c->any.uncompress_failed)
{
CTX cc;
for (cc=c; cc; cc = cc->anchor)
cc->any.uncompress_failed = 1;
log_error ("uncompressing failed: %s\n", g10_errstr(rc));
}
}
else if (rc)
log_error("uncompressing failed: %s\n", g10_errstr(rc));
free_packet (pkt);
c->last_was_session_key = 0;
return rc;
}
/****************
* check the signature
* Returns: 0 = valid signature or an error code
*/
static int
do_check_sig( CTX c, KBNODE node, int *is_selfsig,
int *is_expkey, int *is_revkey )
{
PKT_signature *sig;
gcry_md_hd_t md = NULL, md2 = NULL;
int algo, rc;
assert( node->pkt->pkttype == PKT_SIGNATURE );
if( is_selfsig )
*is_selfsig = 0;
sig = node->pkt->pkt.signature;
algo = sig->digest_algo;
rc = openpgp_md_test_algo(algo);
if (rc)
return rc;
if( sig->sig_class == 0x00 ) {
if( c->mfx.md )
{
if (gcry_md_copy (&md, c->mfx.md ))
BUG ();
}
else /* detached signature */
{
/* signature_check() will enable the md*/
if (gcry_md_open (&md, 0, 0 ))
BUG ();
}
}
else if( sig->sig_class == 0x01 ) {
/* how do we know that we have to hash the (already hashed) text
* in canonical mode ??? (calculating both modes???) */
if( c->mfx.md ) {
if (gcry_md_copy (&md, c->mfx.md ))
BUG ();
if( c->mfx.md2 && gcry_md_copy (&md2, c->mfx.md2 ))
BUG ();
}
else { /* detached signature */
log_debug("Do we really need this here?");
/* signature_check() will enable the md*/
if (gcry_md_open (&md, 0, 0 ))
BUG ();
if (gcry_md_open (&md2, 0, 0 ))
BUG ();
}
}
else if( (sig->sig_class&~3) == 0x10
|| sig->sig_class == 0x18
|| sig->sig_class == 0x1f
|| sig->sig_class == 0x20
|| sig->sig_class == 0x28
|| sig->sig_class == 0x30 ) {
if( c->list->pkt->pkttype == PKT_PUBLIC_KEY
|| c->list->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
return check_key_signature( c->list, node, is_selfsig );
}
else if( sig->sig_class == 0x20 ) {
log_error (_("standalone revocation - "
"use \"gpg --import\" to apply\n"));
return G10ERR_NOT_PROCESSED;
}
else {
log_error("invalid root packet for sigclass %02x\n",
sig->sig_class);
return G10ERR_SIG_CLASS;
}
}
else
return G10ERR_SIG_CLASS;
rc = signature_check2( sig, md, NULL, is_expkey, is_revkey, NULL );
if( gpg_err_code (rc) == GPG_ERR_BAD_SIGNATURE && md2 )
rc = signature_check2( sig, md2, NULL, is_expkey, is_revkey, NULL );
gcry_md_close(md);
gcry_md_close(md2);
return rc;
}
static void
print_userid( PACKET *pkt )
{
if( !pkt )
BUG();
if( pkt->pkttype != PKT_USER_ID ) {
printf("ERROR: unexpected packet type %d", pkt->pkttype );
return;
}
if( opt.with_colons )
{
if(pkt->pkt.user_id->attrib_data)
printf("%u %lu",
pkt->pkt.user_id->numattribs,
pkt->pkt.user_id->attrib_len);
else
print_string( stdout, pkt->pkt.user_id->name,
pkt->pkt.user_id->len, ':');
}
else
print_utf8_string( stdout, pkt->pkt.user_id->name,
pkt->pkt.user_id->len );
}
/****************
* List the certificate in a user friendly way
*/
static void
list_node( CTX c, KBNODE node )
{
int mainkey;
if( !node )
;
else if( (mainkey = (node->pkt->pkttype == PKT_PUBLIC_KEY) )
|| node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
PKT_public_key *pk = node->pkt->pkt.public_key;
if( opt.with_colons )
{
u32 keyid[2];
keyid_from_pk( pk, keyid );
if( mainkey )
c->trustletter = opt.fast_list_mode?
0 : get_validity_info( pk, NULL );
printf("%s:", mainkey? "pub":"sub" );
if( c->trustletter )
putchar( c->trustletter );
printf(":%u:%d:%08lX%08lX:%s:%s::",
nbits_from_pk( pk ),
pk->pubkey_algo,
(ulong)keyid[0],(ulong)keyid[1],
colon_datestr_from_pk( pk ),
colon_strtime (pk->expiredate) );
if( mainkey && !opt.fast_list_mode )
putchar( get_ownertrust_info (pk) );
putchar(':');
}
else
{
printf("%s %4u%c/%s %s",
mainkey? "pub":"sub", nbits_from_pk( pk ),
pubkey_letter( pk->pubkey_algo ), keystr_from_pk( pk ),
datestr_from_pk (pk));
}
if (pk->is_revoked)
{
printf(" [");
printf(_("revoked: %s"),revokestr_from_pk(pk));
printf("]\n");
}
else if (pk->expiredate && !opt.with_colons)
{
printf(" [");
printf(_("expires: %s"),expirestr_from_pk(pk));
printf("]\n");
}
else
putchar ('\n');
if ((mainkey && opt.fingerprint) || opt.fingerprint > 1)
print_fingerprint (pk, NULL, 0);
if (opt.with_colons)
{
if (node->next && node->next->pkt->pkttype == PKT_RING_TRUST)
printf("rtv:1:%u:\n", node->next->pkt->pkt.ring_trust->trustval);
}
if( mainkey ) {
/* and now list all userids with their signatures */
for( node = node->next; node; node = node->next ) {
if( node->pkt->pkttype == PKT_SIGNATURE ) {
list_node(c, node );
}
else if( node->pkt->pkttype == PKT_USER_ID ) {
if( opt.with_colons )
printf("%s:::::::::",
node->pkt->pkt.user_id->attrib_data?"uat":"uid");
else
printf( "uid%*s", 28, "" );
print_userid( node->pkt );
if( opt.with_colons )
putchar(':');
putchar('\n');
if( opt.with_colons
&& node->next
&& node->next->pkt->pkttype == PKT_RING_TRUST ) {
printf("rtv:2:%u:\n",
node->next->pkt->pkt.ring_trust?
node->next->pkt->pkt.ring_trust->trustval : 0);
}
}
else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
list_node(c, node );
}
}
}
}
else if( (mainkey = (node->pkt->pkttype == PKT_SECRET_KEY) )
|| node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
PKT_secret_key *sk = node->pkt->pkt.secret_key;
if( opt.with_colons )
{
u32 keyid[2];
keyid_from_sk( sk, keyid );
printf("%s::%u:%d:%08lX%08lX:%s:%s:::",
mainkey? "sec":"ssb",
nbits_from_sk( sk ),
sk->pubkey_algo,
(ulong)keyid[0],(ulong)keyid[1],
colon_datestr_from_sk( sk ),
colon_strtime (sk->expiredate));
}
else
printf("%s %4u%c/%s %s ", mainkey? "sec":"ssb",
nbits_from_sk( sk ), pubkey_letter( sk->pubkey_algo ),
keystr_from_sk( sk ), datestr_from_sk( sk ));
putchar ('\n');
if ((mainkey && opt.fingerprint) || opt.fingerprint > 1)
print_fingerprint (NULL, sk,0);
if( mainkey ) {
/* and now list all userids with their signatures */
for( node = node->next; node; node = node->next ) {
if( node->pkt->pkttype == PKT_SIGNATURE ) {
list_node(c, node );
}
else if( node->pkt->pkttype == PKT_USER_ID ) {
if( opt.with_colons )
printf("%s:::::::::",
node->pkt->pkt.user_id->attrib_data?"uat":"uid");
else
printf( "uid%*s", 28, "" );
print_userid( node->pkt );
if( opt.with_colons )
putchar(':');
putchar('\n');
}
else if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
list_node(c, node );
}
}
}
}
else if( node->pkt->pkttype == PKT_SIGNATURE ) {
PKT_signature *sig = node->pkt->pkt.signature;
int is_selfsig = 0;
int rc2=0;
size_t n;
char *p;
int sigrc = ' ';
if( !opt.verbose )
return;
if( sig->sig_class == 0x20 || sig->sig_class == 0x30 )
fputs("rev", stdout);
else
fputs("sig", stdout);
if( opt.check_sigs ) {
fflush(stdout);
rc2=do_check_sig( c, node, &is_selfsig, NULL, NULL );
switch (gpg_err_code (rc2)) {
case 0: sigrc = '!'; break;
case GPG_ERR_BAD_SIGNATURE: sigrc = '-'; break;
case GPG_ERR_NO_PUBKEY:
case GPG_ERR_UNUSABLE_PUBKEY: sigrc = '?'; break;
default: sigrc = '%'; break;
}
}
else { /* check whether this is a self signature */
u32 keyid[2];
if( c->list->pkt->pkttype == PKT_PUBLIC_KEY
|| c->list->pkt->pkttype == PKT_SECRET_KEY ) {
if( c->list->pkt->pkttype == PKT_PUBLIC_KEY )
keyid_from_pk( c->list->pkt->pkt.public_key, keyid );
else
keyid_from_sk( c->list->pkt->pkt.secret_key, keyid );
if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] )
is_selfsig = 1;
}
}
if( opt.with_colons ) {
putchar(':');
if( sigrc != ' ' )
putchar(sigrc);
printf("::%d:%08lX%08lX:%s:%s:", sig->pubkey_algo,
(ulong)sig->keyid[0], (ulong)sig->keyid[1],
colon_datestr_from_sig(sig),
colon_expirestr_from_sig(sig));
if(sig->trust_depth || sig->trust_value)
printf("%d %d",sig->trust_depth,sig->trust_value);
printf(":");
if(sig->trust_regexp)
print_string(stdout,sig->trust_regexp,
strlen(sig->trust_regexp),':');
printf(":");
}
else
printf("%c %s %s ",
sigrc, keystr(sig->keyid), datestr_from_sig(sig));
if( sigrc == '%' )
printf("[%s] ", g10_errstr(rc2) );
else if( sigrc == '?' )
;
else if( is_selfsig ) {
if( opt.with_colons )
putchar(':');
fputs( sig->sig_class == 0x18? "[keybind]":"[selfsig]", stdout);
if( opt.with_colons )
putchar(':');
}
else if( !opt.fast_list_mode ) {
p = get_user_id( sig->keyid, &n );
print_string( stdout, p, n, opt.with_colons );
xfree(p);
}
if( opt.with_colons )
printf(":%02x%c:", sig->sig_class, sig->flags.exportable?'x':'l');
putchar('\n');
}
else
log_error("invalid node with packet of type %d\n", node->pkt->pkttype);
}
int
proc_packets( void *anchor, IOBUF a )
{
int rc;
CTX c = xmalloc_clear( sizeof *c );
c->anchor = anchor;
rc = do_proc_packets( c, a );
xfree( c );
return rc;
}
int
proc_signature_packets( void *anchor, IOBUF a,
strlist_t signedfiles, const char *sigfilename )
{
CTX c = xmalloc_clear( sizeof *c );
int rc;
c->anchor = anchor;
c->sigs_only = 1;
c->signed_data.data_fd = -1;
c->signed_data.data_names = signedfiles;
c->signed_data.used = !!signedfiles;
c->sigfilename = sigfilename;
rc = do_proc_packets( c, a );
/* If we have not encountered any signature we print an error
messages, send a NODATA status back and return an error code.
Using log_error is required because verify_files does not check
error codes for each file but we want to terminate the process
with an error. */
if (!rc && !c->any.sig_seen)
{
write_status_text (STATUS_NODATA, "4");
log_error (_("no signature found\n"));
rc = G10ERR_NO_DATA;
}
/* Propagate the signature seen flag upward. Do this only on
success so that we won't issue the nodata status several
times. */
if (!rc && c->anchor && c->any.sig_seen)
c->anchor->any.sig_seen = 1;
xfree( c );
return rc;
}
int
proc_signature_packets_by_fd (void *anchor, IOBUF a, int signed_data_fd )
{
int rc;
CTX c = xcalloc (1, sizeof *c);
c->anchor = anchor;
c->sigs_only = 1;
c->signed_data.data_fd = signed_data_fd;
c->signed_data.data_names = NULL;
c->signed_data.used = (signed_data_fd != -1);
rc = do_proc_packets ( c, a );
/* If we have not encountered any signature we print an error
messages, send a NODATA status back and return an error code.
Using log_error is required because verify_files does not check
error codes for each file but we want to terminate the process
with an error. */
if (!rc && !c->any.sig_seen)
{
write_status_text (STATUS_NODATA, "4");
log_error (_("no signature found\n"));
rc = gpg_error (GPG_ERR_NO_DATA);
}
/* Propagate the signature seen flag upward. Do this only on success
so that we won't issue the nodata status several times. */
if (!rc && c->anchor && c->any.sig_seen)
c->anchor->any.sig_seen = 1;
xfree ( c );
return rc;
}
int
proc_encryption_packets( void *anchor, IOBUF a )
{
CTX c = xmalloc_clear( sizeof *c );
int rc;
c->anchor = anchor;
c->encrypt_only = 1;
rc = do_proc_packets( c, a );
xfree( c );
return rc;
}
static int
check_nesting (CTX c)
{
int level;
for (level=0; c; c = c->anchor)
level++;
if (level > MAX_NESTING_DEPTH)
{
log_error ("input data with too deeply nested packets\n");
write_status_text (STATUS_UNEXPECTED, "1");
return GPG_ERR_BAD_DATA;
}
return 0;
}
static int
do_proc_packets( CTX c, IOBUF a )
{
PACKET *pkt;
int rc = 0;
int any_data = 0;
int newpkt;
rc = check_nesting (c);
if (rc)
return rc;
pkt = xmalloc( sizeof *pkt );
c->iobuf = a;
init_packet(pkt);
while( (rc=parse_packet(a, pkt)) != -1 ) {
any_data = 1;
if( rc ) {
free_packet(pkt);
/* stop processing when an invalid packet has been encountered
* but don't do so when we are doing a --list-packets. */
if (gpg_err_code (rc) == GPG_ERR_INV_PACKET
- && opt.list_packets != 2 )
+ && opt.list_packets == 0 )
break;
continue;
}
newpkt = -1;
if( opt.list_packets ) {
switch( pkt->pkttype ) {
case PKT_PUBKEY_ENC: proc_pubkey_enc( c, pkt ); break;
case PKT_SYMKEY_ENC: proc_symkey_enc( c, pkt ); break;
case PKT_ENCRYPTED:
case PKT_ENCRYPTED_MDC: proc_encrypted( c, pkt ); break;
case PKT_COMPRESSED: rc = proc_compressed( c, pkt ); break;
default: newpkt = 0; break;
}
}
else if( c->sigs_only ) {
switch( pkt->pkttype ) {
case PKT_PUBLIC_KEY:
case PKT_SECRET_KEY:
case PKT_USER_ID:
case PKT_SYMKEY_ENC:
case PKT_PUBKEY_ENC:
case PKT_ENCRYPTED:
case PKT_ENCRYPTED_MDC:
write_status_text( STATUS_UNEXPECTED, "0" );
rc = G10ERR_UNEXPECTED;
goto leave;
case PKT_SIGNATURE: newpkt = add_signature( c, pkt ); break;
case PKT_PLAINTEXT: proc_plaintext( c, pkt ); break;
case PKT_COMPRESSED: rc = proc_compressed( c, pkt ); break;
case PKT_ONEPASS_SIG: newpkt = add_onepass_sig( c, pkt ); break;
case PKT_GPG_CONTROL: newpkt = add_gpg_control(c, pkt); break;
default: newpkt = 0; break;
}
}
else if( c->encrypt_only ) {
switch( pkt->pkttype ) {
case PKT_PUBLIC_KEY:
case PKT_SECRET_KEY:
case PKT_USER_ID:
write_status_text( STATUS_UNEXPECTED, "0" );
rc = G10ERR_UNEXPECTED;
goto leave;
case PKT_SIGNATURE: newpkt = add_signature( c, pkt ); break;
case PKT_SYMKEY_ENC: proc_symkey_enc( c, pkt ); break;
case PKT_PUBKEY_ENC: proc_pubkey_enc( c, pkt ); break;
case PKT_ENCRYPTED:
case PKT_ENCRYPTED_MDC: proc_encrypted( c, pkt ); break;
case PKT_PLAINTEXT: proc_plaintext( c, pkt ); break;
case PKT_COMPRESSED: rc = proc_compressed( c, pkt ); break;
case PKT_ONEPASS_SIG: newpkt = add_onepass_sig( c, pkt ); break;
case PKT_GPG_CONTROL: newpkt = add_gpg_control(c, pkt); break;
default: newpkt = 0; break;
}
}
else {
switch( pkt->pkttype ) {
case PKT_PUBLIC_KEY:
case PKT_SECRET_KEY:
release_list( c );
c->list = new_kbnode( pkt );
newpkt = 1;
break;
case PKT_PUBLIC_SUBKEY:
case PKT_SECRET_SUBKEY:
newpkt = add_subkey( c, pkt );
break;
case PKT_USER_ID: newpkt = add_user_id( c, pkt ); break;
case PKT_SIGNATURE: newpkt = add_signature( c, pkt ); break;
case PKT_PUBKEY_ENC: proc_pubkey_enc( c, pkt ); break;
case PKT_SYMKEY_ENC: proc_symkey_enc( c, pkt ); break;
case PKT_ENCRYPTED:
case PKT_ENCRYPTED_MDC: proc_encrypted( c, pkt ); break;
case PKT_PLAINTEXT: proc_plaintext( c, pkt ); break;
case PKT_COMPRESSED: rc = proc_compressed( c, pkt ); break;
case PKT_ONEPASS_SIG: newpkt = add_onepass_sig( c, pkt ); break;
case PKT_GPG_CONTROL: newpkt = add_gpg_control(c, pkt); break;
case PKT_RING_TRUST: newpkt = add_ring_trust( c, pkt ); break;
default: newpkt = 0; break;
}
}
if (rc)
goto leave;
/* This is a very ugly construct and frankly, I don't remember why
* I used it. Adding the MDC check here is a hack.
* The right solution is to initiate another context for encrypted
* packet and not to reuse the current one ... It works right
* when there is a compression packet inbetween which adds just
* an extra layer.
* Hmmm: Rewrite this whole module here??
*/
if( pkt->pkttype != PKT_SIGNATURE && pkt->pkttype != PKT_MDC )
c->any.data = (pkt->pkttype == PKT_PLAINTEXT);
if( newpkt == -1 )
;
else if( newpkt ) {
pkt = xmalloc( sizeof *pkt );
init_packet(pkt);
}
else
free_packet(pkt);
}
if( rc == G10ERR_INVALID_PACKET )
write_status_text( STATUS_NODATA, "3" );
if( any_data )
rc = 0;
else if( rc == -1 )
write_status_text( STATUS_NODATA, "2" );
leave:
release_list( c );
xfree(c->dek);
free_packet( pkt );
xfree( pkt );
free_md_filter_context( &c->mfx );
return rc;
}
/* Helper for pka_uri_from_sig to parse the to-be-verified address out
of the notation data. */
static pka_info_t *
get_pka_address (PKT_signature *sig)
{
pka_info_t *pka = NULL;
struct notation *nd,*notation;
notation=sig_to_notation(sig);
for(nd=notation;nd;nd=nd->next)
{
if(strcmp(nd->name,"pka-address@gnupg.org")!=0)
continue; /* Not the notation we want. */
/* For now we only use the first valid PKA notation. In future
we might want to keep additional PKA notations in a linked
list. */
if (is_valid_mailbox (nd->value))
{
pka = xmalloc (sizeof *pka + strlen(nd->value));
pka->valid = 0;
pka->checked = 0;
pka->uri = NULL;
strcpy (pka->email, nd->value);
break;
}
}
free_notation(notation);
return pka;
}
/* Return the URI from a DNS PKA record. If this record has already
be retrieved for the signature we merely return it; if not we go
out and try to get that DNS record. */
static const char *
pka_uri_from_sig (PKT_signature *sig)
{
if (!sig->flags.pka_tried)
{
assert (!sig->pka_info);
sig->flags.pka_tried = 1;
sig->pka_info = get_pka_address (sig);
if (sig->pka_info)
{
char *uri;
uri = get_pka_info (sig->pka_info->email, sig->pka_info->fpr);
if (uri)
{
sig->pka_info->valid = 1;
if (!*uri)
xfree (uri);
else
sig->pka_info->uri = uri;
}
}
}
return sig->pka_info? sig->pka_info->uri : NULL;
}
static int
check_sig_and_print( CTX c, KBNODE node )
{
PKT_signature *sig = node->pkt->pkt.signature;
const char *astr;
int rc, is_expkey=0, is_revkey=0;
if (opt.skip_verify)
{
log_info(_("signature verification suppressed\n"));
return 0;
}
/* Check that the message composition is valid.
Per RFC-2440bis (-15) allowed:
S{1,n} -- detached signature.
S{1,n} P -- old style PGP2 signature
O{1,n} P S{1,n} -- standard OpenPGP signature.
C P S{1,n} -- cleartext signature.
O = One-Pass Signature packet.
S = Signature packet.
P = OpenPGP Message packet (Encrypted | Compressed | Literal)
(Note that the current rfc2440bis draft also allows
for a signed message but that does not work as it
introduces ambiguities.)
We keep track of these packages using the marker packet
CTRLPKT_PLAINTEXT_MARK.
C = Marker packet for cleartext signatures.
We reject all other messages.
Actually we are calling this too often, i.e. for verification of
each message but better have some duplicate work than to silently
introduce a bug here.
*/
{
KBNODE n;
int n_onepass, n_sig;
/* log_debug ("checking signature packet composition\n"); */
/* dump_kbnode (c->list); */
n = c->list;
assert (n);
if ( n->pkt->pkttype == PKT_SIGNATURE )
{
/* This is either "S{1,n}" case (detached signature) or
"S{1,n} P" (old style PGP2 signature). */
for (n = n->next; n; n = n->next)
if (n->pkt->pkttype != PKT_SIGNATURE)
break;
if (!n)
; /* Okay, this is a detached signature. */
else if (n->pkt->pkttype == PKT_GPG_CONTROL
&& (n->pkt->pkt.gpg_control->control
== CTRLPKT_PLAINTEXT_MARK) )
{
if (n->next)
goto ambiguous; /* We only allow one P packet. */
}
else
goto ambiguous;
}
else if (n->pkt->pkttype == PKT_ONEPASS_SIG)
{
/* This is the "O{1,n} P S{1,n}" case (standard signature). */
for (n_onepass=1, n = n->next;
n && n->pkt->pkttype == PKT_ONEPASS_SIG; n = n->next)
n_onepass++;
if (!n || !(n->pkt->pkttype == PKT_GPG_CONTROL
&& (n->pkt->pkt.gpg_control->control
== CTRLPKT_PLAINTEXT_MARK)))
goto ambiguous;
for (n_sig=0, n = n->next;
n && n->pkt->pkttype == PKT_SIGNATURE; n = n->next)
n_sig++;
if (!n_sig)
goto ambiguous;
/* If we wanted to disallow multiple sig verification, we'd do
something like this:
if (n && !opt.allow_multisig_verification)
goto ambiguous;
However, now that we have --allow-multiple-messages, this
can stay allowable as we can't get here unless multiple
messages (i.e. multiple literals) are allowed. */
if (n_onepass != n_sig)
{
log_info ("number of one-pass packets does not match "
"number of signature packets\n");
goto ambiguous;
}
}
else if (n->pkt->pkttype == PKT_GPG_CONTROL
&& n->pkt->pkt.gpg_control->control == CTRLPKT_CLEARSIGN_START )
{
/* This is the "C P S{1,n}" case (clear text signature). */
n = n->next;
if (!n || !(n->pkt->pkttype == PKT_GPG_CONTROL
&& (n->pkt->pkt.gpg_control->control
== CTRLPKT_PLAINTEXT_MARK)))
goto ambiguous;
for (n_sig=0, n = n->next;
n && n->pkt->pkttype == PKT_SIGNATURE; n = n->next)
n_sig++;
if (n || !n_sig)
goto ambiguous;
}
else
{
ambiguous:
log_error(_("can't handle this ambiguous signature data\n"));
return 0;
}
}
write_status_text (STATUS_NEWSIG, NULL);
/* (Indendation below not yet changed to GNU style.) */
astr = openpgp_pk_algo_name ( sig->pubkey_algo );
if(keystrlen()>8)
{
log_info(_("Signature made %s\n"),asctimestamp(sig->timestamp));
log_info(_(" using %s key %s\n"),
astr? astr: "?",keystr(sig->keyid));
}
else
log_info(_("Signature made %s using %s key ID %s\n"),
asctimestamp(sig->timestamp), astr? astr: "?",
keystr(sig->keyid));
rc = do_check_sig(c, node, NULL, &is_expkey, &is_revkey );
/* If the key isn't found, check for a preferred keyserver */
if(rc==G10ERR_NO_PUBKEY && sig->flags.pref_ks)
{
const byte *p;
int seq=0;
size_t n;
while((p=enum_sig_subpkt(sig->hashed,SIGSUBPKT_PREF_KS,&n,&seq,NULL)))
{
/* According to my favorite copy editor, in English
grammar, you say "at" if the key is located on a web
page, but "from" if it is located on a keyserver. I'm
not going to even try to make two strings here :) */
log_info(_("Key available at: ") );
print_utf8_string( log_get_stream(), p, n );
log_printf ("\n");
if(opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE
&& opt.keyserver_options.options&KEYSERVER_HONOR_KEYSERVER_URL)
{
struct keyserver_spec *spec;
spec=parse_preferred_keyserver(sig);
if(spec)
{
int res;
glo_ctrl.in_auto_key_retrieve++;
res=keyserver_import_keyid(sig->keyid,spec);
glo_ctrl.in_auto_key_retrieve--;
if(!res)
rc=do_check_sig(c, node, NULL, &is_expkey, &is_revkey );
free_keyserver_spec(spec);
if(!rc)
break;
}
}
}
}
/* If the preferred keyserver thing above didn't work, our second
try is to use the URI from a DNS PKA record. */
if ( rc == G10ERR_NO_PUBKEY
&& opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE
&& opt.keyserver_options.options&KEYSERVER_HONOR_PKA_RECORD)
{
const char *uri = pka_uri_from_sig (sig);
if (uri)
{
/* FIXME: We might want to locate the key using the
fingerprint instead of the keyid. */
int res;
struct keyserver_spec *spec;
spec = parse_keyserver_uri (uri, 1, NULL, 0);
if (spec)
{
glo_ctrl.in_auto_key_retrieve++;
res = keyserver_import_keyid (sig->keyid, spec);
glo_ctrl.in_auto_key_retrieve--;
free_keyserver_spec (spec);
if (!res)
rc = do_check_sig(c, node, NULL, &is_expkey, &is_revkey );
}
}
}
/* If the preferred keyserver thing above didn't work and we got
no information from the DNS PKA, this is a third try. */
if( rc == G10ERR_NO_PUBKEY && opt.keyserver
&& opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE)
{
int res;
glo_ctrl.in_auto_key_retrieve++;
res=keyserver_import_keyid ( sig->keyid, opt.keyserver );
glo_ctrl.in_auto_key_retrieve--;
if(!res)
rc = do_check_sig(c, node, NULL, &is_expkey, &is_revkey );
}
if( !rc || gpg_err_code (rc) == GPG_ERR_BAD_SIGNATURE ) {
KBNODE un, keyblock;
int count=0, statno;
char keyid_str[50];
PKT_public_key *pk=NULL;
if(rc)
statno=STATUS_BADSIG;
else if(sig->flags.expired)
statno=STATUS_EXPSIG;
else if(is_expkey)
statno=STATUS_EXPKEYSIG;
else if(is_revkey)
statno=STATUS_REVKEYSIG;
else
statno=STATUS_GOODSIG;
keyblock = get_pubkeyblock( sig->keyid );
sprintf (keyid_str, "%08lX%08lX [uncertain] ",
(ulong)sig->keyid[0], (ulong)sig->keyid[1]);
/* find and print the primary user ID */
for( un=keyblock; un; un = un->next ) {
char *p;
int valid;
if(un->pkt->pkttype==PKT_PUBLIC_KEY)
{
pk=un->pkt->pkt.public_key;
continue;
}
if( un->pkt->pkttype != PKT_USER_ID )
continue;
if ( !un->pkt->pkt.user_id->created )
continue;
if ( un->pkt->pkt.user_id->is_revoked )
continue;
if ( un->pkt->pkt.user_id->is_expired )
continue;
if ( !un->pkt->pkt.user_id->is_primary )
continue;
/* We want the textual primary user ID here */
if ( un->pkt->pkt.user_id->attrib_data )
continue;
assert(pk);
/* Get it before we print anything to avoid interrupting
the output with the "please do a --check-trustdb"
line. */
valid=get_validity(pk,un->pkt->pkt.user_id);
keyid_str[17] = 0; /* cut off the "[uncertain]" part */
write_status_text_and_buffer (statno, keyid_str,
un->pkt->pkt.user_id->name,
un->pkt->pkt.user_id->len,
-1 );
p=utf8_to_native(un->pkt->pkt.user_id->name,
un->pkt->pkt.user_id->len,0);
if(rc)
log_info(_("BAD signature from \"%s\""),p);
else if(sig->flags.expired)
log_info(_("Expired signature from \"%s\""),p);
else
log_info(_("Good signature from \"%s\""),p);
xfree(p);
if(opt.verify_options&VERIFY_SHOW_UID_VALIDITY)
log_printf (" [%s]\n",trust_value_to_string(valid));
else
log_printf ("\n");
count++;
}
if( !count ) { /* just in case that we have no valid textual
userid */
char *p;
/* Try for an invalid textual userid */
for( un=keyblock; un; un = un->next ) {
if( un->pkt->pkttype == PKT_USER_ID &&
!un->pkt->pkt.user_id->attrib_data )
break;
}
/* Try for any userid at all */
if(!un) {
for( un=keyblock; un; un = un->next ) {
if( un->pkt->pkttype == PKT_USER_ID )
break;
}
}
if (opt.trust_model==TM_ALWAYS || !un)
keyid_str[17] = 0; /* cut off the "[uncertain]" part */
write_status_text_and_buffer (statno, keyid_str,
un? un->pkt->pkt.user_id->name:"[?]",
un? un->pkt->pkt.user_id->len:3,
-1 );
if(un)
p=utf8_to_native(un->pkt->pkt.user_id->name,
un->pkt->pkt.user_id->len,0);
else
p=xstrdup("[?]");
if(rc)
log_info(_("BAD signature from \"%s\""),p);
else if(sig->flags.expired)
log_info(_("Expired signature from \"%s\""),p);
else
log_info(_("Good signature from \"%s\""),p);
if (opt.trust_model!=TM_ALWAYS && un)
log_printf (" %s",_("[uncertain]") );
log_printf ("\n");
}
/* If we have a good signature and already printed
* the primary user ID, print all the other user IDs */
if ( count && !rc
&& !(opt.verify_options&VERIFY_SHOW_PRIMARY_UID_ONLY)) {
char *p;
for( un=keyblock; un; un = un->next ) {
if( un->pkt->pkttype != PKT_USER_ID )
continue;
if((un->pkt->pkt.user_id->is_revoked
|| un->pkt->pkt.user_id->is_expired)
&& !(opt.verify_options&VERIFY_SHOW_UNUSABLE_UIDS))
continue;
/* Only skip textual primaries */
if ( un->pkt->pkt.user_id->is_primary &&
!un->pkt->pkt.user_id->attrib_data )
continue;
if(un->pkt->pkt.user_id->attrib_data)
{
dump_attribs(un->pkt->pkt.user_id,pk,NULL);
if(opt.verify_options&VERIFY_SHOW_PHOTOS)
show_photos(un->pkt->pkt.user_id->attribs,
un->pkt->pkt.user_id->numattribs,
pk,NULL,un->pkt->pkt.user_id);
}
p=utf8_to_native(un->pkt->pkt.user_id->name,
un->pkt->pkt.user_id->len,0);
log_info(_(" aka \"%s\""),p);
xfree(p);
if(opt.verify_options&VERIFY_SHOW_UID_VALIDITY)
{
const char *valid;
if(un->pkt->pkt.user_id->is_revoked)
valid=_("revoked");
else if(un->pkt->pkt.user_id->is_expired)
valid=_("expired");
else
valid=trust_value_to_string(get_validity(pk,
un->pkt->
pkt.user_id));
log_printf (" [%s]\n",valid);
}
else
log_printf ("\n");
}
}
release_kbnode( keyblock );
if( !rc )
{
if(opt.verify_options&VERIFY_SHOW_POLICY_URLS)
show_policy_url(sig,0,1);
else
show_policy_url(sig,0,2);
if(opt.verify_options&VERIFY_SHOW_KEYSERVER_URLS)
show_keyserver_url(sig,0,1);
else
show_keyserver_url(sig,0,2);
if(opt.verify_options&VERIFY_SHOW_NOTATIONS)
show_notation(sig,0,1,
((opt.verify_options&VERIFY_SHOW_STD_NOTATIONS)?1:0)+
((opt.verify_options&VERIFY_SHOW_USER_NOTATIONS)?2:0));
else
show_notation(sig,0,2,0);
}
if( !rc && is_status_enabled() ) {
/* print a status response with the fingerprint */
PKT_public_key *vpk = xmalloc_clear( sizeof *vpk );
if( !get_pubkey( vpk, sig->keyid ) ) {
byte array[MAX_FINGERPRINT_LEN], *p;
char buf[MAX_FINGERPRINT_LEN*4+90], *bufp;
size_t i, n;
bufp = buf;
fingerprint_from_pk( vpk, array, &n );
p = array;
for(i=0; i < n ; i++, p++, bufp += 2)
sprintf(bufp, "%02X", *p );
/* TODO: Replace the reserved '0' in the field below
with bits for status flags (policy url, notation,
etc.). Remember to make the buffer larger to
match! */
sprintf(bufp, " %s %lu %lu %d 0 %d %d %02X ",
strtimestamp( sig->timestamp ),
(ulong)sig->timestamp,(ulong)sig->expiredate,
sig->version,sig->pubkey_algo,sig->digest_algo,
sig->sig_class);
bufp = bufp + strlen (bufp);
if (!vpk->is_primary) {
u32 akid[2];
akid[0] = vpk->main_keyid[0];
akid[1] = vpk->main_keyid[1];
free_public_key (vpk);
vpk = xmalloc_clear( sizeof *vpk );
if (get_pubkey (vpk, akid)) {
/* impossible error, we simply return a zeroed out fpr */
n = MAX_FINGERPRINT_LEN < 20? MAX_FINGERPRINT_LEN : 20;
memset (array, 0, n);
}
else
fingerprint_from_pk( vpk, array, &n );
}
p = array;
for(i=0; i < n ; i++, p++, bufp += 2)
sprintf(bufp, "%02X", *p );
write_status_text( STATUS_VALIDSIG, buf );
}
free_public_key( vpk );
}
if (!rc)
{
if(opt.verify_options&VERIFY_PKA_LOOKUPS)
pka_uri_from_sig (sig); /* Make sure PKA info is available. */
rc = check_signatures_trust( sig );
}
if(sig->flags.expired)
{
log_info(_("Signature expired %s\n"),
asctimestamp(sig->expiredate));
rc=G10ERR_GENERAL; /* need a better error here? */
}
else if(sig->expiredate)
log_info(_("Signature expires %s\n"),asctimestamp(sig->expiredate));
if(opt.verbose)
log_info(_("%s signature, digest algorithm %s\n"),
sig->sig_class==0x00?_("binary"):
sig->sig_class==0x01?_("textmode"):_("unknown"),
gcry_md_algo_name (sig->digest_algo));
if (!rc && !c->signed_data.used)
{
/* Signature is basically good but we test whether the
deprecated command
gpg --verify FILE.sig
was used instead of
gpg --verify FILE.sig FILE
to verify a detached signature. If we figure out that a
data file with a matching name exists, we print a warning.
The problem is that the first form would also verify a
standard signature. This behavior could be used to
create a made up .sig file for a tarball by creating a
standard signature from a valid detached signature packet
(for example from a signed git tag). Then replace the
sig file on the FTP server along with a changed tarball.
Using the first form the verify command would correctly
verify the signature but don't even consider the tarball. */
kbnode_t n;
char *dfile;
dfile = get_matching_datafile (c->sigfilename);
if (dfile)
{
for (n = c->list; n; n = n->next)
if (n->pkt->pkttype != PKT_SIGNATURE)
break;
if (n)
{
/* Not only signature packets in the tree thus this
is not a detached signature. */
log_info (_("WARNING: not a detached signature; "
"file '%s' was NOT verified!\n"), dfile);
}
xfree (dfile);
}
}
if( rc )
g10_errors_seen = 1;
if( opt.batch && rc )
g10_exit(1);
}
else {
char buf[50];
sprintf(buf, "%08lX%08lX %d %d %02x %lu %d",
(ulong)sig->keyid[0], (ulong)sig->keyid[1],
sig->pubkey_algo, sig->digest_algo,
sig->sig_class, (ulong)sig->timestamp, rc );
write_status_text( STATUS_ERRSIG, buf );
if( rc == G10ERR_NO_PUBKEY ) {
buf[16] = 0;
write_status_text( STATUS_NO_PUBKEY, buf );
}
if( rc != G10ERR_NOT_PROCESSED )
log_error(_("Can't check signature: %s\n"), g10_errstr(rc) );
}
return rc;
}
/****************
* Process the tree which starts at node
*/
static void
proc_tree( CTX c, KBNODE node )
{
KBNODE n1;
int rc;
if( opt.list_packets || opt.list_only )
return;
/* we must skip our special plaintext marker packets here becuase
they may be the root packet. These packets are only used in
addionla checks and skipping them here doesn't matter */
while ( node
&& node->pkt->pkttype == PKT_GPG_CONTROL
&& node->pkt->pkt.gpg_control->control
== CTRLPKT_PLAINTEXT_MARK ) {
node = node->next;
}
if (!node)
return;
c->trustletter = ' ';
if( node->pkt->pkttype == PKT_PUBLIC_KEY
|| node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
merge_keys_and_selfsig( node );
list_node( c, node );
}
else if( node->pkt->pkttype == PKT_SECRET_KEY ) {
merge_keys_and_selfsig( node );
list_node( c, node );
}
else if( node->pkt->pkttype == PKT_ONEPASS_SIG ) {
/* check all signatures */
if( !c->any.data ) {
int use_textmode = 0;
free_md_filter_context( &c->mfx );
/* prepare to create all requested message digests */
if (gcry_md_open (&c->mfx.md, 0, 0))
BUG ();
/* fixme: why looking for the signature packet and not the
one-pass packet? */
for ( n1 = node; (n1 = find_next_kbnode(n1, PKT_SIGNATURE )); )
{
gcry_md_enable (c->mfx.md,
n1->pkt->pkt.signature->digest_algo);
}
if (n1 && n1->pkt->pkt.onepass_sig->sig_class == 0x01)
use_textmode = 1;
/* Ask for file and hash it. */
if( c->sigs_only ) {
if (c->signed_data.used && c->signed_data.data_fd != -1)
rc = hash_datafile_by_fd (c->mfx.md, NULL,
c->signed_data.data_fd,
use_textmode);
else
rc = hash_datafiles (c->mfx.md, NULL,
c->signed_data.data_names,
c->sigfilename,
use_textmode );
}
else {
rc = ask_for_detached_datafile (c->mfx.md, c->mfx.md2,
iobuf_get_real_fname(c->iobuf),
use_textmode );
}
if( rc ) {
log_error("can't hash datafile: %s\n", g10_errstr(rc));
return;
}
}
else if ( c->signed_data.used ) {
log_error (_("not a detached signature\n") );
return;
}
for( n1 = node; (n1 = find_next_kbnode(n1, PKT_SIGNATURE )); )
check_sig_and_print( c, n1 );
}
else if( node->pkt->pkttype == PKT_GPG_CONTROL
&& node->pkt->pkt.gpg_control->control
== CTRLPKT_CLEARSIGN_START ) {
/* clear text signed message */
if( !c->any.data ) {
log_error("cleartext signature without data\n" );
return;
}
else if ( c->signed_data.used ) {
log_error (_("not a detached signature\n") );
return;
}
for( n1 = node; (n1 = find_next_kbnode(n1, PKT_SIGNATURE )); )
check_sig_and_print( c, n1 );
}
else if( node->pkt->pkttype == PKT_SIGNATURE ) {
PKT_signature *sig = node->pkt->pkt.signature;
int multiple_ok=1;
n1=find_next_kbnode(node, PKT_SIGNATURE);
if(n1)
{
byte class=sig->sig_class;
byte hash=sig->digest_algo;
for(; n1; (n1 = find_next_kbnode(n1, PKT_SIGNATURE)))
{
/* We can't currently handle multiple signatures of
different classes or digests (we'd pretty much have
to run a different hash context for each), but if
they are all the same, make an exception. */
if(n1->pkt->pkt.signature->sig_class!=class
|| n1->pkt->pkt.signature->digest_algo!=hash)
{
multiple_ok=0;
log_info(_("WARNING: multiple signatures detected. "
"Only the first will be checked.\n"));
break;
}
}
}
if( sig->sig_class != 0x00 && sig->sig_class != 0x01 )
log_info(_("standalone signature of class 0x%02x\n"),
sig->sig_class);
else if( !c->any.data ) {
/* detached signature */
free_md_filter_context( &c->mfx );
if (gcry_md_open (&c->mfx.md, sig->digest_algo, 0))
BUG ();
if( !opt.pgp2_workarounds )
;
else if( sig->digest_algo == DIGEST_ALGO_MD5
&& is_RSA( sig->pubkey_algo ) ) {
/* enable a workaround for a pgp2 bug */
if (gcry_md_open (&c->mfx.md2, DIGEST_ALGO_MD5, 0))
BUG ();
}
else if( sig->digest_algo == DIGEST_ALGO_SHA1
&& sig->pubkey_algo == PUBKEY_ALGO_DSA
&& sig->sig_class == 0x01 ) {
/* enable the workaround also for pgp5 when the detached
* signature has been created in textmode */
if (gcry_md_open (&c->mfx.md2, sig->digest_algo, 0 ))
BUG ();
}
#if 0 /* workaround disabled */
/* Here we have another hack to work around a pgp 2 bug
* It works by not using the textmode for detached signatures;
* this will let the first signature check (on md) fail
* but the second one (on md2) which adds an extra CR should
* then produce the "correct" hash. This is very, very ugly
* hack but it may help in some cases (and break others)
*/
/* c->mfx.md2? 0 :(sig->sig_class == 0x01) */
#endif
if ( DBG_HASHING ) {
gcry_md_debug( c->mfx.md, "verify" );
if ( c->mfx.md2 )
gcry_md_debug( c->mfx.md2, "verify2" );
}
if( c->sigs_only ) {
if (c->signed_data.used && c->signed_data.data_fd != -1)
rc = hash_datafile_by_fd (c->mfx.md, c->mfx.md2,
c->signed_data.data_fd,
(sig->sig_class == 0x01));
else
rc = hash_datafiles (c->mfx.md, c->mfx.md2,
c->signed_data.data_names,
c->sigfilename,
(sig->sig_class == 0x01));
}
else {
rc = ask_for_detached_datafile( c->mfx.md, c->mfx.md2,
iobuf_get_real_fname(c->iobuf),
(sig->sig_class == 0x01) );
}
if( rc ) {
log_error("can't hash datafile: %s\n", g10_errstr(rc));
return;
}
}
else if ( c->signed_data.used ) {
log_error (_("not a detached signature\n") );
return;
}
else if (!opt.quiet)
log_info(_("old style (PGP 2.x) signature\n"));
if(multiple_ok)
for( n1 = node; n1; (n1 = find_next_kbnode(n1, PKT_SIGNATURE )) )
check_sig_and_print( c, n1 );
else
check_sig_and_print( c, node );
}
else {
dump_kbnode (c->list);
log_error(_("invalid root packet detected in proc_tree()\n"));
dump_kbnode (node);
}
}
diff --git a/g10/options.h b/g10/options.h
index cc8718eee..b02c0d920 100644
--- a/g10/options.h
+++ b/g10/options.h
@@ -1,368 +1,368 @@
/* options.h
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
* 2007 Free Software Foundation, Inc.
*
* 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 .
*/
#ifndef G10_OPTIONS_H
#define G10_OPTIONS_H
#include
#include
#include "main.h"
#include "packet.h"
#include "../common/session-env.h"
#ifndef EXTERN_UNLESS_MAIN_MODULE
/* Norcraft can't cope with common symbols */
#if defined (__riscos__) && !defined (INCLUDED_BY_MAIN_MODULE)
#define EXTERN_UNLESS_MAIN_MODULE extern
#else
#define EXTERN_UNLESS_MAIN_MODULE
#endif
#endif
EXTERN_UNLESS_MAIN_MODULE
struct
{
int verbose;
int quiet;
unsigned debug;
int armor;
char *outfile;
off_t max_output;
int dry_run;
int list_only;
int textmode;
int expert;
const char *def_sig_expire;
int ask_sig_expire;
const char *def_cert_expire;
int ask_cert_expire;
int batch; /* run in batch mode */
int answer_yes; /* answer yes on most questions */
int answer_no; /* answer no on most questions */
int check_sigs; /* check key signatures */
int with_colons;
int with_key_data;
int with_fingerprint; /* opt --with-fingerprint active */
int fingerprint; /* list fingerprints */
int list_sigs; /* list signatures */
int no_armor;
- int list_packets; /* list-packets mode: 1=normal, 2=invoked by command*/
+ int list_packets; /* Option --list-packets active. */
int def_cipher_algo;
int force_v3_sigs;
int force_v4_certs;
int force_mdc;
int disable_mdc;
int def_digest_algo;
int cert_digest_algo;
int compress_algo;
int compress_level;
int bz2_compress_level;
int bz2_decompress_lowmem;
const char *def_secret_key;
char *def_recipient;
int def_recipient_self;
int def_cert_level;
int min_cert_level;
int ask_cert_level;
int emit_version; /* 0 = none,
1 = major only,
2 = major and minor,
3 = full version,
4 = full version plus OS string. */
int marginals_needed;
int completes_needed;
int max_cert_depth;
const char *homedir;
const char *agent_program;
/* Options to be passed to the gpg-agent */
session_env_t session_env;
char *lc_ctype;
char *lc_messages;
int skip_verify;
int compress_keys;
int compress_sigs;
/* TM_CLASSIC must be zero to accomodate trustdbs generated before
we started storing the trust model inside the trustdb. */
enum
{
TM_CLASSIC=0, TM_PGP=1, TM_EXTERNAL=2, TM_ALWAYS, TM_DIRECT, TM_AUTO
} trust_model;
int force_ownertrust;
enum
{
CO_GNUPG, CO_RFC4880, CO_RFC2440, CO_RFC1991, CO_PGP2,
CO_PGP6, CO_PGP7, CO_PGP8
} compliance;
enum
{
KF_SHORT, KF_LONG, KF_0xSHORT, KF_0xLONG
} keyid_format;
int pgp2_workarounds;
int shm_coprocess;
const char *set_filename;
strlist_t comments;
int throw_keyid;
const char *photo_viewer;
int s2k_mode;
int s2k_digest_algo;
int s2k_cipher_algo;
unsigned char s2k_count; /* This is the encoded form, not the raw
count */
int simple_sk_checksum; /* create the deprecated rfc2440 secret key
protection */
int not_dash_escaped;
int escape_from;
int lock_once;
struct keyserver_spec
{
char *uri;
char *scheme;
char *auth;
char *host;
char *port;
char *path;
char *opaque;
strlist_t options;
struct
{
unsigned int direct_uri:1;
} flags;
struct keyserver_spec *next;
} *keyserver;
struct
{
unsigned int options;
unsigned int import_options;
unsigned int export_options;
strlist_t other;
} keyserver_options;
int exec_disable;
int exec_path_set;
unsigned int import_options;
unsigned int export_options;
unsigned int list_options;
unsigned int verify_options;
const char *def_preference_list;
const char *def_keyserver_url;
prefitem_t *personal_cipher_prefs;
prefitem_t *personal_digest_prefs;
prefitem_t *personal_compress_prefs;
int no_perm_warn;
int no_mdc_warn;
char *temp_dir;
int no_encrypt_to;
int interactive;
struct notation *sig_notations;
struct notation *cert_notations;
strlist_t sig_policy_url;
strlist_t cert_policy_url;
strlist_t sig_keyserver_url;
strlist_t cert_subpackets;
strlist_t sig_subpackets;
int allow_non_selfsigned_uid;
int allow_freeform_uid;
int no_literal;
ulong set_filesize;
int fast_list_mode;
int ignore_time_conflict;
int ignore_valid_from;
int ignore_crc_error;
int ignore_mdc_error;
int command_fd;
const char *override_session_key;
int show_session_key;
const char *gpg_agent_info;
int try_all_secrets;
int no_expensive_trust_checks;
int no_sig_cache;
int no_auto_check_trustdb;
int preserve_permissions;
int no_homedir_creation;
struct groupitem *grouplist;
int mangle_dos_filenames;
int enable_progress_filter;
unsigned int screen_columns;
unsigned int screen_lines;
byte *show_subpackets;
int rfc2440_text;
/* If true, let write failures on the status-fd exit the process. */
int exit_on_status_write_error;
/* If > 0, limit the number of card insertion prompts to this
value. */
int limit_card_insert_tries;
#ifdef ENABLE_CARD_SUPPORT
/* FIXME: We don't needs this here as it is done in scdaemon. */
const char *ctapi_driver; /* Library to access the ctAPI. */
const char *pcsc_driver; /* Library to access the PC/SC system. */
int disable_ccid; /* Disable the use of the internal CCID driver. */
#endif /*ENABLE_CARD_SUPPORT*/
struct
{
/* If set, require an 0x19 backsig to be present on signatures
made by signing subkeys. If not set, a missing backsig is not
an error (but an invalid backsig still is). */
unsigned int require_cross_cert:1;
unsigned int use_embedded_filename:1;
unsigned int utf8_filename:1;
unsigned int dsa2:1;
unsigned int allow_multiple_messages:1;
unsigned int allow_weak_digest_algos:1;
unsigned int large_rsa:1;
} flags;
/* Linked list of ways to find a key if the key isn't on the local
keyring. */
struct akl
{
enum {
AKL_NODEFAULT,
AKL_LOCAL,
AKL_CERT,
AKL_PKA,
AKL_LDAP,
AKL_KEYSERVER,
AKL_SPEC
} type;
struct keyserver_spec *spec;
struct akl *next;
} *auto_key_locate;
int passphrase_repeat;
} opt;
/* CTRL is used to keep some global variables we currently can't
avoid. Future concurrent versions of gpg will put it into a per
request structure CTRL. */
EXTERN_UNLESS_MAIN_MODULE
struct {
int in_auto_key_retrieve; /* True if we are doing an
auto_key_retrieve. */
} glo_ctrl;
#define DBG_PACKET_VALUE 1 /* debug packet reading/writing */
#define DBG_MPI_VALUE 2 /* debug mpi details */
#define DBG_CIPHER_VALUE 4 /* debug cipher handling */
/* (may reveal sensitive data) */
#define DBG_FILTER_VALUE 8 /* debug internal filter handling */
#define DBG_IOBUF_VALUE 16 /* debug iobuf stuff */
#define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */
#define DBG_CACHE_VALUE 64 /* debug the cacheing */
#define DBG_MEMSTAT_VALUE 128 /* show memory statistics */
#define DBG_TRUST_VALUE 256 /* debug the trustdb */
#define DBG_HASHING_VALUE 512 /* debug hashing operations */
#define DBG_EXTPROG_VALUE 1024 /* debug external program calls */
#define DBG_CARD_IO_VALUE 2048 /* debug smart card I/O. */
/* Fixme: For now alias this value. */
#define DBG_ASSUAN_VALUE DBG_EXTPROG_VALUE
/* Tests for the debugging flags. */
#define DBG_PACKET (opt.debug & DBG_PACKET_VALUE)
#define DBG_CIPHER (opt.debug & DBG_CIPHER_VALUE)
#define DBG_FILTER (opt.debug & DBG_FILTER_VALUE)
#define DBG_CACHE (opt.debug & DBG_CACHE_VALUE)
#define DBG_TRUST (opt.debug & DBG_TRUST_VALUE)
#define DBG_HASHING (opt.debug & DBG_HASHING_VALUE)
#define DBG_EXTPROG (opt.debug & DBG_EXTPROG_VALUE)
#define DBG_CARD_IO (opt.debug & DBG_CARD_IO_VALUE)
#define DBG_ASSUAN (opt.debug & DBG_ASSUAN_VALUE)
/* FIXME: We need to check whey we did not put this into opt. */
#define DBG_MEMORY memory_debug_mode
#define DBG_MEMSTAT memory_stat_debug_mode
EXTERN_UNLESS_MAIN_MODULE int memory_debug_mode;
EXTERN_UNLESS_MAIN_MODULE int memory_stat_debug_mode;
#define GNUPG (opt.compliance==CO_GNUPG)
#define RFC1991 (opt.compliance==CO_RFC1991 || opt.compliance==CO_PGP2)
#define RFC2440 (opt.compliance==CO_RFC2440)
#define RFC4880 (opt.compliance==CO_RFC4880)
#define PGP2 (opt.compliance==CO_PGP2)
#define PGP6 (opt.compliance==CO_PGP6)
#define PGP7 (opt.compliance==CO_PGP7)
#define PGP8 (opt.compliance==CO_PGP8)
#define PGPX (PGP2 || PGP6 || PGP7 || PGP8)
/* Various option flags. Note that there should be no common string
names between the IMPORT_ and EXPORT_ flags as they can be mixed in
the keyserver-options option. */
#define IMPORT_LOCAL_SIGS (1<<0)
#define IMPORT_REPAIR_PKS_SUBKEY_BUG (1<<1)
#define IMPORT_FAST (1<<2)
#define IMPORT_SK2PK (1<<3)
#define IMPORT_MERGE_ONLY (1<<4)
#define IMPORT_MINIMAL (1<<5)
#define IMPORT_CLEAN (1<<6)
#define IMPORT_NO_SECKEY (1<<7)
#define IMPORT_KEEP_OWNERTTRUST (1<<8)
#define EXPORT_LOCAL_SIGS (1<<0)
#define EXPORT_ATTRIBUTES (1<<1)
#define EXPORT_SENSITIVE_REVKEYS (1<<2)
#define EXPORT_RESET_SUBKEY_PASSWD (1<<3)
#define EXPORT_MINIMAL (1<<4)
#define EXPORT_CLEAN (1<<5)
#define EXPORT_SEXP_FORMAT (1<<6)
#define LIST_SHOW_PHOTOS (1<<0)
#define LIST_SHOW_POLICY_URLS (1<<1)
#define LIST_SHOW_STD_NOTATIONS (1<<2)
#define LIST_SHOW_USER_NOTATIONS (1<<3)
#define LIST_SHOW_NOTATIONS (LIST_SHOW_STD_NOTATIONS|LIST_SHOW_USER_NOTATIONS)
#define LIST_SHOW_KEYSERVER_URLS (1<<4)
#define LIST_SHOW_UID_VALIDITY (1<<5)
#define LIST_SHOW_UNUSABLE_UIDS (1<<6)
#define LIST_SHOW_UNUSABLE_SUBKEYS (1<<7)
#define LIST_SHOW_KEYRING (1<<8)
#define LIST_SHOW_SIG_EXPIRE (1<<9)
#define LIST_SHOW_SIG_SUBPACKETS (1<<10)
#define VERIFY_SHOW_PHOTOS (1<<0)
#define VERIFY_SHOW_POLICY_URLS (1<<1)
#define VERIFY_SHOW_STD_NOTATIONS (1<<2)
#define VERIFY_SHOW_USER_NOTATIONS (1<<3)
#define VERIFY_SHOW_NOTATIONS (VERIFY_SHOW_STD_NOTATIONS|VERIFY_SHOW_USER_NOTATIONS)
#define VERIFY_SHOW_KEYSERVER_URLS (1<<4)
#define VERIFY_SHOW_UID_VALIDITY (1<<5)
#define VERIFY_SHOW_UNUSABLE_UIDS (1<<6)
#define VERIFY_PKA_LOOKUPS (1<<7)
#define VERIFY_PKA_TRUST_INCREASE (1<<8)
#define VERIFY_SHOW_PRIMARY_UID_ONLY (1<<9)
#define KEYSERVER_USE_TEMP_FILES (1<<0)
#define KEYSERVER_KEEP_TEMP_FILES (1<<1)
#define KEYSERVER_ADD_FAKE_V3 (1<<2)
#define KEYSERVER_AUTO_KEY_RETRIEVE (1<<3)
#define KEYSERVER_HONOR_KEYSERVER_URL (1<<4)
#define KEYSERVER_HONOR_PKA_RECORD (1<<5)
#endif /*G10_OPTIONS_H*/
diff --git a/g10/parse-packet.c b/g10/parse-packet.c
index c925e9440..1030204e2 100644
--- a/g10/parse-packet.c
+++ b/g10/parse-packet.c
@@ -1,2661 +1,2661 @@
/* parse-packet.c - read packets
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
* 2007 Free Software Foundation, Inc.
*
* 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 .
*/
#include
#include
#include
#include
#include
#include "gpg.h"
#include "packet.h"
#include "iobuf.h"
#include "util.h"
#include "cipher.h"
#include "filter.h"
#include "photoid.h"
#include "options.h"
#include "main.h"
#include "i18n.h"
#include "host2net.h"
/* Maximum length of packets to avoid excessive memory allocation. */
#define MAX_KEY_PACKET_LENGTH (256 * 1024)
#define MAX_UID_PACKET_LENGTH ( 2 * 1024)
#define MAX_COMMENT_PACKET_LENGTH ( 64 * 1024)
#define MAX_ATTR_PACKET_LENGTH ( 16 * 1024*1024)
static int mpi_print_mode;
static int list_mode;
static FILE *listfp;
static int parse( IOBUF inp, PACKET *pkt, int onlykeypkts,
off_t *retpos, int *skip, IOBUF out, int do_skip
#ifdef DEBUG_PARSE_PACKET
,const char *dbg_w, const char *dbg_f, int dbg_l
#endif
);
static int copy_packet( IOBUF inp, IOBUF out, int pkttype,
unsigned long pktlen, int partial );
static void skip_packet( IOBUF inp, int pkttype,
unsigned long pktlen, int partial );
static void *read_rest( IOBUF inp, size_t pktlen, int partial );
static int parse_marker( IOBUF inp, int pkttype, unsigned long pktlen );
static int parse_symkeyenc( IOBUF inp, int pkttype, unsigned long pktlen,
PACKET *packet );
static int parse_pubkeyenc( IOBUF inp, int pkttype, unsigned long pktlen,
PACKET *packet );
static int parse_onepass_sig( IOBUF inp, int pkttype, unsigned long pktlen,
PKT_onepass_sig *ops );
static int parse_key( IOBUF inp, int pkttype, unsigned long pktlen,
byte *hdr, int hdrlen, PACKET *packet );
static int parse_user_id( IOBUF inp, int pkttype, unsigned long pktlen,
PACKET *packet );
static int parse_attribute( IOBUF inp, int pkttype, unsigned long pktlen,
PACKET *packet );
static int parse_comment( IOBUF inp, int pkttype, unsigned long pktlen,
PACKET *packet );
static void parse_trust( IOBUF inp, int pkttype, unsigned long pktlen,
PACKET *packet );
static int parse_plaintext( IOBUF inp, int pkttype, unsigned long pktlen,
PACKET *packet, int new_ctb, int partial);
static int parse_compressed( IOBUF inp, int pkttype, unsigned long pktlen,
PACKET *packet, int new_ctb );
static int parse_encrypted( IOBUF inp, int pkttype, unsigned long pktlen,
PACKET *packet, int new_ctb, int partial);
static int parse_mdc( IOBUF inp, int pkttype, unsigned long pktlen,
PACKET *packet, int new_ctb);
static int parse_gpg_control( IOBUF inp, int pkttype, unsigned long pktlen,
PACKET *packet, int partial );
static unsigned short
read_16(IOBUF inp)
{
unsigned short a;
a = (unsigned short)iobuf_get_noeof(inp) << 8;
a |= iobuf_get_noeof(inp);
return a;
}
static unsigned long
read_32(IOBUF inp)
{
unsigned long a;
a = (unsigned long)iobuf_get_noeof(inp) << 24;
a |= iobuf_get_noeof(inp) << 16;
a |= iobuf_get_noeof(inp) << 8;
a |= iobuf_get_noeof(inp);
return a;
}
/* Read an external representation of an mpi and return the MPI. The
* external format is a 16 bit unsigned value stored in network byte
* order, giving the number of bits for the following integer. The
* integer is stored with MSB first (left padded with zeroes to align
* on a byte boundary).
*/
static gcry_mpi_t
mpi_read (iobuf_t inp, unsigned int *ret_nread, int secure)
{
/*FIXME: Needs to be synced with gnupg14/mpi/mpicoder.c*/
int c, c1, c2, i;
unsigned int nmax = *ret_nread;
unsigned int nbits, nbytes;
size_t nread = 0;
gcry_mpi_t a = NULL;
byte *buf = NULL;
byte *p;
if (!nmax)
goto overflow;
if ( (c = c1 = iobuf_get (inp)) == -1 )
goto leave;
if (++nread == nmax)
goto overflow;
nbits = c << 8;
if ( (c = c2 = iobuf_get (inp)) == -1 )
goto leave;
++nread;
nbits |= c;
if ( nbits > MAX_EXTERN_MPI_BITS )
{
log_error("mpi too large (%u bits)\n", nbits);
goto leave;
}
nbytes = (nbits+7) / 8;
buf = secure ? gcry_xmalloc_secure (nbytes + 2) : gcry_xmalloc (nbytes + 2);
p = buf;
p[0] = c1;
p[1] = c2;
for ( i=0 ; i < nbytes; i++ )
{
p[i+2] = iobuf_get(inp) & 0xff;
if (nread == nmax)
goto overflow;
nread++;
}
if (nread >= 2 && !(buf[0] << 8 | buf[1]))
{
/* Libgcrypt < 1.5.0 accidently rejects zero-length (i.e. zero)
MPIs. We fix this here. */
a = gcry_mpi_new (0);
}
else
{
if ( gcry_mpi_scan( &a, GCRYMPI_FMT_PGP, buf, nread, &nread ) )
a = NULL;
}
*ret_nread = nread;
gcry_free(buf);
return a;
overflow:
log_error ("mpi larger than indicated length (%u bits)\n", 8*nmax);
leave:
*ret_nread = nread;
gcry_free(buf);
return a;
}
int
set_packet_list_mode( int mode )
{
int old = list_mode;
list_mode = mode;
/* FIXME(gcrypt) mpi_print_mode = DBG_MPI; */
/* We use stdout print only if invoked by the --list-packets
command but switch to stderr in all otehr cases. This breaks
the previous behaviour but that seems to be more of a bug than
intentional. I don't believe that any application makes use of
this long standing annoying way of printing to stdout except
when doing a --list-packets. If this assumption fails, it will
be easy to add an option for the listing stream. Note that we
initialize it only once; mainly because some code may switch
the option value later back to 1 and we want to have all output
to the same stream.
Using stderr is not actually very clean because it bypasses the
logging code but it is a special thing anyay. I am not sure
whether using log_stream() would be better. Perhaps we should
enable the list mdoe only with a special option. */
if (!listfp)
- listfp = opt.list_packets == 2 ? stdout : stderr;
+ listfp = opt.list_packets ? stdout : stderr;
return old;
}
static void
unknown_pubkey_warning( int algo )
{
static byte unknown_pubkey_algos[256];
algo &= 0xff;
if( !unknown_pubkey_algos[algo] ) {
if( opt.verbose )
log_info(_("can't handle public key algorithm %d\n"), algo );
unknown_pubkey_algos[algo] = 1;
}
}
/****************
* Parse a Packet and return it in packet
* Returns: 0 := valid packet in pkt
* -1 := no more packets
* >0 := error
* Note: The function may return an error and a partly valid packet;
* caller must free this packet.
*/
#ifdef DEBUG_PARSE_PACKET
int
dbg_parse_packet( IOBUF inp, PACKET *pkt, const char *dbg_f, int dbg_l )
{
int skip, rc;
do {
rc = parse( inp, pkt, 0, NULL, &skip, NULL, 0, "parse", dbg_f, dbg_l );
} while( skip );
return rc;
}
#else
int
parse_packet( IOBUF inp, PACKET *pkt )
{
int skip, rc;
do {
rc = parse( inp, pkt, 0, NULL, &skip, NULL, 0 );
} while( skip );
return rc;
}
#endif
/****************
* Like parse packet, but only return secret or public (sub)key packets.
*/
#ifdef DEBUG_PARSE_PACKET
int
dbg_search_packet( IOBUF inp, PACKET *pkt, off_t *retpos, int with_uid,
const char *dbg_f, int dbg_l )
{
int skip, rc;
do {
rc = parse( inp, pkt, with_uid?2:1, retpos, &skip, NULL, 0, "search", dbg_f, dbg_l );
} while( skip );
return rc;
}
#else
int
search_packet( IOBUF inp, PACKET *pkt, off_t *retpos, int with_uid )
{
int skip, rc;
do {
rc = parse( inp, pkt, with_uid?2:1, retpos, &skip, NULL, 0 );
} while( skip );
return rc;
}
#endif
/****************
* Copy all packets from INP to OUT, thereby removing unused spaces.
*/
#ifdef DEBUG_PARSE_PACKET
int
dbg_copy_all_packets( IOBUF inp, IOBUF out,
const char *dbg_f, int dbg_l )
{
PACKET pkt;
int skip, rc=0;
do {
init_packet(&pkt);
} while( !(rc = parse( inp, &pkt, 0, NULL, &skip, out, 0, "copy", dbg_f, dbg_l )));
return rc;
}
#else
int
copy_all_packets( IOBUF inp, IOBUF out )
{
PACKET pkt;
int skip, rc=0;
do {
init_packet(&pkt);
} while( !(rc = parse( inp, &pkt, 0, NULL, &skip, out, 0 )));
return rc;
}
#endif
/****************
* Copy some packets from INP to OUT, thereby removing unused spaces.
* Stop at offset STOPoff (i.e. don't copy packets at this or later offsets)
*/
#ifdef DEBUG_PARSE_PACKET
int
dbg_copy_some_packets( IOBUF inp, IOBUF out, off_t stopoff,
const char *dbg_f, int dbg_l )
{
PACKET pkt;
int skip, rc=0;
do {
if( iobuf_tell(inp) >= stopoff )
return 0;
init_packet(&pkt);
} while( !(rc = parse( inp, &pkt, 0, NULL, &skip, out, 0,
"some", dbg_f, dbg_l )) );
return rc;
}
#else
int
copy_some_packets( IOBUF inp, IOBUF out, off_t stopoff )
{
PACKET pkt;
int skip, rc=0;
do {
if( iobuf_tell(inp) >= stopoff )
return 0;
init_packet(&pkt);
} while( !(rc = parse( inp, &pkt, 0, NULL, &skip, out, 0 )) );
return rc;
}
#endif
/****************
* Skip over N packets
*/
#ifdef DEBUG_PARSE_PACKET
int
dbg_skip_some_packets( IOBUF inp, unsigned n,
const char *dbg_f, int dbg_l )
{
int skip, rc=0;
PACKET pkt;
for( ;n && !rc; n--) {
init_packet(&pkt);
rc = parse( inp, &pkt, 0, NULL, &skip, NULL, 1, "skip", dbg_f, dbg_l );
}
return rc;
}
#else
int
skip_some_packets( IOBUF inp, unsigned n )
{
int skip, rc=0;
PACKET pkt;
for( ;n && !rc; n--) {
init_packet(&pkt);
rc = parse( inp, &pkt, 0, NULL, &skip, NULL, 1 );
}
return rc;
}
#endif
/****************
* Parse packet. Set the variable skip points to 1 if the packet
* should be skipped; this is the case if either ONLYKEYPKTS is set
* and the parsed packet isn't one or the
* packet-type is 0, indicating deleted stuff.
* if OUT is not NULL, a special copymode is used.
*/
static int
parse( IOBUF inp, PACKET *pkt, int onlykeypkts, off_t *retpos,
int *skip, IOBUF out, int do_skip
#ifdef DEBUG_PARSE_PACKET
,const char *dbg_w, const char *dbg_f, int dbg_l
#endif
)
{
int rc=0, c, ctb, pkttype, lenbytes;
unsigned long pktlen;
byte hdr[8];
int hdrlen;
int new_ctb = 0, partial=0;
int with_uid = (onlykeypkts == 2);
*skip = 0;
assert( !pkt->pkt.generic );
if( retpos )
*retpos = iobuf_tell(inp);
if( (ctb = iobuf_get(inp)) == -1 ) {
rc = -1;
goto leave;
}
hdrlen=0;
hdr[hdrlen++] = ctb;
if( !(ctb & 0x80) ) {
log_error("%s: invalid packet (ctb=%02x)\n", iobuf_where(inp), ctb );
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
pktlen = 0;
new_ctb = !!(ctb & 0x40);
if( new_ctb ) {
pkttype = ctb & 0x3f;
if( (c = iobuf_get(inp)) == -1 ) {
log_error("%s: 1st length byte missing\n", iobuf_where(inp) );
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
/* The follwing code has been here for ages (2002-08-30) but it is
clearly wrong: For example passing a 0 as second argument to
iobuf_set_partial_block_mode stops the partial block mode which we
definitely do not want. Also all values < 224 or 255 are not
valid. Let's disable it and put PKT_COMPRESSED into the list of
allowed packets with partial header until someone complains. */
/* if (pkttype == PKT_COMPRESSED) { */
/* iobuf_set_partial_block_mode(inp, c & 0xff); */
/* pktlen = 0; /\* to indicate partial length *\/ */
/* partial=1; */
/* } */
/* else */
{
hdr[hdrlen++] = c;
if( c < 192 )
pktlen = c;
else if( c < 224 )
{
pktlen = (c - 192) * 256;
if( (c = iobuf_get(inp)) == -1 )
{
log_error("%s: 2nd length byte missing\n",
iobuf_where(inp) );
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
hdr[hdrlen++] = c;
pktlen += c + 192;
}
else if( c == 255 )
{
pktlen =
(unsigned long)(hdr[hdrlen++] = iobuf_get_noeof(inp)) << 24;
pktlen |= (hdr[hdrlen++] = iobuf_get_noeof(inp)) << 16;
pktlen |= (hdr[hdrlen++] = iobuf_get_noeof(inp)) << 8;
if( (c = iobuf_get(inp)) == -1 )
{
log_error("%s: 4 byte length invalid\n",
iobuf_where(inp) );
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
pktlen |= (hdr[hdrlen++] = c );
}
else
{
/* Partial body length. */
switch (pkttype)
{
case PKT_PLAINTEXT:
case PKT_ENCRYPTED:
case PKT_ENCRYPTED_MDC:
case PKT_COMPRESSED:
iobuf_set_partial_block_mode(inp, c & 0xff);
pktlen = 0;/* To indicate partial length. */
partial=1;
break;
default:
log_error("%s: partial length for invalid"
" packet type %d\n", iobuf_where(inp),pkttype);
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
}
}
}
else
{
pkttype = (ctb>>2)&0xf;
lenbytes = ((ctb&3)==3)? 0 : (1<<(ctb & 3));
if( !lenbytes )
{
pktlen = 0; /* don't know the value */
/* This isn't really partial, but we can treat it the same
in a "read until the end" sort of way. */
partial=1;
if(pkttype!=PKT_ENCRYPTED && pkttype!=PKT_PLAINTEXT
&& pkttype!=PKT_COMPRESSED)
{
log_error ("%s: indeterminate length for invalid"
" packet type %d\n", iobuf_where(inp), pkttype );
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
}
else
{
for( ; lenbytes; lenbytes-- )
{
pktlen <<= 8;
pktlen |= hdr[hdrlen++] = iobuf_get_noeof(inp);
}
}
}
if (pktlen == (unsigned long)(-1)) {
/* With some probability this is caused by a problem in the
* the uncompressing layer - in some error cases it just loops
* and spits out 0xff bytes. */
log_error ("%s: garbled packet detected\n", iobuf_where(inp) );
g10_exit (2);
}
if( out && pkttype ) {
rc = iobuf_write (out, hdr, hdrlen);
if (!rc)
rc = copy_packet(inp, out, pkttype, pktlen, partial );
goto leave;
}
if (with_uid && pkttype == PKT_USER_ID)
;
else if( do_skip
|| !pkttype
|| (onlykeypkts && pkttype != PKT_PUBLIC_SUBKEY
&& pkttype != PKT_PUBLIC_KEY
&& pkttype != PKT_SECRET_SUBKEY
&& pkttype != PKT_SECRET_KEY ) ) {
iobuf_skip_rest(inp, pktlen, partial);
*skip = 1;
rc = 0;
goto leave;
}
if( DBG_PACKET ) {
#ifdef DEBUG_PARSE_PACKET
log_debug("parse_packet(iob=%d): type=%d length=%lu%s (%s.%s.%d)\n",
iobuf_id(inp), pkttype, pktlen, new_ctb?" (new_ctb)":"",
dbg_w, dbg_f, dbg_l );
#else
log_debug("parse_packet(iob=%d): type=%d length=%lu%s\n",
iobuf_id(inp), pkttype, pktlen, new_ctb?" (new_ctb)":"" );
#endif
}
pkt->pkttype = pkttype;
rc = G10ERR_UNKNOWN_PACKET; /* default error */
switch( pkttype ) {
case PKT_PUBLIC_KEY:
case PKT_PUBLIC_SUBKEY:
pkt->pkt.public_key = xmalloc_clear(sizeof *pkt->pkt.public_key );
rc = parse_key(inp, pkttype, pktlen, hdr, hdrlen, pkt );
break;
case PKT_SECRET_KEY:
case PKT_SECRET_SUBKEY:
pkt->pkt.secret_key = xmalloc_clear(sizeof *pkt->pkt.secret_key );
rc = parse_key(inp, pkttype, pktlen, hdr, hdrlen, pkt );
break;
case PKT_SYMKEY_ENC:
rc = parse_symkeyenc( inp, pkttype, pktlen, pkt );
break;
case PKT_PUBKEY_ENC:
rc = parse_pubkeyenc(inp, pkttype, pktlen, pkt );
break;
case PKT_SIGNATURE:
pkt->pkt.signature = xmalloc_clear(sizeof *pkt->pkt.signature );
rc = parse_signature(inp, pkttype, pktlen, pkt->pkt.signature );
break;
case PKT_ONEPASS_SIG:
pkt->pkt.onepass_sig = xmalloc_clear(sizeof *pkt->pkt.onepass_sig );
rc = parse_onepass_sig(inp, pkttype, pktlen, pkt->pkt.onepass_sig );
break;
case PKT_USER_ID:
rc = parse_user_id(inp, pkttype, pktlen, pkt );
break;
case PKT_ATTRIBUTE:
pkt->pkttype = pkttype = PKT_USER_ID; /* we store it in the userID */
rc = parse_attribute(inp, pkttype, pktlen, pkt);
break;
case PKT_OLD_COMMENT:
case PKT_COMMENT:
rc = parse_comment(inp, pkttype, pktlen, pkt);
break;
case PKT_RING_TRUST:
parse_trust(inp, pkttype, pktlen, pkt);
rc = 0;
break;
case PKT_PLAINTEXT:
rc = parse_plaintext(inp, pkttype, pktlen, pkt, new_ctb, partial );
break;
case PKT_COMPRESSED:
rc = parse_compressed(inp, pkttype, pktlen, pkt, new_ctb );
break;
case PKT_ENCRYPTED:
case PKT_ENCRYPTED_MDC:
rc = parse_encrypted(inp, pkttype, pktlen, pkt, new_ctb, partial );
break;
case PKT_MDC:
rc = parse_mdc(inp, pkttype, pktlen, pkt, new_ctb );
break;
case PKT_GPG_CONTROL:
rc = parse_gpg_control(inp, pkttype, pktlen, pkt, partial );
break;
case PKT_MARKER:
rc = parse_marker(inp,pkttype,pktlen);
break;
default:
skip_packet(inp, pkttype, pktlen, partial);
break;
}
leave:
if( !rc && iobuf_error(inp) )
rc = G10ERR_INV_KEYRING;
return rc;
}
static void
dump_hex_line( int c, int *i )
{
if( *i && !(*i%8) ) {
if( *i && !(*i%24) )
fprintf (listfp, "\n%4d:", *i );
else
putc (' ', listfp);
}
if( c == -1 )
fprintf (listfp, " EOF" );
else
fprintf (listfp, " %02x", c );
++*i;
}
static int
copy_packet( IOBUF inp, IOBUF out, int pkttype,
unsigned long pktlen, int partial )
{
int rc;
int n;
char buf[100];
if( partial ) {
while( (n = iobuf_read( inp, buf, 100 )) != -1 )
if( (rc=iobuf_write(out, buf, n )) )
return rc; /* write error */
}
else if( !pktlen && pkttype == PKT_COMPRESSED ) {
log_debug("copy_packet: compressed!\n");
/* compressed packet, copy till EOF */
while( (n = iobuf_read( inp, buf, 100 )) != -1 )
if( (rc=iobuf_write(out, buf, n )) )
return rc; /* write error */
}
else {
for( ; pktlen; pktlen -= n ) {
n = pktlen > 100 ? 100 : pktlen;
n = iobuf_read( inp, buf, n );
if( n == -1 )
return gpg_error (GPG_ERR_EOF);
if( (rc=iobuf_write(out, buf, n )) )
return rc; /* write error */
}
}
return 0;
}
static void
skip_packet( IOBUF inp, int pkttype, unsigned long pktlen, int partial )
{
if( list_mode )
{
fprintf (listfp, ":unknown packet: type %2d, length %lu\n",
pkttype, pktlen);
if( pkttype )
{
int c, i=0 ;
fputs("dump:", listfp );
if( partial )
{
while( (c=iobuf_get(inp)) != -1 )
dump_hex_line(c, &i);
}
else
{
for( ; pktlen; pktlen-- )
{
dump_hex_line ((c=iobuf_get(inp)), &i);
if (c == -1)
break;
}
}
putc ('\n', listfp);
return;
}
}
iobuf_skip_rest(inp,pktlen,partial);
}
static void *
read_rest( IOBUF inp, size_t pktlen, int partial )
{
byte *p;
int i;
if( partial ) {
log_error("read_rest: can't store stream data\n");
p = NULL;
}
else {
p = xmalloc( pktlen );
for(i=0; pktlen; pktlen--, i++ )
p[i] = iobuf_get(inp);
}
return p;
}
static int
parse_marker( IOBUF inp, int pkttype, unsigned long pktlen )
{
(void)pkttype;
if(pktlen!=3)
goto fail;
if(iobuf_get(inp)!='P')
{
pktlen--;
goto fail;
}
if(iobuf_get(inp)!='G')
{
pktlen--;
goto fail;
}
if(iobuf_get(inp)!='P')
{
pktlen--;
goto fail;
}
if(list_mode)
fputs(":marker packet: PGP\n", listfp );
return 0;
fail:
log_error("invalid marker packet\n");
iobuf_skip_rest(inp,pktlen,0);
return G10ERR_INVALID_PACKET;
}
static int
parse_symkeyenc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet )
{
PKT_symkey_enc *k;
int rc = 0;
int i, version, s2kmode, cipher_algo, hash_algo, seskeylen, minlen;
if( pktlen < 4 ) {
log_error("packet(%d) too short\n", pkttype);
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
version = iobuf_get_noeof(inp); pktlen--;
if( version != 4 ) {
log_error("packet(%d) with unknown version %d\n", pkttype, version);
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
if( pktlen > 200 ) { /* (we encode the seskeylen in a byte) */
log_error("packet(%d) too large\n", pkttype);
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
cipher_algo = iobuf_get_noeof(inp); pktlen--;
s2kmode = iobuf_get_noeof(inp); pktlen--;
hash_algo = iobuf_get_noeof(inp); pktlen--;
switch( s2kmode ) {
case 0: /* simple s2k */
minlen = 0;
break;
case 1: /* salted s2k */
minlen = 8;
break;
case 3: /* iterated+salted s2k */
minlen = 9;
break;
default:
log_error("unknown S2K %d\n", s2kmode );
goto leave;
}
if( minlen > pktlen ) {
log_error("packet with S2K %d too short\n", s2kmode );
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
seskeylen = pktlen - minlen;
k = packet->pkt.symkey_enc = xmalloc_clear( sizeof *packet->pkt.symkey_enc
+ seskeylen - 1 );
k->version = version;
k->cipher_algo = cipher_algo;
k->s2k.mode = s2kmode;
k->s2k.hash_algo = hash_algo;
if( s2kmode == 1 || s2kmode == 3 ) {
for(i=0; i < 8 && pktlen; i++, pktlen-- )
k->s2k.salt[i] = iobuf_get_noeof(inp);
}
if( s2kmode == 3 ) {
k->s2k.count = iobuf_get(inp); pktlen--;
}
k->seskeylen = seskeylen;
if(k->seskeylen)
{
for(i=0; i < seskeylen && pktlen; i++, pktlen-- )
k->seskey[i] = iobuf_get_noeof(inp);
/* What we're watching out for here is a session key decryptor
with no salt. The RFC says that using salt for this is a
MUST. */
if(s2kmode!=1 && s2kmode!=3)
log_info(_("WARNING: potentially insecure symmetrically"
" encrypted session key\n"));
}
assert( !pktlen );
if( list_mode ) {
fprintf (listfp, ":symkey enc packet: version %d, cipher %d, s2k %d, hash %d",
version, cipher_algo, s2kmode, hash_algo);
if(seskeylen)
fprintf (listfp, ", seskey %d bits",(seskeylen-1)*8);
fprintf (listfp, "\n");
if( s2kmode == 1 || s2kmode == 3 ) {
fprintf (listfp, "\tsalt ");
for(i=0; i < 8; i++ )
fprintf (listfp, "%02x", k->s2k.salt[i]);
if( s2kmode == 3 )
fprintf (listfp, ", count %lu (%lu)",
S2K_DECODE_COUNT((ulong)k->s2k.count),
(ulong)k->s2k.count );
fprintf (listfp, "\n");
}
}
leave:
iobuf_skip_rest(inp, pktlen, 0);
return rc;
}
static int
parse_pubkeyenc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet )
{
unsigned int n;
int rc = 0;
int i, ndata;
PKT_pubkey_enc *k;
k = packet->pkt.pubkey_enc = xmalloc_clear(sizeof *packet->pkt.pubkey_enc);
if( pktlen < 12 ) {
log_error("packet(%d) too short\n", pkttype);
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
k->version = iobuf_get_noeof(inp); pktlen--;
if( k->version != 2 && k->version != 3 ) {
log_error("packet(%d) with unknown version %d\n", pkttype, k->version);
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
k->keyid[0] = read_32(inp); pktlen -= 4;
k->keyid[1] = read_32(inp); pktlen -= 4;
k->pubkey_algo = iobuf_get_noeof(inp); pktlen--;
k->throw_keyid = 0; /* only used as flag for build_packet */
if( list_mode )
fprintf (listfp, ":pubkey enc packet: version %d, algo %d, keyid %08lX%08lX\n",
k->version, k->pubkey_algo, (ulong)k->keyid[0], (ulong)k->keyid[1]);
ndata = pubkey_get_nenc(k->pubkey_algo);
if( !ndata ) {
if( list_mode )
fprintf (listfp, "\tunsupported algorithm %d\n", k->pubkey_algo );
unknown_pubkey_warning( k->pubkey_algo );
k->data[0] = NULL; /* no need to store the encrypted data */
}
else {
for( i=0; i < ndata; i++ ) {
n = pktlen;
k->data[i] = mpi_read(inp, &n, 0); pktlen -=n;
if( list_mode ) {
fprintf (listfp, "\tdata: ");
mpi_print(listfp, k->data[i], mpi_print_mode );
putc ('\n', listfp);
}
if (!k->data[i])
rc = gpg_error (GPG_ERR_INV_PACKET);
}
}
leave:
iobuf_skip_rest(inp, pktlen, 0);
return rc;
}
static void
dump_sig_subpkt( int hashed, int type, int critical,
const byte *buffer, size_t buflen, size_t length )
{
const char *p=NULL;
int i;
/* The CERT has warning out with explains how to use GNUPG to
* detect the ARRs - we print our old message here when it is a faked
* ARR and add an additional notice */
if ( type == SIGSUBPKT_ARR && !hashed ) {
fprintf (listfp,
"\tsubpkt %d len %u (additional recipient request)\n"
"WARNING: PGP versions > 5.0 and < 6.5.8 will automagically "
"encrypt to this key and thereby reveal the plaintext to "
"the owner of this ARR key. Detailed info follows:\n",
type, (unsigned)length );
}
buffer++;
length--;
fprintf (listfp, "\t%s%ssubpkt %d len %u (", /*)*/
critical ? "critical ":"",
hashed ? "hashed ":"", type, (unsigned)length );
if( length > buflen ) {
fprintf (listfp, "too short: buffer is only %u)\n", (unsigned)buflen );
return;
}
switch( type ) {
case SIGSUBPKT_SIG_CREATED:
if( length >= 4 )
fprintf (listfp, "sig created %s",
strtimestamp (buf32_to_u32(buffer)) );
break;
case SIGSUBPKT_SIG_EXPIRE:
if( length >= 4 )
{
if(buf32_to_u32(buffer))
fprintf (listfp, "sig expires after %s",
strtimevalue( buf32_to_u32(buffer) ) );
else
fprintf (listfp, "sig does not expire");
}
break;
case SIGSUBPKT_EXPORTABLE:
if( length )
fprintf (listfp, "%sexportable", *buffer? "":"not ");
break;
case SIGSUBPKT_TRUST:
if(length!=2)
p="[invalid trust subpacket]";
else
fprintf (listfp, "trust signature of depth %d, value %d",buffer[0],buffer[1]);
break;
case SIGSUBPKT_REGEXP:
if(!length)
p="[invalid regexp subpacket]";
else
{
fprintf (listfp, "regular expression: \"");
print_string (listfp, buffer, length, '\"');
p = "\"";
}
break;
case SIGSUBPKT_REVOCABLE:
if( length )
fprintf (listfp, "%srevocable", *buffer? "":"not ");
break;
case SIGSUBPKT_KEY_EXPIRE:
if( length >= 4 )
{
if(buf32_to_u32(buffer))
fprintf (listfp, "key expires after %s",
strtimevalue( buf32_to_u32(buffer) ) );
else
fprintf (listfp, "key does not expire");
}
break;
case SIGSUBPKT_PREF_SYM:
fputs("pref-sym-algos:", listfp );
for( i=0; i < length; i++ )
fprintf (listfp, " %d", buffer[i] );
break;
case SIGSUBPKT_REV_KEY:
fputs("revocation key: ", listfp );
if( length < 22 )
p = "[too short]";
else {
fprintf (listfp, "c=%02x a=%d f=", buffer[0], buffer[1] );
for( i=2; i < length; i++ )
fprintf (listfp, "%02X", buffer[i] );
}
break;
case SIGSUBPKT_ISSUER:
if( length >= 8 )
fprintf (listfp, "issuer key ID %08lX%08lX",
buf32_to_ulong (buffer),
buf32_to_ulong (buffer+4));
break;
case SIGSUBPKT_NOTATION:
{
fputs("notation: ", listfp );
if( length < 8 )
p = "[too short]";
else {
const byte *s = buffer;
size_t n1, n2;
n1 = (s[4] << 8) | s[5];
n2 = (s[6] << 8) | s[7];
s += 8;
if( 8+n1+n2 != length )
p = "[error]";
else {
print_string( listfp, s, n1, ')' );
putc( '=', listfp );
if( *buffer & 0x80 )
print_string( listfp, s+n1, n2, ')' );
else
p = "[not human readable]";
}
}
}
break;
case SIGSUBPKT_PREF_HASH:
fputs("pref-hash-algos:", listfp );
for( i=0; i < length; i++ )
fprintf (listfp, " %d", buffer[i] );
break;
case SIGSUBPKT_PREF_COMPR:
fputs("pref-zip-algos:", listfp );
for( i=0; i < length; i++ )
fprintf (listfp, " %d", buffer[i] );
break;
case SIGSUBPKT_KS_FLAGS:
fputs("key server preferences:",listfp);
for(i=0;i=100 && type<=110)
p="experimental / private subpacket";
else
p = "?";
break;
}
fprintf (listfp, "%s)\n", p? p: "");
}
/****************
* Returns: >= 0 use this offset into buffer
* -1 explicitly reject returning this type
* -2 subpacket too short
*/
int
parse_one_sig_subpkt( const byte *buffer, size_t n, int type )
{
switch( type )
{
case SIGSUBPKT_REV_KEY:
if(n < 22)
break;
return 0;
case SIGSUBPKT_SIG_CREATED:
case SIGSUBPKT_SIG_EXPIRE:
case SIGSUBPKT_KEY_EXPIRE:
if( n < 4 )
break;
return 0;
case SIGSUBPKT_KEY_FLAGS:
case SIGSUBPKT_KS_FLAGS:
case SIGSUBPKT_PREF_SYM:
case SIGSUBPKT_PREF_HASH:
case SIGSUBPKT_PREF_COMPR:
case SIGSUBPKT_POLICY:
case SIGSUBPKT_PREF_KS:
case SIGSUBPKT_FEATURES:
case SIGSUBPKT_REGEXP:
return 0;
case SIGSUBPKT_SIGNATURE:
case SIGSUBPKT_EXPORTABLE:
case SIGSUBPKT_REVOCABLE:
case SIGSUBPKT_REVOC_REASON:
if( !n )
break;
return 0;
case SIGSUBPKT_ISSUER: /* issuer key ID */
if( n < 8 )
break;
return 0;
case SIGSUBPKT_NOTATION:
/* minimum length needed, and the subpacket must be well-formed
where the name length and value length all fit inside the
packet. */
if(n<8 || 8+((buffer[4]<<8)|buffer[5])+((buffer[6]<<8)|buffer[7]) != n)
break;
return 0;
case SIGSUBPKT_PRIMARY_UID:
if ( n != 1 )
break;
return 0;
case SIGSUBPKT_TRUST:
if ( n != 2 )
break;
return 0;
default: return 0;
}
return -2;
}
/* Not many critical notations we understand yet... */
static int
can_handle_critical_notation(const byte *name,size_t len)
{
if(len==32 && memcmp(name,"preferred-email-encoding@pgp.com",32)==0)
return 1;
if(len==21 && memcmp(name,"pka-address@gnupg.org",21)==0)
return 1;
return 0;
}
static int
can_handle_critical( const byte *buffer, size_t n, int type )
{
switch( type )
{
case SIGSUBPKT_NOTATION:
if (n >= 8)
{
size_t notation_len = ((buffer[4] << 8) | buffer[5]);
if (n - 8 >= notation_len)
return can_handle_critical_notation (buffer + 8, notation_len);
}
return 0;
case SIGSUBPKT_SIGNATURE:
case SIGSUBPKT_SIG_CREATED:
case SIGSUBPKT_SIG_EXPIRE:
case SIGSUBPKT_KEY_EXPIRE:
case SIGSUBPKT_EXPORTABLE:
case SIGSUBPKT_REVOCABLE:
case SIGSUBPKT_REV_KEY:
case SIGSUBPKT_ISSUER:/* issuer key ID */
case SIGSUBPKT_PREF_SYM:
case SIGSUBPKT_PREF_HASH:
case SIGSUBPKT_PREF_COMPR:
case SIGSUBPKT_KEY_FLAGS:
case SIGSUBPKT_PRIMARY_UID:
case SIGSUBPKT_FEATURES:
case SIGSUBPKT_TRUST:
case SIGSUBPKT_REGEXP:
/* Is it enough to show the policy or keyserver? */
case SIGSUBPKT_POLICY:
case SIGSUBPKT_PREF_KS:
return 1;
default:
return 0;
}
}
const byte *
enum_sig_subpkt( const subpktarea_t *pktbuf, sigsubpkttype_t reqtype,
size_t *ret_n, int *start, int *critical )
{
const byte *buffer;
int buflen;
int type;
int critical_dummy;
int offset;
size_t n;
int seq = 0;
int reqseq = start? *start: 0;
if(!critical)
critical=&critical_dummy;
if( !pktbuf || reqseq == -1 ) {
static char dummy[] = "x";
/* Return a value different from NULL to indicate that
* there is no critical bit we do not understand. */
return reqtype == SIGSUBPKT_TEST_CRITICAL ? dummy : NULL;
}
buffer = pktbuf->data;
buflen = pktbuf->len;
while( buflen ) {
n = *buffer++; buflen--;
if( n == 255 ) { /* 4 byte length header */
if( buflen < 4 )
goto too_short;
n = buf32_to_size_t (buffer);
buffer += 4;
buflen -= 4;
}
else if( n >= 192 ) { /* 2 byte special encoded length header */
if( buflen < 2 )
goto too_short;
n = (( n - 192 ) << 8) + *buffer + 192;
buffer++;
buflen--;
}
if( buflen < n )
goto too_short;
type = *buffer;
if( type & 0x80 ) {
type &= 0x7f;
*critical = 1;
}
else
*critical = 0;
if( !(++seq > reqseq) )
;
else if( reqtype == SIGSUBPKT_TEST_CRITICAL ) {
if( *critical ) {
if( n-1 > buflen+1 )
goto too_short;
if( !can_handle_critical(buffer+1, n-1, type ) )
{
if(opt.verbose)
log_info(_("subpacket of type %d has "
"critical bit set\n"),type);
if( start )
*start = seq;
return NULL; /* this is an error */
}
}
}
else if( reqtype < 0 ) /* list packets */
dump_sig_subpkt( reqtype == SIGSUBPKT_LIST_HASHED,
type, *critical, buffer, buflen, n );
else if( type == reqtype ) { /* found */
buffer++;
n--;
if( n > buflen )
goto too_short;
if( ret_n )
*ret_n = n;
offset = parse_one_sig_subpkt(buffer, n, type );
switch( offset ) {
case -2:
log_error("subpacket of type %d too short\n", type);
return NULL;
case -1:
return NULL;
default:
break;
}
if( start )
*start = seq;
return buffer+offset;
}
buffer += n; buflen -=n;
}
if( reqtype == SIGSUBPKT_TEST_CRITICAL )
return buffer; /* as value true to indicate that there is no */
/* critical bit we don't understand */
if( start )
*start = -1;
return NULL; /* end of packets; not found */
too_short:
if(opt.verbose)
log_info("buffer shorter than subpacket\n");
if( start )
*start = -1;
return NULL;
}
const byte *
parse_sig_subpkt (const subpktarea_t *buffer, sigsubpkttype_t reqtype,
size_t *ret_n)
{
return enum_sig_subpkt( buffer, reqtype, ret_n, NULL, NULL );
}
const byte *
parse_sig_subpkt2 (PKT_signature *sig, sigsubpkttype_t reqtype,
size_t *ret_n )
{
const byte *p;
p = parse_sig_subpkt (sig->hashed, reqtype, ret_n );
if( !p )
p = parse_sig_subpkt (sig->unhashed, reqtype, ret_n );
return p;
}
/* Find all revocation keys. Look in hashed area only. */
void parse_revkeys(PKT_signature *sig)
{
struct revocation_key *revkey;
int seq=0;
size_t len;
if(sig->sig_class!=0x1F)
return;
while((revkey=
(struct revocation_key *)enum_sig_subpkt(sig->hashed,
SIGSUBPKT_REV_KEY,
&len,&seq,NULL)))
{
if(len==sizeof(struct revocation_key) &&
(revkey->class&0x80)) /* 0x80 bit must be set */
{
sig->revkey=xrealloc(sig->revkey,
sizeof(struct revocation_key *)*(sig->numrevkeys+1));
sig->revkey[sig->numrevkeys]=revkey;
sig->numrevkeys++;
}
}
}
int
parse_signature( IOBUF inp, int pkttype, unsigned long pktlen,
PKT_signature *sig )
{
int md5_len=0;
unsigned n;
int is_v4=0;
int rc=0;
int i, ndata;
if( pktlen < 16 ) {
log_error("packet(%d) too short\n", pkttype);
goto leave;
}
sig->version = iobuf_get_noeof(inp); pktlen--;
if( sig->version == 4 )
is_v4=1;
else if( sig->version != 2 && sig->version != 3 ) {
log_error("packet(%d) with unknown version %d\n",
pkttype, sig->version);
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
if( !is_v4 ) {
md5_len = iobuf_get_noeof(inp); pktlen--;
}
sig->sig_class = iobuf_get_noeof(inp); pktlen--;
if( !is_v4 ) {
sig->timestamp = read_32(inp); pktlen -= 4;
sig->keyid[0] = read_32(inp); pktlen -= 4;
sig->keyid[1] = read_32(inp); pktlen -= 4;
}
sig->pubkey_algo = iobuf_get_noeof(inp); pktlen--;
sig->digest_algo = iobuf_get_noeof(inp); pktlen--;
sig->flags.exportable=1;
sig->flags.revocable=1;
if( is_v4 ) { /* read subpackets */
n = read_16(inp); pktlen -= 2; /* length of hashed data */
if( n > 10000 ) {
log_error("signature packet: hashed data too long\n");
rc = G10ERR_INVALID_PACKET;
goto leave;
}
if( n ) {
sig->hashed = xmalloc (sizeof (*sig->hashed) + n - 1 );
sig->hashed->size = n;
sig->hashed->len = n;
if( iobuf_read (inp, sig->hashed->data, n ) != n ) {
log_error ("premature eof while reading "
"hashed signature data\n");
rc = -1;
goto leave;
}
pktlen -= n;
}
n = read_16(inp); pktlen -= 2; /* length of unhashed data */
if( n > 10000 ) {
log_error("signature packet: unhashed data too long\n");
rc = G10ERR_INVALID_PACKET;
goto leave;
}
if( n ) {
sig->unhashed = xmalloc (sizeof(*sig->unhashed) + n - 1 );
sig->unhashed->size = n;
sig->unhashed->len = n;
if( iobuf_read(inp, sig->unhashed->data, n ) != n ) {
log_error("premature eof while reading "
"unhashed signature data\n");
rc = -1;
goto leave;
}
pktlen -= n;
}
}
if( pktlen < 5 ) { /* sanity check */
log_error("packet(%d) too short\n", pkttype);
rc = G10ERR_INVALID_PACKET;
goto leave;
}
sig->digest_start[0] = iobuf_get_noeof(inp); pktlen--;
sig->digest_start[1] = iobuf_get_noeof(inp); pktlen--;
if( is_v4 && sig->pubkey_algo )
{ /*extract required information */
const byte *p;
size_t len;
/* set sig->flags.unknown_critical if there is a
* critical bit set for packets which we do not understand */
if( !parse_sig_subpkt (sig->hashed, SIGSUBPKT_TEST_CRITICAL, NULL)
|| !parse_sig_subpkt (sig->unhashed, SIGSUBPKT_TEST_CRITICAL,
NULL) )
sig->flags.unknown_critical = 1;
p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_SIG_CREATED, NULL );
if(p)
sig->timestamp = buf32_to_u32 (p);
else if(!(sig->pubkey_algo>=100 && sig->pubkey_algo<=110)
&& opt.verbose)
log_info ("signature packet without timestamp\n");
p = parse_sig_subpkt2( sig, SIGSUBPKT_ISSUER, NULL );
if(p)
{
sig->keyid[0] = buf32_to_u32 (p);
sig->keyid[1] = buf32_to_u32 (p+4);
}
else if(!(sig->pubkey_algo>=100 && sig->pubkey_algo<=110)
&& opt.verbose)
log_info ("signature packet without keyid\n");
p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_SIG_EXPIRE,NULL);
if(p && buf32_to_u32 (p))
sig->expiredate = sig->timestamp + buf32_to_u32 (p);
if(sig->expiredate && sig->expiredate<=make_timestamp())
sig->flags.expired=1;
p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_POLICY,NULL);
if(p)
sig->flags.policy_url=1;
p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_PREF_KS,NULL);
if(p)
sig->flags.pref_ks=1;
p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION,NULL);
if(p)
sig->flags.notation=1;
p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_REVOCABLE,NULL);
if(p && *p==0)
sig->flags.revocable=0;
p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_TRUST,&len);
if(p && len==2)
{
sig->trust_depth=p[0];
sig->trust_value=p[1];
/* Only look for a regexp if there is also a trust
subpacket. */
sig->trust_regexp=
parse_sig_subpkt(sig->hashed,SIGSUBPKT_REGEXP,&len);
/* If the regular expression is of 0 length, there is no
regular expression. */
if(len==0)
sig->trust_regexp=NULL;
}
/* We accept the exportable subpacket from either the hashed
or unhashed areas as older versions of gpg put it in the
unhashed area. In theory, anyway, we should never see this
packet off of a local keyring. */
p=parse_sig_subpkt2(sig,SIGSUBPKT_EXPORTABLE,NULL);
if(p && *p==0)
sig->flags.exportable=0;
/* Find all revocation keys. */
if(sig->sig_class==0x1F)
parse_revkeys(sig);
}
if( list_mode ) {
fprintf (listfp, ":signature packet: algo %d, keyid %08lX%08lX\n"
"\tversion %d, created %lu, md5len %d, sigclass 0x%02x\n"
"\tdigest algo %d, begin of digest %02x %02x\n",
sig->pubkey_algo,
(ulong)sig->keyid[0], (ulong)sig->keyid[1],
sig->version, (ulong)sig->timestamp, md5_len, sig->sig_class,
sig->digest_algo,
sig->digest_start[0], sig->digest_start[1] );
if( is_v4 ) {
parse_sig_subpkt (sig->hashed, SIGSUBPKT_LIST_HASHED, NULL );
parse_sig_subpkt (sig->unhashed, SIGSUBPKT_LIST_UNHASHED, NULL);
}
}
ndata = pubkey_get_nsig(sig->pubkey_algo);
if( !ndata ) {
if( list_mode )
fprintf (listfp, "\tunknown algorithm %d\n", sig->pubkey_algo );
unknown_pubkey_warning( sig->pubkey_algo );
/* We store the plain material in data[0], so that we are able
* to write it back with build_packet() */
if (pktlen > (5 * MAX_EXTERN_MPI_BITS/8))
{
/* However we include a limit to avoid too trivial DoS
attacks by having gpg allocate too much memory. */
log_error ("signature packet: too much data\n");
rc = G10ERR_INVALID_PACKET;
}
else
{
sig->data[0]= gcry_mpi_set_opaque (NULL, read_rest(inp, pktlen, 0),
pktlen*8 );
pktlen = 0;
}
}
else {
for( i=0; i < ndata; i++ ) {
n = pktlen;
sig->data[i] = mpi_read(inp, &n, 0 );
pktlen -=n;
if( list_mode ) {
fprintf (listfp, "\tdata: ");
mpi_print(listfp, sig->data[i], mpi_print_mode );
putc ('\n', listfp);
}
if (!sig->data[i])
rc = G10ERR_INVALID_PACKET;
}
}
leave:
iobuf_skip_rest(inp, pktlen, 0);
return rc;
}
static int
parse_onepass_sig( IOBUF inp, int pkttype, unsigned long pktlen,
PKT_onepass_sig *ops )
{
int version;
int rc = 0;
if( pktlen < 13 ) {
log_error("packet(%d) too short\n", pkttype);
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
version = iobuf_get_noeof(inp); pktlen--;
if( version != 3 ) {
log_error("onepass_sig with unknown version %d\n", version);
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
ops->sig_class = iobuf_get_noeof(inp); pktlen--;
ops->digest_algo = iobuf_get_noeof(inp); pktlen--;
ops->pubkey_algo = iobuf_get_noeof(inp); pktlen--;
ops->keyid[0] = read_32(inp); pktlen -= 4;
ops->keyid[1] = read_32(inp); pktlen -= 4;
ops->last = iobuf_get_noeof(inp); pktlen--;
if( list_mode )
fprintf (listfp,
":onepass_sig packet: keyid %08lX%08lX\n"
"\tversion %d, sigclass 0x%02x, digest %d, pubkey %d, "
"last=%d\n",
(ulong)ops->keyid[0], (ulong)ops->keyid[1],
version, ops->sig_class,
ops->digest_algo, ops->pubkey_algo, ops->last );
leave:
iobuf_skip_rest(inp, pktlen, 0);
return rc;
}
static gcry_mpi_t
read_protected_v3_mpi (IOBUF inp, unsigned long *length)
{
int c;
unsigned int nbits, nbytes;
unsigned char *buf, *p;
gcry_mpi_t val;
if (*length < 2)
{
log_error ("mpi too small\n");
return NULL;
}
if ((c=iobuf_get (inp)) == -1)
return NULL;
--*length;
nbits = c << 8;
if ((c=iobuf_get(inp)) == -1)
return NULL;
--*length;
nbits |= c;
if (nbits > 16384)
{
log_error ("mpi too large (%u bits)\n", nbits);
return NULL;
}
nbytes = (nbits+7) / 8;
buf = p = xmalloc (2 + nbytes);
*p++ = nbits >> 8;
*p++ = nbits;
for (; nbytes && *length; nbytes--, --*length)
*p++ = iobuf_get (inp);
if (nbytes)
{
log_error ("packet shorter than mpi\n");
xfree (buf);
return NULL;
}
/* convert buffer into an opaque MPI */
val = gcry_mpi_set_opaque (NULL, buf, (p-buf)*8);
return val;
}
static int
parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
byte *hdr, int hdrlen, PACKET *pkt)
{
int i, version, algorithm;
unsigned n;
unsigned long timestamp, expiredate, max_expiredate;
int npkey, nskey;
int is_v4=0;
int rc=0;
u32 keyid[2];
(void)hdr;
version = iobuf_get_noeof(inp); pktlen--;
if( pkttype == PKT_PUBLIC_SUBKEY && version == '#' ) {
/* early versions of G10 use old PGP comments packets;
* luckily all those comments are started by a hash */
if( list_mode ) {
fprintf (listfp, ":rfc1991 comment packet: \"" );
for( ; pktlen; pktlen-- ) {
int c;
c = iobuf_get_noeof(inp);
if( c >= ' ' && c <= 'z' )
putc (c, listfp);
else
fprintf (listfp, "\\x%02x", c );
}
fprintf (listfp, "\"\n");
}
iobuf_skip_rest(inp, pktlen, 0);
return 0;
}
else if( version == 4 )
is_v4=1;
else if( version != 2 && version != 3 ) {
log_error("packet(%d) with unknown version %d\n", pkttype, version);
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
if( pktlen < 11 ) {
log_error("packet(%d) too short\n", pkttype);
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
else if (pktlen > MAX_KEY_PACKET_LENGTH) {
log_error ("packet(%d) too large\n", pkttype);
if (list_mode)
fputs (":key packet: [too large]\n", listfp);
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
timestamp = read_32(inp); pktlen -= 4;
if( is_v4 ) {
expiredate = 0; /* have to get it from the selfsignature */
max_expiredate = 0;
}
else {
unsigned short ndays;
ndays = read_16(inp); pktlen -= 2;
if( ndays )
expiredate = timestamp + ndays * 86400L;
else
expiredate = 0;
max_expiredate=expiredate;
}
algorithm = iobuf_get_noeof(inp); pktlen--;
if( list_mode )
fprintf (listfp, ":%s key packet:\n"
"\tversion %d, algo %d, created %lu, expires %lu\n",
pkttype == PKT_PUBLIC_KEY? "public" :
pkttype == PKT_SECRET_KEY? "secret" :
pkttype == PKT_PUBLIC_SUBKEY? "public sub" :
pkttype == PKT_SECRET_SUBKEY? "secret sub" : "??",
version, algorithm, timestamp, expiredate );
if( pkttype == PKT_SECRET_KEY || pkttype == PKT_SECRET_SUBKEY ) {
PKT_secret_key *sk = pkt->pkt.secret_key;
sk->timestamp = timestamp;
sk->expiredate = expiredate;
sk->max_expiredate = max_expiredate;
sk->hdrbytes = hdrlen;
sk->version = version;
sk->is_primary = pkttype == PKT_SECRET_KEY;
sk->pubkey_algo = algorithm;
sk->req_usage = 0;
sk->pubkey_usage = 0; /* not yet used */
}
else {
PKT_public_key *pk = pkt->pkt.public_key;
pk->timestamp = timestamp;
pk->expiredate = expiredate;
pk->max_expiredate = max_expiredate;
pk->hdrbytes = hdrlen;
pk->version = version;
pk->is_primary = pkttype == PKT_PUBLIC_KEY;
pk->pubkey_algo = algorithm;
pk->req_usage = 0;
pk->pubkey_usage = 0; /* not yet used */
pk->is_revoked = 0;
pk->is_disabled = 0;
pk->keyid[0] = 0;
pk->keyid[1] = 0;
}
nskey = pubkey_get_nskey( algorithm );
npkey = pubkey_get_npkey( algorithm );
if( !npkey ) {
if( list_mode )
fprintf (listfp, "\tunknown algorithm %d\n", algorithm );
unknown_pubkey_warning( algorithm );
}
if( pkttype == PKT_SECRET_KEY || pkttype == PKT_SECRET_SUBKEY ) {
PKT_secret_key *sk = pkt->pkt.secret_key;
byte temp[16];
size_t snlen = 0;
if (pktlen < 1)
{
rc = GPG_ERR_INV_PACKET;
goto leave;
}
if( !npkey ) {
sk->skey[0] = gcry_mpi_set_opaque (NULL, read_rest(inp, pktlen, 0),
pktlen*8 );
pktlen = 0;
goto leave;
}
for(i=0; i < npkey; i++ ) {
n = pktlen;
sk->skey[i] = mpi_read(inp, &n, 0 );
pktlen -=n;
if( list_mode ) {
fprintf (listfp, "\tskey[%d]: ", i);
mpi_print(listfp, sk->skey[i], mpi_print_mode );
putc ('\n', listfp);
}
if (!sk->skey[i])
rc = G10ERR_INVALID_PACKET;
}
if (rc) /* one of the MPIs were bad */
goto leave;
sk->protect.algo = iobuf_get_noeof(inp);
pktlen--;
sk->protect.sha1chk = 0;
if( sk->protect.algo ) {
sk->is_protected = 1;
sk->protect.s2k.count = 0;
if( sk->protect.algo == 254 || sk->protect.algo == 255 ) {
if( pktlen < 3 ) {
rc = G10ERR_INVALID_PACKET;
goto leave;
}
sk->protect.sha1chk = (sk->protect.algo == 254);
sk->protect.algo = iobuf_get_noeof(inp);
pktlen--;
/* Note that a sk->protect.algo > 110 is illegal, but
I'm not erroring on it here as otherwise there
would be no way to delete such a key. */
sk->protect.s2k.mode = iobuf_get_noeof(inp);
pktlen--;
sk->protect.s2k.hash_algo = iobuf_get_noeof(inp);
pktlen--;
/* check for the special GNU extension */
if( is_v4 && sk->protect.s2k.mode == 101 ) {
for(i=0; i < 4 && pktlen; i++, pktlen-- )
temp[i] = iobuf_get_noeof(inp);
if( i < 4 || memcmp( temp, "GNU", 3 ) ) {
if( list_mode )
fprintf (listfp, "\tunknown S2K %d\n",
sk->protect.s2k.mode );
rc = G10ERR_INVALID_PACKET;
goto leave;
}
/* here we know that it is a gnu extension
* What follows is the GNU protection mode:
* All values have special meanings
* and they are mapped in the mode with a base of 1000.
*/
sk->protect.s2k.mode = 1000 + temp[3];
}
switch( sk->protect.s2k.mode ) {
case 1:
case 3:
for(i=0; i < 8 && pktlen; i++, pktlen-- )
temp[i] = iobuf_get_noeof(inp);
memcpy(sk->protect.s2k.salt, temp, 8 );
break;
}
switch( sk->protect.s2k.mode ) {
case 0: if( list_mode ) fprintf (listfp, "\tsimple S2K" );
break;
case 1: if( list_mode ) fprintf (listfp, "\tsalted S2K" );
break;
case 3: if( list_mode ) fprintf (listfp, "\titer+salt S2K" );
break;
case 1001: if( list_mode ) fprintf (listfp,
"\tgnu-dummy S2K" );
break;
case 1002: if (list_mode) fprintf (listfp,
"\tgnu-divert-to-card S2K");
break;
default:
if( list_mode )
fprintf (listfp, "\tunknown %sS2K %d\n",
sk->protect.s2k.mode < 1000? "":"GNU ",
sk->protect.s2k.mode );
rc = G10ERR_INVALID_PACKET;
goto leave;
}
if( list_mode ) {
fprintf (listfp, ", algo: %d,%s hash: %d",
sk->protect.algo,
sk->protect.sha1chk?" SHA1 protection,"
:" simple checksum,",
sk->protect.s2k.hash_algo );
if( sk->protect.s2k.mode == 1
|| sk->protect.s2k.mode == 3 ) {
fprintf (listfp, ", salt: ");
for(i=0; i < 8; i++ )
fprintf (listfp, "%02x", sk->protect.s2k.salt[i]);
}
putc ('\n', listfp);
}
if( sk->protect.s2k.mode == 3 ) {
if( pktlen < 1 ) {
rc = G10ERR_INVALID_PACKET;
goto leave;
}
sk->protect.s2k.count = iobuf_get(inp);
pktlen--;
if( list_mode )
fprintf (listfp, "\tprotect count: %lu (%lu)\n",
(ulong)S2K_DECODE_COUNT
((ulong)sk->protect.s2k.count),
(ulong)sk->protect.s2k.count);
}
else if( sk->protect.s2k.mode == 1002 ) {
/* Read the serial number. */
if (pktlen < 1) {
rc = G10ERR_INVALID_PACKET;
goto leave;
}
snlen = iobuf_get (inp);
pktlen--;
if (pktlen < snlen || snlen == -1) {
rc = G10ERR_INVALID_PACKET;
goto leave;
}
}
}
/* Note that a sk->protect.algo > 110 is illegal, but I'm
not erroring on it here as otherwise there would be no
way to delete such a key. */
else { /* old version; no S2K, so we set mode to 0, hash MD5 */
sk->protect.s2k.mode = 0;
sk->protect.s2k.hash_algo = DIGEST_ALGO_MD5;
if( list_mode )
fprintf (listfp, "\tprotect algo: %d (hash algo: %d)\n",
sk->protect.algo, sk->protect.s2k.hash_algo );
}
/* It is really ugly that we don't know the size
* of the IV here in cases we are not aware of the algorithm.
* so a
* sk->protect.ivlen = cipher_get_blocksize(sk->protect.algo);
* won't work. The only solution I see is to hardwire it.
* NOTE: if you change the ivlen above 16, don't forget to
* enlarge temp.
*/
sk->protect.ivlen = openpgp_cipher_blocklen (sk->protect.algo);
assert (sk->protect.ivlen <= sizeof (temp));
if( sk->protect.s2k.mode == 1001 )
sk->protect.ivlen = 0;
else if( sk->protect.s2k.mode == 1002 )
sk->protect.ivlen = snlen < 16? snlen : 16;
if( pktlen < sk->protect.ivlen ) {
rc = G10ERR_INVALID_PACKET;
goto leave;
}
for(i=0; i < sk->protect.ivlen && pktlen; i++, pktlen-- )
temp[i] = iobuf_get_noeof(inp);
if( list_mode ) {
fprintf (listfp,
sk->protect.s2k.mode == 1002? "\tserial-number: "
: "\tprotect IV: ");
for(i=0; i < sk->protect.ivlen; i++ )
fprintf (listfp, " %02x", temp[i] );
putc ('\n', listfp);
}
memcpy(sk->protect.iv, temp, sk->protect.ivlen );
}
else
sk->is_protected = 0;
/* It does not make sense to read it into secure memory.
* If the user is so careless, not to protect his secret key,
* we can assume, that he operates an open system :=(.
* So we put the key into secure memory when we unprotect it. */
if( sk->protect.s2k.mode == 1001
|| sk->protect.s2k.mode == 1002 ) {
/* better set some dummy stuff here */
sk->skey[npkey] = gcry_mpi_set_opaque(NULL,
xstrdup("dummydata"), 10*8);
pktlen = 0;
}
else if( is_v4 && sk->is_protected ) {
/* ugly; the length is encrypted too, so we read all
* stuff up to the end of the packet into the first
* skey element */
if (pktlen < 2) /* At least two bytes for the length. */
{
rc = GPG_ERR_INV_PACKET;
goto leave;
}
sk->skey[npkey] = gcry_mpi_set_opaque (NULL,
read_rest(inp, pktlen, 0),
pktlen*8);
pktlen = 0;
if( list_mode ) {
fprintf (listfp, "\tencrypted stuff follows\n");
}
}
else { /* v3 method: the mpi length is not encrypted */
for(i=npkey; i < nskey; i++ ) {
if ( sk->is_protected ) {
sk->skey[i] = read_protected_v3_mpi (inp, &pktlen);
if( list_mode )
fprintf (listfp, "\tskey[%d]: [encrypted]\n", i);
}
else {
if (pktlen < 2) /* At least two bytes for the length. */
{
rc = GPG_ERR_INV_PACKET;
goto leave;
}
n = pktlen;
sk->skey[i] = mpi_read(inp, &n, 0 );
pktlen -=n;
if( list_mode ) {
fprintf (listfp, "\tskey[%d]: ", i);
mpi_print(listfp, sk->skey[i], mpi_print_mode );
putc ('\n', listfp);
}
}
if (!sk->skey[i])
rc = G10ERR_INVALID_PACKET;
}
if (rc)
goto leave;
if (pktlen < 2)
{
rc = GPG_ERR_INV_PACKET;
goto leave;
}
sk->csum = read_16(inp);
pktlen -= 2;
if( list_mode ) {
fprintf (listfp, "\tchecksum: %04hx\n", sk->csum);
}
}
if (list_mode)
keyid_from_sk (sk, keyid);
}
else {
PKT_public_key *pk = pkt->pkt.public_key;
if (pktlen < 1)
{
rc = GPG_ERR_INV_PACKET;
goto leave;
}
if( !npkey ) {
pk->pkey[0] = gcry_mpi_set_opaque ( NULL,
read_rest(inp, pktlen, 0),
pktlen*8 );
pktlen = 0;
goto leave;
}
for(i=0; i < npkey; i++ ) {
n = pktlen;
pk->pkey[i] = mpi_read(inp, &n, 0 );
pktlen -=n;
if( list_mode ) {
fprintf (listfp, "\tpkey[%d]: ", i);
mpi_print(listfp, pk->pkey[i], mpi_print_mode );
putc ('\n', listfp);
}
if (!pk->pkey[i])
rc = G10ERR_INVALID_PACKET;
}
if (rc)
goto leave;
if (list_mode)
keyid_from_pk (pk, keyid);
}
if (list_mode)
fprintf (listfp, "\tkeyid: %08lX%08lX\n",
(ulong)keyid[0], (ulong)keyid[1]);
leave:
iobuf_skip_rest(inp, pktlen, 0);
return rc;
}
/* Attribute subpackets have the same format as v4 signature
subpackets. This is not part of OpenPGP, but is done in several
versions of PGP nevertheless. */
int
parse_attribute_subpkts(PKT_user_id *uid)
{
size_t n;
int count=0;
struct user_attribute *attribs=NULL;
const byte *buffer=uid->attrib_data;
int buflen=uid->attrib_len;
byte type;
xfree(uid->attribs);
while(buflen)
{
n = *buffer++; buflen--;
if( n == 255 ) { /* 4 byte length header */
if( buflen < 4 )
goto too_short;
n = buf32_to_size_t (buffer);
buffer += 4;
buflen -= 4;
}
else if( n >= 192 ) { /* 2 byte special encoded length header */
if( buflen < 2 )
goto too_short;
n = (( n - 192 ) << 8) + *buffer + 192;
buffer++;
buflen--;
}
if( buflen < n )
goto too_short;
if (!n)
{
/* Too short to encode the subpacket type. */
if (opt.verbose)
log_info ("attribute subpacket too short\n");
break;
}
attribs=xrealloc(attribs,(count+1)*sizeof(struct user_attribute));
memset(&attribs[count],0,sizeof(struct user_attribute));
type=*buffer;
buffer++;
buflen--;
n--;
attribs[count].type=type;
attribs[count].data=buffer;
attribs[count].len=n;
buffer+=n;
buflen-=n;
count++;
}
uid->attribs=attribs;
uid->numattribs=count;
return count;
too_short:
if(opt.verbose)
log_info("buffer shorter than attribute subpacket\n");
uid->attribs=attribs;
uid->numattribs=count;
return count;
}
static int
parse_user_id( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet )
{
byte *p;
/* Cap the size of a user ID at 2k: a value absurdly large enough
that there is no sane user ID string (which is printable text
as of RFC2440bis) that won't fit in it, but yet small enough to
avoid allocation problems. A large pktlen may not be
allocatable, and a very large pktlen could actually cause our
allocation to wrap around in xmalloc to a small number. */
if (pktlen > MAX_UID_PACKET_LENGTH)
{
log_error ("packet(%d) too large\n", pkttype);
iobuf_skip_rest(inp, pktlen, 0);
return G10ERR_INVALID_PACKET;
}
packet->pkt.user_id = xmalloc_clear(sizeof *packet->pkt.user_id + pktlen);
packet->pkt.user_id->len = pktlen;
packet->pkt.user_id->ref=1;
p = packet->pkt.user_id->name;
for( ; pktlen; pktlen--, p++ )
*p = iobuf_get_noeof(inp);
*p = 0;
if( list_mode ) {
int n = packet->pkt.user_id->len;
fprintf (listfp, ":user ID packet: \"");
/* fixme: Hey why don't we replace this with print_string?? */
for(p=packet->pkt.user_id->name; n; p++, n-- ) {
if( *p >= ' ' && *p <= 'z' )
putc (*p, listfp);
else
fprintf (listfp, "\\x%02x", *p );
}
fprintf (listfp, "\"\n");
}
return 0;
}
void
make_attribute_uidname(PKT_user_id *uid, size_t max_namelen)
{
assert ( max_namelen > 70 );
if(uid->numattribs<=0)
sprintf(uid->name,"[bad attribute packet of size %lu]",uid->attrib_len);
else if(uid->numattribs>1)
sprintf(uid->name,"[%d attributes of size %lu]",
uid->numattribs,uid->attrib_len);
else
{
/* Only one attribute, so list it as the "user id" */
if(uid->attribs->type==ATTRIB_IMAGE)
{
u32 len;
byte type;
if(parse_image_header(uid->attribs,&type,&len))
sprintf(uid->name,"[%.20s image of size %lu]",
image_type_to_string(type,1),(ulong)len);
else
sprintf(uid->name,"[invalid image]");
}
else
sprintf(uid->name,"[unknown attribute of size %lu]",
(ulong)uid->attribs->len);
}
uid->len = strlen(uid->name);
}
static int
parse_attribute( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet )
{
byte *p;
(void)pkttype;
/* We better cap the size of an attribute packet to make DoS not
too easy. 16MB should be more then enough for one attribute
packet (ie. a photo). */
if (pktlen > MAX_ATTR_PACKET_LENGTH) {
log_error ("packet(%d) too large\n", pkttype);
if (list_mode)
fprintf (listfp, ":attribute packet: [too large]\n");
iobuf_skip_rest (inp, pktlen, 0);
return G10ERR_INVALID_PACKET;
}
#define EXTRA_UID_NAME_SPACE 71
packet->pkt.user_id = xmalloc_clear(sizeof *packet->pkt.user_id
+ EXTRA_UID_NAME_SPACE);
packet->pkt.user_id->ref=1;
packet->pkt.user_id->attrib_data = xmalloc(pktlen? pktlen:1);
packet->pkt.user_id->attrib_len = pktlen;
p = packet->pkt.user_id->attrib_data;
for( ; pktlen; pktlen--, p++ )
*p = iobuf_get_noeof(inp);
/* Now parse out the individual attribute subpackets. This is
somewhat pointless since there is only one currently defined
attribute type (jpeg), but it is correct by the spec. */
parse_attribute_subpkts(packet->pkt.user_id);
make_attribute_uidname(packet->pkt.user_id, EXTRA_UID_NAME_SPACE);
if( list_mode ) {
fprintf (listfp, ":attribute packet: %s\n", packet->pkt.user_id->name );
}
return 0;
}
static int
parse_comment( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet )
{
byte *p;
/* Cap comment packet at a reasonable value to avoid an integer
overflow in the malloc below. Comment packets are actually not
anymore define my OpenPGP and we even stopped to use our
private comment packet. */
if (pktlen > MAX_COMMENT_PACKET_LENGTH)
{
log_error ("packet(%d) too large\n", pkttype);
iobuf_skip_rest (inp, pktlen, 0);
return G10ERR_INVALID_PACKET;
}
packet->pkt.comment = xmalloc(sizeof *packet->pkt.comment + pktlen - 1);
packet->pkt.comment->len = pktlen;
p = packet->pkt.comment->data;
for( ; pktlen; pktlen--, p++ )
*p = iobuf_get_noeof(inp);
if( list_mode ) {
int n = packet->pkt.comment->len;
fprintf (listfp, ":%scomment packet: \"", pkttype == PKT_OLD_COMMENT?
"OpenPGP draft " : "" );
for(p=packet->pkt.comment->data; n; p++, n-- ) {
if( *p >= ' ' && *p <= 'z' )
putc (*p, listfp);
else
fprintf (listfp, "\\x%02x", *p );
}
fprintf (listfp, "\"\n");
}
return 0;
}
static void
parse_trust( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *pkt )
{
int c;
(void)pkttype;
pkt->pkt.ring_trust = xmalloc( sizeof *pkt->pkt.ring_trust );
if (pktlen)
{
c = iobuf_get_noeof(inp);
pktlen--;
pkt->pkt.ring_trust->trustval = c;
pkt->pkt.ring_trust->sigcache = 0;
if (!c && pktlen==1)
{
c = iobuf_get_noeof (inp);
pktlen--;
/* we require that bit 7 of the sigcache is 0 (easier eof handling)*/
if ( !(c & 0x80) )
pkt->pkt.ring_trust->sigcache = c;
}
if( list_mode )
fprintf (listfp, ":trust packet: flag=%02x sigcache=%02x\n",
pkt->pkt.ring_trust->trustval,
pkt->pkt.ring_trust->sigcache);
}
else
{
pkt->pkt.ring_trust->trustval = 0;
pkt->pkt.ring_trust->sigcache = 0;
if (list_mode)
fprintf (listfp, ":trust packet: empty\n");
}
iobuf_skip_rest (inp, pktlen, 0);
}
static int
parse_plaintext( IOBUF inp, int pkttype, unsigned long pktlen,
PACKET *pkt, int new_ctb, int partial )
{
int rc = 0;
int mode, namelen;
PKT_plaintext *pt;
byte *p;
int c, i;
if( !partial && pktlen < 6 ) {
log_error("packet(%d) too short (%lu)\n", pkttype, (ulong)pktlen);
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
mode = iobuf_get_noeof(inp); if( pktlen ) pktlen--;
namelen = iobuf_get_noeof(inp); if( pktlen ) pktlen--;
/* Note that namelen will never exceed 255 bytes. */
pt = pkt->pkt.plaintext = xmalloc(sizeof *pkt->pkt.plaintext + namelen -1);
pt->new_ctb = new_ctb;
pt->mode = mode;
pt->namelen = namelen;
pt->is_partial = partial;
if( pktlen ) {
for( i=0; pktlen > 4 && i < namelen; pktlen--, i++ )
pt->name[i] = iobuf_get_noeof(inp);
}
else {
for( i=0; i < namelen; i++ )
if( (c=iobuf_get(inp)) == -1 )
break;
else
pt->name[i] = c;
}
pt->timestamp = read_32(inp); if( pktlen) pktlen -= 4;
pt->len = pktlen;
pt->buf = inp;
pktlen = 0;
if( list_mode ) {
fprintf (listfp, ":literal data packet:\n"
"\tmode %c (%X), created %lu, name=\"",
mode >= ' ' && mode <'z'? mode : '?', mode,
(ulong)pt->timestamp );
for(p=pt->name,i=0; i < namelen; p++, i++ ) {
if( *p >= ' ' && *p <= 'z' )
putc (*p, listfp);
else
fprintf (listfp, "\\x%02x", *p );
}
fprintf (listfp, "\",\n\traw data: ");
if(partial)
fprintf (listfp, "unknown length\n");
else
fprintf (listfp, "%lu bytes\n", (ulong)pt->len );
}
leave:
return rc;
}
static int
parse_compressed( IOBUF inp, int pkttype, unsigned long pktlen,
PACKET *pkt, int new_ctb )
{
PKT_compressed *zd;
/* PKTLEN is here 0, but data follows (this should be the last
object in a file or the compress algorithm should know the
length). */
(void)pkttype;
(void)pktlen;
zd = pkt->pkt.compressed = xmalloc (sizeof *pkt->pkt.compressed);
zd->algorithm = iobuf_get_noeof(inp);
zd->len = 0; /* not used */
zd->new_ctb = new_ctb;
zd->buf = inp;
if (list_mode)
fprintf (listfp, ":compressed packet: algo=%d\n", zd->algorithm);
return 0;
}
static int
parse_encrypted( IOBUF inp, int pkttype, unsigned long pktlen,
PACKET *pkt, int new_ctb, int partial )
{
int rc = 0;
PKT_encrypted *ed;
unsigned long orig_pktlen = pktlen;
ed = pkt->pkt.encrypted = xmalloc(sizeof *pkt->pkt.encrypted );
ed->len = pktlen;
/* we don't know the extralen which is (cipher_blocksize+2)
because the algorithm ist not specified in this packet.
However, it is only important to know this for some sanity
checks on the packet length - it doesn't matter that we can't
do it */
ed->extralen = 0;
ed->buf = NULL;
ed->new_ctb = new_ctb;
ed->is_partial = partial;
ed->mdc_method = 0;
if( pkttype == PKT_ENCRYPTED_MDC ) {
/* fixme: add some pktlen sanity checks */
int version;
version = iobuf_get_noeof(inp);
if (orig_pktlen)
pktlen--;
if( version != 1 ) {
log_error("encrypted_mdc packet with unknown version %d\n",
version);
/*skip_rest(inp, pktlen); should we really do this? */
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
ed->mdc_method = DIGEST_ALGO_SHA1;
}
if( orig_pktlen && pktlen < 10 ) { /* actually this is blocksize+2 */
log_error("packet(%d) too short\n", pkttype);
rc = G10ERR_INVALID_PACKET;
iobuf_skip_rest(inp, pktlen, partial);
goto leave;
}
if( list_mode ) {
if( orig_pktlen )
fprintf (listfp, ":encrypted data packet:\n\tlength: %lu\n",
orig_pktlen);
else
fprintf (listfp, ":encrypted data packet:\n\tlength: unknown\n");
if( ed->mdc_method )
fprintf (listfp, "\tmdc_method: %d\n", ed->mdc_method );
}
ed->buf = inp;
leave:
return rc;
}
/* Note, that this code is not anymore used in real life because now
the MDC checking is done right after the encryption in
decrypt_data. */
static int
parse_mdc (IOBUF inp, int pkttype, unsigned long pktlen,
PACKET *pkt, int new_ctb)
{
int rc = 0;
PKT_mdc *mdc;
byte *p;
(void)pkttype;
mdc = pkt->pkt.mdc = xmalloc(sizeof *pkt->pkt.mdc );
if (list_mode)
fprintf (listfp, ":mdc packet: length=%lu\n", pktlen);
if (!new_ctb || pktlen != 20)
{
log_error("mdc_packet with invalid encoding\n");
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
p = mdc->hash;
for (; pktlen; pktlen--, p++)
*p = iobuf_get_noeof(inp);
leave:
return rc;
}
/*
* This packet is internally generated by PGG (by armor.c) to
* transfer some information to the lower layer. To make sure that
* this packet is really a GPG faked one and not one comming from outside,
* we first check that tehre is a unique tag in it.
* The format of such a control packet is:
* n byte session marker
* 1 byte control type CTRLPKT_xxxxx
* m byte control data
*/
static int
parse_gpg_control (IOBUF inp, int pkttype, unsigned long pktlen,
PACKET *packet, int partial)
{
byte *p;
const byte *sesmark;
size_t sesmarklen;
int i;
(void)pkttype;
if ( list_mode )
fprintf (listfp, ":packet 63: length %lu ", pktlen);
sesmark = get_session_marker ( &sesmarklen );
if ( pktlen < sesmarklen+1 ) /* 1 is for the control bytes */
goto skipit;
for( i=0; i < sesmarklen; i++, pktlen-- ) {
if ( sesmark[i] != iobuf_get_noeof(inp) )
goto skipit;
}
if (pktlen > 4096)
goto skipit; /* Definitely too large. We skip it to avoid an
overflow in the malloc. */
if ( list_mode )
puts ("- gpg control packet");
packet->pkt.gpg_control = xmalloc(sizeof *packet->pkt.gpg_control
+ pktlen - 1);
packet->pkt.gpg_control->control = iobuf_get_noeof(inp); pktlen--;
packet->pkt.gpg_control->datalen = pktlen;
p = packet->pkt.gpg_control->data;
for( ; pktlen; pktlen--, p++ )
*p = iobuf_get_noeof(inp);
return 0;
skipit:
if ( list_mode ) {
int c;
i=0;
fprintf (listfp, "- private (rest length %lu)\n", pktlen);
if( partial ) {
while( (c=iobuf_get(inp)) != -1 )
dump_hex_line(c, &i);
}
else {
for( ; pktlen; pktlen-- )
{
dump_hex_line ((c=iobuf_get (inp)), &i);
if (c == -1)
break;
}
}
putc ('\n', listfp);
}
iobuf_skip_rest(inp,pktlen, 0);
return gpg_error (GPG_ERR_INV_PACKET);
}
/* create a gpg control packet to be used internally as a placeholder */
PACKET *
create_gpg_control( ctrlpkttype_t type, const byte *data, size_t datalen )
{
PACKET *packet;
byte *p;
packet = xmalloc( sizeof *packet );
init_packet(packet);
packet->pkttype = PKT_GPG_CONTROL;
packet->pkt.gpg_control = xmalloc(sizeof *packet->pkt.gpg_control
+ datalen - 1);
packet->pkt.gpg_control->control = type;
packet->pkt.gpg_control->datalen = datalen;
p = packet->pkt.gpg_control->data;
for( ; datalen; datalen--, p++ )
*p = *data++;
return packet;
}