diff options
author | DongHun Kwak <dh0128.kwak@samsung.com> | 2022-09-16 07:45:09 +0900 |
---|---|---|
committer | DongHun Kwak <dh0128.kwak@samsung.com> | 2022-09-16 07:45:09 +0900 |
commit | 1e860c1f8b2b0b48617f2ca664aa9acf2fafc8d3 (patch) | |
tree | a945f12e0ecb03e7c1db7db427f469b46f5c71f9 /sm | |
parent | 0fca6c1d0d6088ff4558a6ffc3bc220998699392 (diff) | |
download | gpg2-1e860c1f8b2b0b48617f2ca664aa9acf2fafc8d3.tar.gz gpg2-1e860c1f8b2b0b48617f2ca664aa9acf2fafc8d3.tar.bz2 gpg2-1e860c1f8b2b0b48617f2ca664aa9acf2fafc8d3.zip |
Imported Upstream version 2.2.37upstream/2.2.37
Diffstat (limited to 'sm')
-rw-r--r-- | sm/Makefile.in | 1 | ||||
-rw-r--r-- | sm/call-agent.c | 34 | ||||
-rw-r--r-- | sm/certlist.c | 13 | ||||
-rw-r--r-- | sm/decrypt.c | 8 | ||||
-rw-r--r-- | sm/encrypt.c | 9 | ||||
-rw-r--r-- | sm/gpgsm.c | 290 | ||||
-rw-r--r-- | sm/gpgsm.h | 26 | ||||
-rw-r--r-- | sm/import.c | 3 | ||||
-rw-r--r-- | sm/keylist.c | 28 | ||||
-rw-r--r-- | sm/minip12.c | 1127 | ||||
-rw-r--r-- | sm/minip12.h | 3 | ||||
-rw-r--r-- | sm/verify.c | 7 |
12 files changed, 1048 insertions, 501 deletions
diff --git a/sm/Makefile.in b/sm/Makefile.in index 9dd355e..286b652 100644 --- a/sm/Makefile.in +++ b/sm/Makefile.in @@ -301,6 +301,7 @@ GNUPG_PINENTRY_PGM = @GNUPG_PINENTRY_PGM@ GNUPG_PROTECT_TOOL_PGM = @GNUPG_PROTECT_TOOL_PGM@ GNUPG_SCDAEMON_PGM = @GNUPG_SCDAEMON_PGM@ GPGKEYS_LDAP = @GPGKEYS_LDAP@ +GPGRT_CONFIG = @GPGRT_CONFIG@ GPG_ERROR_CFLAGS = @GPG_ERROR_CFLAGS@ GPG_ERROR_CONFIG = @GPG_ERROR_CONFIG@ GPG_ERROR_LIBS = @GPG_ERROR_LIBS@ diff --git a/sm/call-agent.c b/sm/call-agent.c index a5b17e9..0c271d9 100644 --- a/sm/call-agent.c +++ b/sm/call-agent.c @@ -989,6 +989,8 @@ learn_cb (void *opaque, const void *buffer, size_t length) char *buf; ksba_cert_t cert; int rc; + char *string, *p, *pend; + strlist_t sl; if (parm->error) return 0; @@ -1025,6 +1027,35 @@ learn_cb (void *opaque, const void *buffer, size_t length) return 0; } + /* Ignore certificates matching certain extended usage flags. */ + rc = ksba_cert_get_ext_key_usages (cert, &string); + if (!rc) + { + p = string; + while (p && (pend=strchr (p, ':'))) + { + *pend++ = 0; + for (sl=opt.ignore_cert_with_oid; + sl && strcmp (sl->d, p); sl = sl->next) + ; + if (sl) + { + if (opt.verbose) + log_info ("certificate ignored due to OID %s\n", sl->d); + goto leave; + } + p = pend; + if ((p = strchr (p, '\n'))) + p++; + } + } + else if (gpg_err_code (rc) != GPG_ERR_NO_DATA) + log_error (_("error getting key usage information: %s\n"), + gpg_strerror (rc)); + xfree (string); + string = NULL; + + /* We do not store a certifciate with missing issuers as ephemeral because we can assume that the --learn-card command has been used on purpose. */ @@ -1045,6 +1076,9 @@ learn_cb (void *opaque, const void *buffer, size_t length) } } + leave: + xfree (string); + string = NULL; ksba_cert_release (cert); init_membuf (parm->data, 4096); return 0; diff --git a/sm/certlist.c b/sm/certlist.c index c3e4e82..b1ae58c 100644 --- a/sm/certlist.c +++ b/sm/certlist.c @@ -52,9 +52,11 @@ cert_usage_p (ksba_cert_t cert, int mode, int silent) { gpg_error_t err; unsigned int use; + unsigned int encr_bits, sign_bits; char *extkeyusages; int have_ocsp_signing = 0; + err = ksba_cert_get_ext_key_usages (cert, &extkeyusages); if (gpg_err_code (err) == GPG_ERR_NO_DATA) err = 0; /* no policy given */ @@ -158,10 +160,13 @@ cert_usage_p (ksba_cert_t cert, int mode, int silent) return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } - if ((use & ((mode&1)? - (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT): - (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION))) - ) + encr_bits = (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT); + if ((opt.compat_flags & COMPAT_ALLOW_KA_TO_ENCR)) + encr_bits |= KSBA_KEYUSAGE_KEY_AGREEMENT; + + sign_bits = (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION); + + if ((use & ((mode&1)? encr_bits : sign_bits))) return 0; if (!silent) diff --git a/sm/decrypt.c b/sm/decrypt.c index 38aa832..2aa716f 100644 --- a/sm/decrypt.c +++ b/sm/decrypt.c @@ -1029,7 +1029,13 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) && gnupg_gcrypt_is_compliant (CO_DE_VS)) gpgsm_status (ctrl, STATUS_DECRYPTION_COMPLIANCE_MODE, gnupg_status_compliance_flag (CO_DE_VS)); - + else if (opt.require_compliance + && opt.compliance == CO_DE_VS) + { + log_error (_("operation forced to fail due to" + " unfulfilled compliance rules\n")); + gpgsm_errors_seen = 1; + } } audit_log_ok (ctrl->audit, AUDIT_RECP_RESULT, rc); } diff --git a/sm/encrypt.c b/sm/encrypt.c index 37cfe92..587b568 100644 --- a/sm/encrypt.c +++ b/sm/encrypt.c @@ -535,6 +535,15 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) if (compliant && gnupg_gcrypt_is_compliant (CO_DE_VS)) gpgsm_status (ctrl, STATUS_ENCRYPTION_COMPLIANCE_MODE, gnupg_status_compliance_flag (CO_DE_VS)); + else if (opt.require_compliance + && opt.compliance == CO_DE_VS) + { + log_error (_("operation forced to fail due to" + " unfulfilled compliance rules\n")); + gpgsm_errors_seen = 1; + rc = gpg_error (GPG_ERR_FORBIDDEN); + goto leave; + } /* Main control loop for encryption. */ recpno = 0; @@ -188,6 +188,7 @@ enum cmd_and_opt_values { oSkipVerify, oValidationModel, oKeyServer, + oKeyServer_deprecated, oEncryptTo, oNoEncryptTo, oLoggerFD, @@ -197,6 +198,9 @@ enum cmd_and_opt_values { oNoRandomSeedFile, oNoCommonCertsImport, oIgnoreCertExtension, + oIgnoreCertWithOID, + oRequireCompliance, + oCompatibilityFlags, oNoAutostart }; @@ -251,147 +255,107 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_c (aDumpSecretKeys, "dump-secret-keys", "@"), ARGPARSE_c (aKeydbClearSomeCertFlags, "keydb-clear-some-cert-flags", "@"), - ARGPARSE_group (301, N_("@\nOptions:\n ")), - ARGPARSE_s_n (oArmor, "armor", N_("create ascii armored output")), - ARGPARSE_s_n (oArmor, "armour", "@"), - ARGPARSE_s_n (oBase64, "base64", N_("create base-64 encoded output")), - - ARGPARSE_s_s (oP12Charset, "p12-charset", "@"), + ARGPARSE_header ("Monitor", N_("Options controlling the diagnostic output")), - ARGPARSE_s_i (oPassphraseFD, "passphrase-fd", "@"), - ARGPARSE_s_s (oPinentryMode, "pinentry-mode", "@"), - ARGPARSE_s_s (oRequestOrigin, "request-origin", "@"), + ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")), + ARGPARSE_s_n (oNoVerbose, "no-verbose", "@"), + ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")), + ARGPARSE_s_n (oNoTTY, "no-tty", N_("don't use the terminal at all")), + ARGPARSE_s_n (oNoGreeting, "no-greeting", "@"), + ARGPARSE_s_s (oDebug, "debug", "@"), + ARGPARSE_s_s (oDebugLevel, "debug-level", + N_("|LEVEL|set the debugging level to LEVEL")), + ARGPARSE_s_n (oDebugAll, "debug-all", "@"), + ARGPARSE_s_n (oDebugNone, "debug-none", "@"), + ARGPARSE_s_i (oDebugWait, "debug-wait", "@"), + ARGPARSE_s_n (oDebugAllowCoreDump, "debug-allow-core-dump", "@"), + ARGPARSE_s_n (oDebugNoChainValidation, "debug-no-chain-validation", "@"), + ARGPARSE_s_n (oDebugIgnoreExpiration, "debug-ignore-expiration", "@"), + ARGPARSE_s_s (oLogFile, "log-file", + N_("|FILE|write server mode logs to FILE")), + ARGPARSE_s_n (oNoLogFile, "no-log-file", "@"), + ARGPARSE_s_i (oLoggerFD, "logger-fd", "@"), + ARGPARSE_s_n (oNoSecmemWarn, "no-secmem-warning", "@"), - ARGPARSE_s_n (oAssumeArmor, "assume-armor", - N_("assume input is in PEM format")), - ARGPARSE_s_n (oAssumeBase64, "assume-base64", - N_("assume input is in base-64 format")), - ARGPARSE_s_n (oAssumeBinary, "assume-binary", - N_("assume input is in binary format")), - ARGPARSE_s_s (oRecipient, "recipient", N_("|USER-ID|encrypt for USER-ID")), + ARGPARSE_header ("Configuration", + N_("Options controlling the configuration")), + ARGPARSE_s_s (oHomedir, "homedir", "@"), + ARGPARSE_s_s (oFakedSystemTime, "faked-system-time", "@"), ARGPARSE_s_n (oPreferSystemDirmngr,"prefer-system-dirmngr", "@"), - - ARGPARSE_s_n (oDisableCRLChecks, "disable-crl-checks", - N_("never consult a CRL")), - ARGPARSE_s_n (oEnableCRLChecks, "enable-crl-checks", "@"), - ARGPARSE_s_n (oDisableTrustedCertCRLCheck, - "disable-trusted-cert-crl-check", "@"), - ARGPARSE_s_n (oEnableTrustedCertCRLCheck, - "enable-trusted-cert-crl-check", "@"), - - ARGPARSE_s_n (oForceCRLRefresh, "force-crl-refresh", "@"), - - ARGPARSE_s_n (oDisableOCSP, "disable-ocsp", "@"), - ARGPARSE_s_n (oEnableOCSP, "enable-ocsp", N_("check validity using OCSP")), - ARGPARSE_s_s (oValidationModel, "validation-model", "@"), - ARGPARSE_s_i (oIncludeCerts, "include-certs", N_("|N|number of certificates to include") ), - ARGPARSE_s_s (oPolicyFile, "policy-file", N_("|FILE|take policy information from FILE")), + ARGPARSE_s_s (oCompliance, "compliance", "@"), + ARGPARSE_p_u (oMinRSALength, "min-rsa-length", "@"), + ARGPARSE_s_n (oNoCommonCertsImport, "no-common-certs-import", "@"), + ARGPARSE_s_s (oIgnoreCertExtension, "ignore-cert-extension", "@"), + ARGPARSE_s_s (oIgnoreCertWithOID, "ignore-cert-with-oid", "@"), + ARGPARSE_s_n (oNoAutostart, "no-autostart", "@"), + ARGPARSE_s_s (oAgentProgram, "agent-program", "@"), + ARGPARSE_s_s (oDirmngrProgram, "dirmngr-program", "@"), + ARGPARSE_s_s (oProtectToolProgram, "protect-tool-program", "@"), - ARGPARSE_s_n (oDisablePolicyChecks, "disable-policy-checks", - N_("do not check certificate policies")), - ARGPARSE_s_n (oEnablePolicyChecks, "enable-policy-checks", "@"), - ARGPARSE_s_n (oAutoIssuerKeyRetrieve, "auto-issuer-key-retrieve", - N_("fetch missing issuer certificates")), + ARGPARSE_header ("Input", N_("Options controlling the input")), - ARGPARSE_s_s (oEncryptTo, "encrypt-to", "@"), - ARGPARSE_s_n (oNoEncryptTo, "no-encrypt-to", "@"), + ARGPARSE_s_n (oAssumeArmor, "assume-armor", + N_("assume input is in PEM format")), + ARGPARSE_s_n (oAssumeBase64, "assume-base64", + N_("assume input is in base-64 format")), + ARGPARSE_s_n (oAssumeBinary, "assume-binary", + N_("assume input is in binary format")), - ARGPARSE_s_s (oUser, "local-user", - N_("|USER-ID|use USER-ID to sign or decrypt")), + ARGPARSE_header ("Output", N_("Options controlling the output")), + + ARGPARSE_s_n (oArmor, "armor", N_("create ascii armored output")), + ARGPARSE_s_n (oArmor, "armour", "@"), + ARGPARSE_s_n (oNoArmor, "no-armor", "@"), + ARGPARSE_s_n (oNoArmor, "no-armour", "@"), + ARGPARSE_s_n (oBase64, "base64", N_("create base-64 encoded output")), ARGPARSE_s_s (oOutput, "output", N_("|FILE|write output to FILE")), - ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")), - ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")), - ARGPARSE_s_n (oNoTTY, "no-tty", N_("don't use the terminal at all")), - ARGPARSE_s_s (oLogFile, "log-file", - N_("|FILE|write a server mode log to FILE")), - ARGPARSE_s_n (oNoLogFile, "no-log-file", "@"), - ARGPARSE_s_i (oLoggerFD, "logger-fd", "@"), - ARGPARSE_s_s (oAuditLog, "audit-log", - N_("|FILE|write an audit log to FILE")), - ARGPARSE_s_s (oHtmlAuditLog, "html-audit-log", "@"), - ARGPARSE_s_n (oDryRun, "dry-run", N_("do not make any changes")), - ARGPARSE_s_n (oBatch, "batch", N_("batch mode: never ask")), - ARGPARSE_s_n (oAnswerYes, "yes", N_("assume yes on most questions")), - ARGPARSE_s_n (oAnswerNo, "no", N_("assume no on most questions")), - ARGPARSE_s_s (oKeyring, "keyring", - N_("|FILE|add keyring to the list of keyrings")), + ARGPARSE_header (NULL, N_("Options to specify keys")), + ARGPARSE_s_s (oRecipient, "recipient", N_("|USER-ID|encrypt for USER-ID")), + ARGPARSE_s_s (oUser, "local-user", + N_("|USER-ID|use USER-ID to sign or decrypt")), ARGPARSE_s_s (oDefaultKey, "default-key", N_("|USER-ID|use USER-ID as default secret key")), - + ARGPARSE_s_s (oEncryptTo, "encrypt-to", + N_("|NAME|encrypt to user ID NAME as well")), + ARGPARSE_s_n (oNoEncryptTo, "no-encrypt-to", "@"), /* Not yet used: */ /* ARGPARSE_s_s (oDefRecipient, "default-recipient", */ /* N_("|NAME|use NAME as default recipient")), */ /* ARGPARSE_s_n (oDefRecipientSelf, "default-recipient-self", */ /* N_("use the default key as default recipient")), */ /* ARGPARSE_s_n (oNoDefRecipient, "no-default-recipient", "@"), */ - - ARGPARSE_s_s (oKeyServer, "ldapserver", - N_("|SPEC|use this keyserver to lookup keys")), + ARGPARSE_s_s (oKeyring, "keyring", + N_("|FILE|add keyring to the list of keyrings")), + ARGPARSE_s_n (oNoDefKeyring, "no-default-keyring", "@"), + ARGPARSE_s_s (oKeyServer_deprecated, "ldapserver", "@"), ARGPARSE_s_s (oKeyServer, "keyserver", "@"), - ARGPARSE_conffile (oOptions, "options", N_("|FILE|read options from FILE")), - - ARGPARSE_s_s (oDebug, "debug", "@"), - ARGPARSE_s_s (oDebugLevel, "debug-level", - N_("|LEVEL|set the debugging level to LEVEL")), - ARGPARSE_s_n (oDebugAll, "debug-all", "@"), - ARGPARSE_s_n (oDebugNone, "debug-none", "@"), - ARGPARSE_s_i (oDebugWait, "debug-wait", "@"), - ARGPARSE_s_n (oDebugAllowCoreDump, "debug-allow-core-dump", "@"), - ARGPARSE_s_n (oDebugNoChainValidation, "debug-no-chain-validation", "@"), - ARGPARSE_s_n (oDebugIgnoreExpiration, "debug-ignore-expiration", "@"), - - ARGPARSE_s_i (oStatusFD, "status-fd", - N_("|FD|write status info to this FD")), + ARGPARSE_header ("ImportExport", + N_("Options controlling key import and export")), - ARGPARSE_s_s (oCipherAlgo, "cipher-algo", - N_("|NAME|use cipher algorithm NAME")), - ARGPARSE_s_s (oDigestAlgo, "digest-algo", - N_("|NAME|use message digest algorithm NAME")), - ARGPARSE_s_s (oExtraDigestAlgo, "extra-digest-algo", "@"), + ARGPARSE_s_n (oDisableDirmngr, "disable-dirmngr", + N_("disable all access to the dirmngr")), + ARGPARSE_s_n (oAutoIssuerKeyRetrieve, "auto-issuer-key-retrieve", + N_("fetch missing issuer certificates")), + ARGPARSE_s_s (oP12Charset, "p12-charset", + N_("|NAME|use encoding NAME for PKCS#12 passphrases")), - ARGPARSE_group (302, N_( - "@\n(See the man page for a complete listing of all commands and options)\n" - )), - + ARGPARSE_header ("Keylist", N_("Options controlling key listings")), - /* Hidden options. */ - ARGPARSE_s_s (oCompliance, "compliance", "@"), - ARGPARSE_s_n (oNoVerbose, "no-verbose", "@"), - ARGPARSE_s_n (oEnableSpecialFilenames, "enable-special-filenames", "@"), - ARGPARSE_s_n (oNoSecmemWarn, "no-secmem-warning", "@"), - ARGPARSE_s_n (oNoArmor, "no-armor", "@"), - ARGPARSE_s_n (oNoArmor, "no-armour", "@"), - ARGPARSE_s_n (oNoDefKeyring, "no-default-keyring", "@"), - ARGPARSE_s_n (oNoGreeting, "no-greeting", "@"), - ARGPARSE_noconffile (oNoOptions, "no-options", "@"), - ARGPARSE_s_s (oHomedir, "homedir", "@"), - ARGPARSE_s_s (oAgentProgram, "agent-program", "@"), - ARGPARSE_s_s (oDisplay, "display", "@"), - ARGPARSE_s_s (oTTYname, "ttyname", "@"), - ARGPARSE_s_s (oTTYtype, "ttytype", "@"), - ARGPARSE_s_s (oLCctype, "lc-ctype", "@"), - ARGPARSE_s_s (oLCmessages, "lc-messages", "@"), - ARGPARSE_s_s (oXauthority, "xauthority", "@"), - ARGPARSE_s_s (oDirmngrProgram, "dirmngr-program", "@"), - ARGPARSE_s_n (oDisableDirmngr, "disable-dirmngr", "@"), - ARGPARSE_s_s (oProtectToolProgram, "protect-tool-program", "@"), - ARGPARSE_s_s (oFakedSystemTime, "faked-system-time", "@"), - ARGPARSE_s_n (oNoBatch, "no-batch", "@"), ARGPARSE_s_n (oWithColons, "with-colons", "@"), ARGPARSE_s_n (oWithKeyData,"with-key-data", "@"), ARGPARSE_s_n (oWithValidation, "with-validation", "@"), @@ -401,18 +365,68 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_n (oWithFingerprint, "with-fingerprint", "@"), ARGPARSE_s_n (oWithKeygrip, "with-keygrip", "@"), ARGPARSE_s_n (oWithSecret, "with-secret", "@"), + + ARGPARSE_header ("Security", N_("Options controlling the security")), + + ARGPARSE_s_n (oDisableCRLChecks, "disable-crl-checks", + N_("never consult a CRL")), + ARGPARSE_s_n (oEnableCRLChecks, "enable-crl-checks", "@"), + ARGPARSE_s_n (oDisableTrustedCertCRLCheck, + "disable-trusted-cert-crl-check", + N_("do not check CRLs for root certificates")), + ARGPARSE_s_n (oEnableTrustedCertCRLCheck, + "enable-trusted-cert-crl-check", "@"), + ARGPARSE_s_n (oDisableOCSP, "disable-ocsp", "@"), + ARGPARSE_s_n (oEnableOCSP, "enable-ocsp", N_("check validity using OCSP")), + ARGPARSE_s_n (oDisablePolicyChecks, "disable-policy-checks", + N_("do not check certificate policies")), + ARGPARSE_s_n (oEnablePolicyChecks, "enable-policy-checks", "@"), + ARGPARSE_s_s (oCipherAlgo, "cipher-algo", + N_("|NAME|use cipher algorithm NAME")), + ARGPARSE_s_s (oDigestAlgo, "digest-algo", + N_("|NAME|use message digest algorithm NAME")), + ARGPARSE_s_s (oExtraDigestAlgo, "extra-digest-algo", "@"), ARGPARSE_s_s (oDisableCipherAlgo, "disable-cipher-algo", "@"), ARGPARSE_s_s (oDisablePubkeyAlgo, "disable-pubkey-algo", "@"), ARGPARSE_s_n (oIgnoreTimeConflict, "ignore-time-conflict", "@"), ARGPARSE_s_n (oNoRandomSeedFile, "no-random-seed-file", "@"), + ARGPARSE_s_n (oRequireCompliance, "require-compliance", "@"), - ARGPARSE_p_u (oMinRSALength, "min-rsa-length", "@"), - ARGPARSE_s_n (oNoCommonCertsImport, "no-common-certs-import", "@"), - ARGPARSE_s_s (oIgnoreCertExtension, "ignore-cert-extension", "@"), - ARGPARSE_s_n (oNoAutostart, "no-autostart", "@"), + ARGPARSE_header (NULL, N_("Options for unattended use")), + + ARGPARSE_s_n (oBatch, "batch", N_("batch mode: never ask")), + ARGPARSE_s_n (oNoBatch, "no-batch", "@"), + ARGPARSE_s_n (oAnswerYes, "yes", N_("assume yes on most questions")), + ARGPARSE_s_n (oAnswerNo, "no", N_("assume no on most questions")), + ARGPARSE_s_i (oStatusFD, "status-fd", N_("|FD|write status info to this FD")), + ARGPARSE_s_n (oEnableSpecialFilenames, "enable-special-filenames", "@"), + ARGPARSE_s_i (oPassphraseFD, "passphrase-fd", "@"), + ARGPARSE_s_s (oPinentryMode, "pinentry-mode", "@"), + + + ARGPARSE_header (NULL, N_("Other options")), + + ARGPARSE_conffile (oOptions, "options", N_("|FILE|read options from FILE")), + ARGPARSE_noconffile (oNoOptions, "no-options", "@"), + ARGPARSE_s_n (oDryRun, "dry-run", N_("do not make any changes")), + ARGPARSE_s_s (oRequestOrigin, "request-origin", "@"), + ARGPARSE_s_n (oForceCRLRefresh, "force-crl-refresh", "@"), ARGPARSE_s_n (oEnableIssuerBasedCRLCheck, "enable-issuer-based-crl-check", "@"), + ARGPARSE_s_s (oAuditLog, "audit-log", + N_("|FILE|write an audit log to FILE")), + ARGPARSE_s_s (oHtmlAuditLog, "html-audit-log", "@"), + ARGPARSE_s_s (oDisplay, "display", "@"), + ARGPARSE_s_s (oTTYname, "ttyname", "@"), + ARGPARSE_s_s (oTTYtype, "ttytype", "@"), + ARGPARSE_s_s (oLCctype, "lc-ctype", "@"), + ARGPARSE_s_s (oLCmessages, "lc-messages", "@"), + ARGPARSE_s_s (oXauthority, "xauthority", "@"), + ARGPARSE_s_s (oCompatibilityFlags, "compatibility-flags", "@"), + + ARGPARSE_header (NULL, ""), /* Stop the header group. */ + /* Command aliases. */ ARGPARSE_c (aListKeys, "list-key", "@"), @@ -422,6 +436,10 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_c (aListChain, "check-sigs", "@"), ARGPARSE_c (aDeleteKey, "delete-key", "@"), + ARGPARSE_group (302, N_( + "@\n(See the man page for a complete listing of all commands and options)\n" + )), + ARGPARSE_end () }; @@ -441,6 +459,14 @@ static struct debug_flags_s debug_flags [] = }; +/* The list of compatibility flags. */ +static struct compatibility_flags_s compatibility_flags [] = + { + { COMPAT_ALLOW_KA_TO_ENCR, "allow-ka-to-encr" }, + { 0, NULL } + }; + + /* Global variable to keep an error count. */ int gpgsm_errors_seen = 0; @@ -855,7 +881,7 @@ main ( int argc, char **argv) /* Please note that we may running SUID(ROOT), so be very CAREFUL when adding any stuff between here and the call to secmem_init() somewhere after the option parsing */ - log_set_prefix (GPGSM_NAME, GPGRT_LOG_WITH_PREFIX); + log_set_prefix (GPGSM_NAME, GPGRT_LOG_WITH_PREFIX|GPGRT_LOG_NO_REGISTRY); /* Make sure that our subsystems are ready. */ i18n_init (); @@ -1217,6 +1243,15 @@ main ( int argc, char **argv) case oDebugNoChainValidation: opt.no_chain_validation = 1; break; case oDebugIgnoreExpiration: opt.ignore_expiration = 1; break; + case oCompatibilityFlags: + if (parse_compatibility_flags (pargs.r.ret_str, &opt.compat_flags, + compatibility_flags)) + { + pargs.r_opt = ARGPARSE_INVALID_ARG; + pargs.err = ARGPARSE_PRINT_ERROR; + } + break; + case oStatusFD: ctrl.status_fd = translate_sys2libc_fd_int (pargs.r.ret_int, 1); break; @@ -1362,10 +1397,18 @@ main ( int argc, char **argv) append_to_strlist (&opt.keyserver, pargs.r.ret_str); break; + case oKeyServer_deprecated: + obsolete_option (configname, pargs.lineno, "ldapserver"); + break; + case oIgnoreCertExtension: add_to_strlist (&opt.ignored_cert_extensions, pargs.r.ret_str); break; + case oIgnoreCertWithOID: + add_to_strlist (&opt.ignore_cert_with_oid, pargs.r.ret_str); + break; + case oNoAutostart: opt.autostart = 0; break; case oCompliance: @@ -1387,6 +1430,8 @@ main ( int argc, char **argv) case oMinRSALength: opt.min_rsa_length = pargs.r.ret_ulong; break; + case oRequireCompliance: opt.require_compliance = 1; break; + default: if (configname) pargs.err = ARGPARSE_PRINT_WARNING; @@ -1483,6 +1528,8 @@ main ( int argc, char **argv) gcry_control (GCRYCTL_RESUME_SECMEM_WARN); set_debug (); + if (opt.verbose) /* Print the compatibility flags. */ + parse_compatibility_flags (NULL, &opt.compat_flags, compatibility_flags); gnupg_set_compliance_extra_info (opt.min_rsa_length); /* Although we always use gpgsm_exit, we better install a regualr @@ -1701,39 +1748,20 @@ main ( int argc, char **argv) { case aGPGConfList: { /* List options and default values in the GPG Conf format. */ - char *config_filename_esc = percent_escape (opt.config_filename, NULL); - - es_printf ("%s-%s.conf:%lu:\"%s\n", - GPGCONF_NAME, GPGSM_NAME, - GC_OPT_FLAG_DEFAULT, config_filename_esc); - xfree (config_filename_esc); - es_printf ("verbose:%lu:\n", GC_OPT_FLAG_NONE); - es_printf ("quiet:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("debug-level:%lu:\"none:\n", GC_OPT_FLAG_DEFAULT); - es_printf ("log-file:%lu:\n", GC_OPT_FLAG_NONE); - es_printf ("disable-crl-checks:%lu:\n", GC_OPT_FLAG_NONE); - es_printf ("enable-crl-checks:%lu:\n", GC_OPT_FLAG_NONE); - es_printf ("disable-trusted-cert-crl-check:%lu:\n", GC_OPT_FLAG_NONE); - es_printf ("enable-ocsp:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("include-certs:%lu:%d:\n", GC_OPT_FLAG_DEFAULT, DEFAULT_INCLUDE_CERTS); - es_printf ("disable-policy-checks:%lu:\n", GC_OPT_FLAG_NONE); - es_printf ("auto-issuer-key-retrieve:%lu:\n", GC_OPT_FLAG_NONE); - es_printf ("disable-dirmngr:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("cipher-algo:%lu:\"%s:\n", GC_OPT_FLAG_DEFAULT, DEFAULT_CIPHER_ALGO); es_printf ("p12-charset:%lu:\n", GC_OPT_FLAG_DEFAULT); es_printf ("default-key:%lu:\n", GC_OPT_FLAG_DEFAULT); es_printf ("encrypt-to:%lu:\n", GC_OPT_FLAG_DEFAULT); - es_printf ("keyserver:%lu:\n", GC_OPT_FLAG_NONE); /* The next one is an info only item and should match what proc_parameters actually implements. */ es_printf ("default_pubkey_algo:%lu:\"%s:\n", GC_OPT_FLAG_DEFAULT, "RSA-3072"); - es_printf ("compliance:%lu:\"%s:\n", GC_OPT_FLAG_DEFAULT, "gnupg"); - } break; case aGPGConfTest: @@ -139,7 +139,19 @@ struct OID per string. */ strlist_t ignored_cert_extensions; + /* A list of OIDs which will be used to ignore certificates with + * sunch an OID during --learn-card. */ + strlist_t ignore_cert_with_oid; + + /* The current compliance mode. */ enum gnupg_compliance_mode compliance; + + /* Fail if an operation can't be done in the requested compliance + * mode. */ + int require_compliance; + + /* Compatibility flags (COMPAT_FLAG_xxxx). */ + unsigned int compat_flags; } opt; /* Debug values and macros. */ @@ -159,6 +171,18 @@ struct #define DBG_HASHING (opt.debug & DBG_HASHING_VALUE) #define DBG_IPC (opt.debug & DBG_IPC_VALUE) + +/* Compatibility flags */ +/* Telesec RSA cards produced for NRW in 2022 came with only the + * keyAgreement bit set. This flag allows there use for encryption + * anyway. Example cert: + * Issuer: /CN=DOI CA 10a/OU=DOI/O=PKI-1-Verwaltung/C=DE + * key usage: digitalSignature nonRepudiation keyAgreement + * policies: 1.3.6.1.4.1.7924.1.1:N: + */ +#define COMPAT_ALLOW_KA_TO_ENCR 1 + + /* Forward declaration for an object defined in server.c */ struct server_local_s; @@ -230,6 +254,8 @@ struct rootca_flags_s /*-- gpgsm.c --*/ +extern int gpgsm_errors_seen; + void gpgsm_exit (int rc); void gpgsm_init_default_ctrl (struct server_control_s *ctrl); int gpgsm_parse_validation_model (const char *model); diff --git a/sm/import.c b/sm/import.c index d7c898a..8a75a2b 100644 --- a/sm/import.c +++ b/sm/import.c @@ -777,7 +777,8 @@ parse_p12 (ctrl_t ctrl, ksba_reader_t reader, struct stats_s *stats) goto leave; kparms = p12_parse (p12buffer + p12bufoff, p12buflen - p12bufoff, - passphrase, store_cert_cb, &store_cert_parm, &bad_pass); + passphrase, store_cert_cb, &store_cert_parm, + &bad_pass, NULL); xfree (passphrase); passphrase = NULL; diff --git a/sm/keylist.c b/sm/keylist.c index 4f2d009..2d51aa7 100644 --- a/sm/keylist.c +++ b/sm/keylist.c @@ -251,9 +251,11 @@ print_capabilities (ksba_cert_t cert, estream_t fp) { gpg_error_t err; unsigned int use; + unsigned int is_encr, is_sign, is_cert; size_t buflen; char buffer[1]; + err = ksba_cert_get_user_data (cert, "is_qualified", &buffer, sizeof (buffer), &buflen); if (!err && buflen) @@ -285,17 +287,33 @@ print_capabilities (ksba_cert_t cert, estream_t fp) return; } + is_encr = is_sign = is_cert = 0; + if ((use & (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT))) - es_putc ('e', fp); + is_encr = 1; if ((use & (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION))) - es_putc ('s', fp); + is_sign = 1; if ((use & KSBA_KEYUSAGE_KEY_CERT_SIGN)) + is_cert = 1; + + /* We need to returned the faked key usage to frontends so that they + * can select the right key. Note that we don't do this for the + * human readable keyUsage. */ + if ((opt.compat_flags & COMPAT_ALLOW_KA_TO_ENCR) + && (use & KSBA_KEYUSAGE_KEY_AGREEMENT)) + is_encr = 1; + + if (is_encr) + es_putc ('e', fp); + if (is_sign) + es_putc ('s', fp); + if (is_cert) es_putc ('c', fp); - if ((use & (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT))) + if (is_encr) es_putc ('E', fp); - if ((use & (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION))) + if (is_sign) es_putc ('S', fp); - if ((use & KSBA_KEYUSAGE_KEY_CERT_SIGN)) + if (is_cert) es_putc ('C', fp); } diff --git a/sm/minip12.c b/sm/minip12.c index 9096f0b..ad29fc2 100644 --- a/sm/minip12.c +++ b/sm/minip12.c @@ -1,6 +1,7 @@ /* minip12.c - A minimal pkcs-12 implementation. * Copyright (C) 2002, 2003, 2004, 2006, 2011 Free Software Foundation, Inc. * Copyright (C) 2014 Werner Koch + * Copyright (C) 2022 g10 Code GmbH * * This file is part of GnuPG. * @@ -16,6 +17,15 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, see <https://www.gnu.org/licenses/>. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +/* References: + * RFC-7292 - PKCS #12: Personal Information Exchange Syntax v1.1 + * RFC-8351 - The PKCS #8 EncryptedPrivateKeyInfo Media Type + * RFC-5958 - Asymmetric Key Packages + * RFC-3447 - PKCS #1: RSA Cryptography Specifications Version 2.1 + * RFC-5915 - Elliptic Curve Private Key Structure */ #ifdef HAVE_CONFIG_H @@ -24,17 +34,16 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <assert.h> #include <gcrypt.h> #include <errno.h> -#ifdef TEST -#include <sys/stat.h> -#include <unistd.h> -#endif +#include <ksba.h> +#include "../common/util.h" #include "../common/logging.h" #include "../common/utf8conv.h" +#include "../common/tlv.h" +#include "../common/openpgpdefs.h" /* Only for openpgp_curve_to_oid. */ #include "minip12.h" #ifndef DIM @@ -42,48 +51,6 @@ #endif -enum -{ - UNIVERSAL = 0, - APPLICATION = 1, - ASNCONTEXT = 2, - PRIVATE = 3 -}; - - -enum -{ - TAG_NONE = 0, - TAG_BOOLEAN = 1, - TAG_INTEGER = 2, - TAG_BIT_STRING = 3, - TAG_OCTET_STRING = 4, - TAG_NULL = 5, - TAG_OBJECT_ID = 6, - TAG_OBJECT_DESCRIPTOR = 7, - TAG_EXTERNAL = 8, - TAG_REAL = 9, - TAG_ENUMERATED = 10, - TAG_EMBEDDED_PDV = 11, - TAG_UTF8_STRING = 12, - TAG_REALTIVE_OID = 13, - TAG_SEQUENCE = 16, - TAG_SET = 17, - TAG_NUMERIC_STRING = 18, - TAG_PRINTABLE_STRING = 19, - TAG_TELETEX_STRING = 20, - TAG_VIDEOTEX_STRING = 21, - TAG_IA5_STRING = 22, - TAG_UTC_TIME = 23, - TAG_GENERALIZED_TIME = 24, - TAG_GRAPHIC_STRING = 25, - TAG_VISIBLE_STRING = 26, - TAG_GENERAL_STRING = 27, - TAG_UNIVERSAL_STRING = 28, - TAG_CHARACTER_STRING = 29, - TAG_BMP_STRING = 30 -}; - static unsigned char const oid_data[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01 }; @@ -114,7 +81,8 @@ static unsigned char const oid_aes128_CBC[9] = { static unsigned char const oid_rsaEncryption[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }; - +static unsigned char const oid_pcPublicKey[7] = { + 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01 }; static unsigned char const data_3desiter2048[30] = { 0x30, 0x1C, 0x06, 0x0A, 0x2A, 0x86, 0x48, 0x86, @@ -175,6 +143,26 @@ struct tag_info int ndef; /* It is an indefinite length */ }; +/* Parser communication object. */ +struct p12_parse_ctx_s +{ + /* The callback for parsed certificates and its arg. */ + void (*certcb)(void*, const unsigned char*, size_t); + void *certcbarg; + + /* The supplied parseword. */ + const char *password; + + /* Set to true if the password was wrong. */ + int badpass; + + /* Malloced name of the curve. */ + char *curve; + + /* The private key as an MPI array. */ + gcry_mpi_t *privatekey; +}; + static int opt_verbose; @@ -186,6 +174,108 @@ p12_set_verbosity (int verbose) } +/* static void */ +/* dump_tag_info (struct tag_info *ti) */ +/* { */ +/* log_debug ("p12_parse: ti.class=%d tag=%lu len=%lu nhdr=%d %s%s\n", */ +/* ti->class, ti->tag, ti->length, ti->nhdr, */ +/* ti->is_constructed?" cons":"", */ +/* ti->ndef?" ndef":""); */ +/* } */ + + +/* Wrapper around tlv_builder_add_ptr to add an OID. When we + * eventually put the whole tlv_builder stuff into Libksba, we can add + * such a function there. Right now we don't do this to avoid a + * dependency on Libksba. Function return 1 on error. */ +static int +builder_add_oid (tlv_builder_t tb, int class, const char *oid) +{ + gpg_error_t err; + unsigned char *der; + size_t derlen; + + err = ksba_oid_from_str (oid, &der, &derlen); + if (err) + { + log_error ("%s: error converting '%s' to DER: %s\n", + __func__, oid, gpg_strerror (err)); + return 1; + } + + tlv_builder_add_val (tb, class, TAG_OBJECT_ID, der, derlen); + ksba_free (der); + return 0; +} + + +/* Wrapper around tlv_builder_add_ptr to add an MPI. TAG may either + * be OCTET_STRING or BIT_STRING. When we eventually put the whole + * tlv_builder stuff into Libksba, we can add such a function there. + * Right now we don't do this to avoid a dependency on Libksba. + * Function return 1 on error. STRIP is a hack to remove the first + * octet from the value. */ +static int +builder_add_mpi (tlv_builder_t tb, int class, int tag, gcry_mpi_t mpi, + int strip) +{ + int returncode; + gpg_error_t err; + const unsigned char *s; + unsigned char *freethis = NULL; + unsigned char *freethis2 = NULL; + unsigned int nbits; + size_t n; + + if (gcry_mpi_get_flag (mpi, GCRYMPI_FLAG_OPAQUE)) + { + s = gcry_mpi_get_opaque (mpi, &nbits); + n = (nbits+7)/8; + } + else + { + err = gcry_mpi_aprint (GCRYMPI_FMT_USG, &freethis, &n, mpi); + if (err) + { + log_error ("%s: error converting MPI: %s\n", + __func__, gpg_strerror (err)); + returncode = 1; + goto leave; + } + s = freethis; + } + + if (tag == TAG_BIT_STRING) + { + freethis2 = xtrymalloc_secure (n + 1); + if (!freethis2) + { + err = gpg_error_from_syserror (); + log_error ("%s: error converting MPI: %s\n", + __func__, gpg_strerror (err)); + returncode = 1; + goto leave; + } + freethis2[0] = 0; + memcpy (freethis2+1, s, n); + s = freethis2; + n++; + } + + strip = !!strip; + if (strip && n < 2) + strip = 0; + + tlv_builder_add_val (tb, class, tag, s+strip, n-strip); + returncode = 0; + + leave: + xfree (freethis); + xfree (freethis2); + return returncode; +} + + /* Parse the buffer at the address BUFFER which is of SIZE and return the tag and the length part from the TLV triplet. Update BUFFER and SIZE on success. Checks that the encoded length does not @@ -257,7 +347,7 @@ parse_tag (unsigned char const **buffer, size_t *size, struct tag_info *ti) ti->length = len; } - if (ti->class == UNIVERSAL && !ti->tag) + if (ti->class == CLASS_UNIVERSAL && !ti->tag) ti->length = 0; if (ti->length > length) @@ -304,11 +394,11 @@ cram_octet_string (const unsigned char *input, size_t *length, if (!output) goto bailout; - for (;;) + while (n) { if (parse_tag (&s, &n, &ti)) goto bailout; - if (ti.class == UNIVERSAL && ti.tag == TAG_OCTET_STRING + if (ti.class == CLASS_UNIVERSAL && ti.tag == TAG_OCTET_STRING && !ti.ndef && !ti.is_constructed) { memcpy (d, s, ti.length); @@ -316,7 +406,7 @@ cram_octet_string (const unsigned char *input, size_t *length, d += ti.length; n -= ti.length; } - else if (ti.class == UNIVERSAL && !ti.tag && !ti.is_constructed) + else if (ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed) break; /* Ready */ else goto bailout; @@ -442,7 +532,7 @@ set_key_iv (gcry_cipher_hd_t chd, char *salt, size_t saltlen, int iter, unsigned char keybuf[24]; int rc; - assert (keybytes == 5 || keybytes == 24); + log_assert (keybytes == 5 || keybytes == 24); if (string_to_key (1, salt, saltlen, iter, pw, keybytes, keybuf)) return -1; rc = gcry_cipher_setkey (chd, keybuf, keybytes); @@ -539,7 +629,8 @@ crypt_block (unsigned char *buffer, size_t length, char *salt, size_t saltlen, if (rc) { wipememory (buffer, length); - log_error ( "en/de-crytion failed: %s\n", gpg_strerror (rc)); + log_error ("%scrytion failed (%zu bytes): %s\n", + encrypt?"en":"de", length, gpg_strerror (rc)); goto leave; } @@ -652,7 +743,7 @@ bag_decrypted_data_p (const void *plaintext, size_t length) /* { */ /* # warning debug code is enabled */ - /* FILE *fp = fopen ("tmp-rc2-plain.der", "wb"); */ + /* FILE *fp = fopen ("tmp-minip12-plain-data.der", "wb"); */ /* if (!fp || fwrite (p, n, 1, fp) != 1) */ /* exit (2); */ /* fclose (fp); */ @@ -668,14 +759,11 @@ bag_decrypted_data_p (const void *plaintext, size_t length) return 1; } -/* Note: If R_RESULT is passed as NULL, a key object as already be - processed and thus we need to skip it here. */ + static int -parse_bag_encrypted_data (const unsigned char *buffer, size_t length, - int startoffset, size_t *r_consumed, const char *pw, - void (*certcb)(void*, const unsigned char*, size_t), - void *certcbarg, gcry_mpi_t **r_result, - int *r_badpass) +parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, + const unsigned char *buffer, size_t length, + int startoffset, size_t *r_consumed) { struct tag_info ti; const unsigned char *p = buffer; @@ -687,20 +775,16 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length, char iv[16]; unsigned int iter; unsigned char *plain = NULL; - int bad_pass = 0; unsigned char *cram_buffer = NULL; size_t consumed = 0; /* Number of bytes consumed from the original buffer. */ int is_3des = 0; int is_pbes2 = 0; - gcry_mpi_t *result = NULL; - int result_count; + int keyelem_count; - if (r_result) - *r_result = NULL; where = "start"; if (parse_tag (&p, &n, &ti)) goto bailout; - if (ti.class != ASNCONTEXT || ti.tag) + if (ti.class != CLASS_CONTEXT || ti.tag) goto bailout; if (parse_tag (&p, &n, &ti)) goto bailout; @@ -860,10 +944,10 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length, goto bailout; consumed = p - p_start; - if (ti.class == ASNCONTEXT && ti.tag == 0 && ti.is_constructed && ti.ndef) + if (ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed && ti.ndef) { /* Mozilla exported certs now come with single byte chunks of - octect strings. (Mozilla Firefox 1.0.4). Arghh. */ + octet strings. (Mozilla Firefox 1.0.4). Arghh. */ where = "cram-rc2or3des-ciphertext"; cram_buffer = cram_octet_string ( p, &n, &consumed); if (!cram_buffer) @@ -871,10 +955,23 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length, p = p_start = cram_buffer; if (r_consumed) *r_consumed = consumed; - r_consumed = NULL; /* Ugly hack to not update that value any further. */ + r_consumed = NULL; /* Donot update that value on return. */ + ti.length = n; + } + else if (ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed) + { + where = "octets-rc2or3des-ciphertext"; + n = ti.length; + cram_buffer = cram_octet_string ( p, &n, &consumed); + if (!cram_buffer) + goto bailout; + p = p_start = cram_buffer; + if (r_consumed) + *r_consumed = consumed; + r_consumed = NULL; /* Do not update that value on return. */ ti.length = n; } - else if (ti.class == ASNCONTEXT && ti.tag == 0 && ti.length ) + else if (ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.length ) ; else goto bailout; @@ -890,7 +987,7 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length, goto bailout; } decrypt_block (p, plain, ti.length, salt, saltlen, iter, - iv, is_pbes2?16:0, pw, + iv, is_pbes2?16:0, ctx->password, is_pbes2 ? GCRY_CIPHER_AES128 : is_3des ? GCRY_CIPHER_3DES : GCRY_CIPHER_RFC2268_40, bag_decrypted_data_p); @@ -901,18 +998,18 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length, where = "outer.outer.seq"; if (parse_tag (&p, &n, &ti)) { - bad_pass = 1; + ctx->badpass = 1; goto bailout; } if (ti.class || ti.tag != TAG_SEQUENCE) { - bad_pass = 1; + ctx->badpass = 1; goto bailout; } if (parse_tag (&p, &n, &ti)) { - bad_pass = 1; + ctx->badpass = 1; goto bailout; } @@ -960,7 +1057,7 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length, where = "certbag.before.certheader"; if (parse_tag (&p, &n, &ti)) goto bailout; - if (ti.class != ASNCONTEXT || ti.tag) + if (ti.class != CLASS_CONTEXT || ti.tag) goto bailout; if (iscrlbag) { @@ -968,7 +1065,7 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length, p += ti.length; n -= ti.length; } - else if (iskeybag && (result || !r_result)) + else if (iskeybag && ctx->privatekey) { log_info ("one keyBag already processed; skipping this one\n"); p += ti.length; @@ -1018,16 +1115,17 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length, goto bailout; len = ti.length; - result = gcry_calloc (10, sizeof *result); - if (!result) + log_assert (!ctx->privatekey); + ctx->privatekey = gcry_calloc (10, sizeof *ctx->privatekey); + if (!ctx->privatekey) { - log_error ( "error allocating result array\n"); + log_error ("error allocating private key element array\n"); goto bailout; } - result_count = 0; + keyelem_count = 0; where = "reading.keybag.key-parameters"; - for (result_count = 0; len && result_count < 9;) + for (keyelem_count = 0; len && keyelem_count < 9;) { if ( parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER) @@ -1038,13 +1136,14 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length, if (len < ti.length) goto bailout; len -= ti.length; - if (!result_count && ti.length == 1 && !*p) + if (!keyelem_count && ti.length == 1 && !*p) ; /* ignore the very first one if it is a 0 */ else { int rc; - rc = gcry_mpi_scan (result+result_count, GCRYMPI_FMT_USG, p, + rc = gcry_mpi_scan (ctx->privatekey+keyelem_count, + GCRYMPI_FMT_USG, p, ti.length, NULL); if (rc) { @@ -1052,7 +1151,7 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length, gpg_strerror (rc)); goto bailout; } - result_count++; + keyelem_count++; } p += ti.length; n -= ti.length; @@ -1081,7 +1180,7 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length, where = "certbag.before.octetstring"; if (parse_tag (&p, &n, &ti)) goto bailout; - if (ti.class != ASNCONTEXT || ti.tag) + if (ti.class != CLASS_CONTEXT || ti.tag) goto bailout; if (parse_tag (&p, &n, &ti)) goto bailout; @@ -1089,8 +1188,8 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length, goto bailout; /* Return the certificate. */ - if (certcb) - certcb (certcbarg, p, ti.length); + if (ctx->certcb) + ctx->certcb (ctx->certcbarg, p, ti.length); p += ti.length; n -= ti.length; @@ -1129,32 +1228,21 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length, *r_consumed = consumed; gcry_free (plain); gcry_free (cram_buffer); - if (r_result) - *r_result = result; return 0; bailout: - if (result) - { - int i; - - for (i=0; result[i]; i++) - gcry_mpi_release (result[i]); - gcry_free (result); - } if (r_consumed) *r_consumed = consumed; gcry_free (plain); gcry_free (cram_buffer); log_error ("encryptedData error at \"%s\", offset %u\n", where, (unsigned int)((p - p_start)+startoffset)); - if (bad_pass) + if (ctx->badpass) { /* Note, that the following string might be used by other programs to check for a bad passphrase; it should therefore not be translated or changed. */ log_error ("possibly bad passphrase given\n"); - *r_badpass = 1; } return -1; } @@ -1171,7 +1259,7 @@ bag_data_p (const void *plaintext, size_t length) /* { */ /* # warning debug code is enabled */ -/* FILE *fp = fopen ("tmp-3des-plain-key.der", "wb"); */ +/* FILE *fp = fopen ("tmp-minip12-plain-key.der", "wb"); */ /* if (!fp || fwrite (p, n, 1, fp) != 1) */ /* exit (2); */ /* fclose (fp); */ @@ -1187,11 +1275,13 @@ bag_data_p (const void *plaintext, size_t length) } -static gcry_mpi_t * -parse_bag_data (const unsigned char *buffer, size_t length, int startoffset, - size_t *r_consumed, const char *pw) +static gpg_error_t +parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, + const unsigned char *buffer, size_t length, + int startoffset, + size_t *r_consumed) { - int rc; + gpg_error_t err = 0; struct tag_info ti; const unsigned char *p = buffer; const unsigned char *p_start = buffer; @@ -1203,63 +1293,15 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset, unsigned int iter; int len; unsigned char *plain = NULL; - gcry_mpi_t *result = NULL; - int result_count, i; unsigned char *cram_buffer = NULL; size_t consumed = 0; /* Number of bytes consumed from the original buffer. */ int is_pbes2 = 0; + int keyelem_count = 0; - where = "start"; - if (parse_tag (&p, &n, &ti)) - goto bailout; - if (ti.class != ASNCONTEXT || ti.tag) - goto bailout; - if (parse_tag (&p, &n, &ti)) - goto bailout; - if (ti.class || ti.tag != TAG_OCTET_STRING) - goto bailout; - - consumed = p - p_start; - if (ti.is_constructed && ti.ndef) - { - /* Mozilla exported certs now come with single byte chunks of - octect strings. (Mozilla Firefox 1.0.4). Arghh. */ - where = "cram-data.outersegs"; - cram_buffer = cram_octet_string ( p, &n, &consumed); - if (!cram_buffer) - goto bailout; - p = p_start = cram_buffer; - if (r_consumed) - *r_consumed = consumed; - r_consumed = NULL; /* Ugly hack to not update that value any further. */ - } - - - where = "data.outerseqs"; - if (parse_tag (&p, &n, &ti)) - goto bailout; - if (ti.class || ti.tag != TAG_SEQUENCE) - goto bailout; - if (parse_tag (&p, &n, &ti)) - goto bailout; - if (ti.class || ti.tag != TAG_SEQUENCE) - goto bailout; - - where = "data.objectidentifier"; - if (parse_tag (&p, &n, &ti)) - goto bailout; - if (ti.class || ti.tag != TAG_OBJECT_ID - || ti.length != DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag) - || memcmp (p, oid_pkcs_12_pkcs_8ShroudedKeyBag, - DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag))) - goto bailout; - p += DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag); - n -= DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag); - - where = "shrouded,outerseqs"; + where = "shrouded_key_bag"; if (parse_tag (&p, &n, &ti)) goto bailout; - if (ti.class != ASNCONTEXT || ti.tag) + if (ti.class != CLASS_CONTEXT || ti.tag) goto bailout; if (parse_tag (&p, &n, &ti)) goto bailout; @@ -1292,7 +1334,7 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset, if (is_pbes2) { - where = "pkcs5PBES2-params"; + where = "shrouded_key_bag.pkcs5PBES2-params"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_SEQUENCE) @@ -1357,7 +1399,7 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset, } else { - where = "3des-params"; + where = "shrouded_key_bag.3des-params"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_SEQUENCE) @@ -1383,7 +1425,7 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset, } } - where = "3desoraes-ciphertext"; + where = "shrouded_key_bag.3desoraes-ciphertext"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_OCTET_STRING || !ti.length ) @@ -1401,14 +1443,14 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset, } consumed += p - p_start + ti.length; decrypt_block (p, plain, ti.length, salt, saltlen, iter, - iv, is_pbes2? 16:0, pw, + iv, is_pbes2? 16:0, ctx->password, is_pbes2? GCRY_CIPHER_AES128 : GCRY_CIPHER_3DES, bag_data_p); n = ti.length; startoffset = 0; p_start = p = plain; - where = "decrypted-text"; + where = "shrouded_key_bag.decrypted-text"; if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE) goto bailout; if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER @@ -1423,13 +1465,44 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset, if (len < ti.nhdr) goto bailout; len -= ti.nhdr; - if (ti.class || ti.tag != TAG_OBJECT_ID - || ti.length != DIM(oid_rsaEncryption) - || memcmp (p, oid_rsaEncryption, - DIM(oid_rsaEncryption))) + if (ti.class || ti.tag != TAG_OBJECT_ID) + goto bailout; + /* gpgrt_log_printhex (p, ti.length, "OID:"); */ + if (ti.length == DIM(oid_rsaEncryption) + && !memcmp (p, oid_rsaEncryption, DIM(oid_rsaEncryption))) + { + p += DIM (oid_rsaEncryption); + n -= DIM (oid_rsaEncryption); + } + else if (ti.length == DIM(oid_pcPublicKey) + && !memcmp (p, oid_pcPublicKey, DIM(oid_pcPublicKey))) + { + /* See RFC-5915 for the format. */ + p += DIM (oid_pcPublicKey); + n -= DIM (oid_pcPublicKey); + if (len < ti.length) + goto bailout; + len -= ti.length; + if (n < len) + goto bailout; + if (parse_tag (&p, &n, &ti)) + goto bailout; + /* gpgrt_log_debug ("ti=%d/%lu len=%lu\n",ti.class,ti.tag,ti.length); */ + if (len < ti.nhdr) + goto bailout; + len -= ti.nhdr; + if (ti.class || ti.tag != TAG_OBJECT_ID) + goto bailout; + ksba_free (ctx->curve); + ctx->curve = ksba_oid_to_str (p, ti.length); + if (!ctx->curve) + goto bailout; + /* log_debug ("OID of curve is: %s\n", curve); */ + p += ti.length; + n -= ti.length; + } + else goto bailout; - p += DIM (oid_rsaEncryption); - n -= DIM (oid_rsaEncryption); if (len < ti.length) goto bailout; len -= ti.length; @@ -1443,16 +1516,22 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset, goto bailout; len = ti.length; - result = gcry_calloc (10, sizeof *result); - if (!result) + if (ctx->privatekey) { - log_error ( "error allocating result array\n"); + log_error ("a key has already been received\n"); goto bailout; } - result_count = 0; + ctx->privatekey = gcry_calloc (10, sizeof *ctx->privatekey); + if (!ctx->privatekey) + { - where = "reading.key-parameters"; - for (result_count=0; len && result_count < 9;) + log_error ("error allocating privatekey element array\n"); + goto bailout; + } + keyelem_count = 0; + + where = "shrouded_key_bag.reading.key-parameters"; + if (ctx->curve) /* ECC case. */ { if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER) goto bailout; @@ -1462,45 +1541,283 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset, if (len < ti.length) goto bailout; len -= ti.length; - if (!result_count && ti.length == 1 && !*p) - ; /* ignore the very first one if it is a 0 */ - else + if (ti.length != 1 && *p != 1) { - rc = gcry_mpi_scan (result+result_count, GCRYMPI_FMT_USG, p, - ti.length, NULL); - if (rc) - { - log_error ("error parsing key parameter: %s\n", - gpg_strerror (rc)); - goto bailout; - } - result_count++; + log_error ("error parsing private ecPublicKey parameter: %s\n", + "bad version"); + goto bailout; + } + p += ti.length; + n -= ti.length; + if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_OCTET_STRING) + goto bailout; + if (len < ti.nhdr) + goto bailout; + len -= ti.nhdr; + if (len < ti.length) + goto bailout; + len -= ti.length; + /* log_printhex (p, ti.length, "ecc q="); */ + err = gcry_mpi_scan (ctx->privatekey, GCRYMPI_FMT_USG, + p, ti.length, NULL); + if (err) + { + log_error ("error parsing key parameter: %s\n", gpg_strerror (err)); + goto bailout; } p += ti.length; n -= ti.length; + + len = 0; /* Skip the rest. */ + } + else /* RSA case */ + { + for (keyelem_count=0; len && keyelem_count < 9;) + { + if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER) + goto bailout; + if (len < ti.nhdr) + goto bailout; + len -= ti.nhdr; + if (len < ti.length) + goto bailout; + len -= ti.length; + if (!keyelem_count && ti.length == 1 && !*p) + ; /* ignore the very first one if it is a 0 */ + else + { + err = gcry_mpi_scan (ctx->privatekey+keyelem_count, + GCRYMPI_FMT_USG, p, ti.length, NULL); + if (err) + { + log_error ("error parsing key parameter: %s\n", + gpg_strerror (err)); + goto bailout; + } + keyelem_count++; + } + p += ti.length; + n -= ti.length; + } } if (len) goto bailout; + goto leave; + + bailout: + gcry_free (plain); + log_error ("data error at \"%s\", offset %zu\n", + where, (size_t)((p - p_start) + startoffset)); + if (!err) + err = gpg_error (GPG_ERR_GENERAL); + + leave: gcry_free (cram_buffer); if (r_consumed) *r_consumed = consumed; - return result; + return err; +} + + +static gpg_error_t +parse_cert_bag (struct p12_parse_ctx_s *ctx, + const unsigned char *buffer, size_t length, + int startoffset, + size_t *r_consumed) +{ + gpg_error_t err = 0; + struct tag_info ti; + const unsigned char *p = buffer; + const unsigned char *p_start = buffer; + size_t n = length; + const char *where; + size_t consumed = 0; /* Number of bytes consumed from the original buffer. */ + + if (opt_verbose) + log_info ("processing certBag\n"); + + /* Expect: + * [0] + * SEQUENCE + * OBJECT IDENTIFIER pkcs-12-certBag + */ + where = "certbag.before.certheader"; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.class != CLASS_CONTEXT || ti.tag) + goto bailout; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.class || ti.tag != TAG_SEQUENCE) + goto bailout; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.class || ti.tag != TAG_OBJECT_ID + || ti.length != DIM(oid_x509Certificate_for_pkcs_12) + || memcmp (p, oid_x509Certificate_for_pkcs_12, + DIM(oid_x509Certificate_for_pkcs_12))) + goto bailout; + p += DIM(oid_x509Certificate_for_pkcs_12); + n -= DIM(oid_x509Certificate_for_pkcs_12); + + /* Expect: + * [0] + * OCTET STRING encapsulates -- the certificates + */ + where = "certbag.before.octetstring"; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.class != CLASS_CONTEXT || ti.tag) + goto bailout; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.class || ti.tag != TAG_OCTET_STRING || ti.ndef) + goto bailout; + + /* Return the certificate from the octet string. */ + if (ctx->certcb) + ctx->certcb (ctx->certcbarg, p, ti.length); + + p += ti.length; + n -= ti.length; + + if (!n) + goto leave; /* ready. */ + + /* Expect: + * SET + * SEQUENCE -- we actually ignore this. + */ + where = "certbag.attribute_set"; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (!ti.class && ti.tag == TAG_SET && !ti.ndef) + { /* Comsume the optional SET. */ + p += ti.length; + n -= ti.length; + if (parse_tag (&p, &n, &ti)) + goto bailout; + } + + goto leave; bailout: - gcry_free (plain); - if (result) + log_error ( "data error at \"%s\", offset %u\n", + where, (unsigned int)((p - p_start) + startoffset)); + err = gpg_error (GPG_ERR_GENERAL); + + leave: + if (r_consumed) + *r_consumed = consumed; + return err; +} + + +static gpg_error_t +parse_bag_data (struct p12_parse_ctx_s *ctx, + const unsigned char *buffer, size_t length, int startoffset, + size_t *r_consumed) +{ + gpg_error_t err = 0; + struct tag_info ti; + const unsigned char *p = buffer; + const unsigned char *p_start = buffer; + size_t n = length; + const char *where; + unsigned char *cram_buffer = NULL; + size_t consumed = 0; /* Number of bytes consumed from the original buffer. */ + + /* Expect: + * [0] + * OCTET STRING, encapsulates + */ + where = "data"; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.class != CLASS_CONTEXT || ti.tag) + goto bailout; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.class || ti.tag != TAG_OCTET_STRING) + goto bailout; + + + consumed = p - p_start; + if (ti.is_constructed && ti.ndef) + { + /* Mozilla exported certs now come with single byte chunks of + octet strings. (Mozilla Firefox 1.0.4). Arghh. */ + where = "data.cram_os"; + cram_buffer = cram_octet_string ( p, &n, &consumed); + if (!cram_buffer) + goto bailout; + p = p_start = cram_buffer; + if (r_consumed) + *r_consumed = consumed; + r_consumed = NULL; /* Ugly hack to not update that value on return. */ + } + + /* Expect: + * SEQUENCE + * SEQUENCE + */ + where = "data.2seqs"; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.class || ti.tag != TAG_SEQUENCE) + goto bailout; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.class || ti.tag != TAG_SEQUENCE) + goto bailout; + + /* Expect: + * OBJECT IDENTIFIER + */ + where = "data.oid"; + if (parse_tag (&p, &n, &ti)) + goto bailout; + if (ti.class || ti.tag != TAG_OBJECT_ID) + goto bailout; + + /* Now divert to the actual parser. */ + if (ti.length == DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag) + && !memcmp (p, oid_pkcs_12_pkcs_8ShroudedKeyBag, + DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag))) { - for (i=0; result[i]; i++) - gcry_mpi_release (result[i]); - gcry_free (result); + p += DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag); + n -= DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag); + + if (parse_shrouded_key_bag (ctx, p, n, + startoffset + (p - p_start), r_consumed)) + goto bailout; } - gcry_free (cram_buffer); + else if ( ti.length == DIM(oid_pkcs_12_CertBag) + && !memcmp (p, oid_pkcs_12_CertBag, DIM(oid_pkcs_12_CertBag))) + { + p += DIM(oid_pkcs_12_CertBag); + n -= DIM(oid_pkcs_12_CertBag); + + if (parse_cert_bag (ctx, p, n, + startoffset + (p - p_start), r_consumed)) + goto bailout; + } + else + goto bailout; + + goto leave; + + bailout: log_error ( "data error at \"%s\", offset %u\n", - where, (unsigned int)((p - buffer) + startoffset)); - if (r_consumed) + where, (unsigned int)((p - p_start) + startoffset)); + err = gpg_error (GPG_ERR_GENERAL); + + leave: + gcry_free (cram_buffer); + if (r_consumed) /* Store the number of consumed bytes unless already done. */ *r_consumed = consumed; - return NULL; + return err; } @@ -1509,11 +1826,12 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset, that it is only able to look for 3DES encoded encryptedData and tries to extract the first private key object it finds. In case of an error NULL is returned. CERTCB and CERRTCBARG are used to pass - X.509 certificates back to the caller. */ + X.509 certificates back to the caller. If R_CURVE is not NULL and + an ECC key was found the OID of the curve is stored there. */ gcry_mpi_t * p12_parse (const unsigned char *buffer, size_t length, const char *pw, void (*certcb)(void*, const unsigned char*, size_t), - void *certcbarg, int *r_badpass) + void *certcbarg, int *r_badpass, char **r_curve) { struct tag_info ti; const unsigned char *p = buffer; @@ -1522,10 +1840,17 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, const char *where; int bagseqlength, len; int bagseqndef, lenndef; - gcry_mpi_t *result = NULL; unsigned char *cram_buffer = NULL; + size_t consumed; + struct p12_parse_ctx_s ctx = { NULL }; *r_badpass = 0; + + ctx.certcb = certcb; + ctx.certcbarg = certcbarg; + ctx.password = pw; + + where = "pfx"; if (parse_tag (&p, &n, &ti)) goto bailout; @@ -1554,17 +1879,17 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, if (parse_tag (&p, &n, &ti)) goto bailout; - if (ti.class != ASNCONTEXT || ti.tag) + if (ti.class != CLASS_CONTEXT || ti.tag) goto bailout; if (parse_tag (&p, &n, &ti)) goto bailout; - if (ti.class != UNIVERSAL || ti.tag != TAG_OCTET_STRING) + if (ti.class != CLASS_UNIVERSAL || ti.tag != TAG_OCTET_STRING) goto bailout; if (ti.is_constructed && ti.ndef) { /* Mozilla exported certs now come with single byte chunks of - octect strings. (Mozilla Firefox 1.0.4). Arghh. */ + octet strings. (Mozilla Firefox 1.0.4). Arghh. */ where = "cram-bags"; cram_buffer = cram_octet_string ( p, &n, NULL); if (!cram_buffer) @@ -1575,19 +1900,20 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, where = "bags"; if (parse_tag (&p, &n, &ti)) goto bailout; - if (ti.class != UNIVERSAL || ti.tag != TAG_SEQUENCE) + if (ti.class != CLASS_UNIVERSAL || ti.tag != TAG_SEQUENCE) goto bailout; bagseqndef = ti.ndef; bagseqlength = ti.length; while (bagseqlength || bagseqndef) { -/* log_debug ( "at offset %u\n", (p - p_start)); */ + /* log_debug ("p12_parse: at offset %ld\n", (p - p_start)); */ where = "bag-sequence"; if (parse_tag (&p, &n, &ti)) goto bailout; - if (bagseqndef && ti.class == UNIVERSAL && !ti.tag && !ti.is_constructed) + if (bagseqndef && ti.class == CLASS_UNIVERSAL + && !ti.tag && !ti.is_constructed) break; /* Ready */ - if (ti.class != UNIVERSAL || ti.tag != TAG_SEQUENCE) + if (ti.class != CLASS_UNIVERSAL || ti.tag != TAG_SEQUENCE) goto bailout; if (!bagseqndef) @@ -1612,16 +1938,14 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, if (ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_encryptedData) && !memcmp (p, oid_encryptedData, DIM(oid_encryptedData))) { - size_t consumed = 0; p += DIM(oid_encryptedData); n -= DIM(oid_encryptedData); if (!lenndef) len -= DIM(oid_encryptedData); where = "bag.encryptedData"; - if (parse_bag_encrypted_data (p, n, (p - p_start), &consumed, pw, - certcb, certcbarg, - result? NULL : &result, r_badpass)) + consumed = 0; + if (parse_bag_encrypted_data (&ctx, p, n, (p - p_start), &consumed)) goto bailout; if (lenndef) len += consumed; @@ -1629,30 +1953,21 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, else if (ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_data) && !memcmp (p, oid_data, DIM(oid_data))) { - if (result) - { - log_info ("already got an key object, skipping this one\n"); - p += ti.length; - n -= ti.length; - } - else - { - size_t consumed = 0; - - p += DIM(oid_data); - n -= DIM(oid_data); - if (!lenndef) - len -= DIM(oid_data); - result = parse_bag_data (p, n, (p - p_start), &consumed, pw); - if (!result) - goto bailout; - if (lenndef) - len += consumed; - } + p += DIM(oid_data); + n -= DIM(oid_data); + if (!lenndef) + len -= DIM(oid_data); + + where = "bag.data"; + consumed = 0; + if (parse_bag_data (&ctx, p, n, (p - p_start), &consumed)) + goto bailout; + if (lenndef) + len += consumed; } else { - log_info ("unknown bag type - skipped\n"); + log_info ("unknown outer bag type - skipped\n"); p += ti.length; n -= ti.length; } @@ -1666,25 +1981,35 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, /* Need to skip the Null Tag. */ if (parse_tag (&p, &n, &ti)) goto bailout; - if (!(ti.class == UNIVERSAL && !ti.tag && !ti.is_constructed)) + if (!(ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed)) goto bailout; } } gcry_free (cram_buffer); - return result; + if (r_curve) + *r_curve = ctx.curve; + else + gcry_free (ctx.curve); + + return ctx.privatekey; + bailout: log_error ("error at \"%s\", offset %u\n", where, (unsigned int)(p - p_start)); - if (result) + if (ctx.privatekey) { int i; - for (i=0; result[i]; i++) - gcry_mpi_release (result[i]); - gcry_free (result); + for (i=0; ctx.privatekey[i]; i++) + gcry_mpi_release (ctx.privatekey[i]); + gcry_free (ctx.privatekey); + ctx.privatekey = NULL; } gcry_free (cram_buffer); + gcry_free (ctx.curve); + if (r_curve) + *r_curve = NULL; return NULL; } @@ -1876,7 +2201,7 @@ create_final (struct buffer_s *sequences, const char *pw, size_t *r_length) /* Ready. */ resultlen = p - result; if (needed != resultlen) - log_debug ("length mismatch: %lu, %lu\n", + log_debug ("p12_parse: warning: length mismatch: %lu, %lu\n", (unsigned long)needed, (unsigned long)resultlen); *r_length = resultlen; @@ -1885,36 +2210,36 @@ create_final (struct buffer_s *sequences, const char *pw, size_t *r_length) /* Build a DER encoded SEQUENCE with the key: - - SEQUENCE { - INTEGER 0 - SEQUENCE { - OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1) - NULL - } - OCTET STRING, encapsulates { - SEQUENCE { - INTEGER 0 - INTEGER - INTEGER - INTEGER - INTEGER - INTEGER - INTEGER - INTEGER - INTEGER - } - } - } - - MODE controls what is being generated: - 0 - As described above - 1 - Ditto but without the padding - 2 - Only the inner part (pkcs#1) -*/ + * + * SEQUENCE { -- OneAsymmetricKey (RFC-5958) + * INTEGER 0 + * SEQUENCE { + * OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1) + * NULL + * } + * OCTET STRING, encapsulates { + * SEQUENCE { -- RSAPrivateKey (RFC-3447) + * INTEGER 0 -- Version + * INTEGER -- n + * INTEGER -- e + * INTEGER -- d + * INTEGER -- p + * INTEGER -- q + * INTEGER -- d mod (p-1) + * INTEGER -- d mod (q-1) + * INTEGER -- q^-1 mod p + * } + * } + * } + * + * MODE controls what is being generated: + * 0 - As described above + * 1 - Ditto but without the padding + * 2 - Only the inner part (pkcs#1) + */ static unsigned char * -build_key_sequence (gcry_mpi_t *kparms, int mode, size_t *r_length) +build_rsa_key_sequence (gcry_mpi_t *kparms, int mode, size_t *r_length) { int rc, i; size_t needed, n; @@ -2030,7 +2355,7 @@ build_key_sequence (gcry_mpi_t *kparms, int mode, size_t *r_length) } plainlen = p - plain; - assert (needed == plainlen); + log_assert (needed == plainlen); if (!mode) { @@ -2045,6 +2370,156 @@ build_key_sequence (gcry_mpi_t *kparms, int mode, size_t *r_length) } +/* Build a DER encoded SEQUENCE for an ECC key: + * + * SEQUENCE { -- OneAsymmetricKey (RFC-5958) + * INTEGER 0 + * SEQUENCE { + * OBJECT IDENTIFIER ecPublicKey (1 2 840 10045 2 1) + * OBJECT IDENTIFIER -- curvename + * } + * OCTET STRING, encapsulates { + * SEQUENCE { -- ECPrivateKey + * INTEGER 1 -- version + * OCTET STRING -- privateKey + * [1] { + * BIT STRING - publicKey + * } + * } + * } + * } + * + * For details see RFC-5480 and RFC-5915 (ECparameters are not created). + * + * KPARMS[0] := Opaque MPI with the curve name as dotted-decimal string. + * KPARMS[1] := Opaque MPI with the public key (q) + * KPARMS[2] := Opaque MPI with the private key (d) + * MODE controls what is being generated: + * 0 - As described above + * 1 - Ditto but without the extra padding needed for pcsk#12 + * 2 - Only the octet string (ECPrivateKey) + */ + +static unsigned char * +build_ecc_key_sequence (gcry_mpi_t *kparms, int mode, size_t *r_length) +{ + gpg_error_t err; + unsigned int nbits, n; + const unsigned char *s; + char *p; + tlv_builder_t tb; + void *result; + size_t resultlen; + const char *curve; + unsigned int curvebits; + int e; + int i; + int strip_one; + + for (i=0; kparms[i]; i++) + ; + if (i != 3) + { + log_error ("%s: invalid number of parameters\n", __func__); + return NULL; + } + + s = gcry_mpi_get_opaque (kparms[0], &nbits); + n = (nbits+7)/8; + p = xtrymalloc (n + 1); + if (!p) + { + err = gpg_error_from_syserror (); + log_error ("%s:%d: error getting parameter: %s\n", + __func__, __LINE__, gpg_strerror (err)); + return NULL; + } + memcpy (p, s, n); + p[n] = 0; + /* We need to use our OpenPGP mapping to turn a curve name into its + * canonical numerical OID. We should have a Libgcrypt function to + * do this; see bug report #4926. */ + curve = openpgp_curve_to_oid (p, &curvebits, NULL); + xfree (p); + if (!curve) + { + err = gpg_error (GPG_ERR_UNKNOWN_CURVE); + log_error ("%s:%d: error getting parameter: %s\n", + __func__, __LINE__, gpg_strerror (err)); + return NULL; + } + + /* Unfortunately the private key D may come with a single leading + * zero byte. This is becuase at some point it was treated as + * signed MPI and the code made sure that it is always interpreted + * as unsigned. Fortunately we got the size of the curve and can + * detect such a case reliable. */ + s = gcry_mpi_get_opaque (kparms[2], &nbits); + n = (nbits+7)/8; + strip_one = (n == (curvebits+7)/8 + 1 && !*s); + + + tb = tlv_builder_new (1); + if (!tb) + { + err = gpg_error_from_syserror (); + log_error ("%s:%d: error creating new TLV builder: %s\n", + __func__, __LINE__, gpg_strerror (err)); + return NULL; + } + e = 0; + tlv_builder_add_tag (tb, 0, TAG_SEQUENCE); + tlv_builder_add_ptr (tb, 0, TAG_INTEGER, "\0", 1); + tlv_builder_add_tag (tb, 0, TAG_SEQUENCE); + e|= builder_add_oid (tb, 0, "1.2.840.10045.2.1"); + e|= builder_add_oid (tb, 0, curve); + tlv_builder_add_end (tb); + tlv_builder_add_tag (tb, 0, TAG_OCTET_STRING); + tlv_builder_add_tag (tb, 0, TAG_SEQUENCE); + tlv_builder_add_ptr (tb, 0, TAG_INTEGER, "\x01", 1); + e|= builder_add_mpi (tb, 0, TAG_OCTET_STRING, kparms[2], strip_one); + tlv_builder_add_tag (tb, CLASS_CONTEXT, 1); + e|= builder_add_mpi (tb, 0, TAG_BIT_STRING, kparms[1], 0); + tlv_builder_add_end (tb); + tlv_builder_add_end (tb); + tlv_builder_add_end (tb); + tlv_builder_add_end (tb); + + err = tlv_builder_finalize (tb, &result, &resultlen); + if (err || e) + { + if (!err) + err = gpg_error (GPG_ERR_GENERAL); + log_error ("%s:%d: tlv building failed: %s\n", + __func__, __LINE__, gpg_strerror (err)); + return NULL; + } + + /* Append some pad characters if needed. */ + if (!mode && (n = 8 - resultlen % 8)) + { + p = xtrymalloc_secure (resultlen + n); + if (!p) + { + err = gpg_error_from_syserror (); + log_error ("%s:%d: error allocating buffer: %s\n", + __func__, __LINE__, gpg_strerror (err)); + xfree (result); + return NULL; + } + memcpy (p, result, resultlen); + xfree (result); + result = p; + p = (unsigned char*)result + resultlen; + for (i=0; i < n; i++, resultlen++) + *p++ = n; + } + + *r_length = resultlen; + + return result; +} + static unsigned char * build_key_bag (unsigned char *buffer, size_t buflen, char *salt, @@ -2167,7 +2642,7 @@ build_key_bag (unsigned char *buffer, size_t buflen, char *salt, keybaglen = p - keybag; if (needed != keybaglen) - log_debug ("length mismatch: %lu, %lu\n", + log_debug ("p12_parse: warning: length mismatch: %lu, %lu\n", (unsigned long)needed, (unsigned long)keybaglen); *r_length = keybaglen; @@ -2266,7 +2741,7 @@ build_cert_bag (unsigned char *buffer, size_t buflen, char *salt, certbaglen = p - certbag; if (needed != certbaglen) - log_debug ("length mismatch: %lu, %lu\n", + log_debug ("p12_parse: warning: length mismatch: %lu, %lu\n", (unsigned long)needed, (unsigned long)certbaglen); *r_length = certbaglen; @@ -2284,7 +2759,7 @@ build_cert_sequence (const unsigned char *buffer, size_t buflen, size_t certseqlen; int i; - assert (strlen (keyidstr) == 8); + log_assert (strlen (keyidstr) == 8); /* Walk 8 steps down to collect the info: */ @@ -2377,7 +2852,7 @@ build_cert_sequence (const unsigned char *buffer, size_t buflen, certseqlen = p - certseq; if (needed != certseqlen) - log_debug ("length mismatch: %lu, %lu\n", + log_debug ("p12_parse: warning: length mismatch: %lu, %lu\n", (unsigned long)needed, (unsigned long)certseqlen); /* Append some pad characters; we already allocated extra space. */ @@ -2489,7 +2964,16 @@ p12_build (gcry_mpi_t *kparms, const void *cert, size_t certlen, if (kparms) { /* Encode the key. */ - buffer = build_key_sequence (kparms, 0, &buflen); + int i; + + /* Right, that is a stupid way to distinguish ECC from RSA. */ + for (i=0; kparms[i]; i++) + ; + + if (i == 3 && gcry_mpi_get_flag (kparms[0], GCRYMPI_FLAG_OPAQUE)) + buffer = build_ecc_key_sequence (kparms, 0, &buflen); + else + buffer = build_rsa_key_sequence (kparms, 0, &buflen); if (!buffer) goto failure; @@ -2534,101 +3018,28 @@ p12_build (gcry_mpi_t *kparms, const void *cert, size_t certlen, } -/* This is actually not a pkcs#12 function but one which creates an - unencrypted a pkcs#1 private key. */ +/* This is actually not a PKCS#12 function but one which creates an + * unencrypted PKCS#1 private key. */ unsigned char * p12_raw_build (gcry_mpi_t *kparms, int rawmode, size_t *r_length) { unsigned char *buffer; size_t buflen; + int i; - assert (rawmode == 1 || rawmode == 2); - buffer = build_key_sequence (kparms, rawmode, &buflen); + log_assert (rawmode == 1 || rawmode == 2); + + /* Right, that is a stupid way to distinguish ECC from RSA. */ + for (i=0; kparms[i]; i++) + ; + + if (gcry_mpi_get_flag (kparms[0], GCRYMPI_FLAG_OPAQUE)) + buffer = build_ecc_key_sequence (kparms, rawmode, &buflen); + else + buffer = build_rsa_key_sequence (kparms, rawmode, &buflen); if (!buffer) return NULL; *r_length = buflen; return buffer; } - - -#ifdef TEST - -static void -cert_cb (void *opaque, const unsigned char *cert, size_t certlen) -{ - printf ("got a certificate of %u bytes length\n", certlen); -} - -int -main (int argc, char **argv) -{ - FILE *fp; - struct stat st; - unsigned char *buf; - size_t buflen; - gcry_mpi_t *result; - int badpass; - - if (argc != 3) - { - fprintf (stderr, "usage: testp12 file passphrase\n"); - return 1; - } - - gcry_control (GCRYCTL_DISABLE_SECMEM, NULL); - gcry_control (GCRYCTL_INITIALIZATION_FINISHED, NULL); - - fp = gnupg_fopen (argv[1], "rb"); - if (!fp) - { - fprintf (stderr, "can't open '%s': %s\n", argv[1], strerror (errno)); - return 1; - } - - if (fstat (fileno(fp), &st)) - { - fprintf (stderr, "can't stat '%s': %s\n", argv[1], strerror (errno)); - return 1; - } - - buflen = st.st_size; - buf = gcry_malloc (buflen+1); - if (!buf || fread (buf, buflen, 1, fp) != 1) - { - fprintf (stderr, "error reading '%s': %s\n", argv[1], strerror (errno)); - return 1; - } - fclose (fp); - - result = p12_parse (buf, buflen, argv[2], cert_cb, NULL, &badpass); - if (result) - { - int i, rc; - unsigned char *tmpbuf; - - for (i=0; result[i]; i++) - { - rc = gcry_mpi_aprint (GCRYMPI_FMT_HEX, &tmpbuf, - NULL, result[i]); - if (rc) - printf ("%d: [error printing number: %s]\n", - i, gpg_strerror (rc)); - else - { - printf ("%d: %s\n", i, tmpbuf); - gcry_free (tmpbuf); - } - } - } - - return 0; - -} - -/* -Local Variables: -compile-command: "gcc -Wall -O0 -g -DTEST=1 -o minip12 minip12.c ../common/libcommon.a -L /usr/local/lib -lgcrypt -lgpg-error" -End: -*/ -#endif /* TEST */ diff --git a/sm/minip12.h b/sm/minip12.h index 343b733..84c5f5f 100644 --- a/sm/minip12.h +++ b/sm/minip12.h @@ -22,12 +22,13 @@ #include <gcrypt.h> + void p12_set_verbosity (int verbose); gcry_mpi_t *p12_parse (const unsigned char *buffer, size_t length, const char *pw, void (*certcb)(void*, const unsigned char*, size_t), - void *certcbarg, int *r_badpass); + void *certcbarg, int *r_badpass, char **r_curve); unsigned char *p12_build (gcry_mpi_t *kparms, const void *cert, size_t certlen, diff --git a/sm/verify.c b/sm/verify.c index 3c5279e..5510f42 100644 --- a/sm/verify.c +++ b/sm/verify.c @@ -516,6 +516,13 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) && gnupg_digest_is_compliant (CO_DE_VS, sigval_hash_algo)) gpgsm_status (ctrl, STATUS_VERIFICATION_COMPLIANCE_MODE, gnupg_status_compliance_flag (CO_DE_VS)); + else if (opt.require_compliance + && opt.compliance == CO_DE_VS) + { + log_error (_("operation forced to fail due to" + " unfulfilled compliance rules\n")); + gpgsm_errors_seen = 1; + } /* Now we can check the signature. */ |