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; }