diff options
author | DongHun Kwak <dh0128.kwak@samsung.com> | 2022-09-16 07:45:20 +0900 |
---|---|---|
committer | DongHun Kwak <dh0128.kwak@samsung.com> | 2022-09-16 07:45:20 +0900 |
commit | 3c74890c721740d405adcfeb89aeecc03ef49e6c (patch) | |
tree | d4f678c2af3256823abe109bfb70dbc197c3f19d /sm | |
parent | b8e317c2a634907810564598cde8cf691ef03d88 (diff) | |
download | gpg2-3c74890c721740d405adcfeb89aeecc03ef49e6c.tar.gz gpg2-3c74890c721740d405adcfeb89aeecc03ef49e6c.tar.bz2 gpg2-3c74890c721740d405adcfeb89aeecc03ef49e6c.zip |
Imported Upstream version 2.3.2upstream/2.3.2
Diffstat (limited to 'sm')
-rw-r--r-- | sm/call-dirmngr.c | 14 | ||||
-rw-r--r-- | sm/certchain.c | 13 | ||||
-rw-r--r-- | sm/certcheck.c | 1 | ||||
-rw-r--r-- | sm/decrypt.c | 548 | ||||
-rw-r--r-- | sm/encrypt.c | 1 | ||||
-rw-r--r-- | sm/export.c | 4 | ||||
-rw-r--r-- | sm/gpgsm.c | 166 | ||||
-rw-r--r-- | sm/gpgsm.h | 15 | ||||
-rw-r--r-- | sm/import.c | 5 | ||||
-rw-r--r-- | sm/keydb.c | 88 | ||||
-rw-r--r-- | sm/keylist.c | 52 | ||||
-rw-r--r-- | sm/server.c | 26 |
12 files changed, 671 insertions, 262 deletions
diff --git a/sm/call-dirmngr.c b/sm/call-dirmngr.c index 8222d99..709f317 100644 --- a/sm/call-dirmngr.c +++ b/sm/call-dirmngr.c @@ -167,7 +167,7 @@ warn_version_mismatch (ctrl_t ctrl, assuan_context_t ctx, static void prepare_dirmngr (ctrl_t ctrl, assuan_context_t ctx, gpg_error_t err) { - struct keyserver_spec *server; + strlist_t server; if (!err) err = warn_version_mismatch (ctrl, ctx, DIRMNGR_NAME, 0); @@ -188,13 +188,13 @@ prepare_dirmngr (ctrl_t ctrl, assuan_context_t ctx, gpg_error_t err) while (server) { char line[ASSUAN_LINELENGTH]; - char *user = server->user ? server->user : ""; - char *pass = server->pass ? server->pass : ""; - char *base = server->base ? server->base : ""; - snprintf (line, DIM (line), "LDAPSERVER %s:%i:%s:%s:%s:%s", - server->host, server->port, user, pass, base, - server->use_ldaps? "ldaps":""); + /* If the host is "ldap" we prefix the entire line with "ldap:" + * to avoid an ambiguity on the server due to the introduction + * of this optional prefix. */ + snprintf (line, DIM (line), "LDAPSERVER %s%s", + !strncmp (server->d, "ldap:", 5)? "ldap:":"", + server->d); assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); /* The code below is not required because we don't return an error. */ diff --git a/sm/certchain.c b/sm/certchain.c index e23a1c4..ee17599 100644 --- a/sm/certchain.c +++ b/sm/certchain.c @@ -459,7 +459,8 @@ find_up_search_by_keyid (ctrl_t ctrl, KEYDB_HANDLE kh, rc = keydb_get_cert (kh, &cert); if (rc) { - log_error ("keydb_get_cert() failed: rc=%d\n", rc); + log_error ("keydb_get_cert failed in %s: %s <%s>\n", + __func__, gpg_strerror (rc), gpg_strsource (rc)); rc = gpg_error (GPG_ERR_NOT_FOUND); goto leave; } @@ -1084,8 +1085,8 @@ gpgsm_walk_cert_chain (ctrl_t ctrl, ksba_cert_t start, ksba_cert_t *r_next) err = keydb_get_cert (kh, r_next); if (err) { - log_error ("keydb_get_cert() failed: %s <%s>\n", - gpg_strerror (err), gpg_strsource (err)); + log_error ("keydb_get_cert failed in %s: %s <%s>\n", + __func__, gpg_strerror (err), gpg_strsource (err)); err = gpg_error (GPG_ERR_GENERAL); } @@ -1824,7 +1825,8 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg, rc = keydb_get_cert (kh, &issuer_cert); if (rc) { - log_error ("keydb_get_cert() failed: rc=%d\n", rc); + log_error ("keydb_get_cert failed in %s: %s <%s>\n", + __func__, gpg_strerror (rc), gpg_strsource (rc)); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } @@ -2253,7 +2255,8 @@ gpgsm_basic_cert_check (ctrl_t ctrl, ksba_cert_t cert) rc = keydb_get_cert (kh, &issuer_cert); if (rc) { - log_error ("keydb_get_cert() failed: rc=%d\n", rc); + log_error ("keydb_get_cert failed in %s: %s <%s>\n", + __func__, gpg_strerror (rc), gpg_strsource (rc)); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } diff --git a/sm/certcheck.c b/sm/certcheck.c index fca4575..f4db858 100644 --- a/sm/certcheck.c +++ b/sm/certcheck.c @@ -294,7 +294,6 @@ extract_pss_params (gcry_sexp_t s_sig, int *r_algo, unsigned int *r_saltlen) if (*r_saltlen < 20) { log_error ("length of PSS salt too short\n"); - gcry_sexp_release (s_sig); return gpg_error (GPG_ERR_DIGEST_ALGO); } if (!*r_algo) diff --git a/sm/decrypt.c b/sm/decrypt.c index aa91b37..1fe2522 100644 --- a/sm/decrypt.c +++ b/sm/decrypt.c @@ -1,7 +1,7 @@ /* decrypt.c - Decrypt a message * Copyright (C) 2001, 2003, 2010 Free Software Foundation, Inc. * Copyright (C) 2001-2019 Werner Koch - * Copyright (C) 2015-2020 g10 Code GmbH + * Copyright (C) 2015-2021 g10 Code GmbH * * This file is part of GnuPG. * @@ -37,6 +37,15 @@ #include "../common/tlv.h" #include "../common/compliance.h" +/* We can provide an enum value which is only availabale with KSBA + * 1.6.0 so that we can compile even against older versions. Some + * calls will of course return an error in this case. This value is + * currently not used because the cipher mode is sufficient here. */ +/* #if KSBA_VERSION_NUMBER < 0x010600 /\* 1.6.0 *\/ */ +/* # define KSBA_CT_AUTHENVELOPED_DATA 10 */ +/* #endif */ + + struct decrypt_filter_parm_s { int algo; @@ -51,6 +60,7 @@ struct decrypt_filter_parm_s char helpblock[16]; /* needed because there is no block buffering in libgcrypt (yet) */ int helpblocklen; + int is_de_vs; /* Helper to track CO_DE_VS state. */ }; @@ -400,6 +410,367 @@ ecdh_decrypt (unsigned char *secret, size_t secretlen, } +/* Helper for pwri_decrypt to parse the derive info. + * Example data for (DER,DERLEN): + * SEQUENCE { + * OCTET STRING + * 60 76 4B E9 5E DF 3C F8 B2 F9 B6 C2 7D 5A FB 90 + * 23 B6 47 DF + * INTEGER 10000 + * SEQUENCE { + * OBJECT IDENTIFIER + * hmacWithSHA512 (1 2 840 113549 2 11) + * NULL + * } + * } + */ +static gpg_error_t +pwri_parse_pbkdf2 (const unsigned char *der, size_t derlen, + unsigned char const **r_salt, unsigned int *r_saltlen, + unsigned long *r_iterations, + int *r_digest) +{ + gpg_error_t err; + size_t objlen, hdrlen; + int class, tag, constructed, ndef; + char *oidstr; + + err = parse_ber_header (&der, &derlen, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > derlen || tag != TAG_SEQUENCE + || !constructed || ndef)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + return err; + derlen = objlen; + + err = parse_ber_header (&der, &derlen, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > derlen || tag != TAG_OCTET_STRING + || constructed || ndef)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + return err; + *r_salt = der; + *r_saltlen = objlen; + der += objlen; + derlen -= objlen; + + err = parse_ber_header (&der, &derlen, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > derlen || tag != TAG_INTEGER + || constructed || ndef)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + return err; + *r_iterations = 0; + for (; objlen; objlen--) + { + *r_iterations <<= 8; + *r_iterations |= (*der++) & 0xff; + derlen--; + } + + err = parse_ber_header (&der, &derlen, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > derlen || tag != TAG_SEQUENCE + || !constructed || ndef)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + return err; + derlen = objlen; + + err = parse_ber_header (&der, &derlen, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > derlen || tag != TAG_OBJECT_ID + || constructed || ndef)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + return err; + + oidstr = ksba_oid_to_str (der, objlen); + if (!oidstr) + return gpg_error_from_syserror (); + *r_digest = gcry_md_map_name (oidstr); + if (*r_digest) + ; + else if (!strcmp (oidstr, "1.2.840.113549.2.7")) + *r_digest = GCRY_MD_SHA1; + else if (!strcmp (oidstr, "1.2.840.113549.2.8")) + *r_digest = GCRY_MD_SHA224; + else if (!strcmp (oidstr, "1.2.840.113549.2.9")) + *r_digest = GCRY_MD_SHA256; + else if (!strcmp (oidstr, "1.2.840.113549.2.10")) + *r_digest = GCRY_MD_SHA384; + else if (!strcmp (oidstr, "1.2.840.113549.2.11")) + *r_digest = GCRY_MD_SHA512; + else + err = gpg_error (GPG_ERR_DIGEST_ALGO); + ksba_free (oidstr); + + return err; +} + + +/* Password based decryption. + * ENC_VAL has the form: + * (enc-val + * (pwri + * (derive-algo <oid>) --| both are optional + * (derive-parm <der>) --| + * (encr-algo <oid>) + * (encr-parm <iv>) + * (encr-key <key>))) -- this is the encrypted session key + * + */ +static gpg_error_t +pwri_decrypt (ctrl_t ctrl, gcry_sexp_t enc_val, + unsigned char **r_result, unsigned int *r_resultlen, + struct decrypt_filter_parm_s *parm) +{ + gpg_error_t err; + gcry_buffer_t ioarray[5] = { {0} }; + char *derive_algo_str = NULL; + char *encr_algo_str = NULL; + const unsigned char *dparm; /* Alias for ioarray[1]. */ + unsigned int dparmlen; + const unsigned char *eparm; /* Alias for ioarray[3]. */ + unsigned int eparmlen; + const unsigned char *ekey; /* Alias for ioarray[4]. */ + unsigned int ekeylen; + unsigned char kek[32]; + unsigned int keklen; + int encr_algo; + enum gcry_cipher_modes encr_mode; + gcry_cipher_hd_t encr_hd = NULL; + unsigned char *result = NULL; + unsigned int resultlen; + unsigned int blklen; + const unsigned char *salt; /* Points int dparm. */ + unsigned int saltlen; + unsigned long iterations; + int digest_algo; + char *passphrase = NULL; + + + *r_resultlen = 0; + *r_result = NULL; + + err = gcry_sexp_extract_param (enc_val, "enc-val!pwri", + "&'derive-algo'?'derive-parm'?" + "'encr-algo''encr-parm''encr-key'", + ioarray+0, ioarray+1, + ioarray+2, ioarray+3, ioarray+4, NULL); + if (err) + { + /* If this is not pwri element, it is likly a kekri element + * which we do not yet support. Change the error back to the + * original as returned by ksba_cms_get_issuer. */ + if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) + err = gpg_error (GPG_ERR_UNSUPPORTED_CMS_OBJ); + else + log_error ("extracting PWRI parameter failed: %s\n", + gpg_strerror (err)); + goto leave; + } + + if (ioarray[0].data) + { + derive_algo_str = string_from_gcry_buffer (ioarray+0); + if (!derive_algo_str) + { + err = gpg_error_from_syserror (); + goto leave; + } + } + dparm = ioarray[1].data; + dparmlen = ioarray[1].len; + encr_algo_str = string_from_gcry_buffer (ioarray+2); + if (!encr_algo_str) + { + err = gpg_error_from_syserror (); + goto leave; + } + eparm = ioarray[3].data; + eparmlen = ioarray[3].len; + ekey = ioarray[4].data; + ekeylen = ioarray[4].len; + + /* Check parameters. */ + if (DBG_CRYPTO) + { + if (derive_algo_str) + { + log_debug ("derive algo: %s\n", derive_algo_str); + log_printhex (dparm, dparmlen, "derive parm:"); + } + log_debug ("encr algo .: %s\n", encr_algo_str); + log_printhex (eparm, eparmlen, "encr parm .:"); + log_printhex (ekey, ekeylen, "encr key .:"); + } + + if (!derive_algo_str) + { + err = gpg_error (GPG_ERR_NOT_SUPPORTED); + log_info ("PWRI with no key derivation detected\n"); + goto leave; + } + if (strcmp (derive_algo_str, "1.2.840.113549.1.5.12")) + { + err = gpg_error (GPG_ERR_NOT_SUPPORTED); + log_info ("PWRI does not use PBKDF2 (but %s)\n", derive_algo_str); + goto leave; + } + + digest_algo = 0; /*(silence cc warning)*/ + err = pwri_parse_pbkdf2 (dparm, dparmlen, + &salt, &saltlen, &iterations, &digest_algo); + if (err) + { + log_error ("parsing PWRI parameter failed: %s\n", gpg_strerror (err)); + goto leave; + } + + parm->is_de_vs = (parm->is_de_vs + && gnupg_digest_is_compliant (CO_DE_VS, digest_algo)); + + + encr_algo = gcry_cipher_map_name (encr_algo_str); + encr_mode = gcry_cipher_mode_from_oid (encr_algo_str); + if (!encr_algo || !encr_mode) + { + log_error ("PWRI uses unknown algorithm %s\n", encr_algo_str); + err = gpg_error (GPG_ERR_CIPHER_ALGO); + goto leave; + } + + parm->is_de_vs = + (parm->is_de_vs + && gnupg_cipher_is_compliant (CO_DE_VS, encr_algo, encr_mode)); + + keklen = gcry_cipher_get_algo_keylen (encr_algo); + blklen = gcry_cipher_get_algo_blklen (encr_algo); + if (!keklen || keklen > sizeof kek || blklen != 16 ) + { + log_error ("PWRI algorithm %s cannot be used\n", encr_algo_str); + err = gpg_error (GPG_ERR_INV_KEYLEN); + goto leave; + } + if ((ekeylen % blklen) || (ekeylen / blklen < 2)) + { + /* Note that we need at least two full blocks. */ + log_error ("PWRI uses a wrong length of encrypted key\n"); + err = gpg_error (GPG_ERR_INV_KEYLEN); + goto leave; + } + + err = gpgsm_agent_ask_passphrase + (ctrl, + i18n_utf8 (N_("Please enter the passphrase for decryption.")), + 0, &passphrase); + if (err) + goto leave; + + err = gcry_kdf_derive (passphrase, strlen (passphrase), + GCRY_KDF_PBKDF2, digest_algo, + salt, saltlen, iterations, + keklen, kek); + if (passphrase) + { + wipememory (passphrase, strlen (passphrase)); + xfree (passphrase); + passphrase = NULL; + } + if (err) + { + log_error ("deriving key from passphrase failed: %s\n", + gpg_strerror (err)); + goto leave; + } + + if (DBG_CRYPTO) + log_printhex (kek, keklen, "KEK .......:"); + + /* Unwrap the key. */ + resultlen = ekeylen; + result = xtrymalloc_secure (resultlen); + if (!result) + { + err = gpg_error_from_syserror (); + goto leave; + } + + err = gcry_cipher_open (&encr_hd, encr_algo, encr_mode, 0); + if (err) + { + log_error ("PWRI failed to open cipher: %s\n", gpg_strerror (err)); + goto leave; + } + + err = gcry_cipher_setkey (encr_hd, kek, keklen); + wipememory (kek, sizeof kek); + if (!err) + err = gcry_cipher_setiv (encr_hd, ekey + ekeylen - 2 * blklen, blklen); + if (!err) + err = gcry_cipher_decrypt (encr_hd, result + ekeylen - blklen, blklen, + ekey + ekeylen - blklen, blklen); + if (!err) + err = gcry_cipher_setiv (encr_hd, result + ekeylen - blklen, blklen); + if (!err) + err = gcry_cipher_decrypt (encr_hd, result, ekeylen - blklen, + ekey, ekeylen - blklen); + /* (We assume that that eparm is the octet string with the IV) */ + if (!err) + err = gcry_cipher_setiv (encr_hd, eparm, eparmlen); + if (!err) + err = gcry_cipher_decrypt (encr_hd, result, resultlen, NULL, 0); + + if (err) + { + log_error ("KEK decryption failed for PWRI: %s\n", gpg_strerror (err)); + goto leave; + } + + if (DBG_CRYPTO) + log_printhex (result, resultlen, "Frame .....:"); + + if (result[0] < 8 /* At least 64 bits */ + || (result[0] % 8) /* Multiple of 64 bits */ + || result[0] > resultlen - 4 /* Not more than the size of the input */ + || ( (result[1] ^ result[4]) /* Matching check bytes. */ + & (result[2] ^ result[5]) + & (result[3] ^ result[6]) ) != 0xff) + { + err = gpg_error (GPG_ERR_BAD_PASSPHRASE); + goto leave; + } + + *r_resultlen = result[0]; + *r_result = memmove (result, result + 4, result[0]); + result = NULL; + + leave: + if (result) + { + wipememory (result, resultlen); + xfree (result); + } + if (passphrase) + { + wipememory (passphrase, strlen (passphrase)); + xfree (passphrase); + } + gcry_cipher_close (encr_hd); + xfree (derive_algo_str); + xfree (encr_algo_str); + xfree (ioarray[0].data); + xfree (ioarray[1].data); + xfree (ioarray[2].data); + xfree (ioarray[3].data); + xfree (ioarray[4].data); + return err; +} + /* Decrypt the session key and fill in the parm structure. The algo and the IV is expected to be already in PARM. */ @@ -411,23 +782,47 @@ prepare_decryption (ctrl_t ctrl, const char *hexkeygrip, { char *seskey = NULL; size_t n, seskeylen; + int pwri = !hexkeygrip && !pk_algo; int rc; if (DBG_CRYPTO) log_printcanon ("decrypting:", enc_val, 0); - rc = gpgsm_agent_pkdecrypt (ctrl, hexkeygrip, desc, enc_val, - &seskey, &seskeylen); - if (rc) + + if (!pwri) { - log_error ("error decrypting session key: %s\n", gpg_strerror (rc)); - goto leave; - } + rc = gpgsm_agent_pkdecrypt (ctrl, hexkeygrip, desc, enc_val, + &seskey, &seskeylen); + if (rc) + { + log_error ("error decrypting session key: %s\n", gpg_strerror (rc)); + goto leave; + } - if (DBG_CRYPTO) - log_printhex (seskey, seskeylen, "DEK frame:"); + if (DBG_CRYPTO) + log_printhex (seskey, seskeylen, "DEK frame:"); + } n=0; - if (pk_algo == GCRY_PK_ECC) + if (pwri) /* Password based encryption. */ + { + gcry_sexp_t s_enc_val; + unsigned char *decrypted; + unsigned int decryptedlen; + + rc = gcry_sexp_sscan (&s_enc_val, NULL, enc_val, + gcry_sexp_canon_len (enc_val, 0, NULL, NULL)); + if (rc) + goto leave; + + rc = pwri_decrypt (ctrl, s_enc_val, &decrypted, &decryptedlen, parm); + gcry_sexp_release (s_enc_val); + if (rc) + goto leave; + xfree (seskey); + seskey = decrypted; + seskeylen = decryptedlen; + } + else if (pk_algo == GCRY_PK_ECC) { gcry_sexp_t s_enc_val; unsigned char *decrypted; @@ -488,7 +883,10 @@ prepare_decryption (ctrl_t ctrl, const char *hexkeygrip, } if (DBG_CRYPTO) - log_printhex (seskey+n, seskeylen-n, "CEK .....:"); + { + log_printhex (seskey+n, seskeylen-n, "CEK .......:"); + log_printhex (parm->iv, parm->ivlen, "IV ........:"); + } if (opt.verbose) log_info (_("%s.%s encrypted data\n"), @@ -515,7 +913,20 @@ prepare_decryption (ctrl_t ctrl, const char *hexkeygrip, goto leave; } - gcry_cipher_setiv (parm->hd, parm->iv, parm->ivlen); + rc = gcry_cipher_setiv (parm->hd, parm->iv, parm->ivlen); + if (rc) + { + log_error("IV setup failed: %s\n", gpg_strerror(rc) ); + goto leave; + } + + if (parm->mode == GCRY_CIPHER_MODE_GCM) + { + /* GCM mode really sucks in CMS. We need to know the AAD before + * we start decrypting but CMS puts the AAD after the content. + * Thus temporary files are required. Let's hope that no real + * messages with actual AAD are ever used. OCB Rules! */ + } leave: xfree (seskey); @@ -616,6 +1027,36 @@ decrypt_filter (void *arg, } +/* This is the GCM version of decrypt_filter. */ +static gpg_error_t +decrypt_gcm_filter (void *arg, + const void *inbuf, size_t inlen, size_t *inused, + void *outbuf, size_t maxoutlen, size_t *outlen) +{ + struct decrypt_filter_parm_s *parm = arg; + + if (!inlen) + return gpg_error (GPG_ERR_BUG); + + if (maxoutlen < parm->blklen) + return gpg_error (GPG_ERR_BUG); + + if (inlen > maxoutlen) + inlen = maxoutlen; + + *inused = inlen; + if (inlen) + { + gcry_cipher_decrypt (parm->hd, outbuf, inlen, inbuf, inlen); + *outlen = inlen; + parm->any_data = 1; + } + else + *outlen = 0; + return 0; +} + + /* Perform a decrypt operation. */ int @@ -704,7 +1145,6 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) int algo, mode; const char *algoid; int any_key = 0; - int is_de_vs; /* Computed compliance with CO_DE_VS. */ audit_log (ctrl->audit, AUDIT_GOT_DATA); @@ -748,14 +1188,17 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) } /* For CMS, CO_DE_VS demands CBC mode. */ - is_de_vs = gnupg_cipher_is_compliant (CO_DE_VS, algo, mode); + dfparm.is_de_vs = gnupg_cipher_is_compliant (CO_DE_VS, algo, mode); audit_log_i (ctrl->audit, AUDIT_DATA_CIPHER_ALGO, algo); dfparm.algo = algo; dfparm.mode = mode; dfparm.blklen = gcry_cipher_get_algo_blklen (algo); if (dfparm.blklen > sizeof (dfparm.helpblock)) - return gpg_error (GPG_ERR_BUG); + { + rc = gpg_error (GPG_ERR_BUG); + goto leave; + } rc = ksba_cms_get_content_enc_iv (cms, dfparm.iv, @@ -781,6 +1224,7 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) ksba_cert_t cert = NULL; unsigned int nbits; int pk_algo = 0; + int maybe_pwri = 0; *kidbuf = 0; @@ -788,9 +1232,15 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) if (tmp_rc == -1 && recp) break; /* no more recipients */ audit_log_i (ctrl->audit, AUDIT_NEW_RECP, recp); - if (tmp_rc) - log_error ("recp %d - error getting info: %s\n", - recp, gpg_strerror (tmp_rc)); + if (gpg_err_code (tmp_rc) == GPG_ERR_UNSUPPORTED_CMS_OBJ) + { + maybe_pwri = 1; + } + else if (tmp_rc) + { + log_error ("recp %d - error getting info: %s\n", + recp, gpg_strerror (tmp_rc)); + } else { if (opt.verbose) @@ -885,29 +1335,37 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) } /* Check that all certs are compliant with CO_DE_VS. */ - is_de_vs = (is_de_vs - && gnupg_pk_is_compliant (CO_DE_VS, pk_algo, 0, - NULL, nbits, NULL)); + dfparm.is_de_vs = + (dfparm.is_de_vs + && gnupg_pk_is_compliant (CO_DE_VS, pk_algo, 0, + NULL, nbits, NULL)); oops: if (rc) { /* We cannot check compliance of certs that we * don't have. */ - is_de_vs = 0; + dfparm.is_de_vs = 0; } xfree (issuer); xfree (serial); ksba_cert_release (cert); } - if (!hexkeygrip || !pk_algo) + if ((!hexkeygrip || !pk_algo) && !maybe_pwri) ; else if (!(enc_val = ksba_cms_get_enc_val (cms, recp))) - log_error ("recp %d - error getting encrypted session key\n", - recp); + { + log_error ("recp %d - error getting encrypted session key\n", + recp); + if (maybe_pwri) + log_info ("(possibly unsupported KEK info)\n"); + } else { + if (maybe_pwri && opt.verbose) + log_info ("recp %d - KEKRI or PWRI\n", recp); + rc = prepare_decryption (ctrl, hexkeygrip, pk_algo, nbits, desc, enc_val, &dfparm); xfree (enc_val); @@ -921,11 +1379,14 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) else { /* setup the bulk decrypter */ any_key = 1; - ksba_writer_set_filter (writer, - decrypt_filter, - &dfparm); - - if (is_de_vs && gnupg_gcrypt_is_compliant (CO_DE_VS)) + ksba_writer_set_filter + (writer, + dfparm.mode == GCRY_CIPHER_MODE_GCM? + decrypt_gcm_filter : decrypt_filter, + &dfparm); + + if (dfparm.is_de_vs + && gnupg_gcrypt_is_compliant (CO_DE_VS)) gpgsm_status (ctrl, STATUS_DECRYPTION_COMPLIANCE_MODE, gnupg_status_compliance_flag (CO_DE_VS)); @@ -977,7 +1438,11 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) else if (stopreason == KSBA_SR_END_DATA) { ksba_writer_set_filter (writer, NULL, NULL); - if (dfparm.any_data) + if (dfparm.mode == GCRY_CIPHER_MODE_GCM) + { + /* Nothing yet to do. We wait for the ready event. */ + } + else if (dfparm.any_data ) { /* write the last block with padding removed */ int i, npadding = dfparm.lastblock[dfparm.blklen-1]; if (!npadding || npadding > dfparm.blklen) @@ -1003,7 +1468,28 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) } } } + else if (stopreason == KSBA_SR_READY) + { + if (dfparm.mode == GCRY_CIPHER_MODE_GCM) + { + char *authtag; + size_t authtaglen; + rc = ksba_cms_get_message_digest (cms, 0, &authtag, &authtaglen); + if (rc) + { + log_error ("error getting authtag: %s\n", gpg_strerror (rc)); + goto leave; + } + if (DBG_CRYPTO) + log_printhex (authtag, authtaglen, "Authtag ...:"); + rc = gcry_cipher_checktag (dfparm.hd, authtag, authtaglen); + xfree (authtag); + if (rc) + log_error ("data is not authentic: %s\n", gpg_strerror (rc)); + goto leave; + } + } } while (stopreason != KSBA_SR_READY); diff --git a/sm/encrypt.c b/sm/encrypt.c index 92ca341..ba2428e 100644 --- a/sm/encrypt.c +++ b/sm/encrypt.c @@ -473,6 +473,7 @@ encrypt_dek (const DEK dek, ksba_cert_t cert, int pk_algo, rc = encode_session_key (dek, &s_data); if (rc) { + gcry_sexp_release (s_pkey); log_error ("encode_session_key failed: %s\n", gpg_strerror (rc)); return rc; } diff --git a/sm/export.c b/sm/export.c index 32f0456..54893b5 100644 --- a/sm/export.c +++ b/sm/export.c @@ -724,8 +724,8 @@ export_p12 (ctrl_t ctrl, const unsigned char *certimg, size_t certimglen, { err = gpgsm_agent_ask_passphrase (ctrl, - i18n_utf8 ("Please enter the passphrase to protect the " - "new PKCS#12 object."), + i18n_utf8 (N_("Please enter the passphrase to protect the " + "new PKCS#12 object.")), 1, &passphrase); if (err) goto leave; @@ -348,8 +348,9 @@ static gpgrt_opt_t opts[] = { 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, "keyserver", + ARGPARSE_s_s (oKeyServer, "ldapserver", N_("|SPEC|use this keyserver to lookup keys")), + ARGPARSE_s_s (oKeyServer, "keyserver", "@"), ARGPARSE_s_n (oUseKeyboxd, "use-keyboxd", "@"), @@ -840,133 +841,6 @@ parse_validation_model (const char *model) } -/* Release the list of SERVERS. As usual it is okay to call this - function with SERVERS passed as NULL. */ -void -keyserver_list_free (struct keyserver_spec *servers) -{ - while (servers) - { - struct keyserver_spec *tmp = servers->next; - xfree (servers->host); - xfree (servers->user); - if (servers->pass) - memset (servers->pass, 0, strlen (servers->pass)); - xfree (servers->pass); - xfree (servers->base); - xfree (servers); - servers = tmp; - } -} - -/* See also dirmngr ldapserver_parse_one(). */ -struct keyserver_spec * -parse_keyserver_line (char *line, - const char *filename, unsigned int lineno) -{ - char *p; - char *endp; - const char *s; - struct keyserver_spec *server; - int fieldno; - int fail = 0; - int i; - - if (!filename) - { - filename = "[cmd]"; - lineno = 0; - } - - /* Parse the colon separated fields. */ - server = xcalloc (1, sizeof *server); - for (fieldno = 1, p = line; p; p = endp, fieldno++ ) - { - endp = strchr (p, ':'); - if (endp) - *endp++ = '\0'; - trim_spaces (p); - switch (fieldno) - { - case 1: - if (*p) - server->host = xstrdup (p); - else - { - log_error (_("%s:%u: no hostname given\n"), - filename, lineno); - fail = 1; - } - break; - - case 2: - if (*p) - server->port = atoi (p); - break; - - case 3: - if (*p) - server->user = xstrdup (p); - break; - - case 4: - if (*p && !server->user) - { - log_error (_("%s:%u: password given without user\n"), - filename, lineno); - fail = 1; - } - else if (*p) - server->pass = xstrdup (p); - break; - - case 5: - if (*p) - server->base = xstrdup (p); - break; - - case 6: - { - char **flags = NULL; - - flags = strtokenize (p, ","); - if (!flags) - log_fatal ("strtokenize failed: %s\n", - gpg_strerror (gpg_error_from_syserror ())); - - for (i=0; (s = flags[i]); i++) - { - if (!*s) - ; - else if (!ascii_strcasecmp (s, "ldaps")) - server->use_ldaps = 1; - else if (!ascii_strcasecmp (s, "ldap")) - server->use_ldaps = 0; - else - log_info (_("%s:%u: ignoring unknown flag '%s'\n"), - filename, lineno, s); - } - - xfree (flags); - } - break; - - default: - /* (We silently ignore extra fields.) */ - break; - } - } - - if (fail) - { - log_info (_("%s:%u: skipping this line\n"), filename, lineno); - keyserver_list_free (server); - server = NULL; - } - - return server; -} - int main ( int argc, char **argv) @@ -1008,7 +882,6 @@ main ( int argc, char **argv) estream_t htmlauditfp = NULL; struct assuan_malloc_hooks malloc_hooks; int pwfd = -1; - int no_logfile = 0; static const char *homedirvalue; static const char *changeuser; @@ -1359,7 +1232,7 @@ main ( int argc, char **argv) break; case oLogFile: logfile = pargs.r.ret_str; break; - case oNoLogFile: logfile = NULL; no_logfile = 1; break; + case oNoLogFile: logfile = NULL; break; case oAuditLog: auditlog = pargs.r.ret_str; break; case oHtmlAuditLog: htmlauditlog = pargs.r.ret_str; break; @@ -1542,21 +1415,7 @@ main ( int argc, char **argv) case oValidationModel: parse_validation_model (pargs.r.ret_str); break; case oKeyServer: - { - struct keyserver_spec *keyserver; - keyserver = parse_keyserver_line (pargs.r.ret_str, - configname, pargs.lineno); - if (! keyserver) - log_error (_("could not parse keyserver\n")); - else - { - /* FIXME: Keep last next pointer. */ - struct keyserver_spec **next_p = &opt.keyserver; - while (*next_p) - next_p = &(*next_p)->next; - *next_p = keyserver; - } - } + append_to_strlist (&opt.keyserver, pargs.r.ret_str); break; case oIgnoreCertExtension: @@ -1626,12 +1485,6 @@ main ( int argc, char **argv) gpgsm_exit(2); } - if (!logfile && !no_logfile) - { - logfile = comopt.logfile; - comopt.logfile = NULL; - } - if (opt.use_keyboxd) log_info ("Note: Please move option \"%s\" to \"common.conf\"\n", "use-keyboxd"); @@ -2072,13 +1925,16 @@ main ( int argc, char **argv) set_binary (stdin); if (!argc) - gpgsm_decrypt (&ctrl, 0, fp); /* from stdin */ + err = gpgsm_decrypt (&ctrl, 0, fp); /* from stdin */ else if (argc == 1) - gpgsm_decrypt (&ctrl, open_read (*argv), fp); /* from file */ + err = gpgsm_decrypt (&ctrl, open_read (*argv), fp); /* from file */ else wrong_args ("--decrypt [filename]"); - es_fclose (fp); + if (err) + gpgrt_fcancel (fp); + else + es_fclose (fp); } break; @@ -2289,7 +2145,7 @@ main ( int argc, char **argv) /* cleanup */ gpgsm_deinit_default_ctrl (&ctrl); - keyserver_list_free (opt.keyserver); + free_strlist (opt.keyserver); opt.keyserver = NULL; gpgsm_release_certlist (recplist); gpgsm_release_certlist (signerlist); @@ -45,19 +45,6 @@ #define MAX_DIGEST_LEN 64 /* Fits for SHA-512 */ -struct keyserver_spec -{ - struct keyserver_spec *next; - - char *host; - int port; - char *user; - char *pass; - char *base; - unsigned int use_ldaps:1; -}; - - /* A large struct named "opt" to keep global flags. */ EXTERN_UNLESS_MAIN_MODULE struct @@ -155,7 +142,7 @@ struct the integrity of the software at runtime. */ - struct keyserver_spec *keyserver; + strlist_t keyserver; /* A list of certificate extension OIDs which are ignored so that one can claim that a critical extension has been handled. One diff --git a/sm/import.c b/sm/import.c index 3d08254..d506913 100644 --- a/sm/import.c +++ b/sm/import.c @@ -459,7 +459,8 @@ reimport_one (ctrl_t ctrl, struct stats_s *stats, int in_fd) err = keydb_get_cert (kh, &cert); if (err) { - log_error ("keydb_get_cert() failed: %s\n", gpg_strerror (err)); + log_error ("keydb_get_cert failed in %s: %s <%s>\n", + __func__, gpg_strerror (err), gpg_strsource (err)); print_import_problem (ctrl, NULL, 1); stats->not_imported++; continue; @@ -771,7 +772,7 @@ parse_p12 (ctrl_t ctrl, ksba_reader_t reader, struct stats_s *stats) err = gpgsm_agent_ask_passphrase (ctrl, - i18n_utf8 ("Please enter the passphrase to unprotect the PKCS#12 object."), + i18n_utf8 (N_("Please enter the passphrase to unprotect the PKCS#12 object.")), 0, &passphrase); if (err) goto leave; @@ -72,6 +72,11 @@ struct keydb_local_s char *buf; size_t len; } search_result; + /* The "stack" used by keydb_push_found_state. */ + struct { + char *buf; + size_t len; + } saved_search_result; /* This flag set while an operation is running on this context. */ unsigned int is_active : 1; @@ -268,7 +273,7 @@ maybe_create_keybox (char *filename, int force, int *r_created) } /* Now the real test while we are locked. */ - if (!access(filename, F_OK)) + if (!gnupg_access(filename, F_OK)) { rc = 0; /* Okay, we may access the file now. */ goto leave; @@ -855,7 +860,7 @@ unlock_all (KEYDB_HANDLE hd) -/* Push the last found state if any. */ +/* Push the last found state if any. Only one state is saved. */ void keydb_push_found_state (KEYDB_HANDLE hd) { @@ -863,25 +868,33 @@ keydb_push_found_state (KEYDB_HANDLE hd) return; if (hd->use_keyboxd) - return; /* FIXME: Do we need this? */ - - if (hd->found < 0 || hd->found >= hd->used) { - hd->saved_found = -1; - return; + xfree (hd->kbl->saved_search_result.buf); + hd->kbl->saved_search_result.buf = hd->kbl->search_result.buf; + hd->kbl->saved_search_result.len = hd->kbl->search_result.len; + hd->kbl->search_result.buf = NULL; + hd->kbl->search_result.len = 0; } - - switch (hd->active[hd->found].type) + else { - case KEYDB_RESOURCE_TYPE_NONE: - break; - case KEYDB_RESOURCE_TYPE_KEYBOX: - keybox_push_found_state (hd->active[hd->found].u.kr); - break; + if (hd->found < 0 || hd->found >= hd->used) + hd->saved_found = -1; + else + { + switch (hd->active[hd->found].type) + { + case KEYDB_RESOURCE_TYPE_NONE: + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: + keybox_push_found_state (hd->active[hd->found].u.kr); + break; + } + + hd->saved_found = hd->found; + hd->found = -1; + } } - hd->saved_found = hd->found; - hd->found = -1; if (DBG_CLOCK) log_clock ("%s: done (hd=%p)\n", __func__, hd); } @@ -895,21 +908,32 @@ keydb_pop_found_state (KEYDB_HANDLE hd) return; if (hd->use_keyboxd) - return; /* FIXME: Do we need this? */ - - hd->found = hd->saved_found; - hd->saved_found = -1; - if (hd->found < 0 || hd->found >= hd->used) - return; - - switch (hd->active[hd->found].type) { - case KEYDB_RESOURCE_TYPE_NONE: - break; - case KEYDB_RESOURCE_TYPE_KEYBOX: - keybox_pop_found_state (hd->active[hd->found].u.kr); - break; + xfree (hd->kbl->search_result.buf); + hd->kbl->search_result.buf = hd->kbl->saved_search_result.buf; + hd->kbl->search_result.len = hd->kbl->saved_search_result.len; + hd->kbl->saved_search_result.buf = NULL; + hd->kbl->saved_search_result.len = 0; + } + else + { + hd->found = hd->saved_found; + hd->saved_found = -1; + if (hd->found < 0 || hd->found >= hd->used) + ; + else + { + switch (hd->active[hd->found].type) + { + case KEYDB_RESOURCE_TYPE_NONE: + break; + case KEYDB_RESOURCE_TYPE_KEYBOX: + keybox_pop_found_state (hd->active[hd->found].u.kr); + break; + } + } } + if (DBG_CLOCK) log_clock ("%s: done (hd=%p)\n", __func__, hd); } @@ -955,9 +979,6 @@ keydb_get_cert (KEYDB_HANDLE hd, ksba_cert_t *r_cert) ksba_cert_release (cert); goto leave; } - xfree (hd->kbl->search_result.buf); - hd->kbl->search_result.buf = NULL; - hd->kbl->search_result.len = 0; *r_cert = cert; goto leave; } @@ -1617,7 +1638,8 @@ keydb_search (ctrl_t ctrl, KEYDB_HANDLE hd, break; case KEYDB_SEARCH_MODE_MAIL: - snprintf (line, sizeof line, "SEARCH --x509 <%s", desc[0].u.name); + snprintf (line, sizeof line, "SEARCH --x509 <%s", + desc[0].u.name + (desc[0].u.name[0] == '<')); break; case KEYDB_SEARCH_MODE_MAILSUB: diff --git a/sm/keylist.c b/sm/keylist.c index 3c9e10c..f571ee2 100644 --- a/sm/keylist.c +++ b/sm/keylist.c @@ -792,6 +792,8 @@ list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd, ksba_name_t name, name2; unsigned int reason; const unsigned char *cert_der = NULL; + char *algostr; + int algoid; (void)have_secret; @@ -845,6 +847,47 @@ list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd, es_fprintf (fp, " md5_fpr: %s\n", dn?dn:"error"); xfree (dn); + algoid = 0; + algostr = gpgsm_pubkey_algo_string (cert, &algoid); + + /* For RSA we support printing an OpenPGP v4 fingerprint under the + * assumption that the not-before date would be used as the OpenPGP + * key creation date. */ + if (algoid == GCRY_PK_RSA) + { + ksba_sexp_t pk; + size_t pklen; + const unsigned char *m, *e; + size_t mlen, elen; + unsigned char fpr20[20]; + time_t tmpt; + unsigned long keytime; + + pk = ksba_cert_get_public_key (cert); + if (pk) + { + ksba_cert_get_validity (cert, 0, t); + tmpt = isotime2epoch (t); + keytime = (tmpt == (time_t)(-1))? 0 : (u32)tmpt; + + pklen = gcry_sexp_canon_len (pk, 0, NULL, NULL); + if (!pklen) + log_error ("libksba did not return a proper S-Exp\n"); + else if (!get_rsa_pk_from_canon_sexp (pk, pklen, + &m, &mlen, &e, &elen) + && !compute_openpgp_fpr_rsa (4, + keytime, + m, mlen, e, elen, + fpr20, NULL)) + { + char *fpr = bin2hex (fpr20, 20, NULL); + es_fprintf (fp, " pgp_fpr: %s\n", fpr); + xfree (fpr); + } + ksba_free (pk); + } + } + dn = gpgsm_get_certid (cert); es_fprintf (fp, " certid: %s\n", dn?dn:"error"); xfree (dn); @@ -866,13 +909,7 @@ list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd, s = get_oid_desc (oid, 0, NULL); es_fprintf (fp, " hashAlgo: %s%s%s%s\n", oid, s?" (":"",s?s:"",s?")":""); - { - char *algostr; - - algostr = gpgsm_pubkey_algo_string (cert, NULL); - es_fprintf (fp, " keyType: %s\n", algostr? algostr : "[error]"); - xfree (algostr); - } + es_fprintf (fp, " keyType: %s\n", algostr? algostr : "[error]"); /* subjectKeyIdentifier */ es_fputs (" subjKeyId: ", fp); @@ -1154,6 +1191,7 @@ list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd, es_fprintf (fp, " [stored as ephemeral]\n"); } + xfree (algostr); } diff --git a/sm/server.c b/sm/server.c index 874f0db..2a6d7c3 100644 --- a/sm/server.c +++ b/sm/server.c @@ -724,8 +724,13 @@ cmd_export (assuan_context_t ctx, char *line) if (opt_secret) { - if (!list || !*list->d) + if (!list) return set_error (GPG_ERR_NO_DATA, "No key given"); + if (!*list->d) + { + free_strlist (list); + return set_error (GPG_ERR_NO_DATA, "No key given"); + } if (list->next) return set_error (GPG_ERR_TOO_MANY, "Only one key allowed"); } @@ -1014,17 +1019,27 @@ do_listkeys (assuan_context_t ctx, char *line, int mode) int outfd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1); if ( outfd == -1 ) - return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL); + { + free_strlist (list); + return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL); + } fp = es_fdopen_nc (outfd, "w"); if (!fp) - return set_error (gpg_err_code_from_syserror (), "es_fdopen() failed"); + { + free_strlist (list); + return set_error (gpg_err_code_from_syserror (), + "es_fdopen() failed"); + } } else { fp = es_fopencookie (ctx, "w", data_line_cookie_functions); if (!fp) - return set_error (GPG_ERR_ASS_GENERAL, - "error setting up a data stream"); + { + free_strlist (list); + return set_error (GPG_ERR_ASS_GENERAL, + "error setting up a data stream"); + } } ctrl->with_colons = 1; @@ -1034,6 +1049,7 @@ do_listkeys (assuan_context_t ctx, char *line, int mode) if (ctrl->server_local->list_external) listmode |= (1<<7); err = gpgsm_list_keys (assuan_get_pointer (ctx), list, fp, listmode); + free_strlist (list); es_fclose (fp); if (ctrl->server_local->list_to_output) |