diff --git a/sm/call-agent.c b/sm/call-agent.c index ec8020517..9287091bc 100644 --- a/sm/call-agent.c +++ b/sm/call-agent.c @@ -1,1488 +1,1487 @@ /* call-agent.c - Divert GPGSM operations to the agent * Copyright (C) 2001, 2002, 2003, 2005, 2007, * 2008, 2009, 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include -#include #ifdef HAVE_LOCALE_H #include #endif #include "gpgsm.h" #include #include #include "../common/i18n.h" #include "../common/asshelp.h" #include "keydb.h" /* fixme: Move this to import.c */ #include "../common/membuf.h" #include "../common/shareddefs.h" #include "passphrase.h" static assuan_context_t agent_ctx = NULL; struct cipher_parm_s { ctrl_t ctrl; assuan_context_t ctx; const unsigned char *ciphertext; size_t ciphertextlen; }; struct genkey_parm_s { ctrl_t ctrl; assuan_context_t ctx; const unsigned char *sexp; size_t sexplen; }; struct learn_parm_s { int error; ctrl_t ctrl; assuan_context_t ctx; membuf_t *data; }; struct import_key_parm_s { ctrl_t ctrl; assuan_context_t ctx; const void *key; size_t keylen; }; struct sethash_inq_parm_s { assuan_context_t ctx; const void *data; size_t datalen; }; struct default_inq_parm_s { ctrl_t ctrl; assuan_context_t ctx; }; /* Print a warning if the server's version number is less than our version number. Returns an error code on a connection problem. */ static gpg_error_t warn_version_mismatch (ctrl_t ctrl, assuan_context_t ctx, const char *servername, int mode) { gpg_error_t err; char *serverversion; const char *myversion = gpgrt_strusage (13); err = get_assuan_server_version (ctx, mode, &serverversion); if (err) log_error (_("error getting version from '%s': %s\n"), servername, gpg_strerror (err)); else if (compare_version_strings (serverversion, myversion) < 0) { char *warn; warn = xtryasprintf (_("server '%s' is older than us (%s < %s)"), servername, serverversion, myversion); if (!warn) err = gpg_error_from_syserror (); else { log_info (_("WARNING: %s\n"), warn); if (!opt.quiet) { log_info (_("Note: Outdated servers may lack important" " security fixes.\n")); log_info (_("Note: Use the command \"%s\" to restart them.\n"), "gpgconf --kill all"); } gpgsm_status2 (ctrl, STATUS_WARNING, "server_version_mismatch 0", warn, NULL); xfree (warn); } } xfree (serverversion); return err; } /* Try to connect to the agent via socket or fork it off and work by pipes. Handle the server's initial greeting */ static int start_agent (ctrl_t ctrl) { int rc; if (agent_ctx) rc = 0; /* fixme: We need a context for each thread or serialize the access to the agent (which is suitable given that the agent is not MT. */ else { rc = start_new_gpg_agent (&agent_ctx, GPG_ERR_SOURCE_DEFAULT, opt.agent_program, opt.lc_ctype, opt.lc_messages, opt.session_env, opt.autostart, opt.verbose, DBG_IPC, gpgsm_status2, ctrl); if (!opt.autostart && gpg_err_code (rc) == GPG_ERR_NO_AGENT) { static int shown; if (!shown) { shown = 1; log_info (_("no gpg-agent running in this session\n")); } } else if (!rc && !(rc = warn_version_mismatch (ctrl, agent_ctx, GPG_AGENT_NAME, 0))) { /* Tell the agent that we support Pinentry notifications. No error checking so that it will work also with older agents. */ assuan_transact (agent_ctx, "OPTION allow-pinentry-notify", NULL, NULL, NULL, NULL, NULL, NULL); /* Pass on the pinentry mode. */ if (opt.pinentry_mode) { char *tmp = xasprintf ("OPTION pinentry-mode=%s", str_pinentry_mode (opt.pinentry_mode)); rc = assuan_transact (agent_ctx, tmp, NULL, NULL, NULL, NULL, NULL, NULL); xfree (tmp); if (rc) log_error ("setting pinentry mode '%s' failed: %s\n", str_pinentry_mode (opt.pinentry_mode), gpg_strerror (rc)); } /* Pass on the request origin. */ if (opt.request_origin) { char *tmp = xasprintf ("OPTION pretend-request-origin=%s", str_request_origin (opt.request_origin)); rc = assuan_transact (agent_ctx, tmp, NULL, NULL, NULL, NULL, NULL, NULL); xfree (tmp); if (rc) log_error ("setting request origin '%s' failed: %s\n", str_request_origin (opt.request_origin), gpg_strerror (rc)); } /* In DE_VS mode under Windows we require that the JENT RNG * is active. */ #ifdef HAVE_W32_SYSTEM if (!rc && opt.compliance == CO_DE_VS) { if (assuan_transact (agent_ctx, "GETINFO jent_active", NULL, NULL, NULL, NULL, NULL, NULL)) { rc = gpg_error (GPG_ERR_FORBIDDEN); log_error (_("%s is not compliant with %s mode\n"), GPG_AGENT_NAME, gnupg_compliance_option_string (opt.compliance)); gpgsm_status_with_error (ctrl, STATUS_ERROR, "random-compliance", rc); } } #endif /*HAVE_W32_SYSTEM*/ } } if (!ctrl->agent_seen) { ctrl->agent_seen = 1; audit_log_ok (ctrl->audit, AUDIT_AGENT_READY, rc); } return rc; } /* This is the default inquiry callback. It mainly handles the Pinentry notifications. */ static gpg_error_t default_inq_cb (void *opaque, const char *line) { gpg_error_t err = 0; struct default_inq_parm_s *parm = opaque; ctrl_t ctrl = parm->ctrl; if (has_leading_keyword (line, "PINENTRY_LAUNCHED")) { err = gpgsm_proxy_pinentry_notify (ctrl, line); if (err) log_error (_("failed to proxy %s inquiry to client\n"), "PINENTRY_LAUNCHED"); /* We do not pass errors to avoid breaking other code. */ } else if ((has_leading_keyword (line, "PASSPHRASE") || has_leading_keyword (line, "NEW_PASSPHRASE")) && opt.pinentry_mode == PINENTRY_MODE_LOOPBACK && have_static_passphrase ()) { const char *s = get_static_passphrase (); err = assuan_send_data (parm->ctx, s, strlen (s)); } else log_error ("ignoring gpg-agent inquiry '%s'\n", line); return err; } /* This is the inquiry callback required by the SETHASH command. */ static gpg_error_t sethash_inq_cb (void *opaque, const char *line) { gpg_error_t err = 0; struct sethash_inq_parm_s *parm = opaque; if (has_leading_keyword (line, "TBSDATA")) { err = assuan_send_data (parm->ctx, parm->data, parm->datalen); } else log_error ("ignoring gpg-agent inquiry '%s'\n", line); return err; } /* Call the agent to do a sign operation using the key identified by * the hex string KEYGRIP. If DIGESTALGO is given (DIGEST,DIGESTLEN) * gives the to be signed hash created using the given algo. If * DIGESTALGO is not given (i.e. zero) (DIGEST,DIGESTALGO) give the * entire data to-be-signed. */ int gpgsm_agent_pksign (ctrl_t ctrl, const char *keygrip, const char *desc, unsigned char *digest, size_t digestlen, int digestalgo, unsigned char **r_buf, size_t *r_buflen ) { int rc, i; char *p, line[ASSUAN_LINELENGTH]; membuf_t data; size_t len; struct default_inq_parm_s inq_parm; *r_buf = NULL; rc = start_agent (ctrl); if (rc) return rc; inq_parm.ctrl = ctrl; inq_parm.ctx = agent_ctx; if (digestalgo && digestlen*2 + 50 > DIM(line)) return gpg_error (GPG_ERR_GENERAL); rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); if (rc) return rc; snprintf (line, DIM(line), "SIGKEY %s", keygrip); rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (rc) return rc; if (desc) { snprintf (line, DIM(line), "SETKEYDESC %s", desc); rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (rc) return rc; } if (!digestalgo) { struct sethash_inq_parm_s sethash_inq_parm; sethash_inq_parm.ctx = agent_ctx; sethash_inq_parm.data = digest; sethash_inq_parm.datalen = digestlen; rc = assuan_transact (agent_ctx, "SETHASH --inquire", NULL, NULL, sethash_inq_cb, &sethash_inq_parm, NULL, NULL); } else { snprintf (line, sizeof line, "SETHASH %d ", digestalgo); p = line + strlen (line); for (i=0; i < digestlen ; i++, p += 2 ) sprintf (p, "%02X", digest[i]); rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); } if (rc) return rc; init_membuf (&data, 1024); rc = assuan_transact (agent_ctx, "PKSIGN", put_membuf_cb, &data, default_inq_cb, &inq_parm, NULL, NULL); if (rc) { xfree (get_membuf (&data, &len)); return rc; } *r_buf = get_membuf (&data, r_buflen); if (!gcry_sexp_canon_len (*r_buf, *r_buflen, NULL, NULL)) { xfree (*r_buf); *r_buf = NULL; return gpg_error (GPG_ERR_INV_VALUE); } return *r_buf? 0 : out_of_core (); } /* Call the scdaemon to do a sign operation using the key identified by the hex string KEYID. */ int gpgsm_scd_pksign (ctrl_t ctrl, const char *keyid, const char *desc, unsigned char *digest, size_t digestlen, int digestalgo, unsigned char **r_buf, size_t *r_buflen ) { int rc, i, pkalgo; char *p, line[ASSUAN_LINELENGTH]; membuf_t data; size_t len; const char *hashopt; unsigned char *sigbuf; size_t sigbuflen; struct default_inq_parm_s inq_parm; gcry_sexp_t sig; (void)desc; *r_buf = NULL; switch(digestalgo) { case GCRY_MD_SHA1: hashopt = "--hash=sha1"; break; case GCRY_MD_RMD160:hashopt = "--hash=rmd160"; break; case GCRY_MD_MD5: hashopt = "--hash=md5"; break; case GCRY_MD_SHA256:hashopt = "--hash=sha256"; break; case GCRY_MD_SHA512:hashopt = "--hash=sha512"; break; default: return gpg_error (GPG_ERR_DIGEST_ALGO); } rc = start_agent (ctrl); if (rc) return rc; inq_parm.ctrl = ctrl; inq_parm.ctx = agent_ctx; if (digestlen*2 + 50 > DIM(line)) return gpg_error (GPG_ERR_GENERAL); /* Get the key type from the scdaemon. */ snprintf (line, DIM(line), "SCD READKEY %s", keyid); init_membuf (&data, 1024); rc = assuan_transact (agent_ctx, line, put_membuf_cb, &data, NULL, NULL, NULL, NULL); if (rc) { xfree (get_membuf (&data, &len)); return rc; } p = get_membuf (&data, &len); pkalgo = get_pk_algo_from_canon_sexp (p, len); xfree (p); if (!pkalgo) return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); p = stpcpy (line, "SCD SETDATA " ); for (i=0; i < digestlen ; i++, p += 2 ) sprintf (p, "%02X", digest[i]); rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (rc) return rc; init_membuf (&data, 1024); snprintf (line, DIM(line), "SCD PKSIGN %s %s", hashopt, keyid); rc = assuan_transact (agent_ctx, line, put_membuf_cb, &data, default_inq_cb, &inq_parm, NULL, NULL); if (rc) { xfree (get_membuf (&data, &len)); return rc; } sigbuf = get_membuf (&data, &sigbuflen); switch(pkalgo) { case GCRY_PK_RSA: rc = gcry_sexp_build (&sig, NULL, "(sig-val(rsa(s%b)))", (int)sigbuflen, sigbuf); break; case GCRY_PK_ECC: rc = gcry_sexp_build (&sig, NULL, "(sig-val(ecdsa(r%b)(s%b)))", (int)sigbuflen/2, sigbuf, (int)sigbuflen/2, sigbuf + sigbuflen/2); break; case GCRY_PK_EDDSA: rc = gcry_sexp_build (&sig, NULL, "(sig-val(eddsa(r%b)(s%b)))", (int)sigbuflen/2, sigbuf, (int)sigbuflen/2, sigbuf + sigbuflen/2); break; default: rc = gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); break; } xfree (sigbuf); if (rc) return rc; rc = make_canon_sexp (sig, r_buf, r_buflen); gcry_sexp_release (sig); if (rc) return rc; - assert (gcry_sexp_canon_len (*r_buf, *r_buflen, NULL, NULL)); + log_assert (gcry_sexp_canon_len (*r_buf, *r_buflen, NULL, NULL)); return 0; } /* Handle a CIPHERTEXT inquiry. Note, we only send the data, assuan_transact takes care of flushing and writing the end */ static gpg_error_t inq_ciphertext_cb (void *opaque, const char *line) { struct cipher_parm_s *parm = opaque; int rc; if (has_leading_keyword (line, "CIPHERTEXT")) { assuan_begin_confidential (parm->ctx); rc = assuan_send_data (parm->ctx, parm->ciphertext, parm->ciphertextlen); assuan_end_confidential (parm->ctx); } else { struct default_inq_parm_s inq_parm = { parm->ctrl, parm->ctx }; rc = default_inq_cb (&inq_parm, line); } return rc; } /* Call the agent to do a decrypt operation using the key identified by the hex string KEYGRIP. */ int gpgsm_agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, ksba_const_sexp_t ciphertext, char **r_buf, size_t *r_buflen ) { int rc; char line[ASSUAN_LINELENGTH]; membuf_t data; struct cipher_parm_s cipher_parm; size_t n, len; char *p, *buf, *endp; size_t ciphertextlen; if (!keygrip || strlen(keygrip) != 40 || !ciphertext || !r_buf || !r_buflen) return gpg_error (GPG_ERR_INV_VALUE); *r_buf = NULL; ciphertextlen = gcry_sexp_canon_len (ciphertext, 0, NULL, NULL); if (!ciphertextlen) return gpg_error (GPG_ERR_INV_VALUE); rc = start_agent (ctrl); if (rc) return rc; rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); if (rc) return rc; - assert ( DIM(line) >= 50 ); + log_assert ( DIM(line) >= 50 ); snprintf (line, DIM(line), "SETKEY %s", keygrip); rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (rc) return rc; if (desc) { snprintf (line, DIM(line), "SETKEYDESC %s", desc); rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (rc) return rc; } init_membuf (&data, 1024); cipher_parm.ctrl = ctrl; cipher_parm.ctx = agent_ctx; cipher_parm.ciphertext = ciphertext; cipher_parm.ciphertextlen = ciphertextlen; rc = assuan_transact (agent_ctx, "PKDECRYPT", put_membuf_cb, &data, inq_ciphertext_cb, &cipher_parm, NULL, NULL); if (rc) { xfree (get_membuf (&data, &len)); return rc; } /* Make sure it is 0 terminated so we can invoke strtoul safely. */ put_membuf (&data, "", 1); buf = get_membuf (&data, &len); if (!buf) return gpg_error (GPG_ERR_ENOMEM); log_assert (len); /* (we forced Nul termination.) */ if (*buf == '(') { if (len < 13 || memcmp (buf, "(5:value", 8) ) /* "(5:valueN:D)\0" */ return gpg_error (GPG_ERR_INV_SEXP); /* Trim any spurious trailing Nuls: */ while (buf[len-1] == 0) len--; if (buf[len-1] != ')') return gpg_error (GPG_ERR_INV_SEXP); len--; /* Drop the final close-paren: */ p = buf + 8; /* Skip leading parenthesis and the value tag. */ len -= 8; /* Count only the data of the second part. */ } else { /* For compatibility with older gpg-agents handle the old style incomplete S-exps. */ len--; /* Do not count the Nul. */ p = buf; } n = strtoul (p, &endp, 10); if (!n || *endp != ':') return gpg_error (GPG_ERR_INV_SEXP); endp++; if (endp-p+n != len) return gpg_error (GPG_ERR_INV_SEXP); /* Oops: Inconsistent S-Exp. */ memmove (buf, endp, n); *r_buflen = n; *r_buf = buf; return 0; } /* Handle a KEYPARMS inquiry. Note, we only send the data, assuan_transact takes care of flushing and writing the end */ static gpg_error_t inq_genkey_parms (void *opaque, const char *line) { struct genkey_parm_s *parm = opaque; int rc; if (has_leading_keyword (line, "KEYPARAM")) { rc = assuan_send_data (parm->ctx, parm->sexp, parm->sexplen); } else { struct default_inq_parm_s inq_parm = { parm->ctrl, parm->ctx }; rc = default_inq_cb (&inq_parm, line); } return rc; } /* Call the agent to generate a newkey */ int gpgsm_agent_genkey (ctrl_t ctrl, ksba_const_sexp_t keyparms, ksba_sexp_t *r_pubkey) { int rc; struct genkey_parm_s gk_parm; membuf_t data; size_t len; unsigned char *buf; *r_pubkey = NULL; rc = start_agent (ctrl); if (rc) return rc; rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); if (rc) return rc; init_membuf (&data, 1024); gk_parm.ctrl = ctrl; gk_parm.ctx = agent_ctx; gk_parm.sexp = keyparms; gk_parm.sexplen = gcry_sexp_canon_len (keyparms, 0, NULL, NULL); if (!gk_parm.sexplen) return gpg_error (GPG_ERR_INV_VALUE); rc = assuan_transact (agent_ctx, "GENKEY", put_membuf_cb, &data, inq_genkey_parms, &gk_parm, NULL, NULL); if (rc) { xfree (get_membuf (&data, &len)); return rc; } buf = get_membuf (&data, &len); if (!buf) return gpg_error (GPG_ERR_ENOMEM); if (!gcry_sexp_canon_len (buf, len, NULL, NULL)) { xfree (buf); return gpg_error (GPG_ERR_INV_SEXP); } *r_pubkey = buf; return 0; } /* Call the agent to read the public key part for a given keygrip. If FROMCARD is true, the key is directly read from the current smartcard. In this case HEXKEYGRIP should be the keyID (e.g. OPENPGP.3). */ int gpgsm_agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip, ksba_sexp_t *r_pubkey) { int rc; membuf_t data; size_t len; unsigned char *buf; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s inq_parm; *r_pubkey = NULL; rc = start_agent (ctrl); if (rc) return rc; inq_parm.ctrl = ctrl; inq_parm.ctx = agent_ctx; rc = assuan_transact (agent_ctx, "RESET",NULL, NULL, NULL, NULL, NULL, NULL); if (rc) return rc; snprintf (line, DIM(line), "%sREADKEY %s", fromcard? "SCD ":"", hexkeygrip); init_membuf (&data, 1024); rc = assuan_transact (agent_ctx, line, put_membuf_cb, &data, default_inq_cb, &inq_parm, NULL, NULL); if (rc) { xfree (get_membuf (&data, &len)); return rc; } buf = get_membuf (&data, &len); if (!buf) return gpg_error (GPG_ERR_ENOMEM); if (!gcry_sexp_canon_len (buf, len, NULL, NULL)) { xfree (buf); return gpg_error (GPG_ERR_INV_SEXP); } *r_pubkey = buf; return 0; } /* Take the serial number from LINE and return it verbatim in a newly allocated string. We make sure that only hex characters are returned. */ static char * store_serialno (const char *line) { const char *s; char *p; for (s=line; hexdigitp (s); s++) ; p = xtrymalloc (s + 1 - line); if (p) { memcpy (p, line, s-line); p[s-line] = 0; } return p; } /* Callback for the gpgsm_agent_serialno function. */ static gpg_error_t scd_serialno_status_cb (void *opaque, const char *line) { char **r_serialno = opaque; const char *keyword = line; int keywordlen; for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) line++; if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen)) { xfree (*r_serialno); *r_serialno = store_serialno (line); } return 0; } /* Call the agent to read the serial number of the current card. */ int gpgsm_agent_scd_serialno (ctrl_t ctrl, char **r_serialno) { int rc; char *serialno = NULL; struct default_inq_parm_s inq_parm; *r_serialno = NULL; rc = start_agent (ctrl); if (rc) return rc; inq_parm.ctrl = ctrl; inq_parm.ctx = agent_ctx; rc = assuan_transact (agent_ctx, "SCD SERIALNO", NULL, NULL, default_inq_cb, &inq_parm, scd_serialno_status_cb, &serialno); if (!rc && !serialno) rc = gpg_error (GPG_ERR_INTERNAL); if (rc) { xfree (serialno); return rc; } *r_serialno = serialno; return 0; } /* Callback for the gpgsm_agent_serialno function. */ static gpg_error_t scd_keypairinfo_status_cb (void *opaque, const char *line) { strlist_t *listaddr = opaque; const char *keyword = line; int keywordlen; strlist_t sl; char *p; for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) line++; if (keywordlen == 11 && !memcmp (keyword, "KEYPAIRINFO", keywordlen)) { sl = append_to_strlist (listaddr, line); p = sl->d; /* Make sure that we only have two tokens so that future * extensions of the format won't change the format expected by * the caller. */ while (*p && !spacep (p)) p++; if (*p) { while (spacep (p)) p++; while (*p && !spacep (p)) p++; if (*p) { *p++ = 0; while (spacep (p)) p++; while (*p && !spacep (p)) { switch (*p++) { case 'c': sl->flags |= GCRY_PK_USAGE_CERT; break; case 's': sl->flags |= GCRY_PK_USAGE_SIGN; break; case 'e': sl->flags |= GCRY_PK_USAGE_ENCR; break; case 'a': sl->flags |= GCRY_PK_USAGE_AUTH; break; } } } } } return 0; } /* Call the agent to read the keypairinfo lines of the current card. The list is returned as a string made up of the keygrip, a space and the keyid. The flags of the string carry the usage bits. */ int gpgsm_agent_scd_keypairinfo (ctrl_t ctrl, strlist_t *r_list) { int rc; strlist_t list = NULL; struct default_inq_parm_s inq_parm; *r_list = NULL; rc = start_agent (ctrl); if (rc) return rc; inq_parm.ctrl = ctrl; inq_parm.ctx = agent_ctx; rc = assuan_transact (agent_ctx, "SCD LEARN --keypairinfo", NULL, NULL, default_inq_cb, &inq_parm, scd_keypairinfo_status_cb, &list); if (!rc && !list) rc = gpg_error (GPG_ERR_NO_DATA); if (rc) { free_strlist (list); return rc; } *r_list = list; return 0; } static gpg_error_t istrusted_status_cb (void *opaque, const char *line) { struct rootca_flags_s *flags = opaque; const char *s; if ((s = has_leading_keyword (line, "TRUSTLISTFLAG"))) { line = s; if (has_leading_keyword (line, "relax")) flags->relax = 1; else if (has_leading_keyword (line, "cm")) flags->chain_model = 1; } return 0; } /* Ask the agent whether the certificate is in the list of trusted keys. The certificate is either specified by the CERT object or by the fingerprint HEXFPR. ROOTCA_FLAGS is guaranteed to be cleared on error. */ int gpgsm_agent_istrusted (ctrl_t ctrl, ksba_cert_t cert, const char *hexfpr, struct rootca_flags_s *rootca_flags) { int rc; char line[ASSUAN_LINELENGTH]; memset (rootca_flags, 0, sizeof *rootca_flags); if (cert && hexfpr) return gpg_error (GPG_ERR_INV_ARG); rc = start_agent (ctrl); if (rc) return rc; if (hexfpr) { snprintf (line, DIM(line), "ISTRUSTED %s", hexfpr); } else { char *fpr; fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); if (!fpr) { log_error ("error getting the fingerprint\n"); return gpg_error (GPG_ERR_GENERAL); } snprintf (line, DIM(line), "ISTRUSTED %s", fpr); xfree (fpr); } rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, istrusted_status_cb, rootca_flags); if (!rc) rootca_flags->valid = 1; return rc; } /* Ask the agent to mark CERT as a trusted Root-CA one */ int gpgsm_agent_marktrusted (ctrl_t ctrl, ksba_cert_t cert) { int rc; char *fpr, *dn, *dnfmt; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s inq_parm; rc = start_agent (ctrl); if (rc) return rc; inq_parm.ctrl = ctrl; inq_parm.ctx = agent_ctx; fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); if (!fpr) { log_error ("error getting the fingerprint\n"); return gpg_error (GPG_ERR_GENERAL); } dn = ksba_cert_get_issuer (cert, 0); if (!dn) { xfree (fpr); return gpg_error (GPG_ERR_GENERAL); } dnfmt = gpgsm_format_name2 (dn, 0); xfree (dn); if (!dnfmt) return gpg_error_from_syserror (); snprintf (line, DIM(line), "MARKTRUSTED %s S %s", fpr, dnfmt); ksba_free (dnfmt); xfree (fpr); rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &inq_parm, NULL, NULL); return rc; } /* Ask the agent whether the a corresponding secret key is available for the given keygrip */ int gpgsm_agent_havekey (ctrl_t ctrl, const char *hexkeygrip) { int rc; char line[ASSUAN_LINELENGTH]; rc = start_agent (ctrl); if (rc) return rc; if (!hexkeygrip || strlen (hexkeygrip) != 40) return gpg_error (GPG_ERR_INV_VALUE); snprintf (line, DIM(line), "HAVEKEY %s", hexkeygrip); rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); return rc; } static gpg_error_t learn_status_cb (void *opaque, const char *line) { struct learn_parm_s *parm = opaque; const char *s; /* Pass progress data to the caller. */ if ((s = has_leading_keyword (line, "PROGRESS"))) { line = s; if (parm->ctrl) { if (gpgsm_status (parm->ctrl, STATUS_PROGRESS, line)) return gpg_error (GPG_ERR_ASS_CANCELED); } } return 0; } static gpg_error_t learn_cb (void *opaque, const void *buffer, size_t length) { struct learn_parm_s *parm = opaque; size_t len; char *buf; ksba_cert_t cert; int rc; if (parm->error) return 0; if (buffer) { put_membuf (parm->data, buffer, length); return 0; } /* END encountered - process what we have */ buf = get_membuf (parm->data, &len); if (!buf) { parm->error = gpg_error (GPG_ERR_ENOMEM); return 0; } if (gpgsm_status (parm->ctrl, STATUS_PROGRESS, "learncard C 0 0")) return gpg_error (GPG_ERR_ASS_CANCELED); /* FIXME: this should go into import.c */ rc = ksba_cert_new (&cert); if (rc) { parm->error = rc; return 0; } rc = ksba_cert_init_from_mem (cert, buf, len); if (rc) { log_error ("failed to parse a certificate: %s\n", gpg_strerror (rc)); ksba_cert_release (cert); parm->error = rc; return 0; } /* 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. */ rc = gpgsm_basic_cert_check (parm->ctrl, cert); if (rc && gpg_err_code (rc) != GPG_ERR_MISSING_CERT && gpg_err_code (rc) != GPG_ERR_MISSING_ISSUER_CERT) log_error ("invalid certificate: %s\n", gpg_strerror (rc)); else { int existed; if (!keydb_store_cert (parm->ctrl, cert, 0, &existed)) { if (opt.verbose > 1 && existed) log_info ("certificate already in DB\n"); else if (opt.verbose && !existed) log_info ("certificate imported\n"); } } ksba_cert_release (cert); init_membuf (parm->data, 4096); return 0; } /* Call the agent to learn about a smartcard */ int gpgsm_agent_learn (ctrl_t ctrl) { int rc; struct learn_parm_s learn_parm; membuf_t data; size_t len; rc = start_agent (ctrl); if (rc) return rc; rc = warn_version_mismatch (ctrl, agent_ctx, SCDAEMON_NAME, 2); if (rc) return rc; init_membuf (&data, 4096); learn_parm.error = 0; learn_parm.ctrl = ctrl; learn_parm.ctx = agent_ctx; learn_parm.data = &data; rc = assuan_transact (agent_ctx, "LEARN --send", learn_cb, &learn_parm, NULL, NULL, learn_status_cb, &learn_parm); xfree (get_membuf (&data, &len)); if (rc) return rc; return learn_parm.error; } /* Ask the agent to change the passphrase of the key identified by HEXKEYGRIP. If DESC is not NULL, display instead of the default description message. */ int gpgsm_agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc) { int rc; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s inq_parm; rc = start_agent (ctrl); if (rc) return rc; inq_parm.ctrl = ctrl; inq_parm.ctx = agent_ctx; if (!hexkeygrip || strlen (hexkeygrip) != 40) return gpg_error (GPG_ERR_INV_VALUE); if (desc) { snprintf (line, DIM(line), "SETKEYDESC %s", desc); rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (rc) return rc; } snprintf (line, DIM(line), "PASSWD %s", hexkeygrip); rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &inq_parm, NULL, NULL); return rc; } /* Ask the agent to pop up a confirmation dialog with the text DESC and an okay and cancel button. */ gpg_error_t gpgsm_agent_get_confirmation (ctrl_t ctrl, const char *desc) { int rc; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s inq_parm; rc = start_agent (ctrl); if (rc) return rc; inq_parm.ctrl = ctrl; inq_parm.ctx = agent_ctx; snprintf (line, DIM(line), "GET_CONFIRMATION %s", desc); rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &inq_parm, NULL, NULL); return rc; } /* Return 0 if the agent is alive. This is useful to make sure that an agent has been started. */ gpg_error_t gpgsm_agent_send_nop (ctrl_t ctrl) { int rc; rc = start_agent (ctrl); if (!rc) rc = assuan_transact (agent_ctx, "NOP", NULL, NULL, NULL, NULL, NULL, NULL); return rc; } static gpg_error_t keyinfo_status_cb (void *opaque, const char *line) { char **serialno = opaque; const char *s, *s2; if ((s = has_leading_keyword (line, "KEYINFO")) && !*serialno) { s = strchr (s, ' '); if (s && s[1] == 'T' && s[2] == ' ' && s[3]) { s += 3; s2 = strchr (s, ' '); if ( s2 > s ) { *serialno = xtrymalloc ((s2 - s)+1); if (*serialno) { memcpy (*serialno, s, s2 - s); (*serialno)[s2 - s] = 0; } } } } return 0; } /* Return the serial number for a secret key. If the returned serial number is NULL, the key is not stored on a smartcard. Caller needs to free R_SERIALNO. */ gpg_error_t gpgsm_agent_keyinfo (ctrl_t ctrl, const char *hexkeygrip, char **r_serialno) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; char *serialno = NULL; *r_serialno = NULL; err = start_agent (ctrl); if (err) return err; if (!hexkeygrip || strlen (hexkeygrip) != 40) return gpg_error (GPG_ERR_INV_VALUE); snprintf (line, DIM(line), "KEYINFO %s", hexkeygrip); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, keyinfo_status_cb, &serialno); if (!err && serialno) { /* Sanity check for bad characters. */ if (strpbrk (serialno, ":\n\r")) err = GPG_ERR_INV_VALUE; } if (err) xfree (serialno); else *r_serialno = serialno; return err; } /* Ask for the passphrase (this is used for pkcs#12 import/export. On success the caller needs to free the string stored at R_PASSPHRASE. On error NULL will be stored at R_PASSPHRASE and an appropriate error code returned. If REPEAT is true the agent tries to get a new passphrase (i.e. asks the user to confirm it). */ gpg_error_t gpgsm_agent_ask_passphrase (ctrl_t ctrl, const char *desc_msg, int repeat, char **r_passphrase) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; char *arg4 = NULL; membuf_t data; struct default_inq_parm_s inq_parm; *r_passphrase = NULL; err = start_agent (ctrl); if (err) return err; inq_parm.ctrl = ctrl; inq_parm.ctx = agent_ctx; if (desc_msg && *desc_msg && !(arg4 = percent_plus_escape (desc_msg))) return gpg_error_from_syserror (); snprintf (line, DIM(line), "GET_PASSPHRASE --data%s -- X X X %s", repeat? " --repeat=1 --check --qualitybar":"", arg4); xfree (arg4); init_membuf_secure (&data, 64); err = assuan_transact (agent_ctx, line, put_membuf_cb, &data, default_inq_cb, &inq_parm, NULL, NULL); if (err) xfree (get_membuf (&data, NULL)); else { put_membuf (&data, "", 1); *r_passphrase = get_membuf (&data, NULL); if (!*r_passphrase) err = gpg_error_from_syserror (); } return err; } /* Retrieve a key encryption key from the agent. With FOREXPORT true the key shall be use for export, with false for import. On success the new key is stored at R_KEY and its length at R_KEKLEN. */ gpg_error_t gpgsm_agent_keywrap_key (ctrl_t ctrl, int forexport, void **r_kek, size_t *r_keklen) { gpg_error_t err; membuf_t data; size_t len; unsigned char *buf; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s inq_parm; *r_kek = NULL; err = start_agent (ctrl); if (err) return err; inq_parm.ctrl = ctrl; inq_parm.ctx = agent_ctx; snprintf (line, DIM(line), "KEYWRAP_KEY %s", forexport? "--export":"--import"); init_membuf_secure (&data, 64); err = assuan_transact (agent_ctx, line, put_membuf_cb, &data, default_inq_cb, &inq_parm, NULL, NULL); if (err) { xfree (get_membuf (&data, &len)); return err; } buf = get_membuf (&data, &len); if (!buf) return gpg_error_from_syserror (); *r_kek = buf; *r_keklen = len; return 0; } /* Handle the inquiry for an IMPORT_KEY command. */ static gpg_error_t inq_import_key_parms (void *opaque, const char *line) { struct import_key_parm_s *parm = opaque; gpg_error_t err; if (has_leading_keyword (line, "KEYDATA")) { assuan_begin_confidential (parm->ctx); err = assuan_send_data (parm->ctx, parm->key, parm->keylen); assuan_end_confidential (parm->ctx); } else { struct default_inq_parm_s inq_parm = { parm->ctrl, parm->ctx }; err = default_inq_cb (&inq_parm, line); } return err; } /* Call the agent to import a key into the agent. */ gpg_error_t gpgsm_agent_import_key (ctrl_t ctrl, const void *key, size_t keylen) { gpg_error_t err; struct import_key_parm_s parm; err = start_agent (ctrl); if (err) return err; parm.ctrl = ctrl; parm.ctx = agent_ctx; parm.key = key; parm.keylen = keylen; err = assuan_transact (agent_ctx, "IMPORT_KEY", NULL, NULL, inq_import_key_parms, &parm, NULL, NULL); return err; } /* Receive a secret key from the agent. KEYGRIP is the hexified keygrip, DESC a prompt to be displayed with the agent's passphrase question (needs to be plus+percent escaped). On success the key is stored as a canonical S-expression at R_RESULT and R_RESULTLEN. */ gpg_error_t gpgsm_agent_export_key (ctrl_t ctrl, const char *keygrip, const char *desc, unsigned char **r_result, size_t *r_resultlen) { gpg_error_t err; membuf_t data; size_t len; unsigned char *buf; char line[ASSUAN_LINELENGTH]; struct default_inq_parm_s inq_parm; *r_result = NULL; err = start_agent (ctrl); if (err) return err; inq_parm.ctrl = ctrl; inq_parm.ctx = agent_ctx; if (desc) { snprintf (line, DIM(line), "SETKEYDESC %s", desc); err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); if (err) return err; } snprintf (line, DIM(line), "EXPORT_KEY %s", keygrip); init_membuf_secure (&data, 1024); err = assuan_transact (agent_ctx, line, put_membuf_cb, &data, default_inq_cb, &inq_parm, NULL, NULL); if (err) { xfree (get_membuf (&data, &len)); return err; } buf = get_membuf (&data, &len); if (!buf) return gpg_error_from_syserror (); *r_result = buf; *r_resultlen = len; return 0; } diff --git a/sm/call-dirmngr.c b/sm/call-dirmngr.c index d9083c9c9..c9ec8f1e7 100644 --- a/sm/call-dirmngr.c +++ b/sm/call-dirmngr.c @@ -1,1069 +1,1068 @@ /* call-dirmngr.c - Communication with the dirmngr * Copyright (C) 2002, 2003, 2005, 2007, 2008, * 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include -#include #include #include "gpgsm.h" #include #include #include "../common/i18n.h" #include "keydb.h" #include "../common/asshelp.h" struct membuf { size_t len; size_t size; char *buf; int out_of_core; }; /* fixme: We need a context for each thread or serialize the access to the dirmngr. */ static assuan_context_t dirmngr_ctx = NULL; static assuan_context_t dirmngr2_ctx = NULL; static int dirmngr_ctx_locked; static int dirmngr2_ctx_locked; struct inq_certificate_parm_s { ctrl_t ctrl; assuan_context_t ctx; ksba_cert_t cert; ksba_cert_t issuer_cert; }; struct isvalid_status_parm_s { ctrl_t ctrl; int seen; unsigned char fpr[20]; }; struct lookup_parm_s { ctrl_t ctrl; assuan_context_t ctx; void (*cb)(void *, ksba_cert_t); void *cb_value; struct membuf data; int error; }; struct run_command_parm_s { ctrl_t ctrl; assuan_context_t ctx; }; static gpg_error_t get_cached_cert (assuan_context_t ctx, const unsigned char *fpr, ksba_cert_t *r_cert); /* A simple implementation of a dynamic buffer. Use init_membuf() to create a buffer, put_membuf to append bytes and get_membuf to release and return the buffer. Allocation errors are detected but only returned at the final get_membuf(), this helps not to clutter the code with out of core checks. */ static void init_membuf (struct membuf *mb, int initiallen) { mb->len = 0; mb->size = initiallen; mb->out_of_core = 0; mb->buf = xtrymalloc (initiallen); if (!mb->buf) mb->out_of_core = 1; } static void put_membuf (struct membuf *mb, const void *buf, size_t len) { if (mb->out_of_core) return; if (mb->len + len >= mb->size) { char *p; mb->size += len + 1024; p = xtryrealloc (mb->buf, mb->size); if (!p) { mb->out_of_core = 1; return; } mb->buf = p; } memcpy (mb->buf + mb->len, buf, len); mb->len += len; } static void * get_membuf (struct membuf *mb, size_t *len) { char *p; if (mb->out_of_core) { xfree (mb->buf); mb->buf = NULL; return NULL; } p = mb->buf; *len = mb->len; mb->buf = NULL; mb->out_of_core = 1; /* don't allow a reuse */ return p; } /* Print a warning if the server's version number is less than our version number. Returns an error code on a connection problem. */ static gpg_error_t warn_version_mismatch (ctrl_t ctrl, assuan_context_t ctx, const char *servername, int mode) { gpg_error_t err; char *serverversion; const char *myversion = gpgrt_strusage (13); err = get_assuan_server_version (ctx, mode, &serverversion); if (err) log_error (_("error getting version from '%s': %s\n"), servername, gpg_strerror (err)); else if (compare_version_strings (serverversion, myversion) < 0) { char *warn; warn = xtryasprintf (_("server '%s' is older than us (%s < %s)"), servername, serverversion, myversion); if (!warn) err = gpg_error_from_syserror (); else { log_info (_("WARNING: %s\n"), warn); if (!opt.quiet) { log_info (_("Note: Outdated servers may lack important" " security fixes.\n")); log_info (_("Note: Use the command \"%s\" to restart them.\n"), "gpgconf --kill all"); } gpgsm_status2 (ctrl, STATUS_WARNING, "server_version_mismatch 0", warn, NULL); xfree (warn); } } xfree (serverversion); return err; } /* This function prepares the dirmngr for a new session. The audit-events option is used so that other dirmngr clients won't get disturbed by such events. */ static void prepare_dirmngr (ctrl_t ctrl, assuan_context_t ctx, gpg_error_t err) { struct keyserver_spec *server; if (!err) err = warn_version_mismatch (ctrl, ctx, DIRMNGR_NAME, 0); if (!err) { err = assuan_transact (ctx, "OPTION audit-events=1", NULL, NULL, NULL, NULL, NULL, NULL); if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION) err = 0; /* Allow the use of old dirmngr versions. */ } audit_log_ok (ctrl->audit, AUDIT_DIRMNGR_READY, err); if (!ctx || err) return; server = opt.keyserver; 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":""); assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); /* The code below is not required because we don't return an error. */ /* err = [above call] */ /* if (gpg_err_code (err) == GPG_ERR_ASS_UNKNOWN_CMD) */ /* err = 0; /\* Allow the use of old dirmngr versions. *\/ */ server = server->next; } } /* Return a new assuan context for a Dirmngr connection. */ static gpg_error_t start_dirmngr_ext (ctrl_t ctrl, assuan_context_t *ctx_r) { gpg_error_t err; assuan_context_t ctx; if (opt.disable_dirmngr || ctrl->offline) return gpg_error (GPG_ERR_NO_DIRMNGR); if (*ctx_r) return 0; /* Note: if you change this to multiple connections, you also need to take care of the implicit option sending caching. */ err = start_new_dirmngr (&ctx, GPG_ERR_SOURCE_DEFAULT, opt.dirmngr_program, opt.autostart, opt.verbose, DBG_IPC, gpgsm_status2, ctrl); if (!opt.autostart && gpg_err_code (err) == GPG_ERR_NO_DIRMNGR) { static int shown; if (!shown) { shown = 1; log_info (_("no dirmngr running in this session\n")); } } prepare_dirmngr (ctrl, ctx, err); if (err) return err; *ctx_r = ctx; return 0; } static int start_dirmngr (ctrl_t ctrl) { gpg_error_t err; - assert (! dirmngr_ctx_locked); + log_assert (! dirmngr_ctx_locked); dirmngr_ctx_locked = 1; err = start_dirmngr_ext (ctrl, &dirmngr_ctx); /* We do not check ERR but the existence of a context because the error might come from a failed command send to the dirmngr. Fixme: Why don't we close the drimngr context if we encountered an error in prepare_dirmngr? */ if (!dirmngr_ctx) dirmngr_ctx_locked = 0; return err; } static void release_dirmngr (ctrl_t ctrl) { (void)ctrl; if (!dirmngr_ctx_locked) log_error ("WARNING: trying to release a non-locked dirmngr ctx\n"); dirmngr_ctx_locked = 0; } static int start_dirmngr2 (ctrl_t ctrl) { gpg_error_t err; - assert (! dirmngr2_ctx_locked); + log_assert (! dirmngr2_ctx_locked); dirmngr2_ctx_locked = 1; err = start_dirmngr_ext (ctrl, &dirmngr2_ctx); if (!dirmngr2_ctx) dirmngr2_ctx_locked = 0; return err; } static void release_dirmngr2 (ctrl_t ctrl) { (void)ctrl; if (!dirmngr2_ctx_locked) log_error ("WARNING: trying to release a non-locked dirmngr2 ctx\n"); dirmngr2_ctx_locked = 0; } /* Handle a SENDCERT inquiry. */ static gpg_error_t inq_certificate (void *opaque, const char *line) { struct inq_certificate_parm_s *parm = opaque; const char *s; int rc; size_t n; const unsigned char *der; size_t derlen; int issuer_mode = 0; ksba_sexp_t ski = NULL; if ((s = has_leading_keyword (line, "SENDCERT"))) { line = s; } else if ((s = has_leading_keyword (line, "SENDCERT_SKI"))) { /* Send a certificate where a sourceKeyIdentifier is included. */ line = s; ski = make_simple_sexp_from_hexstr (line, &n); line += n; while (*line == ' ') line++; } else if ((s = has_leading_keyword (line, "SENDISSUERCERT"))) { line = s; issuer_mode = 1; } else if ((s = has_leading_keyword (line, "ISTRUSTED"))) { /* The server is asking us whether the certificate is a trusted root certificate. */ char fpr[41]; struct rootca_flags_s rootca_flags; line = s; for (s=line,n=0; hexdigitp (s); s++, n++) ; if (*s || n != 40) return gpg_error (GPG_ERR_ASS_PARAMETER); for (s=line, n=0; n < 40; s++, n++) fpr[n] = (*s >= 'a')? (*s & 0xdf): *s; fpr[n] = 0; if (!gpgsm_agent_istrusted (parm->ctrl, NULL, fpr, &rootca_flags)) rc = assuan_send_data (parm->ctx, "1", 1); else rc = 0; return rc; } else { log_error ("unsupported inquiry '%s'\n", line); return gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE); } if (!*line) { /* Send the current certificate. */ der = ksba_cert_get_image (issuer_mode? parm->issuer_cert : parm->cert, &derlen); if (!der) rc = gpg_error (GPG_ERR_INV_CERT_OBJ); else rc = assuan_send_data (parm->ctx, der, derlen); } else if (issuer_mode) { log_error ("sending specific issuer certificate back " "is not yet implemented\n"); rc = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE); } else { /* Send the given certificate. */ int err; ksba_cert_t cert; err = gpgsm_find_cert (parm->ctrl, line, ski, &cert, 1); if (err) { log_error ("certificate not found: %s\n", gpg_strerror (err)); rc = gpg_error (GPG_ERR_NOT_FOUND); } else { der = ksba_cert_get_image (cert, &derlen); if (!der) rc = gpg_error (GPG_ERR_INV_CERT_OBJ); else rc = assuan_send_data (parm->ctx, der, derlen); ksba_cert_release (cert); } } xfree (ski); return rc; } /* Take a 20 byte hexencoded string and put it into the provided 20 byte buffer FPR in binary format. */ static int unhexify_fpr (const char *hexstr, unsigned char *fpr) { const char *s; int n; for (s=hexstr, n=0; hexdigitp (s); s++, n++) ; if (*s || (n != 40)) return 0; /* no fingerprint (invalid or wrong length). */ for (s=hexstr, n=0; *s; s += 2, n++) fpr[n] = xtoi_2 (s); return 1; /* okay */ } static gpg_error_t isvalid_status_cb (void *opaque, const char *line) { struct isvalid_status_parm_s *parm = opaque; const char *s; if ((s = has_leading_keyword (line, "PROGRESS"))) { if (parm->ctrl) { line = s; if (gpgsm_status (parm->ctrl, STATUS_PROGRESS, line)) return gpg_error (GPG_ERR_ASS_CANCELED); } } else if ((s = has_leading_keyword (line, "ONLY_VALID_IF_CERT_VALID"))) { parm->seen++; if (!*s || !unhexify_fpr (s, parm->fpr)) parm->seen++; /* Bump it to indicate an error. */ } return 0; } /* Call the directory manager to check whether the certificate is valid Returns 0 for valid or usually one of the errors: GPG_ERR_CERTIFICATE_REVOKED GPG_ERR_NO_CRL_KNOWN GPG_ERR_CRL_TOO_OLD Values for USE_OCSP: 0 = Do CRL check. 1 = Do an OCSP check but fallback to CRL unless CRLS are disabled. 2 = Do only an OCSP check using only the default responder. */ int gpgsm_dirmngr_isvalid (ctrl_t ctrl, ksba_cert_t cert, ksba_cert_t issuer_cert, int use_ocsp) { static int did_options; int rc; char *certid, *certfpr; char line[ASSUAN_LINELENGTH]; struct inq_certificate_parm_s parm; struct isvalid_status_parm_s stparm; rc = start_dirmngr (ctrl); if (rc) return rc; certfpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); certid = gpgsm_get_certid (cert); if (!certid) { log_error ("error getting the certificate ID\n"); release_dirmngr (ctrl); return gpg_error (GPG_ERR_GENERAL); } if (opt.verbose > 1) { char *fpr = gpgsm_get_fingerprint_string (cert, GCRY_MD_SHA1); log_info ("asking dirmngr about %s%s\n", fpr, use_ocsp? " (using OCSP)":""); xfree (fpr); } parm.ctx = dirmngr_ctx; parm.ctrl = ctrl; parm.cert = cert; parm.issuer_cert = issuer_cert; stparm.ctrl = ctrl; stparm.seen = 0; memset (stparm.fpr, 0, 20); /* It is sufficient to send the options only once because we have * one connection per process only. */ if (!did_options) { if (opt.force_crl_refresh) assuan_transact (dirmngr_ctx, "OPTION force-crl-refresh=1", NULL, NULL, NULL, NULL, NULL, NULL); did_options = 1; } snprintf (line, DIM(line), "ISVALID%s%s %s%s%s", use_ocsp == 2 || opt.no_crl_check ? " --only-ocsp":"", use_ocsp == 2? " --force-default-responder":"", certid, use_ocsp? " ":"", use_ocsp? certfpr:""); xfree (certid); xfree (certfpr); rc = assuan_transact (dirmngr_ctx, line, NULL, NULL, inq_certificate, &parm, isvalid_status_cb, &stparm); if (opt.verbose > 1) log_info ("response of dirmngr: %s\n", rc? gpg_strerror (rc): "okay"); if (!rc && stparm.seen) { /* Need to also check the certificate validity. */ if (stparm.seen != 1) { log_error ("communication problem with dirmngr detected\n"); rc = gpg_error (GPG_ERR_INV_CRL); } else { ksba_cert_t rspcert = NULL; if (get_cached_cert (dirmngr_ctx, stparm.fpr, &rspcert)) { /* Ooops: Something went wrong getting the certificate from the dirmngr. Try our own cert store now. */ KEYDB_HANDLE kh; kh = keydb_new (); if (!kh) rc = gpg_error (GPG_ERR_ENOMEM); if (!rc) rc = keydb_search_fpr (ctrl, kh, stparm.fpr); if (!rc) rc = keydb_get_cert (kh, &rspcert); if (rc) { log_error ("unable to find the certificate used " "by the dirmngr: %s\n", gpg_strerror (rc)); rc = gpg_error (GPG_ERR_INV_CRL); } keydb_release (kh); } if (!rc) { rc = gpgsm_cert_use_ocsp_p (rspcert); if (rc) rc = gpg_error (GPG_ERR_INV_CRL); else { /* Note the no_dirmngr flag: This avoids checking this certificate over and over again. */ rc = gpgsm_validate_chain (ctrl, rspcert, "", NULL, 0, NULL, VALIDATE_FLAG_NO_DIRMNGR, NULL); if (rc) { log_error ("invalid certificate used for CRL/OCSP: %s\n", gpg_strerror (rc)); rc = gpg_error (GPG_ERR_INV_CRL); } } } ksba_cert_release (rspcert); } } release_dirmngr (ctrl); return rc; } /* Lookup helpers*/ static gpg_error_t lookup_cb (void *opaque, const void *buffer, size_t length) { struct lookup_parm_s *parm = opaque; size_t len; char *buf; ksba_cert_t cert; int rc; if (parm->error) return 0; if (buffer) { put_membuf (&parm->data, buffer, length); return 0; } /* END encountered - process what we have */ buf = get_membuf (&parm->data, &len); if (!buf) { parm->error = gpg_error (GPG_ERR_ENOMEM); return 0; } rc = ksba_cert_new (&cert); if (rc) { parm->error = rc; return 0; } rc = ksba_cert_init_from_mem (cert, buf, len); if (rc) { log_error ("failed to parse a certificate: %s\n", gpg_strerror (rc)); } else { parm->cb (parm->cb_value, cert); } ksba_cert_release (cert); init_membuf (&parm->data, 4096); return 0; } /* Return a properly escaped pattern from NAMES. The only error return is NULL to indicate a malloc failure. */ static char * pattern_from_strlist (strlist_t names) { strlist_t sl; int n; const char *s; char *pattern, *p; for (n=0, sl=names; sl; sl = sl->next) { for (s=sl->d; *s; s++, n++) { if (*s == '%' || *s == ' ' || *s == '+') n += 2; } n++; } p = pattern = xtrymalloc (n+1); if (!pattern) return NULL; for (sl=names; sl; sl = sl->next) { for (s=sl->d; *s; s++) { switch (*s) { case '%': *p++ = '%'; *p++ = '2'; *p++ = '5'; break; case ' ': *p++ = '%'; *p++ = '2'; *p++ = '0'; break; case '+': *p++ = '%'; *p++ = '2'; *p++ = 'B'; break; default: *p++ = *s; break; } } *p++ = ' '; } if (p == pattern) *pattern = 0; /* is empty */ else p[-1] = '\0'; /* remove trailing blank */ return pattern; } static gpg_error_t lookup_status_cb (void *opaque, const char *line) { struct lookup_parm_s *parm = opaque; const char *s; if ((s = has_leading_keyword (line, "PROGRESS"))) { if (parm->ctrl) { line = s; if (gpgsm_status (parm->ctrl, STATUS_PROGRESS, line)) return gpg_error (GPG_ERR_ASS_CANCELED); } } else if ((s = has_leading_keyword (line, "TRUNCATED"))) { if (parm->ctrl) { line = s; gpgsm_status (parm->ctrl, STATUS_TRUNCATED, line); } } return 0; } /* Run the Directory Manager's lookup command using the pattern compiled from the strings given in NAMES or from URI. The caller must provide the callback CB which will be passed cert by cert. Note that CTRL is optional. With CACHE_ONLY the dirmngr will search only its own key cache. */ int gpgsm_dirmngr_lookup (ctrl_t ctrl, strlist_t names, const char *uri, int cache_only, void (*cb)(void*, ksba_cert_t), void *cb_value) { int rc; char line[ASSUAN_LINELENGTH]; struct lookup_parm_s parm; size_t len; assuan_context_t ctx; const char *s; if ((names && uri) || (!names && !uri)) return gpg_error (GPG_ERR_INV_ARG); /* The lookup function can be invoked from the callback of a lookup function, for example to walk the chain. */ if (!dirmngr_ctx_locked) { rc = start_dirmngr (ctrl); if (rc) return rc; ctx = dirmngr_ctx; } else if (!dirmngr2_ctx_locked) { rc = start_dirmngr2 (ctrl); if (rc) return rc; ctx = dirmngr2_ctx; } else { log_fatal ("both dirmngr contexts are in use\n"); } if (names) { char *pattern = pattern_from_strlist (names); if (!pattern) { if (ctx == dirmngr_ctx) release_dirmngr (ctrl); else release_dirmngr2 (ctrl); return out_of_core (); } snprintf (line, DIM(line), "LOOKUP%s %s", cache_only? " --cache-only":"", pattern); xfree (pattern); } else { for (s=uri; *s; s++) if (*s <= ' ') { if (ctx == dirmngr_ctx) release_dirmngr (ctrl); else release_dirmngr2 (ctrl); return gpg_error (GPG_ERR_INV_URI); } snprintf (line, DIM(line), "LOOKUP --url %s", uri); } parm.ctrl = ctrl; parm.ctx = ctx; parm.cb = cb; parm.cb_value = cb_value; parm.error = 0; init_membuf (&parm.data, 4096); rc = assuan_transact (ctx, line, lookup_cb, &parm, NULL, NULL, lookup_status_cb, &parm); xfree (get_membuf (&parm.data, &len)); if (ctx == dirmngr_ctx) release_dirmngr (ctrl); else release_dirmngr2 (ctrl); if (rc) return rc; return parm.error; } static gpg_error_t get_cached_cert_data_cb (void *opaque, const void *buffer, size_t length) { struct membuf *mb = opaque; if (buffer) put_membuf (mb, buffer, length); return 0; } /* Return a certificate from the Directory Manager's cache. This function only returns one certificate which must be specified using the fingerprint FPR and will be stored at R_CERT. On error NULL is stored at R_CERT and an error code returned. Note that the caller must provide the locked dirmngr context CTX. */ static gpg_error_t get_cached_cert (assuan_context_t ctx, const unsigned char *fpr, ksba_cert_t *r_cert) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; char hexfpr[2*20+1]; struct membuf mb; char *buf; size_t buflen = 0; ksba_cert_t cert; *r_cert = NULL; bin2hex (fpr, 20, hexfpr); snprintf (line, DIM(line), "LOOKUP --single --cache-only 0x%s", hexfpr); init_membuf (&mb, 4096); err = assuan_transact (ctx, line, get_cached_cert_data_cb, &mb, NULL, NULL, NULL, NULL); buf = get_membuf (&mb, &buflen); if (err) { xfree (buf); return err; } if (!buf) return gpg_error (GPG_ERR_ENOMEM); err = ksba_cert_new (&cert); if (err) { xfree (buf); return err; } err = ksba_cert_init_from_mem (cert, buf, buflen); xfree (buf); if (err) { log_error ("failed to parse a certificate: %s\n", gpg_strerror (err)); ksba_cert_release (cert); return err; } *r_cert = cert; return 0; } /* Run Command helpers*/ /* Fairly simple callback to write all output of dirmngr to stdout. */ static gpg_error_t run_command_cb (void *opaque, const void *buffer, size_t length) { (void)opaque; if (buffer) { if ( fwrite (buffer, length, 1, stdout) != 1 ) log_error ("error writing to stdout: %s\n", strerror (errno)); } return 0; } /* Handle inquiries from the dirmngr COMMAND. */ static gpg_error_t run_command_inq_cb (void *opaque, const char *line) { struct run_command_parm_s *parm = opaque; const char *s; int rc = 0; if ((s = has_leading_keyword (line, "SENDCERT"))) { /* send the given certificate */ int err; ksba_cert_t cert; const unsigned char *der; size_t derlen; line = s; if (!*line) return gpg_error (GPG_ERR_ASS_PARAMETER); err = gpgsm_find_cert (parm->ctrl, line, NULL, &cert, 1); if (err) { log_error ("certificate not found: %s\n", gpg_strerror (err)); rc = gpg_error (GPG_ERR_NOT_FOUND); } else { der = ksba_cert_get_image (cert, &derlen); if (!der) rc = gpg_error (GPG_ERR_INV_CERT_OBJ); else rc = assuan_send_data (parm->ctx, der, derlen); ksba_cert_release (cert); } } else if ((s = has_leading_keyword (line, "PRINTINFO"))) { /* Simply show the message given in the argument. */ line = s; log_info ("dirmngr: %s\n", line); } else { log_error ("unsupported inquiry '%s'\n", line); rc = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE); } return rc; } static gpg_error_t run_command_status_cb (void *opaque, const char *line) { ctrl_t ctrl = opaque; const char *s; if (opt.verbose) { log_info ("dirmngr status: %s\n", line); } if ((s = has_leading_keyword (line, "PROGRESS"))) { if (ctrl) { line = s; if (gpgsm_status (ctrl, STATUS_PROGRESS, line)) return gpg_error (GPG_ERR_ASS_CANCELED); } } return 0; } /* Pass COMMAND to dirmngr and print all output generated by Dirmngr to stdout. A couple of inquiries are defined (see above). ARGC arguments in ARGV are given to the Dirmngr. Spaces, plus and percent characters within the argument strings are percent escaped so that blanks can act as delimiters. */ int gpgsm_dirmngr_run_command (ctrl_t ctrl, const char *command, int argc, char **argv) { int rc; int i; const char *s; char *line, *p; size_t len; struct run_command_parm_s parm; rc = start_dirmngr (ctrl); if (rc) return rc; parm.ctrl = ctrl; parm.ctx = dirmngr_ctx; len = strlen (command) + 1; for (i=0; i < argc; i++) len += 1 + 3*strlen (argv[i]); /* enough space for percent escaping */ line = xtrymalloc (len); if (!line) { release_dirmngr (ctrl); return out_of_core (); } p = stpcpy (line, command); for (i=0; i < argc; i++) { *p++ = ' '; for (s=argv[i]; *s; s++) { if (!isascii (*s)) *p++ = *s; else if (*s == ' ') *p++ = '+'; else if (!isprint (*s) || *s == '+') { sprintf (p, "%%%02X", *(const unsigned char *)s); p += 3; } else *p++ = *s; } } *p = 0; rc = assuan_transact (dirmngr_ctx, line, run_command_cb, NULL, run_command_inq_cb, &parm, run_command_status_cb, ctrl); xfree (line); log_info ("response of dirmngr: %s\n", rc? gpg_strerror (rc): "okay"); release_dirmngr (ctrl); return rc; } diff --git a/sm/certchain.c b/sm/certchain.c index 1555a84a7..d92b2e833 100644 --- a/sm/certchain.c +++ b/sm/certchain.c @@ -1,2381 +1,2380 @@ /* certchain.c - certificate chain validation * Copyright (C) 2001, 2002, 2003, 2004, 2005, * 2006, 2007, 2008, 2011 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include #include -#include #include "gpgsm.h" #include #include #include "keydb.h" #include "../kbx/keybox.h" /* for KEYBOX_FLAG_* */ #include "../common/i18n.h" #include "../common/tlv.h" /* The OID for the authorityInfoAccess's caIssuers. */ static const char oidstr_caIssuers[] = "1.3.6.1.5.5.7.48.2"; /* Object to keep track of certain root certificates. */ struct marktrusted_info_s { struct marktrusted_info_s *next; unsigned char fpr[20]; }; static struct marktrusted_info_s *marktrusted_info; /* While running the validation function we want to keep track of the certificates in the chain. This type is used for that. */ struct chain_item_s { struct chain_item_s *next; ksba_cert_t cert; /* The certificate. */ int is_root; /* The certificate is the root certificate. */ }; typedef struct chain_item_s *chain_item_t; static int is_root_cert (ksba_cert_t cert, const char *issuerdn, const char *subjectdn); static int get_regtp_ca_info (ctrl_t ctrl, ksba_cert_t cert, int *chainlen); /* This function returns true if we already asked during this session whether the root certificate CERT shall be marked as trusted. */ static int already_asked_marktrusted (ksba_cert_t cert) { unsigned char fpr[20]; struct marktrusted_info_s *r; gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, fpr, NULL); /* No context switches in the loop! */ for (r=marktrusted_info; r; r= r->next) if (!memcmp (r->fpr, fpr, 20)) return 1; return 0; } /* Flag certificate CERT as already asked whether it shall be marked as trusted. */ static void set_already_asked_marktrusted (ksba_cert_t cert) { unsigned char fpr[20]; struct marktrusted_info_s *r; gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, fpr, NULL); for (r=marktrusted_info; r; r= r->next) if (!memcmp (r->fpr, fpr, 20)) return; /* Already marked. */ r = xtrycalloc (1, sizeof *r); if (!r) return; memcpy (r->fpr, fpr, 20); r->next = marktrusted_info; marktrusted_info = r; } /* If LISTMODE is true, print FORMAT using LISTMODE to FP. If LISTMODE is false, use the string to print an log_info or, if IS_ERROR is true, and log_error. */ static void do_list (int is_error, int listmode, estream_t fp, const char *format, ...) { va_list arg_ptr; va_start (arg_ptr, format) ; if (listmode) { if (fp) { es_fputs (" [", fp); es_vfprintf (fp, format, arg_ptr); es_fputs ("]\n", fp); } } else { log_logv (is_error? GPGRT_LOGLVL_ERROR: GPGRT_LOGLVL_INFO, format, arg_ptr); log_printf ("\n"); } va_end (arg_ptr); } /* Return 0 if A and B are equal. */ static int compare_certs (ksba_cert_t a, ksba_cert_t b) { const unsigned char *img_a, *img_b; size_t len_a, len_b; img_a = ksba_cert_get_image (a, &len_a); if (!img_a) return 1; img_b = ksba_cert_get_image (b, &len_b); if (!img_b) return 1; return !(len_a == len_b && !memcmp (img_a, img_b, len_a)); } /* Return true if CERT has the validityModel extensions and defines the use of the chain model. */ static int has_validation_model_chain (ksba_cert_t cert, int listmode, estream_t listfp) { gpg_error_t err; int idx, yes; const char *oid; size_t off, derlen, objlen, hdrlen; const unsigned char *der; int class, tag, constructed, ndef; char *oidbuf; for (idx=0; !(err=ksba_cert_get_extension (cert, idx, &oid, NULL, &off, &derlen));idx++) if (!strcmp (oid, "1.3.6.1.4.1.8301.3.5") ) break; if (err) return 0; /* Not found. */ der = ksba_cert_get_image (cert, NULL); if (!der) { err = gpg_error (GPG_ERR_INV_OBJ); /* Oops */ goto leave; } der += off; err = parse_ber_header (&der, &derlen, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > derlen || tag != TAG_SEQUENCE)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto leave; derlen = objlen; err = parse_ber_header (&der, &derlen, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > derlen || tag != TAG_OBJECT_ID)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) goto leave; oidbuf = ksba_oid_to_str (der, objlen); if (!oidbuf) { err = gpg_error_from_syserror (); goto leave; } if (opt.verbose) do_list (0, listmode, listfp, _("validation model requested by certificate: %s"), !strcmp (oidbuf, "1.3.6.1.4.1.8301.3.5.1")? _("chain") : !strcmp (oidbuf, "1.3.6.1.4.1.8301.3.5.2")? _("shell") : /* */ oidbuf); yes = !strcmp (oidbuf, "1.3.6.1.4.1.8301.3.5.1"); ksba_free (oidbuf); return yes; leave: log_error ("error parsing validityModel: %s\n", gpg_strerror (err)); return 0; } static int unknown_criticals (ksba_cert_t cert, int listmode, estream_t fp) { static const char *known[] = { "2.5.29.15", /* keyUsage */ "2.5.29.17", /* subjectAltName Japanese DoCoMo certs mark them as critical. PKIX only requires them as critical if subjectName is empty. I don't know whether our code gracefully handles such empry subjectNames but that is another story. */ "2.5.29.19", /* basic Constraints */ "2.5.29.32", /* certificatePolicies */ "2.5.29.37", /* extendedKeyUsage - handled by certlist.c */ "1.3.6.1.4.1.8301.3.5", /* validityModel - handled here. */ NULL }; int rc = 0, i, idx, crit; const char *oid; gpg_error_t err; int unsupported; strlist_t sl; for (idx=0; !(err=ksba_cert_get_extension (cert, idx, &oid, &crit, NULL, NULL));idx++) { if (!crit) continue; for (i=0; known[i] && strcmp (known[i],oid); i++) ; unsupported = !known[i]; /* If this critical extension is not supported. Check the list of to be ignored extensions to see whether we claim that it is supported. */ if (unsupported && opt.ignored_cert_extensions) { for (sl=opt.ignored_cert_extensions; sl && strcmp (sl->d, oid); sl = sl->next) ; if (sl) unsupported = 0; } if (unsupported) { do_list (1, listmode, fp, _("critical certificate extension %s is not supported"), oid); rc = gpg_error (GPG_ERR_UNSUPPORTED_CERT); } } /* We ignore the error codes EOF as well as no-value. The later will occur for certificates with no extensions at all. */ if (err && gpg_err_code (err) != GPG_ERR_EOF && gpg_err_code (err) != GPG_ERR_NO_VALUE) rc = err; return rc; } /* Check whether CERT is an allowed certificate. This requires that CERT matches all requirements for such a CA, i.e. the BasicConstraints extension. The function returns 0 on success and the allowed length of the chain at CHAINLEN. */ static int allowed_ca (ctrl_t ctrl, ksba_cert_t cert, int *chainlen, int listmode, estream_t fp) { gpg_error_t err; int flag; err = ksba_cert_is_ca (cert, &flag, chainlen); if (err) return err; if (!flag) { if (get_regtp_ca_info (ctrl, cert, chainlen)) { /* Note that dirmngr takes a different way to cope with such certs. */ return 0; /* RegTP issued certificate. */ } do_list (1, listmode, fp,_("issuer certificate is not marked as a CA")); return gpg_error (GPG_ERR_BAD_CA_CERT); } return 0; } static int check_cert_policy (ksba_cert_t cert, int listmode, estream_t fplist) { gpg_error_t err; char *policies; FILE *fp; int any_critical; err = ksba_cert_get_cert_policies (cert, &policies); if (gpg_err_code (err) == GPG_ERR_NO_DATA) return 0; /* No policy given. */ if (err) return err; /* STRING is a line delimited list of certificate policies as stored in the certificate. The line itself is colon delimited where the first field is the OID of the policy and the second field either N or C for normal or critical extension */ if (opt.verbose > 1 && !listmode) log_info ("certificate's policy list: %s\n", policies); /* The check is very minimal but won't give false positives */ any_critical = !!strstr (policies, ":C"); if (!opt.policy_file) { xfree (policies); if (any_critical) { do_list (1, listmode, fplist, _("critical marked policy without configured policies")); return gpg_error (GPG_ERR_NO_POLICY_MATCH); } return 0; } fp = fopen (opt.policy_file, "r"); if (!fp) { if (opt.verbose || errno != ENOENT) log_info (_("failed to open '%s': %s\n"), opt.policy_file, strerror (errno)); xfree (policies); /* With no critical policies this is only a warning */ if (!any_critical) { if (!opt.quiet) do_list (0, listmode, fplist, _("Note: non-critical certificate policy not allowed")); return 0; } do_list (1, listmode, fplist, _("certificate policy not allowed")); return gpg_error (GPG_ERR_NO_POLICY_MATCH); } for (;;) { int c; char *p, line[256]; char *haystack, *allowed; /* read line */ do { if (!fgets (line, DIM(line)-1, fp) ) { gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); xfree (policies); if (feof (fp)) { fclose (fp); /* With no critical policies this is only a warning */ if (!any_critical) { do_list (0, listmode, fplist, _("Note: non-critical certificate policy not allowed")); return 0; } do_list (1, listmode, fplist, _("certificate policy not allowed")); return gpg_error (GPG_ERR_NO_POLICY_MATCH); } fclose (fp); return tmperr; } if (!*line || line[strlen(line)-1] != '\n') { /* eat until end of line */ while ( (c=getc (fp)) != EOF && c != '\n') ; fclose (fp); xfree (policies); return gpg_error (*line? GPG_ERR_LINE_TOO_LONG : GPG_ERR_INCOMPLETE_LINE); } /* Allow for empty lines and spaces */ for (p=line; spacep (p); p++) ; } while (!*p || *p == '\n' || *p == '#'); /* Parse line. Note that the line has always a LF and spacep does not consider a LF a space. Thus strpbrk will always succeed. */ for (allowed=line; spacep (allowed); allowed++) ; p = strpbrk (allowed, " :\n"); if (!*p || p == allowed) { fclose (fp); xfree (policies); return gpg_error (GPG_ERR_CONFIGURATION); } *p = 0; /* strip the rest of the line */ /* See whether we find ALLOWED (which is an OID) in POLICIES */ for (haystack=policies; (p=strstr (haystack, allowed)); haystack = p+1) { if ( !(p == policies || p[-1] == '\n') ) continue; /* Does not match the begin of a line. */ if (p[strlen (allowed)] != ':') continue; /* The length does not match. */ /* Yep - it does match so return okay. */ fclose (fp); xfree (policies); return 0; } } } /* Helper function for find_up. This resets the key handle and search for an issuer ISSUER with a subjectKeyIdentifier of KEYID. Returns 0 on success or -1 when not found. */ static int find_up_search_by_keyid (ctrl_t ctrl, KEYDB_HANDLE kh, const char *issuer, ksba_sexp_t keyid) { int rc; ksba_cert_t cert = NULL; ksba_sexp_t subj = NULL; ksba_isotime_t not_before, not_after, last_not_before, ne_last_not_before; ksba_cert_t found_cert = NULL; ksba_cert_t ne_found_cert = NULL; keydb_search_reset (kh); while (!(rc = keydb_search_subject (ctrl, kh, issuer))) { ksba_cert_release (cert); cert = NULL; rc = keydb_get_cert (kh, &cert); if (rc) { log_error ("keydb_get_cert() failed: rc=%d\n", rc); rc = -1; goto leave; } xfree (subj); if (!ksba_cert_get_subj_key_id (cert, NULL, &subj)) { if (!cmp_simple_canon_sexp (keyid, subj)) { /* Found matching cert. */ rc = ksba_cert_get_validity (cert, 0, not_before); if (!rc) rc = ksba_cert_get_validity (cert, 1, not_after); if (rc) { log_error ("keydb_get_validity() failed: rc=%d\n", rc); rc = -1; goto leave; } if (!found_cert || strcmp (last_not_before, not_before) < 0) { /* This certificate is the first one found or newer * than the previous one. This copes with * re-issuing CA certificates while keeping the same * key information. */ gnupg_copy_time (last_not_before, not_before); ksba_cert_release (found_cert); ksba_cert_ref ((found_cert = cert)); keydb_push_found_state (kh); } if (*not_after && strcmp (ctrl->current_time, not_after) > 0 ) ; /* CERT has expired - don't consider it. */ else if (!ne_found_cert || strcmp (ne_last_not_before, not_before) < 0) { /* This certificate is the first non-expired one * found or newer than the previous non-expired one. */ gnupg_copy_time (ne_last_not_before, not_before); ksba_cert_release (ne_found_cert); ksba_cert_ref ((ne_found_cert = cert)); } } } } if (!found_cert) goto leave; /* Take the last saved one. Note that push/pop_found_state are * misnomers because there is no stack of states. Renaming them to * save/restore_found_state would be better. */ keydb_pop_found_state (kh); rc = 0; /* Ignore EOF or other error after the first cert. */ /* We need to consider some corner cases. It is possible that we * have a long term certificate (e.g. valid from 2008 to 2033) as * well as a re-issued (i.e. using the same key material) short term * certificate (say from 2016 to 2019). Using the short term * certificate is the proper solution. But we need to take care if * there is no re-issued new short term certificate (e.g. from 2020 * to 2023) available. In that case it is better to use the long * term certificate which is still valid. The code may run into * minor problems in the case of the chain validation mode. Given * that this corner case is due to non-diligent PKI management we * ignore this problem. */ /* The most common case is that the found certificate is not expired * and thus identical to the one found from the list of non-expired * certs. We can stop here. */ if (found_cert == ne_found_cert) goto leave; /* If we do not have a non expired certificate the actual cert is * expired and we can also stop here. */ if (!ne_found_cert) goto leave; /* Now we need to see whether the found certificate is expired and * only in this case we return the certificate found in the list of * non-expired certs. */ rc = ksba_cert_get_validity (found_cert, 1, not_after); if (rc) { log_error ("keydb_get_validity() failed: rc=%d\n", rc); rc = -1; goto leave; } if (*not_after && strcmp (ctrl->current_time, not_after) > 0 ) { /* CERT has expired. Use the NE_FOUND_CERT. Because we have no * found state for this we need to search for it again. */ unsigned char fpr[20]; gpgsm_get_fingerprint (ne_found_cert, GCRY_MD_SHA1, fpr, NULL); keydb_search_reset (kh); rc = keydb_search_fpr (ctrl, kh, fpr); if (rc) { log_error ("keydb_search_fpr() failed: rc=%d\n", rc); rc = -1; goto leave; } /* Ready. The NE_FOUND_CERT is available via keydb_get_cert. */ } leave: ksba_cert_release (found_cert); ksba_cert_release (ne_found_cert); ksba_cert_release (cert); xfree (subj); return rc? -1:0; } struct find_up_store_certs_s { ctrl_t ctrl; int count; unsigned int want_fpr:1; unsigned int got_fpr:1; unsigned char fpr[20]; }; static void find_up_store_certs_cb (void *cb_value, ksba_cert_t cert) { struct find_up_store_certs_s *parm = cb_value; if (keydb_store_cert (parm->ctrl, cert, 1, NULL)) log_error ("error storing issuer certificate as ephemeral\n"); else if (parm->want_fpr && !parm->got_fpr) { if (!gpgsm_get_fingerprint (cert, 0, parm->fpr, NULL)) log_error (_("failed to get the fingerprint\n")); else parm->got_fpr = 1; } parm->count++; } /* Helper for find_up(). Locate the certificate for ISSUER using an external lookup. KH is the keydb context we are currently using. On success 0 is returned and the certificate may be retrieved from the keydb using keydb_get_cert(). KEYID is the keyIdentifier from the AKI or NULL. */ static int find_up_external (ctrl_t ctrl, KEYDB_HANDLE kh, const char *issuer, ksba_sexp_t keyid) { int rc; strlist_t names = NULL; struct find_up_store_certs_s find_up_store_certs_parm; char *pattern; const char *s; find_up_store_certs_parm.ctrl = ctrl; find_up_store_certs_parm.want_fpr = 0; find_up_store_certs_parm.got_fpr = 0; find_up_store_certs_parm.count = 0; if (opt.verbose) log_info (_("looking up issuer at external location\n")); /* The Dirmngr process is confused about unknown attributes. As a quick and ugly hack we locate the CN and use the issuer string starting at this attribite. Fixme: we should have far better parsing for external lookups in the Dirmngr. */ s = strstr (issuer, "CN="); if (!s || s == issuer || s[-1] != ',') s = issuer; pattern = xtrymalloc (strlen (s)+2); if (!pattern) return gpg_error_from_syserror (); strcpy (stpcpy (pattern, "/"), s); add_to_strlist (&names, pattern); xfree (pattern); rc = gpgsm_dirmngr_lookup (ctrl, names, NULL, 0, find_up_store_certs_cb, &find_up_store_certs_parm); free_strlist (names); if (opt.verbose) log_info (_("number of issuers matching: %d\n"), find_up_store_certs_parm.count); if (rc) { log_error ("external key lookup failed: %s\n", gpg_strerror (rc)); rc = -1; } else if (!find_up_store_certs_parm.count) rc = -1; else { int old; /* The issuers are currently stored in the ephemeral key DB, so we temporary switch to ephemeral mode. */ old = keydb_set_ephemeral (kh, 1); if (keyid) rc = find_up_search_by_keyid (ctrl, kh, issuer, keyid); else { keydb_search_reset (kh); rc = keydb_search_subject (ctrl, kh, issuer); } keydb_set_ephemeral (kh, old); } return rc; } /* Helper for find_up(). Locate the certificate for CERT using the * caIssuer from the authorityInfoAccess. KH is the keydb context we * are currently using. On success 0 is returned and the certificate * may be retrieved from the keydb using keydb_get_cert(). If no * suitable authorityInfoAccess is encoded in the certificate * GPG_ERR_NOT_FOUND is returned. */ static gpg_error_t find_up_via_auth_info_access (ctrl_t ctrl, KEYDB_HANDLE kh, ksba_cert_t cert) { gpg_error_t err; struct find_up_store_certs_s find_up_store_certs_parm; char *url, *ldapurl; int idx, i; char *oid; ksba_name_t name; find_up_store_certs_parm.ctrl = ctrl; find_up_store_certs_parm.want_fpr = 1; find_up_store_certs_parm.got_fpr = 0; find_up_store_certs_parm.count = 0; /* Find suitable URLs; if there is a http scheme we prefer that. */ url = ldapurl = NULL; for (idx=0; !url && !(err = ksba_cert_get_authority_info_access (cert, idx, &oid, &name)); idx++) { if (!strcmp (oid, oidstr_caIssuers)) { for (i=0; !url && ksba_name_enum (name, i); i++) { char *p = ksba_name_get_uri (name, i); if (p) { if (!strncmp (p, "http:", 5) || !strncmp (p, "https:", 6)) url = p; else if (ldapurl) xfree (p); /* We already got one. */ else if (!strncmp (p, "ldap:",5) || !strncmp (p, "ldaps:",6)) ldapurl = p; } else xfree (p); } } ksba_name_release (name); ksba_free (oid); } if (err && gpg_err_code (err) != GPG_ERR_EOF) { log_error (_("can't get authorityInfoAccess: %s\n"), gpg_strerror (err)); return err; } if (!url && ldapurl) { /* No HTTP scheme; fallback to LDAP if available. */ url = ldapurl; ldapurl = NULL; } xfree (ldapurl); if (!url) return gpg_error (GPG_ERR_NOT_FOUND); if (opt.verbose) log_info ("looking up issuer via authorityInfoAccess.caIssuers\n"); err = gpgsm_dirmngr_lookup (ctrl, NULL, url, 0, find_up_store_certs_cb, &find_up_store_certs_parm); /* Although we might receive several certificates we use only the * first one. Or more exacty the first one for which we retrieved * the fingerprint. */ if (opt.verbose) log_info ("number of caIssuers found: %d\n", find_up_store_certs_parm.count); if (err) { log_error ("external URL lookup failed: %s\n", gpg_strerror (err)); err = gpg_error (GPG_ERR_NOT_FOUND); } else if (!find_up_store_certs_parm.got_fpr) err = gpg_error (GPG_ERR_NOT_FOUND); else { int old; /* The retrieved certificates are currently stored in the * ephemeral key DB, so we temporary switch to ephemeral * mode. */ old = keydb_set_ephemeral (kh, 1); keydb_search_reset (kh); err = keydb_search_fpr (ctrl, kh, find_up_store_certs_parm.fpr); keydb_set_ephemeral (kh, old); } return err; } /* Helper for find_up(). Ask the dirmngr for the certificate for ISSUER with optional SERIALNO. KH is the keydb context we are currently using. With SUBJECT_MODE set, ISSUER is searched as the subject. On success 0 is returned and the certificate is available in the ephemeral DB. */ static int find_up_dirmngr (ctrl_t ctrl, KEYDB_HANDLE kh, ksba_sexp_t serialno, const char *issuer, int subject_mode) { int rc; strlist_t names = NULL; struct find_up_store_certs_s find_up_store_certs_parm; char *pattern; (void)kh; find_up_store_certs_parm.ctrl = ctrl; find_up_store_certs_parm.count = 0; if (opt.verbose) log_info (_("looking up issuer from the Dirmngr cache\n")); if (subject_mode) { pattern = xtrymalloc (strlen (issuer)+2); if (pattern) strcpy (stpcpy (pattern, "/"), issuer); } else if (serialno) pattern = gpgsm_format_sn_issuer (serialno, issuer); else { pattern = xtrymalloc (strlen (issuer)+3); if (pattern) strcpy (stpcpy (pattern, "#/"), issuer); } if (!pattern) return gpg_error_from_syserror (); add_to_strlist (&names, pattern); xfree (pattern); rc = gpgsm_dirmngr_lookup (ctrl, names, NULL, 1, find_up_store_certs_cb, &find_up_store_certs_parm); free_strlist (names); if (opt.verbose) log_info (_("number of matching certificates: %d\n"), find_up_store_certs_parm.count); if (rc && !opt.quiet) log_info (_("dirmngr cache-only key lookup failed: %s\n"), gpg_strerror (rc)); return (!rc && find_up_store_certs_parm.count)? 0 : -1; } /* Locate issuing certificate for CERT. ISSUER is the name of the issuer used as a fallback if the other methods don't work. If FIND_NEXT is true, the function shall return the next possible issuer. The certificate itself is not directly returned but a keydb_get_cert on the keydb context KH will return it. Returns 0 on success, -1 if not found or an error code. */ static int find_up (ctrl_t ctrl, KEYDB_HANDLE kh, ksba_cert_t cert, const char *issuer, int find_next) { ksba_name_t authid; ksba_sexp_t authidno; ksba_sexp_t keyid; int rc = -1; if (DBG_X509) log_debug ("looking for parent certificate\n"); if (!ksba_cert_get_auth_key_id (cert, &keyid, &authid, &authidno)) { const char *s = ksba_name_enum (authid, 0); if (s && *authidno) { rc = keydb_search_issuer_sn (ctrl, kh, s, authidno); if (rc) keydb_search_reset (kh); if (!rc && DBG_X509) log_debug (" found via authid and sn+issuer\n"); /* In case of an error, try to get the certificate from the dirmngr. That is done by trying to put that certificate into the ephemeral DB and let the code below do the actual retrieve. Thus there is no error checking. Skipped in find_next mode as usual. */ if (rc == -1 && !find_next) find_up_dirmngr (ctrl, kh, authidno, s, 0); /* In case of an error try the ephemeral DB. We can't do that in find_next mode because we can't keep the search state then. */ if (rc == -1 && !find_next) { int old = keydb_set_ephemeral (kh, 1); if (!old) { rc = keydb_search_issuer_sn (ctrl, kh, s, authidno); if (rc) keydb_search_reset (kh); if (!rc && DBG_X509) log_debug (" found via authid and sn+issuer (ephem)\n"); } keydb_set_ephemeral (kh, old); } if (rc) rc = -1; /* Need to make sure to have this error code. */ } if (rc == -1 && keyid && !find_next) { /* Not found by AKI.issuer_sn. Lets try the AKI.ki instead. Loop over all certificates with that issuer as subject and stop for the one with a matching subjectKeyIdentifier. */ /* Fixme: Should we also search in the dirmngr? */ rc = find_up_search_by_keyid (ctrl, kh, issuer, keyid); if (!rc && DBG_X509) log_debug (" found via authid and keyid\n"); if (rc) { int old = keydb_set_ephemeral (kh, 1); if (!old) rc = find_up_search_by_keyid (ctrl, kh, issuer, keyid); if (!rc && DBG_X509) log_debug (" found via authid and keyid (ephem)\n"); keydb_set_ephemeral (kh, old); } if (rc) rc = -1; /* Need to make sure to have this error code. */ } /* If we still didn't found it, try to find it via the subject from the dirmngr-cache. */ if (rc == -1 && !find_next) { if (!find_up_dirmngr (ctrl, kh, NULL, issuer, 1)) { int old = keydb_set_ephemeral (kh, 1); if (keyid) rc = find_up_search_by_keyid (ctrl, kh, issuer, keyid); else { keydb_search_reset (kh); rc = keydb_search_subject (ctrl, kh, issuer); } keydb_set_ephemeral (kh, old); } if (rc) rc = -1; /* Need to make sure to have this error code. */ if (!rc && DBG_X509) log_debug (" found via authid and issuer from dirmngr cache\n"); } /* If we still didn't found it, try an external lookup. */ if (rc == -1 && !find_next && !ctrl->offline) { /* We allow AIA also if CRLs are enabled; both can be used * as a web bug so it does not make sense to not use AIA if * CRL checks are enabled. */ if ((opt.auto_issuer_key_retrieve || !opt.no_crl_check) && !find_up_via_auth_info_access (ctrl, kh, cert)) { if (DBG_X509) log_debug (" found via authorityInfoAccess.caIssuers\n"); rc = 0; } else if (opt.auto_issuer_key_retrieve) { rc = find_up_external (ctrl, kh, issuer, keyid); if (!rc && DBG_X509) log_debug (" found via authid and external lookup\n"); } } /* Print a note so that the user does not feel too helpless when an issuer certificate was found and gpgsm prints BAD signature because it is not the correct one. */ if (rc == -1 && opt.quiet) ; else if (rc == -1) { log_info ("%sissuer certificate ", find_next?"next ":""); if (keyid) { log_printf ("{"); gpgsm_dump_serial (keyid); log_printf ("} "); } if (authidno) { log_printf ("(#"); gpgsm_dump_serial (authidno); log_printf ("/"); gpgsm_dump_string (s); log_printf (") "); } log_printf ("not found using authorityKeyIdentifier\n"); } else if (rc) log_error ("failed to find authorityKeyIdentifier: rc=%d\n", rc); xfree (keyid); ksba_name_release (authid); xfree (authidno); } if (rc) /* Not found via authorithyKeyIdentifier, try regular issuer name. */ rc = keydb_search_subject (ctrl, kh, issuer); if (rc == -1 && !find_next) { int old; /* Also try to get it from the Dirmngr cache. The function merely puts it into the ephemeral database. */ find_up_dirmngr (ctrl, kh, NULL, issuer, 0); /* Not found, let us see whether we have one in the ephemeral key DB. */ old = keydb_set_ephemeral (kh, 1); if (!old) { keydb_search_reset (kh); rc = keydb_search_subject (ctrl, kh, issuer); } keydb_set_ephemeral (kh, old); if (!rc && DBG_X509) log_debug (" found via issuer\n"); } /* Still not found. If enabled, try an external lookup. */ if (rc == -1 && !find_next && !ctrl->offline) { if ((opt.auto_issuer_key_retrieve || !opt.no_crl_check) && !find_up_via_auth_info_access (ctrl, kh, cert)) { if (DBG_X509) log_debug (" found via authorityInfoAccess.caIssuers\n"); rc = 0; } else if (opt.auto_issuer_key_retrieve) { rc = find_up_external (ctrl, kh, issuer, NULL); if (!rc && DBG_X509) log_debug (" found via issuer and external lookup\n"); } } return rc; } /* Return the next certificate up in the chain starting at START. Returns -1 when there are no more certificates. */ int gpgsm_walk_cert_chain (ctrl_t ctrl, ksba_cert_t start, ksba_cert_t *r_next) { int rc = 0; char *issuer = NULL; char *subject = NULL; KEYDB_HANDLE kh = keydb_new (); *r_next = NULL; if (!kh) { log_error (_("failed to allocate keyDB handle\n")); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } issuer = ksba_cert_get_issuer (start, 0); subject = ksba_cert_get_subject (start, 0); if (!issuer) { log_error ("no issuer found in certificate\n"); rc = gpg_error (GPG_ERR_BAD_CERT); goto leave; } if (!subject) { log_error ("no subject found in certificate\n"); rc = gpg_error (GPG_ERR_BAD_CERT); goto leave; } if (is_root_cert (start, issuer, subject)) { rc = -1; /* we are at the root */ goto leave; } rc = find_up (ctrl, kh, start, issuer, 0); if (rc) { /* It is quite common not to have a certificate, so better don't print an error here. */ if (rc != -1 && opt.verbose > 1) log_error ("failed to find issuer's certificate: rc=%d\n", rc); rc = gpg_error (GPG_ERR_MISSING_ISSUER_CERT); goto leave; } rc = keydb_get_cert (kh, r_next); if (rc) { log_error ("keydb_get_cert() failed: rc=%d\n", rc); rc = gpg_error (GPG_ERR_GENERAL); } leave: xfree (issuer); xfree (subject); keydb_release (kh); return rc; } /* Helper for gpgsm_is_root_cert. This one is used if the subject and issuer DNs are already known. */ static int is_root_cert (ksba_cert_t cert, const char *issuerdn, const char *subjectdn) { gpg_error_t err; int result = 0; ksba_sexp_t serialno; ksba_sexp_t ak_keyid; ksba_name_t ak_name; ksba_sexp_t ak_sn; const char *ak_name_str; ksba_sexp_t subj_keyid = NULL; if (!issuerdn || !subjectdn) return 0; /* No. */ if (strcmp (issuerdn, subjectdn)) return 0; /* No. */ err = ksba_cert_get_auth_key_id (cert, &ak_keyid, &ak_name, &ak_sn); if (err) { if (gpg_err_code (err) == GPG_ERR_NO_DATA) return 1; /* Yes. Without a authorityKeyIdentifier this needs to be the Root certificate (our trust anchor). */ log_error ("error getting authorityKeyIdentifier: %s\n", gpg_strerror (err)); return 0; /* Well, it is broken anyway. Return No. */ } serialno = ksba_cert_get_serial (cert); if (!serialno) { log_error ("error getting serialno: %s\n", gpg_strerror (err)); goto leave; } /* Check whether the auth name's matches the issuer name+sn. If that is the case this is a root certificate. */ ak_name_str = ksba_name_enum (ak_name, 0); if (ak_name_str && !strcmp (ak_name_str, issuerdn) && !cmp_simple_canon_sexp (ak_sn, serialno)) { result = 1; /* Right, CERT is self-signed. */ goto leave; } /* Similar for the ak_keyid. */ if (ak_keyid && !ksba_cert_get_subj_key_id (cert, NULL, &subj_keyid) && !cmp_simple_canon_sexp (ak_keyid, subj_keyid)) { result = 1; /* Right, CERT is self-signed. */ goto leave; } leave: ksba_free (subj_keyid); ksba_free (ak_keyid); ksba_name_release (ak_name); ksba_free (ak_sn); ksba_free (serialno); return result; } /* Check whether the CERT is a root certificate. Returns True if this is the case. */ int gpgsm_is_root_cert (ksba_cert_t cert) { char *issuer; char *subject; int yes; issuer = ksba_cert_get_issuer (cert, 0); subject = ksba_cert_get_subject (cert, 0); yes = is_root_cert (cert, issuer, subject); xfree (issuer); xfree (subject); return yes; } /* This is a helper for gpgsm_validate_chain. */ static gpg_error_t is_cert_still_valid (ctrl_t ctrl, int force_ocsp, int lm, estream_t fp, ksba_cert_t subject_cert, ksba_cert_t issuer_cert, int *any_revoked, int *any_no_crl, int *any_crl_too_old) { gpg_error_t err; if (ctrl->offline || (opt.no_crl_check && !ctrl->use_ocsp)) { audit_log_ok (ctrl->audit, AUDIT_CRL_CHECK, gpg_error (GPG_ERR_NOT_ENABLED)); return 0; } if (!(force_ocsp || ctrl->use_ocsp) && !opt.enable_issuer_based_crl_check) { err = ksba_cert_get_crl_dist_point (subject_cert, 0, NULL, NULL, NULL); if (gpg_err_code (err) == GPG_ERR_EOF) { /* No DP specified in the certificate. Thus the CA does not * consider a CRL useful and the user of the certificate * also does not consider this to be a critical thing. In * this case we can conclude that the certificate shall not * be revocable. Note that we reach this point here only if * no OCSP responder shall be used. */ audit_log_ok (ctrl->audit, AUDIT_CRL_CHECK, gpg_error (GPG_ERR_TRUE)); return 0; } } err = gpgsm_dirmngr_isvalid (ctrl, subject_cert, issuer_cert, force_ocsp? 2 : !!ctrl->use_ocsp); audit_log_ok (ctrl->audit, AUDIT_CRL_CHECK, err); if (err) { if (!lm) gpgsm_cert_log_name (NULL, subject_cert); switch (gpg_err_code (err)) { case GPG_ERR_CERT_REVOKED: do_list (1, lm, fp, _("certificate has been revoked")); *any_revoked = 1; /* Store that in the keybox so that key listings are able to return the revoked flag. We don't care about error, though. */ keydb_set_cert_flags (ctrl, subject_cert, 1, KEYBOX_FLAG_VALIDITY, 0, ~0, VALIDITY_REVOKED); break; case GPG_ERR_NO_CRL_KNOWN: do_list (1, lm, fp, _("no CRL found for certificate")); *any_no_crl = 1; break; case GPG_ERR_NO_DATA: do_list (1, lm, fp, _("the status of the certificate is unknown")); *any_no_crl = 1; break; case GPG_ERR_CRL_TOO_OLD: do_list (1, lm, fp, _("the available CRL is too old")); if (!lm) log_info (_("please make sure that the " "\"dirmngr\" is properly installed\n")); *any_crl_too_old = 1; break; default: do_list (1, lm, fp, _("checking the CRL failed: %s"), gpg_strerror (err)); return err; } } return 0; } /* Helper for gpgsm_validate_chain to check the validity period of SUBJECT_CERT. The caller needs to pass EXPTIME which will be updated to the nearest expiration time seen. A DEPTH of 0 indicates the target certificate, -1 the final root certificate and other values intermediate certificates. */ static gpg_error_t check_validity_period (ksba_isotime_t current_time, ksba_cert_t subject_cert, ksba_isotime_t exptime, int listmode, estream_t listfp, int depth) { gpg_error_t err; ksba_isotime_t not_before, not_after; err = ksba_cert_get_validity (subject_cert, 0, not_before); if (!err) err = ksba_cert_get_validity (subject_cert, 1, not_after); if (err) { do_list (1, listmode, listfp, _("certificate with invalid validity: %s"), gpg_strerror (err)); return gpg_error (GPG_ERR_BAD_CERT); } if (*not_after) { if (!*exptime) gnupg_copy_time (exptime, not_after); else if (strcmp (not_after, exptime) < 0 ) gnupg_copy_time (exptime, not_after); } if (*not_before && strcmp (current_time, not_before) < 0 ) { do_list (1, listmode, listfp, depth == 0 ? _("certificate not yet valid") : depth == -1 ? _("root certificate not yet valid") : /* other */ _("intermediate certificate not yet valid")); if (!listmode) { log_info (" (valid from "); dump_isotime (not_before); log_printf (")\n"); } return gpg_error (GPG_ERR_CERT_TOO_YOUNG); } if (*not_after && strcmp (current_time, not_after) > 0 ) { do_list (opt.ignore_expiration?0:1, listmode, listfp, depth == 0 ? _("certificate has expired") : depth == -1 ? _("root certificate has expired") : /* other */ _("intermediate certificate has expired")); if (!listmode) { log_info (" (expired at "); dump_isotime (not_after); log_printf (")\n"); } if (opt.ignore_expiration) log_info ("WARNING: ignoring expiration\n"); else return gpg_error (GPG_ERR_CERT_EXPIRED); } return 0; } /* This is a variant of check_validity_period used with the chain model. The extra constraint here is that notBefore and notAfter must exists and if the additional argument CHECK_TIME is given this time is used to check the validity period of SUBJECT_CERT. */ static gpg_error_t check_validity_period_cm (ksba_isotime_t current_time, ksba_isotime_t check_time, ksba_cert_t subject_cert, ksba_isotime_t exptime, int listmode, estream_t listfp, int depth) { gpg_error_t err; ksba_isotime_t not_before, not_after; err = ksba_cert_get_validity (subject_cert, 0, not_before); if (!err) err = ksba_cert_get_validity (subject_cert, 1, not_after); if (err) { do_list (1, listmode, listfp, _("certificate with invalid validity: %s"), gpg_strerror (err)); return gpg_error (GPG_ERR_BAD_CERT); } if (!*not_before || !*not_after) { do_list (1, listmode, listfp, _("required certificate attributes missing: %s%s%s"), !*not_before? "notBefore":"", (!*not_before && !*not_after)? ", ":"", !*not_before? "notAfter":""); return gpg_error (GPG_ERR_BAD_CERT); } if (strcmp (not_before, not_after) > 0 ) { do_list (1, listmode, listfp, _("certificate with invalid validity")); log_info (" (valid from "); dump_isotime (not_before); log_printf (" expired at "); dump_isotime (not_after); log_printf (")\n"); return gpg_error (GPG_ERR_BAD_CERT); } if (!*exptime) gnupg_copy_time (exptime, not_after); else if (strcmp (not_after, exptime) < 0 ) gnupg_copy_time (exptime, not_after); if (strcmp (current_time, not_before) < 0 ) { do_list (1, listmode, listfp, depth == 0 ? _("certificate not yet valid") : depth == -1 ? _("root certificate not yet valid") : /* other */ _("intermediate certificate not yet valid")); if (!listmode) { log_info (" (valid from "); dump_isotime (not_before); log_printf (")\n"); } return gpg_error (GPG_ERR_CERT_TOO_YOUNG); } if (*check_time && (strcmp (check_time, not_before) < 0 || strcmp (check_time, not_after) > 0)) { /* Note that we don't need a case for the root certificate because its own consistency has already been checked. */ do_list(opt.ignore_expiration?0:1, listmode, listfp, depth == 0 ? _("signature not created during lifetime of certificate") : depth == 1 ? _("certificate not created during lifetime of issuer") : _("intermediate certificate not created during lifetime " "of issuer")); if (!listmode) { log_info (depth== 0? _(" ( signature created at ") : /* */ _(" (certificate created at ") ); dump_isotime (check_time); log_printf (")\n"); log_info (depth==0? _(" (certificate valid from ") : /* */ _(" ( issuer valid from ") ); dump_isotime (not_before); log_info (" to "); dump_isotime (not_after); log_printf (")\n"); } if (opt.ignore_expiration) log_info ("WARNING: ignoring expiration\n"); else return gpg_error (GPG_ERR_CERT_EXPIRED); } return 0; } /* Ask the user whether he wants to mark the certificate CERT trusted. Returns true if the CERT is the trusted. We also check whether the agent is at all enabled to allow marktrusted and don't call it in this session again if it is not. */ static int ask_marktrusted (ctrl_t ctrl, ksba_cert_t cert, int listmode) { static int no_more_questions; int rc; char *fpr; int success = 0; fpr = gpgsm_get_fingerprint_string (cert, GCRY_MD_SHA1); log_info (_("fingerprint=%s\n"), fpr? fpr : "?"); xfree (fpr); if (no_more_questions) rc = gpg_error (GPG_ERR_NOT_SUPPORTED); else rc = gpgsm_agent_marktrusted (ctrl, cert); if (!rc) { log_info (_("root certificate has now been marked as trusted\n")); success = 1; } else if (!listmode) { gpgsm_dump_cert ("issuer", cert); log_info ("after checking the fingerprint, you may want " "to add it manually to the list of trusted certificates.\n"); } if (gpg_err_code (rc) == GPG_ERR_NOT_SUPPORTED) { if (!no_more_questions) log_info (_("interactive marking as trusted " "not enabled in gpg-agent\n")); no_more_questions = 1; } else if (gpg_err_code (rc) == GPG_ERR_CANCELED) { log_info (_("interactive marking as trusted " "disabled for this session\n")); no_more_questions = 1; } else set_already_asked_marktrusted (cert); return success; } /* Validate a chain and optionally return the nearest expiration time in R_EXPTIME. With LISTMODE set to 1 a special listmode is activated where only information about the certificate is printed to LISTFP and no output is send to the usual log stream. If CHECKTIME_ARG is set, it is used only in the chain model instead of the current time. Defined flag bits VALIDATE_FLAG_NO_DIRMNGR - Do not do any dirmngr isvalid checks. VALIDATE_FLAG_CHAIN_MODEL - Check according to chain model. VALIDATE_FLAG_STEED - Check according to the STEED model. */ static int do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg, ksba_isotime_t r_exptime, int listmode, estream_t listfp, unsigned int flags, struct rootca_flags_s *rootca_flags) { int rc = 0, depth, maxdepth; char *issuer = NULL; char *subject = NULL; KEYDB_HANDLE kh = NULL; ksba_cert_t subject_cert = NULL, issuer_cert = NULL; ksba_isotime_t current_time; ksba_isotime_t check_time; ksba_isotime_t exptime; int any_expired = 0; int any_revoked = 0; int any_no_crl = 0; int any_crl_too_old = 0; int any_no_policy_match = 0; int is_qualified = -1; /* Indicates whether the certificate stems from a qualified root certificate. -1 = unknown, 0 = no, 1 = yes. */ chain_item_t chain = NULL; /* A list of all certificates in the chain. */ gnupg_get_isotime (current_time); gnupg_copy_time (ctrl->current_time, current_time); if ( (flags & VALIDATE_FLAG_CHAIN_MODEL) ) { if (!strcmp (checktime_arg, "19700101T000000")) { do_list (1, listmode, listfp, _("WARNING: creation time of signature not known - " "assuming current time")); gnupg_copy_time (check_time, current_time); } else gnupg_copy_time (check_time, checktime_arg); } else *check_time = 0; if (r_exptime) *r_exptime = 0; *exptime = 0; if (opt.no_chain_validation && !listmode) { log_info ("WARNING: bypassing certificate chain validation\n"); return 0; } kh = keydb_new (); if (!kh) { log_error (_("failed to allocate keyDB handle\n")); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } if (DBG_X509 && !listmode) gpgsm_dump_cert ("target", cert); subject_cert = cert; ksba_cert_ref (subject_cert); maxdepth = 50; depth = 0; for (;;) { int is_root; gpg_error_t istrusted_rc = -1; /* Put the certificate on our list. */ { chain_item_t ci; ci = xtrycalloc (1, sizeof *ci); if (!ci) { rc = gpg_error_from_syserror (); goto leave; } ksba_cert_ref (subject_cert); ci->cert = subject_cert; ci->next = chain; chain = ci; } xfree (issuer); xfree (subject); issuer = ksba_cert_get_issuer (subject_cert, 0); subject = ksba_cert_get_subject (subject_cert, 0); if (!issuer) { do_list (1, listmode, listfp, _("no issuer found in certificate")); rc = gpg_error (GPG_ERR_BAD_CERT); goto leave; } /* Is this a self-issued certificate (i.e. the root certificate)? */ is_root = is_root_cert (subject_cert, issuer, subject); if (is_root) { chain->is_root = 1; /* Check early whether the certificate is listed as trusted. We used to do this only later but changed it to call the check right here so that we can access special flags associated with that specific root certificate. */ if (gpgsm_cert_has_well_known_private_key (subject_cert)) { memset (rootca_flags, 0, sizeof *rootca_flags); istrusted_rc = ((flags & VALIDATE_FLAG_STEED) ? 0 : gpg_error (GPG_ERR_NOT_TRUSTED)); } else istrusted_rc = gpgsm_agent_istrusted (ctrl, subject_cert, NULL, rootca_flags); audit_log_cert (ctrl->audit, AUDIT_ROOT_TRUSTED, subject_cert, istrusted_rc); /* If the chain model extended attribute is used, make sure that our chain model flag is set. */ if (!(flags & VALIDATE_FLAG_STEED) && has_validation_model_chain (subject_cert, listmode, listfp)) rootca_flags->chain_model = 1; } /* Check the validity period. */ if ( (flags & VALIDATE_FLAG_CHAIN_MODEL) ) rc = check_validity_period_cm (current_time, check_time, subject_cert, exptime, listmode, listfp, (depth && is_root)? -1: depth); else rc = check_validity_period (current_time, subject_cert, exptime, listmode, listfp, (depth && is_root)? -1: depth); if (gpg_err_code (rc) == GPG_ERR_CERT_EXPIRED) any_expired = 1; else if (rc) goto leave; /* Assert that we understand all critical extensions. */ rc = unknown_criticals (subject_cert, listmode, listfp); if (rc) goto leave; /* Do a policy check. */ if (!opt.no_policy_check) { rc = check_cert_policy (subject_cert, listmode, listfp); if (gpg_err_code (rc) == GPG_ERR_NO_POLICY_MATCH) { any_no_policy_match = 1; rc = 1; /* Be on the safe side and set RC. */ } else if (rc) goto leave; } /* If this is the root certificate we are at the end of the chain. */ if (is_root) { if (!istrusted_rc) ; /* No need to check the certificate for a trusted one. */ else if (gpgsm_check_cert_sig (subject_cert, subject_cert) ) { /* We only check the signature if the certificate is not trusted for better diagnostics. */ do_list (1, listmode, listfp, _("self-signed certificate has a BAD signature")); if (DBG_X509) { gpgsm_dump_cert ("self-signing cert", subject_cert); } rc = gpg_error (depth? GPG_ERR_BAD_CERT_CHAIN : GPG_ERR_BAD_CERT); goto leave; } if (!rootca_flags->relax) { rc = allowed_ca (ctrl, subject_cert, NULL, listmode, listfp); if (rc) goto leave; } /* Set the flag for qualified signatures. This flag is deduced from a list of root certificates allowed for qualified signatures. */ if (is_qualified == -1 && !(flags & VALIDATE_FLAG_STEED)) { gpg_error_t err; size_t buflen; char buf[1]; if (!ksba_cert_get_user_data (cert, "is_qualified", &buf, sizeof (buf), &buflen) && buflen) { /* We already checked this for this certificate, thus we simply take it from the user data. */ is_qualified = !!*buf; } else { /* Need to consult the list of root certificates for qualified signatures. */ err = gpgsm_is_in_qualified_list (ctrl, subject_cert, NULL); if (!err) is_qualified = 1; else if ( gpg_err_code (err) == GPG_ERR_NOT_FOUND) is_qualified = 0; else log_error ("checking the list of qualified " "root certificates failed: %s\n", gpg_strerror (err)); if ( is_qualified != -1 ) { /* Cache the result but don't care too much about an error. */ buf[0] = !!is_qualified; err = ksba_cert_set_user_data (subject_cert, "is_qualified", buf, 1); if (err) log_error ("set_user_data(is_qualified) failed: %s\n", gpg_strerror (err)); } } } /* Act on the check for a trusted root certificates. */ rc = istrusted_rc; if (!rc) ; else if (gpg_err_code (rc) == GPG_ERR_NOT_TRUSTED) { do_list (0, listmode, listfp, _("root certificate is not marked trusted")); /* If we already figured out that the certificate is expired it does not make much sense to ask the user whether they want to trust the root certificate. We should do this only if the certificate under question will then be usable. If the certificate has a well known private key asking the user does not make any sense. */ if ( !any_expired && !gpgsm_cert_has_well_known_private_key (subject_cert) && (!listmode || !already_asked_marktrusted (subject_cert)) && ask_marktrusted (ctrl, subject_cert, listmode) ) rc = 0; } else { log_error (_("checking the trust list failed: %s\n"), gpg_strerror (rc)); } if (rc) goto leave; /* Check for revocations etc. */ if ((flags & VALIDATE_FLAG_NO_DIRMNGR)) ; else if ((flags & VALIDATE_FLAG_STEED)) ; /* Fixme: check revocations via DNS. */ else if (opt.no_trusted_cert_crl_check || rootca_flags->relax) ; else rc = is_cert_still_valid (ctrl, (flags & VALIDATE_FLAG_CHAIN_MODEL), listmode, listfp, subject_cert, subject_cert, &any_revoked, &any_no_crl, &any_crl_too_old); if (rc) goto leave; break; /* Okay: a self-signed certificate is an end-point. */ } /* End is_root. */ /* Take care that the chain does not get too long. */ if ((depth+1) > maxdepth) { do_list (1, listmode, listfp, _("certificate chain too long\n")); rc = gpg_error (GPG_ERR_BAD_CERT_CHAIN); goto leave; } /* Find the next cert up the tree. */ keydb_search_reset (kh); rc = find_up (ctrl, kh, subject_cert, issuer, 0); if (rc) { if (rc == -1) { do_list (0, listmode, listfp, _("issuer certificate not found")); if (!listmode) { log_info ("issuer certificate: #/"); gpgsm_dump_string (issuer); log_printf ("\n"); } } else log_error ("failed to find issuer's certificate: rc=%d\n", rc); rc = gpg_error (GPG_ERR_MISSING_ISSUER_CERT); goto leave; } ksba_cert_release (issuer_cert); issuer_cert = NULL; rc = keydb_get_cert (kh, &issuer_cert); if (rc) { log_error ("keydb_get_cert() failed: rc=%d\n", rc); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } try_another_cert: if (DBG_X509) { log_debug ("got issuer's certificate:\n"); gpgsm_dump_cert ("issuer", issuer_cert); } rc = gpgsm_check_cert_sig (issuer_cert, subject_cert); if (rc) { do_list (0, listmode, listfp, _("certificate has a BAD signature")); if (DBG_X509) { gpgsm_dump_cert ("signing issuer", issuer_cert); gpgsm_dump_cert ("signed subject", subject_cert); } if (gpg_err_code (rc) == GPG_ERR_BAD_SIGNATURE) { /* We now try to find other issuer certificates which might have been used. This is required because some CAs are reusing the issuer and subject DN for new root certificates. */ /* FIXME: Do this only if we don't have an AKI.keyIdentifier */ rc = find_up (ctrl, kh, subject_cert, issuer, 1); if (!rc) { ksba_cert_t tmp_cert; rc = keydb_get_cert (kh, &tmp_cert); if (rc || !compare_certs (issuer_cert, tmp_cert)) { /* The find next did not work or returned an identical certificate. We better stop here to avoid infinite checks. */ /* No need to set RC because it is not used: rc = gpg_error (GPG_ERR_BAD_SIGNATURE); */ ksba_cert_release (tmp_cert); } else { do_list (0, listmode, listfp, _("found another possible matching " "CA certificate - trying again")); ksba_cert_release (issuer_cert); issuer_cert = tmp_cert; goto try_another_cert; } } } /* We give a more descriptive error code than the one returned from the signature checking. */ rc = gpg_error (GPG_ERR_BAD_CERT_CHAIN); goto leave; } is_root = gpgsm_is_root_cert (issuer_cert); istrusted_rc = -1; /* Check that a CA is allowed to issue certificates. */ { int chainlen; rc = allowed_ca (ctrl, issuer_cert, &chainlen, listmode, listfp); if (rc) { /* Not allowed. Check whether this is a trusted root certificate and whether we allow special exceptions. We could carry the result of the test over to the regular root check at the top of the loop but for clarity we won't do that. Given that the majority of certificates carry proper BasicContraints our way of overriding an error in the way is justified for performance reasons. */ if (is_root) { if (gpgsm_cert_has_well_known_private_key (issuer_cert)) { memset (rootca_flags, 0, sizeof *rootca_flags); istrusted_rc = ((flags & VALIDATE_FLAG_STEED) ? 0 : gpg_error (GPG_ERR_NOT_TRUSTED)); } else istrusted_rc = gpgsm_agent_istrusted (ctrl, issuer_cert, NULL, rootca_flags); if (!istrusted_rc && rootca_flags->relax) { /* Ignore the error due to the relax flag. */ rc = 0; chainlen = -1; } } } if (rc) goto leave; if (chainlen >= 0 && depth > chainlen) { do_list (1, listmode, listfp, _("certificate chain longer than allowed by CA (%d)"), chainlen); rc = gpg_error (GPG_ERR_BAD_CERT_CHAIN); goto leave; } } /* Is the certificate allowed to sign other certificates. */ if (!listmode) { rc = gpgsm_cert_use_cert_p (issuer_cert); if (rc) { char numbuf[50]; sprintf (numbuf, "%d", rc); gpgsm_status2 (ctrl, STATUS_ERROR, "certcert.issuer.keyusage", numbuf, NULL); goto leave; } } /* Check for revocations etc. Note that for a root certificate this test is done a second time later. This should eventually be fixed. */ if ((flags & VALIDATE_FLAG_NO_DIRMNGR)) rc = 0; else if ((flags & VALIDATE_FLAG_STEED)) rc = 0; /* Fixme: XXX */ else if (is_root && (opt.no_trusted_cert_crl_check || (!istrusted_rc && rootca_flags->relax))) rc = 0; else rc = is_cert_still_valid (ctrl, (flags & VALIDATE_FLAG_CHAIN_MODEL), listmode, listfp, subject_cert, issuer_cert, &any_revoked, &any_no_crl, &any_crl_too_old); if (rc) goto leave; if (opt.verbose && !listmode) log_info (depth == 0 ? _("certificate is good\n") : !is_root ? _("intermediate certificate is good\n") : /* other */ _("root certificate is good\n")); /* Under the chain model the next check time is the creation time of the subject certificate. */ if ( (flags & VALIDATE_FLAG_CHAIN_MODEL) ) { rc = ksba_cert_get_validity (subject_cert, 0, check_time); if (rc) { /* That will never happen as we have already checked this above. */ BUG (); } } /* For the next round the current issuer becomes the new subject. */ keydb_search_reset (kh); ksba_cert_release (subject_cert); subject_cert = issuer_cert; issuer_cert = NULL; depth++; } /* End chain traversal. */ if (!listmode && !opt.quiet) { if (opt.no_policy_check) log_info ("policies not checked due to %s option\n", "--disable-policy-checks"); if (ctrl->offline || (opt.no_crl_check && !ctrl->use_ocsp)) log_info ("CRLs not checked due to %s option\n", ctrl->offline ? "offline" : "--disable-crl-checks"); } if (!rc) { /* If we encountered an error somewhere during the checks, set the error code to the most critical one */ if (any_revoked) rc = gpg_error (GPG_ERR_CERT_REVOKED); else if (any_expired) rc = gpg_error (GPG_ERR_CERT_EXPIRED); else if (any_no_crl) rc = gpg_error (GPG_ERR_NO_CRL_KNOWN); else if (any_crl_too_old) rc = gpg_error (GPG_ERR_CRL_TOO_OLD); else if (any_no_policy_match) rc = gpg_error (GPG_ERR_NO_POLICY_MATCH); } leave: /* If we have traversed a complete chain up to the root we will reset the ephemeral flag for all these certificates. This is done regardless of any error because those errors may only be transient. */ if (chain && chain->is_root) { gpg_error_t err; chain_item_t ci; for (ci = chain; ci; ci = ci->next) { /* Note that it is possible for the last certificate in the chain (i.e. our target certificate) that it has not yet been stored in the keybox and thus the flag can't be set. We ignore this error because it will later be stored anyway. */ err = keydb_set_cert_flags (ctrl, ci->cert, 1, KEYBOX_FLAG_BLOB, 0, KEYBOX_FLAG_BLOB_EPHEMERAL, 0); if (!ci->next && gpg_err_code (err) == GPG_ERR_NOT_FOUND) ; else if (err) log_error ("clearing ephemeral flag failed: %s\n", gpg_strerror (err)); } } /* If we have figured something about the qualified signature capability of the certificate under question, store the result as user data in all certificates of the chain. We do this even if the validation itself failed. */ if (is_qualified != -1 && !(flags & VALIDATE_FLAG_STEED)) { gpg_error_t err; chain_item_t ci; char buf[1]; buf[0] = !!is_qualified; for (ci = chain; ci; ci = ci->next) { err = ksba_cert_set_user_data (ci->cert, "is_qualified", buf, 1); if (err) { log_error ("set_user_data(is_qualified) failed: %s\n", gpg_strerror (err)); if (!rc) rc = err; } } } /* If auditing has been enabled, record what is in the chain. */ if (ctrl->audit) { chain_item_t ci; audit_log (ctrl->audit, AUDIT_CHAIN_BEGIN); for (ci = chain; ci; ci = ci->next) { audit_log_cert (ctrl->audit, ci->is_root? AUDIT_CHAIN_ROOTCERT : AUDIT_CHAIN_CERT, ci->cert, 0); } audit_log (ctrl->audit, AUDIT_CHAIN_END); } if (r_exptime) gnupg_copy_time (r_exptime, exptime); xfree (issuer); xfree (subject); keydb_release (kh); while (chain) { chain_item_t ci_next = chain->next; ksba_cert_release (chain->cert); xfree (chain); chain = ci_next; } ksba_cert_release (issuer_cert); ksba_cert_release (subject_cert); return rc; } /* Validate a certificate chain. For a description see do_validate_chain. This function is a wrapper to handle a root certificate with the chain_model flag set. If RETFLAGS is not NULL, flags indicating now the verification was done are stored there. The only defined vits for RETFLAGS are VALIDATE_FLAG_CHAIN_MODEL and VALIDATE_FLAG_STEED. If you are verifying a signature you should set CHECKTIME to the creation time of the signature. If your are verifying a certificate, set it nil (i.e. the empty string). If the creation date of the signature is not known use the special date "19700101T000000" which is treated in a special way here. */ int gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime, ksba_isotime_t r_exptime, int listmode, estream_t listfp, unsigned int flags, unsigned int *retflags) { int rc; struct rootca_flags_s rootca_flags; unsigned int dummy_retflags; if (!retflags) retflags = &dummy_retflags; /* If the session requested a certain validation mode make sure the corresponding flags are set. */ if (ctrl->validation_model == 1) flags |= VALIDATE_FLAG_CHAIN_MODEL; else if (ctrl->validation_model == 2) flags |= VALIDATE_FLAG_STEED; /* If the chain model was forced, set this immediately into RETFLAGS. */ *retflags = (flags & VALIDATE_FLAG_CHAIN_MODEL); memset (&rootca_flags, 0, sizeof rootca_flags); rc = do_validate_chain (ctrl, cert, checktime, r_exptime, listmode, listfp, flags, &rootca_flags); if (!rc && (flags & VALIDATE_FLAG_STEED)) { *retflags |= VALIDATE_FLAG_STEED; } else if (gpg_err_code (rc) == GPG_ERR_CERT_EXPIRED && !(flags & VALIDATE_FLAG_CHAIN_MODEL) && (rootca_flags.valid && rootca_flags.chain_model)) { do_list (0, listmode, listfp, _("switching to chain model")); rc = do_validate_chain (ctrl, cert, checktime, r_exptime, listmode, listfp, (flags |= VALIDATE_FLAG_CHAIN_MODEL), &rootca_flags); *retflags |= VALIDATE_FLAG_CHAIN_MODEL; } if (opt.verbose) do_list (0, listmode, listfp, _("validation model used: %s"), (*retflags & VALIDATE_FLAG_STEED)? "steed" : (*retflags & VALIDATE_FLAG_CHAIN_MODEL)? _("chain"):_("shell")); return rc; } /* Check that the given certificate is valid but DO NOT check any constraints. We assume that the issuers certificate is already in the DB and that this one is valid; which it should be because it has been checked using this function. */ int gpgsm_basic_cert_check (ctrl_t ctrl, ksba_cert_t cert) { int rc = 0; char *issuer = NULL; char *subject = NULL; KEYDB_HANDLE kh; ksba_cert_t issuer_cert = NULL; if (opt.no_chain_validation) { log_info ("WARNING: bypassing basic certificate checks\n"); return 0; } kh = keydb_new (); if (!kh) { log_error (_("failed to allocate keyDB handle\n")); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } issuer = ksba_cert_get_issuer (cert, 0); subject = ksba_cert_get_subject (cert, 0); if (!issuer) { log_error ("no issuer found in certificate\n"); rc = gpg_error (GPG_ERR_BAD_CERT); goto leave; } if (is_root_cert (cert, issuer, subject)) { rc = gpgsm_check_cert_sig (cert, cert); if (rc) { log_error ("self-signed certificate has a BAD signature: %s\n", gpg_strerror (rc)); if (DBG_X509) { gpgsm_dump_cert ("self-signing cert", cert); } rc = gpg_error (GPG_ERR_BAD_CERT); goto leave; } } else { /* Find the next cert up the tree. */ keydb_search_reset (kh); rc = find_up (ctrl, kh, cert, issuer, 0); if (rc) { if (rc == -1) { log_info ("issuer certificate (#/"); gpgsm_dump_string (issuer); log_printf (") not found\n"); } else log_error ("failed to find issuer's certificate: rc=%d\n", rc); rc = gpg_error (GPG_ERR_MISSING_ISSUER_CERT); goto leave; } ksba_cert_release (issuer_cert); issuer_cert = NULL; rc = keydb_get_cert (kh, &issuer_cert); if (rc) { log_error ("keydb_get_cert() failed: rc=%d\n", rc); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } rc = gpgsm_check_cert_sig (issuer_cert, cert); if (rc) { log_error ("certificate has a BAD signature: %s\n", gpg_strerror (rc)); if (DBG_X509) { gpgsm_dump_cert ("signing issuer", issuer_cert); gpgsm_dump_cert ("signed subject", cert); } rc = gpg_error (GPG_ERR_BAD_CERT); goto leave; } if (opt.verbose) log_info (_("certificate is good\n")); } leave: xfree (issuer); xfree (subject); keydb_release (kh); ksba_cert_release (issuer_cert); return rc; } /* Check whether the certificate CERT has been issued by the German authority for qualified signature. They do not set the basicConstraints and thus we need this workaround. It works by looking up the root certificate and checking whether that one is listed as a qualified certificate for Germany. We also try to cache this data but as long as don't keep a reference to the certificate this won't be used. Returns: True if CERT is a RegTP issued CA cert (i.e. the root certificate itself or one of the CAs). In that case CHAINLEN will receive the length of the chain which is either 0 or 1. */ static int get_regtp_ca_info (ctrl_t ctrl, ksba_cert_t cert, int *chainlen) { gpg_error_t err; ksba_cert_t next; int rc = 0; int i, depth; char country[3]; ksba_cert_t array[4]; char buf[2]; size_t buflen; int dummy_chainlen; if (!chainlen) chainlen = &dummy_chainlen; *chainlen = 0; err = ksba_cert_get_user_data (cert, "regtp_ca_chainlen", &buf, sizeof (buf), &buflen); if (!err) { /* Got info. */ if (buflen < 2 || !*buf) return 0; /* Nothing found. */ *chainlen = buf[1]; return 1; /* This is a regtp CA. */ } else if (gpg_err_code (err) != GPG_ERR_NOT_FOUND) { log_error ("ksba_cert_get_user_data(%s) failed: %s\n", "regtp_ca_chainlen", gpg_strerror (err)); return 0; /* Nothing found. */ } /* Need to gather the info. This requires to walk up the chain until we have found the root. Because we are only interested in German Bundesnetzagentur (former RegTP) derived certificates 3 levels are enough. (The German signature law demands a 3 tier hierarchy; thus there is only one CA between the EE and the Root CA.) */ memset (&array, 0, sizeof array); depth = 0; ksba_cert_ref (cert); array[depth++] = cert; ksba_cert_ref (cert); while (depth < DIM(array) && !(rc=gpgsm_walk_cert_chain (ctrl, cert, &next))) { ksba_cert_release (cert); ksba_cert_ref (next); array[depth++] = next; cert = next; } ksba_cert_release (cert); if (rc != -1 || !depth || depth == DIM(array) ) { /* We did not reached the root. */ goto leave; } /* If this is a German signature law issued certificate, we store additional information. */ if (!gpgsm_is_in_qualified_list (NULL, array[depth-1], country) && !strcmp (country, "de")) { /* Setting the pathlen for the root CA and the CA flag for the next one is all what we need to do. */ err = ksba_cert_set_user_data (array[depth-1], "regtp_ca_chainlen", "\x01\x01", 2); if (!err && depth > 1) err = ksba_cert_set_user_data (array[depth-2], "regtp_ca_chainlen", "\x01\x00", 2); if (err) log_error ("ksba_set_user_data(%s) failed: %s\n", "regtp_ca_chainlen", gpg_strerror (err)); for (i=0; i < depth; i++) ksba_cert_release (array[i]); *chainlen = (depth>1? 0:1); return 1; } leave: /* Nothing special with this certificate. Mark the target certificate anyway to avoid duplicate lookups. */ err = ksba_cert_set_user_data (cert, "regtp_ca_chainlen", "", 1); if (err) log_error ("ksba_set_user_data(%s) failed: %s\n", "regtp_ca_chainlen", gpg_strerror (err)); for (i=0; i < depth; i++) ksba_cert_release (array[i]); return 0; } diff --git a/sm/certcheck.c b/sm/certcheck.c index 450e589bb..fca45759b 100644 --- a/sm/certcheck.c +++ b/sm/certcheck.c @@ -1,731 +1,730 @@ /* certcheck.c - check one certificate * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc. * Copyright (C) 2001-2019 Werner Koch * Copyright (C) 2015-2020 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * SPDX-License-Identifier: GPL-3.0-or-later */ #include #include #include #include #include #include #include -#include #include "gpgsm.h" #include #include #include "keydb.h" #include "../common/i18n.h" #include "../common/membuf.h" /* Return the number of bits of the Q parameter from the DSA key KEY. */ static unsigned int get_dsa_qbits (gcry_sexp_t key) { gcry_sexp_t l1, l2; gcry_mpi_t q; unsigned int nbits; l1 = gcry_sexp_find_token (key, "public-key", 0); if (!l1) return 0; /* Does not contain a key object. */ l2 = gcry_sexp_cadr (l1); gcry_sexp_release (l1); l1 = gcry_sexp_find_token (l2, "q", 1); gcry_sexp_release (l2); if (!l1) return 0; /* Invalid object. */ q = gcry_sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG); gcry_sexp_release (l1); if (!q) return 0; /* Missing value. */ nbits = gcry_mpi_get_nbits (q); gcry_mpi_release (q); return nbits; } static int do_encode_md (gcry_md_hd_t md, int algo, int pkalgo, unsigned int nbits, gcry_sexp_t pkey, gcry_mpi_t *r_val) { int n; size_t nframe; unsigned char *frame; if (pkalgo == GCRY_PK_DSA || pkalgo == GCRY_PK_ECC) { unsigned int qbits0, qbits; if ( pkalgo == GCRY_PK_ECC ) { qbits0 = gcry_pk_get_nbits (pkey); qbits = qbits0 == 521? 512 : qbits0; } else qbits0 = qbits = get_dsa_qbits (pkey); if ( (qbits%8) ) { log_error(_("DSA requires the hash length to be a" " multiple of 8 bits\n")); return gpg_error (GPG_ERR_INTERNAL); } /* Don't allow any Q smaller than 160 bits. We don't want someone to issue signatures from a key with a 16-bit Q or something like that, which would look correct but allow trivial forgeries. Yes, I know this rules out using MD5 with DSA. ;) */ if (qbits < 160) { log_error (_("%s key uses an unsafe (%u bit) hash\n"), gcry_pk_algo_name (pkalgo), qbits0); return gpg_error (GPG_ERR_INTERNAL); } /* Check if we're too short. Too long is safe as we'll automatically left-truncate. */ nframe = gcry_md_get_algo_dlen (algo); if (nframe < qbits/8) { log_error (_("a %u bit hash is not valid for a %u bit %s key\n"), (unsigned int)nframe*8, qbits0, gcry_pk_algo_name (pkalgo)); /* FIXME: we need to check the requirements for ECDSA. */ if (nframe < 20 || pkalgo == GCRY_PK_DSA ) return gpg_error (GPG_ERR_INTERNAL); } frame = xtrymalloc (nframe); if (!frame) return out_of_core (); memcpy (frame, gcry_md_read (md, algo), nframe); n = nframe; /* Truncate. */ if (n > qbits/8) n = qbits/8; } else { int i; unsigned char asn[100]; size_t asnlen; size_t len; nframe = (nbits+7) / 8; asnlen = DIM(asn); if (!algo || gcry_md_test_algo (algo)) return gpg_error (GPG_ERR_DIGEST_ALGO); if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen)) { log_error ("no object identifier for algo %d\n", algo); return gpg_error (GPG_ERR_INTERNAL); } len = gcry_md_get_algo_dlen (algo); if ( len + asnlen + 4 > nframe ) { log_error ("can't encode a %d bit MD into a %d bits frame\n", (int)(len*8), (int)nbits); return gpg_error (GPG_ERR_INTERNAL); } /* We encode the MD in this way: * * 0 A PAD(n bytes) 0 ASN(asnlen bytes) MD(len bytes) * * PAD consists of FF bytes. */ frame = xtrymalloc (nframe); if (!frame) return out_of_core (); n = 0; frame[n++] = 0; frame[n++] = 1; /* block type */ i = nframe - len - asnlen -3 ; - assert ( i > 1 ); + log_assert ( i > 1 ); memset ( frame+n, 0xff, i ); n += i; frame[n++] = 0; memcpy ( frame+n, asn, asnlen ); n += asnlen; memcpy ( frame+n, gcry_md_read(md, algo), len ); n += len; - assert ( n == nframe ); + log_assert ( n == nframe ); } if (DBG_CRYPTO) { int j; log_debug ("encoded hash:"); for (j=0; j < nframe; j++) log_printf (" %02X", frame[j]); log_printf ("\n"); } gcry_mpi_scan (r_val, GCRYMPI_FMT_USG, frame, n, &nframe); xfree (frame); return 0; } /* Return the public key algorithm id from the S-expression PKEY. FIXME: libgcrypt should provide such a function. Note that this implementation uses the names as used by libksba. */ static int pk_algo_from_sexp (gcry_sexp_t pkey) { gcry_sexp_t l1, l2; const char *name; size_t n; int algo; l1 = gcry_sexp_find_token (pkey, "public-key", 0); if (!l1) return 0; /* Not found. */ l2 = gcry_sexp_cadr (l1); gcry_sexp_release (l1); name = gcry_sexp_nth_data (l2, 0, &n); if (!name) algo = 0; /* Not found. */ else if (n==3 && !memcmp (name, "rsa", 3)) algo = GCRY_PK_RSA; else if (n==3 && !memcmp (name, "dsa", 3)) algo = GCRY_PK_DSA; else if (n==3 && !memcmp (name, "ecc", 3)) algo = GCRY_PK_ECC; else if (n==13 && !memcmp (name, "ambiguous-rsa", 13)) algo = GCRY_PK_RSA; else algo = 0; gcry_sexp_release (l2); return algo; } /* Return the hash algorithm's algo id from its name given in the * non-null termnated string in (buffer,buflen). Returns 0 on failure * or if the algo is not known. */ static int hash_algo_from_buffer (const void *buffer, size_t buflen) { char *string; int algo; string = xtrymalloc (buflen + 1); if (!string) { log_error (_("out of core\n")); return 0; } memcpy (string, buffer, buflen); string[buflen] = 0; algo = gcry_md_map_name (string); if (!algo) log_error ("unknown digest algorithm '%s' used in certificate\n", string); xfree (string); return algo; } /* Return an unsigned integer from the non-null termnated string * (buffer,buflen). Returns 0 on failure. */ static unsigned int uint_from_buffer (const void *buffer, size_t buflen) { char *string; unsigned int val; string = xtrymalloc (buflen + 1); if (!string) { log_error (_("out of core\n")); return 0; } memcpy (string, buffer, buflen); string[buflen] = 0; val = strtoul (string, NULL, 10); xfree (string); return val; } /* Extract the hash algorithm and the salt length from the sigval. */ static gpg_error_t extract_pss_params (gcry_sexp_t s_sig, int *r_algo, unsigned int *r_saltlen) { gpg_error_t err; gcry_buffer_t ioarray[2] = { {0}, {0} }; err = gcry_sexp_extract_param (s_sig, "sig-val", "&'hash-algo''salt-length'", ioarray+0, ioarray+1, NULL); if (err) { log_error ("extracting params from PSS failed: %s\n", gpg_strerror (err)); return err; } *r_algo = hash_algo_from_buffer (ioarray[0].data, ioarray[0].len); *r_saltlen = uint_from_buffer (ioarray[1].data, ioarray[1].len); xfree (ioarray[0].data); xfree (ioarray[1].data); 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) { return gpg_error (GPG_ERR_DIGEST_ALGO); } /* PSS has no hash function firewall like PKCS#1 and thus offers * a path for hash algorithm replacement. To avoid this it makes * sense to restrict the allowed hash algorithms and also allow only * matching salt lengths. According to Peter Gutmann: * "Beware of bugs in the above signature scheme; * I have only proved it secure, not implemented it" * - Apologies to Donald Knuth. * https://www.metzdowd.com/pipermail/cryptography/2019-November/035449.html * * Given the set of supported algorithms currently available in * Libgcrypt and the extra hash checks we have in some compliance * modes, it would be hard to trick gpgsm to verify a forged * signature. However, if eventually someone adds the xor256 hash * algorithm (1.3.6.1.4.1.3029.3.2) to Libgcrypt we would be doomed. */ switch (*r_algo) { case GCRY_MD_SHA1: case GCRY_MD_SHA256: case GCRY_MD_SHA384: case GCRY_MD_SHA512: case GCRY_MD_SHA3_256: case GCRY_MD_SHA3_384: case GCRY_MD_SHA3_512: break; default: log_error ("PSS hash algorithm '%s' rejected\n", gcry_md_algo_name (*r_algo)); return gpg_error (GPG_ERR_DIGEST_ALGO); } if (gcry_md_get_algo_dlen (*r_algo) != *r_saltlen) { log_error ("PSS hash algorithm '%s' rejected due to salt length %u\n", gcry_md_algo_name (*r_algo), *r_saltlen); return gpg_error (GPG_ERR_DIGEST_ALGO); } return 0; } /* Check the signature on CERT using the ISSUER-CERT. This function does only test the cryptographic signature and nothing else. It is assumed that the ISSUER_CERT is valid. */ int gpgsm_check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert) { const char *algoid; gcry_md_hd_t md = NULL; void *certder = NULL; size_t certderlen; int rc, algo; ksba_sexp_t p; size_t n; gcry_sexp_t s_sig, s_data, s_pkey; int use_pss = 0; int use_eddsa = 0; unsigned int saltlen; /* Note that we map the 4 algos which current Libgcrypt versions are * not aware of the OID. */ algo = gcry_md_map_name ( (algoid=ksba_cert_get_digest_algo (cert))); if (!algo && algoid && !strcmp (algoid, "1.2.840.113549.1.1.10")) use_pss = 1; else if (algoid && !strcmp (algoid, "1.3.101.112")) use_eddsa = 1; else if (algoid && !strcmp (algoid, "1.3.101.113")) use_eddsa = 2; else if (!algo && algoid && !strcmp (algoid, "1.2.840.10045.4.3.1")) algo = GCRY_MD_SHA224; /* ecdsa-with-sha224 */ else if (!algo && algoid && !strcmp (algoid, "1.2.840.10045.4.3.2")) algo = GCRY_MD_SHA256; /* ecdsa-with-sha256 */ else if (!algo && algoid && !strcmp (algoid, "1.2.840.10045.4.3.3")) algo = GCRY_MD_SHA384; /* ecdsa-with-sha384 */ else if (!algo && algoid && !strcmp (algoid, "1.2.840.10045.4.3.4")) algo = GCRY_MD_SHA512; /* ecdsa-with-sha512 */ else if (!algo) { log_error ("unknown digest algorithm '%s' used in certificate\n", algoid? algoid:"?"); if (algoid && ( !strcmp (algoid, "1.2.840.113549.1.1.2") ||!strcmp (algoid, "1.2.840.113549.2.2"))) log_info (_("(this is the MD2 algorithm)\n")); return gpg_error (GPG_ERR_GENERAL); } /* The the signature from the certificate. */ p = ksba_cert_get_sig_val (cert); n = gcry_sexp_canon_len (p, 0, NULL, NULL); if (!n) { log_error ("libksba did not return a proper S-Exp\n"); ksba_free (p); return gpg_error (GPG_ERR_BUG); } rc = gcry_sexp_sscan ( &s_sig, NULL, (char*)p, n); ksba_free (p); if (rc) { log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc)); return rc; } if (DBG_CRYPTO) gcry_log_debugsxp ("sigval", s_sig); if (use_pss) { rc = extract_pss_params (s_sig, &algo, &saltlen); if (rc) { gcry_sexp_release (s_sig); return rc; } } /* Hash the to-be-signed parts of the certificate or but them into a * buffer for the EdDSA algorithms. */ if (use_eddsa) { membuf_t mb; init_membuf (&mb, 2048); rc = ksba_cert_hash (cert, 1, (void (*)(void *, const void*,size_t))put_membuf, &mb); if (rc) { log_error ("ksba_cert_hash failed: %s\n", gpg_strerror (rc)); xfree (get_membuf (&mb, NULL)); return rc; } certder = get_membuf (&mb, &certderlen); if (!certder) { rc = gpg_error_from_syserror (); log_error ("getting tbsCertificate failed: %s\n", gpg_strerror (rc)); return rc; } } else { rc = gcry_md_open (&md, algo, 0); if (rc) { log_error ("md_open failed: %s\n", gpg_strerror (rc)); return rc; } if (DBG_HASHING) gcry_md_debug (md, "hash.cert"); rc = ksba_cert_hash (cert, 1, HASH_FNC, md); if (rc) { log_error ("ksba_cert_hash failed: %s\n", gpg_strerror (rc)); gcry_md_close (md); return rc; } gcry_md_final (md); } /* Get the public key from the certificate. */ p = ksba_cert_get_public_key (issuer_cert); n = gcry_sexp_canon_len (p, 0, NULL, NULL); if (!n) { log_error ("libksba did not return a proper S-Exp\n"); gcry_md_close (md); ksba_free (p); gcry_sexp_release (s_sig); xfree (certder); return gpg_error (GPG_ERR_BUG); } rc = gcry_sexp_sscan ( &s_pkey, NULL, (char*)p, n); ksba_free (p); if (rc) { log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc)); gcry_md_close (md); gcry_sexp_release (s_sig); xfree (certder); return rc; } if (DBG_CRYPTO) gcry_log_debugsxp ("pubkey:", s_pkey); if (use_pss) { rc = gcry_sexp_build (&s_data, NULL, "(data (flags pss)" "(hash %s %b)" "(salt-length %u))", hash_algo_to_string (algo), (int)gcry_md_get_algo_dlen (algo), gcry_md_read (md, algo), saltlen); if (rc) BUG (); } else if (use_eddsa) { rc = gcry_sexp_build (&s_data, NULL, "(data(flags eddsa)(hash-algo %s)(value %b))", use_eddsa == 1? "sha512":"shake256", (int)certderlen, certder); xfree (certder); certder = NULL; if (rc) { log_error ("building data for eddsa failed: %s\n", gpg_strerror (rc)); gcry_sexp_release (s_sig); return rc; } } else { /* RSA or DSA: Prepare the hash for verification. */ gcry_mpi_t frame; rc = do_encode_md (md, algo, pk_algo_from_sexp (s_pkey), gcry_pk_get_nbits (s_pkey), s_pkey, &frame); if (rc) { gcry_md_close (md); gcry_sexp_release (s_sig); gcry_sexp_release (s_pkey); return rc; } if ( gcry_sexp_build (&s_data, NULL, "%m", frame) ) BUG (); gcry_mpi_release (frame); } if (DBG_CRYPTO) gcry_log_debugsxp ("data:", s_data); /* Verify. */ rc = gcry_pk_verify (s_sig, s_data, s_pkey); if (DBG_X509) log_debug ("gcry_pk_verify: %s\n", gpg_strerror (rc)); if (use_eddsa && (gpg_err_code (rc) == GPG_ERR_INTERNAL || gpg_err_code (rc) == GPG_ERR_INV_CURVE)) { /* Let's assume that this is a certificate for an ECDH key * signed using EdDSA. This won't work. We should have located * the public key using subjectKeyIdentifier (SKI) to not run * into this problem. However, we don't do this for self-signed * certificates and we don't have a way to search for arbitrary * keys based on the SKI. Note: The sample certificate from * RFC-8410 uses a SHA-1 hash of the public key for the SKI; so * we are not able to verify it. */ ksba_sexp_t ski; const unsigned char *skider; size_t skiderlen; if (DBG_X509) log_debug ("retrying using the ski\n"); if (!ksba_cert_get_subj_key_id (issuer_cert, NULL, &ski)) { skider = gpgsm_get_serial (ski, &skiderlen); if (!skider) ; else if (skiderlen == (use_eddsa==1? 32:57)) { /* Here we assume that the SKI is actually the public key. */ gcry_sexp_release (s_pkey); rc = gcry_sexp_build (&s_pkey, NULL, "(public-key(ecc(curve%s)(q%b)))", use_eddsa==1? "1.3.101.112":"1.3.101.113", (int)skiderlen, skider); if (rc) log_error ("building pubkey from SKI failed: %s\n", gpg_strerror (rc)); else rc = gcry_pk_verify (s_sig, s_data, s_pkey); if (DBG_X509) log_debug ("gcry_pk_verify: %s\n", gpg_strerror (rc)); } else if (skiderlen == 20) { log_printhex (skider, skiderlen, "ski might be the SHA-1:"); } else { if (DBG_X509) log_debug(skider, skiderlen, "ski is:"); } ksba_free (ski); } } gcry_md_close (md); gcry_sexp_release (s_sig); gcry_sexp_release (s_data); gcry_sexp_release (s_pkey); return rc; } int gpgsm_check_cms_signature (ksba_cert_t cert, gcry_sexp_t s_sig, gcry_md_hd_t md, int mdalgo, unsigned int pkalgoflags, int *r_pkalgo) { int rc; ksba_sexp_t p; gcry_sexp_t s_hash, s_pkey; size_t n; int pkalgo; int use_pss; unsigned int saltlen = 0; if (r_pkalgo) *r_pkalgo = 0; /* Check whether rsaPSS is needed. This information is indicated in * the SIG-VAL and already provided to us by the caller so that we * do not need to parse this out. */ use_pss = !!(pkalgoflags & PK_ALGO_FLAG_RSAPSS); if (use_pss) { int algo; rc = extract_pss_params (s_sig, &algo, &saltlen); if (rc) { gcry_sexp_release (s_sig); return rc; } if (algo != mdalgo) { log_error ("PSS hash algo mismatch (%d/%d)\n", mdalgo, algo); gcry_sexp_release (s_sig); return gpg_error (GPG_ERR_DIGEST_ALGO); } } p = ksba_cert_get_public_key (cert); n = gcry_sexp_canon_len (p, 0, NULL, NULL); if (!n) { log_error ("libksba did not return a proper S-Exp\n"); ksba_free (p); return gpg_error (GPG_ERR_BUG); } if (DBG_CRYPTO) log_printhex (p, n, "public key: "); rc = gcry_sexp_sscan ( &s_pkey, NULL, (char*)p, n); ksba_free (p); if (rc) { log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc)); return rc; } pkalgo = pk_algo_from_sexp (s_pkey); if (r_pkalgo) *r_pkalgo = pkalgo; if (use_pss) { rc = gcry_sexp_build (&s_hash, NULL, "(data (flags pss)" "(hash %s %b)" "(salt-length %u))", hash_algo_to_string (mdalgo), (int)gcry_md_get_algo_dlen (mdalgo), gcry_md_read (md, mdalgo), saltlen); if (rc) BUG (); } else { /* RSA or DSA: Prepare the hash for verification. */ gcry_mpi_t frame; rc = do_encode_md (md, mdalgo, pkalgo, gcry_pk_get_nbits (s_pkey), s_pkey, &frame); if (rc) { gcry_sexp_release (s_pkey); return rc; } /* put hash into the S-Exp s_hash */ if ( gcry_sexp_build (&s_hash, NULL, "%m", frame) ) BUG (); gcry_mpi_release (frame); } rc = gcry_pk_verify (s_sig, s_hash, s_pkey); if (DBG_X509) log_debug ("gcry_pk_verify: %s\n", gpg_strerror (rc)); gcry_sexp_release (s_hash); gcry_sexp_release (s_pkey); return rc; } int gpgsm_create_cms_signature (ctrl_t ctrl, ksba_cert_t cert, gcry_md_hd_t md, int mdalgo, unsigned char **r_sigval) { int rc; char *grip, *desc; size_t siglen; grip = gpgsm_get_keygrip_hexstring (cert); if (!grip) return gpg_error (GPG_ERR_BAD_CERT); desc = gpgsm_format_keydesc (cert); rc = gpgsm_agent_pksign (ctrl, grip, desc, gcry_md_read(md, mdalgo), gcry_md_get_algo_dlen (mdalgo), mdalgo, r_sigval, &siglen); xfree (desc); xfree (grip); return rc; } diff --git a/sm/certdump.c b/sm/certdump.c index 62451ba95..ae257d232 100644 --- a/sm/certdump.c +++ b/sm/certdump.c @@ -1,963 +1,962 @@ /* certdump.c - Dump a certificate for debugging * Copyright (C) 2001-2010, 2014-2015 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include -#include #ifdef HAVE_LOCALE_H #include #endif #ifdef HAVE_LANGINFO_CODESET #include #endif #include "gpgsm.h" #include #include #include "keydb.h" #include "../common/i18n.h" #include "../common/membuf.h" struct dn_array_s { char *key; char *value; int multivalued; int done; }; /* Get the first first element from the s-expression SN and return a * pointer to it. Stores the length at R_LENGTH. Returns NULL for no * value or an invalid expression. */ const void * gpgsm_get_serial (ksba_const_sexp_t sn, size_t *r_length) { const char *p = (const char *)sn; unsigned long n; char *endp; if (!p || *p != '(') return NULL; p++; n = strtoul (p, &endp, 10); p = endp; if (*p++ != ':') return NULL; *r_length = n; return p; } /* Print the first element of an S-Expression. */ void gpgsm_print_serial (estream_t fp, ksba_const_sexp_t sn) { const char *p = (const char *)sn; unsigned long n; char *endp; if (!p) es_fputs (_("none"), fp); else if (*p != '(') es_fputs ("[Internal error - not an S-expression]", fp); else { p++; n = strtoul (p, &endp, 10); p = endp; if (*p++ != ':') es_fputs ("[Internal Error - invalid S-expression]", fp); else es_write_hexstring (fp, p, n, 0, NULL); } } /* Print the first element of an S-Expression in decimal notation * assuming it is a non-negative integer. */ void gpgsm_print_serial_decimal (estream_t fp, ksba_const_sexp_t sn) { const char *p = (const char *)sn; unsigned long n, i; char *endp; gcry_mpi_t a, r, ten; #if GCRYPT_VERSION_NUMBER >= 0x010900 /* >= 1.9.0 */ unsigned int dd; #else unsigned char numbuf[10]; #endif if (!p) es_fputs (_("none"), fp); else if (*p != '(') es_fputs ("[Internal error - not an S-expression]", fp); else { p++; n = strtoul (p, &endp, 10); p = endp; if (*p++ != ':') es_fputs ("[Internal Error - invalid S-expression]", fp); else if (gcry_mpi_scan (&a, GCRYMPI_FMT_USG, p, n, NULL)) es_fputs ("[Internal Error - can't convert to decimal]", fp); else { membuf_t mb = MEMBUF_ZERO; char *buf; int c; ten = gcry_mpi_set_ui (NULL, 10); r = gcry_mpi_new (0); do { gcry_mpi_div (a, r, a, ten, 0); #if GCRYPT_VERSION_NUMBER >= 0x010900 /* >= 1.9.0 */ gcry_mpi_get_ui (&dd, r); put_membuf_printf (&mb, "%u", dd); #else *numbuf = 0; /* Need to clear because USB format prints * an empty string for a value of 0. */ gcry_mpi_print (GCRYMPI_FMT_USG, numbuf, 10, NULL, r); put_membuf_printf (&mb, "%u", (unsigned int)*numbuf); #endif } while (gcry_mpi_cmp_ui (a, 0)); /* Make sure we have at least an empty string, get it, * reverse it, and print it. */ put_membuf (&mb, "", 1); buf = get_membuf (&mb, NULL); if (!buf) es_fputs ("[Internal Error - out of core]", fp); else { n = strlen (buf); for (i=0; i < n/2; i++) { c = buf[i]; buf[i] = buf[n-1-i]; buf[n-1-i] = c; } es_fputs (buf, fp); xfree (buf); } gcry_mpi_release (r); gcry_mpi_release (ten); gcry_mpi_release (a); } } } /* Dump the serial number or any other simple S-expression. */ void gpgsm_dump_serial (ksba_const_sexp_t sn) { const char *p = (const char *)sn; unsigned long n; char *endp; if (!p) log_printf ("none"); else if (*p != '(') log_printf ("ERROR - not an S-expression"); else { p++; n = strtoul (p, &endp, 10); p = endp; if (*p!=':') log_printf ("ERROR - invalid S-expression"); else { for (p++; n; n--, p++) log_printf ("%02X", *(const unsigned char *)p); } } } char * gpgsm_format_serial (ksba_const_sexp_t sn) { const char *p = (const char *)sn; unsigned long n; char *endp; char *buffer; int i; if (!p) return NULL; if (*p != '(') BUG (); /* Not a valid S-expression. */ p++; n = strtoul (p, &endp, 10); p = endp; if (*p!=':') BUG (); /* Not a valid S-expression. */ p++; buffer = xtrymalloc (n*2+1); if (buffer) { for (i=0; n; n--, p++, i+=2) sprintf (buffer+i, "%02X", *(unsigned char *)p); buffer[i] = 0; } return buffer; } void gpgsm_print_time (estream_t fp, ksba_isotime_t t) { if (!t || !*t) es_fputs (_("none"), fp); else es_fprintf (fp, "%.4s-%.2s-%.2s %.2s:%.2s:%s", t, t+4, t+6, t+9, t+11, t+13); } void gpgsm_dump_string (const char *string) { if (!string) log_printf ("[error]"); else { const unsigned char *s; for (s=(const unsigned char*)string; *s; s++) { if (*s < ' ' || (*s >= 0x7f && *s <= 0xa0)) break; } if (!*s && *string != '[') log_printf ("%s", string); else { log_printf ( "[ "); log_printhex (string, strlen (string), NULL); log_printf ( " ]"); } } } /* This simple dump function is mainly used for debugging purposes. */ void gpgsm_dump_cert (const char *text, ksba_cert_t cert) { ksba_sexp_t sexp; char *p; char *dn; ksba_isotime_t t; log_debug ("BEGIN Certificate '%s':\n", text? text:""); if (cert) { sexp = ksba_cert_get_serial (cert); log_debug (" serial: "); gpgsm_dump_serial (sexp); ksba_free (sexp); log_printf ("\n"); ksba_cert_get_validity (cert, 0, t); log_debug (" notBefore: "); dump_isotime (t); log_printf ("\n"); ksba_cert_get_validity (cert, 1, t); log_debug (" notAfter: "); dump_isotime (t); log_printf ("\n"); dn = ksba_cert_get_issuer (cert, 0); log_debug (" issuer: "); gpgsm_dump_string (dn); ksba_free (dn); log_printf ("\n"); dn = ksba_cert_get_subject (cert, 0); log_debug (" subject: "); gpgsm_dump_string (dn); ksba_free (dn); log_printf ("\n"); log_debug (" hash algo: %s\n", ksba_cert_get_digest_algo (cert)); p = gpgsm_get_fingerprint_string (cert, 0); log_debug (" SHA1 Fingerprint: %s\n", p); xfree (p); } log_debug ("END Certificate\n"); } /* Return a new string holding the format serial number and issuer ("#SN/issuer"). No filtering on invalid characters is done. Caller must release the string. On memory failure NULL is returned. */ char * gpgsm_format_sn_issuer (ksba_sexp_t sn, const char *issuer) { char *p, *p1; if (sn && issuer) { p1 = gpgsm_format_serial (sn); if (!p1) p = xtrystrdup ("[invalid SN]"); else { p = xtrymalloc (strlen (p1) + strlen (issuer) + 2 + 1); if (p) { *p = '#'; strcpy (stpcpy (stpcpy (p+1, p1),"/"), issuer); } xfree (p1); } } else p = xtrystrdup ("[invalid SN/issuer]"); return p; } /* Log the certificate's name in "#SN/ISSUERDN" format along with TEXT. */ void gpgsm_cert_log_name (const char *text, ksba_cert_t cert) { log_info ("%s", text? text:"certificate" ); if (cert) { ksba_sexp_t sn; char *p; p = ksba_cert_get_issuer (cert, 0); sn = ksba_cert_get_serial (cert); if (p && sn) { log_printf (" #"); gpgsm_dump_serial (sn); log_printf ("/"); gpgsm_dump_string (p); } else log_printf (" [invalid]"); ksba_free (sn); xfree (p); } log_printf ("\n"); } /* helper for the rfc2253 string parser */ static const unsigned char * parse_dn_part (struct dn_array_s *array, const unsigned char *string) { static struct { const char *label; const char *oid; } label_map[] = { /* Warning: When adding new labels, make sure that the buffer below we be allocated large enough. */ {"EMail", "1.2.840.113549.1.9.1" }, {"T", "2.5.4.12" }, {"GN", "2.5.4.42" }, {"SN", "2.5.4.4" }, {"NameDistinguisher", "0.2.262.1.10.7.20"}, {"ADDR", "2.5.4.16" }, {"BC", "2.5.4.15" }, {"D", "2.5.4.13" }, {"PostalCode", "2.5.4.17" }, {"Pseudo", "2.5.4.65" }, {"SerialNumber", "2.5.4.5" }, {NULL, NULL} }; const unsigned char *s, *s1; size_t n; char *p; int i; /* Parse attributeType */ for (s = string+1; *s && *s != '='; s++) ; if (!*s) return NULL; /* error */ n = s - string; if (!n) return NULL; /* empty key */ /* We need to allocate a few bytes more due to the possible mapping from the shorter OID to the longer label. */ array->key = p = xtrymalloc (n+10); if (!array->key) return NULL; memcpy (p, string, n); p[n] = 0; trim_trailing_spaces (p); if (digitp (p)) { for (i=0; label_map[i].label; i++ ) if ( !strcmp (p, label_map[i].oid) ) { strcpy (p, label_map[i].label); break; } } string = s + 1; if (*string == '#') { /* hexstring */ string++; for (s=string; hexdigitp (s); s++) s++; n = s - string; if (!n || (n & 1)) return NULL; /* Empty or odd number of digits. */ n /= 2; array->value = p = xtrymalloc (n+1); if (!p) return NULL; for (s1=string; n; s1 += 2, n--, p++) { *(unsigned char *)p = xtoi_2 (s1); if (!*p) *p = 0x01; /* Better print a wrong value than truncating the string. */ } *p = 0; } else { /* regular v3 quoted string */ for (n=0, s=string; *s; s++) { if (*s == '\\') { /* pair */ s++; if (*s == ',' || *s == '=' || *s == '+' || *s == '<' || *s == '>' || *s == '#' || *s == ';' || *s == '\\' || *s == '\"' || *s == ' ') n++; else if (hexdigitp (s) && hexdigitp (s+1)) { s++; n++; } else return NULL; /* invalid escape sequence */ } else if (*s == '\"') return NULL; /* invalid encoding */ else if (*s == ',' || *s == '=' || *s == '+' || *s == '<' || *s == '>' || *s == ';' ) break; else n++; } array->value = p = xtrymalloc (n+1); if (!p) return NULL; for (s=string; n; s++, n--) { if (*s == '\\') { s++; if (hexdigitp (s)) { *(unsigned char *)p++ = xtoi_2 (s); s++; } else *p++ = *s; } else *p++ = *s; } *p = 0; } return s; } /* Parse a DN and return an array-ized one. This is not a validating parser and it does not support any old-stylish syntax; KSBA is expected to return only rfc2253 compatible strings. */ static struct dn_array_s * parse_dn (const unsigned char *string) { struct dn_array_s *array; size_t arrayidx, arraysize; int i; arraysize = 7; /* C,ST,L,O,OU,CN,email */ arrayidx = 0; array = xtrymalloc ((arraysize+1) * sizeof *array); if (!array) return NULL; while (*string) { while (*string == ' ') string++; if (!*string) break; /* ready */ if (arrayidx >= arraysize) { struct dn_array_s *a2; arraysize += 5; a2 = xtryrealloc (array, (arraysize+1) * sizeof *array); if (!a2) goto failure; array = a2; } array[arrayidx].key = NULL; array[arrayidx].value = NULL; string = parse_dn_part (array+arrayidx, string); if (!string) goto failure; while (*string == ' ') string++; array[arrayidx].multivalued = (*string == '+'); array[arrayidx].done = 0; arrayidx++; if (*string && *string != ',' && *string != ';' && *string != '+') goto failure; /* invalid delimiter */ if (*string) string++; } array[arrayidx].key = NULL; array[arrayidx].value = NULL; return array; failure: for (i=0; i < arrayidx; i++) { xfree (array[i].key); xfree (array[i].value); } xfree (array); return NULL; } /* Print a DN part to STREAM. */ static void print_dn_part (estream_t stream, struct dn_array_s *dn, const char *key, int translate) { struct dn_array_s *first_dn = dn; for (; dn->key; dn++) { if (!dn->done && !strcmp (dn->key, key)) { /* Forward to the last multi-valued RDN, so that we can print them all in reverse in the correct order. Note that this overrides the standard sequence but that seems to a reasonable thing to do with multi-valued RDNs. */ while (dn->multivalued && dn[1].key) dn++; next: if (!dn->done && dn->value && *dn->value) { es_fprintf (stream, "/%s=", dn->key); if (translate) print_utf8_buffer3 (stream, dn->value, strlen (dn->value), "/"); else es_write_sanitized (stream, dn->value, strlen (dn->value), "/", NULL); } dn->done = 1; if (dn > first_dn && dn[-1].multivalued) { dn--; goto next; } } } } /* Print all parts of a DN in a "standard" sequence. We first print all the known parts, followed by the uncommon ones */ static void print_dn_parts (estream_t stream, struct dn_array_s *dn, int translate) { const char *stdpart[] = { "CN", "OU", "O", "STREET", "L", "ST", "C", "EMail", NULL }; int i; for (i=0; stdpart[i]; i++) print_dn_part (stream, dn, stdpart[i], translate); /* Now print the rest without any specific ordering */ for (; dn->key; dn++) print_dn_part (stream, dn, dn->key, translate); } /* Print the S-Expression in BUF to extended STREAM, which has a valid length of BUFLEN, as a human readable string in one line to FP. */ static void pretty_es_print_sexp (estream_t fp, const unsigned char *buf, size_t buflen) { size_t len; gcry_sexp_t sexp; char *result, *p; if ( gcry_sexp_sscan (&sexp, NULL, (const char*)buf, buflen) ) { es_fputs (_("[Error - invalid encoding]"), fp); return; } len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0); - assert (len); + log_assert (len); result = xtrymalloc (len); if (!result) { es_fputs (_("[Error - out of core]"), fp); gcry_sexp_release (sexp); return; } len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, result, len); - assert (len); + log_assert (len); for (p = result; len; len--, p++) { if (*p == '\n') { if (len > 1) /* Avoid printing the trailing LF. */ es_fputs ("\\n", fp); } else if (*p == '\r') es_fputs ("\\r", fp); else if (*p == '\v') es_fputs ("\\v", fp); else if (*p == '\t') es_fputs ("\\t", fp); else es_putc (*p, fp); } xfree (result); gcry_sexp_release (sexp); } /* This is a variant of gpgsm_print_name sending it output to an estream. */ void gpgsm_es_print_name2 (estream_t fp, const char *name, int translate) { const unsigned char *s = (const unsigned char *)name; int i; if (!s) { es_fputs (_("[Error - No name]"), fp); } else if (*s == '<') { const char *s2 = strchr ( (char*)s+1, '>'); if (s2) { if (translate) print_utf8_buffer (fp, s + 1, s2 - (char*)s - 1); else es_write_sanitized (fp, s + 1, s2 - (char*)s - 1, NULL, NULL); } } else if (*s == '(') { pretty_es_print_sexp (fp, s, gcry_sexp_canon_len (s, 0, NULL, NULL)); } else if (!((*s >= '0' && *s < '9') || (*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z'))) es_fputs (_("[Error - invalid encoding]"), fp); else { struct dn_array_s *dn = parse_dn (s); if (!dn) es_fputs (_("[Error - invalid DN]"), fp); else { print_dn_parts (fp, dn, translate); for (i=0; dn[i].key; i++) { xfree (dn[i].key); xfree (dn[i].value); } xfree (dn); } } } void gpgsm_es_print_name (estream_t fp, const char *name) { gpgsm_es_print_name2 (fp, name, 1); } /* A cookie structure used for the memory stream. */ struct format_name_cookie { char *buffer; /* Malloced buffer with the data to deliver. */ size_t size; /* Allocated size of this buffer. */ size_t len; /* strlen (buffer). */ int error; /* system error code if any. */ }; /* The writer function for the memory stream. */ static gpgrt_ssize_t format_name_writer (void *cookie, const void *buffer, size_t size) { struct format_name_cookie *c = cookie; char *p; /* FIXME: Replace the whole thing using es_fopenmem based code. */ if (!buffer) /* Flush. */ return 0; /* (Actually we could use SIZE because that should be 0 too.) */ if (!c->buffer) { p = xtrymalloc (size + 1 + 1); if (p) { c->size = size + 1; c->buffer = p; c->len = 0; } } else if (c->len + size < c->len) { p = NULL; gpg_err_set_errno (ENOMEM); } else if (c->size < c->len + size) { p = xtryrealloc (c->buffer, c->len + size + 1); if (p) { c->size = c->len + size; c->buffer = p; } } else p = c->buffer; if (!p) { c->error = errno; xfree (c->buffer); c->buffer = NULL; gpg_err_set_errno (c->error); return -1; } memcpy (p + c->len, buffer, size); c->len += size; p[c->len] = 0; /* Terminate string. */ return (gpgrt_ssize_t)size; } /* Format NAME which is expected to be in rfc2253 format into a better human readable format. Caller must free the returned string. NULL is returned in case of an error. With TRANSLATE set to true the name will be translated to the native encoding. Note that NAME is internally always UTF-8 encoded. */ char * gpgsm_format_name2 (const char *name, int translate) { estream_t fp; struct format_name_cookie cookie; es_cookie_io_functions_t io = { NULL }; memset (&cookie, 0, sizeof cookie); io.func_write = format_name_writer; fp = es_fopencookie (&cookie, "w", io); if (!fp) { int save_errno = errno; log_error ("error creating memory stream: %s\n", strerror (save_errno)); gpg_err_set_errno (save_errno); return NULL; } gpgsm_es_print_name2 (fp, name, translate); es_fclose (fp); if (cookie.error || !cookie.buffer) { xfree (cookie.buffer); gpg_err_set_errno (cookie.error); return NULL; } return cookie.buffer; } char * gpgsm_format_name (const char *name) { return gpgsm_format_name2 (name, 1); } /* Return fingerprint and a percent escaped name in a human readable format suitable for status messages like GOODSIG. May return NULL on error (out of core). */ char * gpgsm_fpr_and_name_for_status (ksba_cert_t cert) { char *fpr, *name, *p; char *buffer; fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); if (!fpr) return NULL; name = ksba_cert_get_subject (cert, 0); if (!name) { xfree (fpr); return NULL; } p = gpgsm_format_name2 (name, 0); ksba_free (name); name = p; if (!name) { xfree (fpr); return NULL; } buffer = xtrymalloc (strlen (fpr) + 1 + 3*strlen (name) + 1); if (buffer) { const char *s; p = stpcpy (stpcpy (buffer, fpr), " "); for (s = name; *s; s++) { if (*s < ' ') { sprintf (p, "%%%02X", *(const unsigned char*)s); p += 3; } else *p++ = *s; } *p = 0; } xfree (fpr); xfree (name); return buffer; } /* Create a key description for the CERT, this may be passed to the pinentry. The caller must free the returned string. NULL may be returned on error. */ char * gpgsm_format_keydesc (ksba_cert_t cert) { char *name, *subject, *buffer; ksba_isotime_t t; char created[20]; char expires[20]; char *sn; ksba_sexp_t sexp; char *orig_codeset; name = ksba_cert_get_subject (cert, 0); subject = name? gpgsm_format_name2 (name, 0) : NULL; ksba_free (name); name = NULL; sexp = ksba_cert_get_serial (cert); sn = sexp? gpgsm_format_serial (sexp) : NULL; ksba_free (sexp); ksba_cert_get_validity (cert, 0, t); if (*t) sprintf (created, "%.4s-%.2s-%.2s", t, t+4, t+6); else *created = 0; ksba_cert_get_validity (cert, 1, t); if (*t) sprintf (expires, "%.4s-%.2s-%.2s", t, t+4, t+6); else *expires = 0; orig_codeset = i18n_switchto_utf8 (); name = xtryasprintf (_("Please enter the passphrase to unlock the" " secret key for the X.509 certificate:\n" "\"%s\"\n" "S/N %s, ID 0x%08lX,\n" "created %s, expires %s.\n" ), subject? subject:"?", sn? sn: "?", gpgsm_get_short_fingerprint (cert, NULL), created, expires); i18n_switchback (orig_codeset); if (!name) { xfree (subject); xfree (sn); return NULL; } xfree (subject); xfree (sn); buffer = percent_plus_escape (name); xfree (name); return buffer; } diff --git a/sm/certlist.c b/sm/certlist.c index b3d113bfd..95666dc8f 100644 --- a/sm/certlist.c +++ b/sm/certlist.c @@ -1,608 +1,607 @@ /* certlist.c - build list of certificates * Copyright (C) 2001, 2003, 2004, 2005, 2007, * 2008, 2011 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include -#include #include "gpgsm.h" #include #include #include "keydb.h" #include "../common/i18n.h" static const char oid_kp_serverAuth[] = "1.3.6.1.5.5.7.3.1"; static const char oid_kp_clientAuth[] = "1.3.6.1.5.5.7.3.2"; static const char oid_kp_codeSigning[] = "1.3.6.1.5.5.7.3.3"; static const char oid_kp_emailProtection[]= "1.3.6.1.5.5.7.3.4"; static const char oid_kp_timeStamping[] = "1.3.6.1.5.5.7.3.8"; static const char oid_kp_ocspSigning[] = "1.3.6.1.5.5.7.3.9"; /* Return 0 if the cert is usable for encryption. A MODE of 0 checks for signing a MODE of 1 checks for encryption, a MODE of 2 checks for verification and a MODE of 3 for decryption (just for debugging). MODE 4 is for certificate signing, MODE for COSP response signing. */ static int cert_usage_p (ksba_cert_t cert, int mode, int silent) { gpg_error_t err; unsigned int use; 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 */ if (!err) { unsigned int extusemask = ~0; /* Allow all. */ if (extkeyusages) { char *p, *pend; int any_critical = 0; extusemask = 0; p = extkeyusages; while (p && (pend=strchr (p, ':'))) { *pend++ = 0; /* Only care about critical flagged usages. */ if ( *pend == 'C' ) { any_critical = 1; if ( !strcmp (p, oid_kp_serverAuth)) extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE | KSBA_KEYUSAGE_KEY_ENCIPHERMENT | KSBA_KEYUSAGE_KEY_AGREEMENT); else if ( !strcmp (p, oid_kp_clientAuth)) extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE | KSBA_KEYUSAGE_KEY_AGREEMENT); else if ( !strcmp (p, oid_kp_codeSigning)) extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE); else if ( !strcmp (p, oid_kp_emailProtection)) extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE | KSBA_KEYUSAGE_NON_REPUDIATION | KSBA_KEYUSAGE_KEY_ENCIPHERMENT | KSBA_KEYUSAGE_KEY_AGREEMENT); else if ( !strcmp (p, oid_kp_timeStamping)) extusemask |= (KSBA_KEYUSAGE_DIGITAL_SIGNATURE | KSBA_KEYUSAGE_NON_REPUDIATION); } /* This is a hack to cope with OCSP. Note that we do not yet fully comply with the requirements and that the entire CRL/OCSP checking thing should undergo a thorough review and probably redesign. */ if ( !strcmp (p, oid_kp_ocspSigning)) have_ocsp_signing = 1; if ((p = strchr (pend, '\n'))) p++; } xfree (extkeyusages); extkeyusages = NULL; if (!any_critical) extusemask = ~0; /* Reset to the don't care mask. */ } err = ksba_cert_get_key_usage (cert, &use); if (gpg_err_code (err) == GPG_ERR_NO_DATA) { err = 0; if (opt.verbose && mode < 2 && !silent) log_info (_("no key usage specified - assuming all usages\n")); use = ~0; } /* Apply extKeyUsage. */ use &= extusemask; } if (err) { log_error (_("error getting key usage information: %s\n"), gpg_strerror (err)); xfree (extkeyusages); return err; } if (mode == 4) { if ((use & (KSBA_KEYUSAGE_KEY_CERT_SIGN))) return 0; if (!silent) log_info (_("certificate should not have " "been used for certification\n")); return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } if (mode == 5) { if (use != ~0 && (have_ocsp_signing || (use & (KSBA_KEYUSAGE_KEY_CERT_SIGN |KSBA_KEYUSAGE_CRL_SIGN)))) return 0; if (!silent) log_info (_("certificate should not have " "been used for OCSP response signing\n")); 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))) ) return 0; if (!silent) log_info (mode==3? _("certificate should not have been used for encryption\n"): mode==2? _("certificate should not have been used for signing\n"): mode==1? _("certificate is not usable for encryption\n"): /**/ _("certificate is not usable for signing\n")); return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } /* Return 0 if the cert is usable for signing */ int gpgsm_cert_use_sign_p (ksba_cert_t cert, int silent) { return cert_usage_p (cert, 0, silent); } /* Return 0 if the cert is usable for encryption */ int gpgsm_cert_use_encrypt_p (ksba_cert_t cert) { return cert_usage_p (cert, 1, 0); } int gpgsm_cert_use_verify_p (ksba_cert_t cert) { return cert_usage_p (cert, 2, 0); } int gpgsm_cert_use_decrypt_p (ksba_cert_t cert) { return cert_usage_p (cert, 3, 0); } int gpgsm_cert_use_cert_p (ksba_cert_t cert) { return cert_usage_p (cert, 4, 0); } int gpgsm_cert_use_ocsp_p (ksba_cert_t cert) { return cert_usage_p (cert, 5, 0); } /* Return true if CERT has the well known private key extension. */ int gpgsm_cert_has_well_known_private_key (ksba_cert_t cert) { int idx; const char *oid; for (idx=0; !ksba_cert_get_extension (cert, idx, &oid, NULL, NULL, NULL);idx++) if (!strcmp (oid, "1.3.6.1.4.1.11591.2.2.2") ) return 1; /* Yes. */ return 0; /* No. */ } static int same_subject_issuer (const char *subject, const char *issuer, ksba_cert_t cert) { char *subject2 = ksba_cert_get_subject (cert, 0); char *issuer2 = ksba_cert_get_issuer (cert, 0); int tmp; tmp = (subject && subject2 && !strcmp (subject, subject2) && issuer && issuer2 && !strcmp (issuer, issuer2)); xfree (subject2); xfree (issuer2); return tmp; } /* Return true if CERT_A is the same as CERT_B. */ int gpgsm_certs_identical_p (ksba_cert_t cert_a, ksba_cert_t cert_b) { const unsigned char *img_a, *img_b; size_t len_a, len_b; img_a = ksba_cert_get_image (cert_a, &len_a); if (img_a) { img_b = ksba_cert_get_image (cert_b, &len_b); if (img_b && len_a == len_b && !memcmp (img_a, img_b, len_a)) return 1; /* Identical. */ } return 0; } /* Return true if CERT is already contained in CERTLIST. */ static int is_cert_in_certlist (ksba_cert_t cert, certlist_t certlist) { const unsigned char *img_a, *img_b; size_t len_a, len_b; img_a = ksba_cert_get_image (cert, &len_a); if (img_a) { for ( ; certlist; certlist = certlist->next) { img_b = ksba_cert_get_image (certlist->cert, &len_b); if (img_b && len_a == len_b && !memcmp (img_a, img_b, len_a)) return 1; /* Already contained. */ } } return 0; } /* Add CERT to the list of certificates at CERTADDR but avoid duplicates. */ int gpgsm_add_cert_to_certlist (ctrl_t ctrl, ksba_cert_t cert, certlist_t *listaddr, int is_encrypt_to) { (void)ctrl; if (!is_cert_in_certlist (cert, *listaddr)) { certlist_t cl = xtrycalloc (1, sizeof *cl); if (!cl) return out_of_core (); cl->cert = cert; ksba_cert_ref (cert); cl->next = *listaddr; cl->is_encrypt_to = is_encrypt_to; *listaddr = cl; } return 0; } /* Add a certificate to a list of certificate and make sure that it is a valid certificate. With SECRET set to true a secret key must be available for the certificate. IS_ENCRYPT_TO sets the corresponding flag in the new create LISTADDR item. */ int gpgsm_add_to_certlist (ctrl_t ctrl, const char *name, int secret, certlist_t *listaddr, int is_encrypt_to) { int rc; KEYDB_SEARCH_DESC desc; KEYDB_HANDLE kh = NULL; ksba_cert_t cert = NULL; rc = classify_user_id (name, &desc, 0); if (!rc) { kh = keydb_new (); if (!kh) rc = gpg_error (GPG_ERR_ENOMEM); else { int wrong_usage = 0; char *first_subject = NULL; char *first_issuer = NULL; get_next: rc = keydb_search (ctrl, kh, &desc, 1); if (!rc) rc = keydb_get_cert (kh, &cert); if (!rc) { if (!first_subject) { /* Save the subject and the issuer for key usage and ambiguous name tests. */ first_subject = ksba_cert_get_subject (cert, 0); first_issuer = ksba_cert_get_issuer (cert, 0); } rc = secret? gpgsm_cert_use_sign_p (cert, 0) : gpgsm_cert_use_encrypt_p (cert); if (gpg_err_code (rc) == GPG_ERR_WRONG_KEY_USAGE) { /* There might be another certificate with the correct usage, so we try again */ if (!wrong_usage || same_subject_issuer (first_subject, first_issuer,cert)) { if (!wrong_usage) wrong_usage = rc; /* save error of the first match */ ksba_cert_release (cert); cert = NULL; log_info (_("looking for another certificate\n")); goto get_next; } else wrong_usage = rc; } } /* We want the error code from the first match in this case. */ if (rc && wrong_usage) rc = wrong_usage; if (!rc) { certlist_t dup_certs = NULL; next_ambigious: rc = keydb_search (ctrl, kh, &desc, 1); if (rc == -1) rc = 0; else if (!rc) { ksba_cert_t cert2 = NULL; /* If this is the first possible duplicate, add the original certificate to our list of duplicates. */ if (!dup_certs) gpgsm_add_cert_to_certlist (ctrl, cert, &dup_certs, 0); /* We have to ignore ambiguous names as long as there only fault is a bad key usage. This is required to support encryption and signing certificates of the same subject. Further we ignore them if they are due to an identical certificate (which may happen if a certificate is accidentally duplicated in the keybox). */ if (!keydb_get_cert (kh, &cert2)) { int tmp = (same_subject_issuer (first_subject, first_issuer, cert2) && ((gpg_err_code ( secret? gpgsm_cert_use_sign_p (cert2,0) : gpgsm_cert_use_encrypt_p (cert2) ) ) == GPG_ERR_WRONG_KEY_USAGE)); if (tmp) gpgsm_add_cert_to_certlist (ctrl, cert2, &dup_certs, 0); else { if (is_cert_in_certlist (cert2, dup_certs)) tmp = 1; } ksba_cert_release (cert2); if (tmp) goto next_ambigious; } rc = gpg_error (GPG_ERR_AMBIGUOUS_NAME); } gpgsm_release_certlist (dup_certs); } xfree (first_subject); xfree (first_issuer); first_subject = NULL; first_issuer = NULL; if (!rc && !is_cert_in_certlist (cert, *listaddr)) { if (!rc && secret) { char *p; rc = gpg_error (GPG_ERR_NO_SECKEY); p = gpgsm_get_keygrip_hexstring (cert); if (p) { if (!gpgsm_agent_havekey (ctrl, p)) rc = 0; xfree (p); } } if (!rc) rc = gpgsm_validate_chain (ctrl, cert, "", NULL, 0, NULL, 0, NULL); if (!rc) { certlist_t cl = xtrycalloc (1, sizeof *cl); if (!cl) rc = out_of_core (); else { cl->cert = cert; cert = NULL; cl->next = *listaddr; cl->is_encrypt_to = is_encrypt_to; *listaddr = cl; } } } } } keydb_release (kh); ksba_cert_release (cert); return rc == -1? gpg_error (GPG_ERR_NO_PUBKEY): rc; } void gpgsm_release_certlist (certlist_t list) { while (list) { certlist_t cl = list->next; ksba_cert_release (list->cert); xfree (list); list = cl; } } /* Like gpgsm_add_to_certlist, but look only for one certificate. No chain validation is done. If KEYID is not NULL it is taken as an additional filter value which must match the subjectKeyIdentifier. */ int gpgsm_find_cert (ctrl_t ctrl, const char *name, ksba_sexp_t keyid, ksba_cert_t *r_cert, int allow_ambiguous) { int rc; KEYDB_SEARCH_DESC desc; KEYDB_HANDLE kh = NULL; *r_cert = NULL; rc = classify_user_id (name, &desc, 0); if (!rc) { kh = keydb_new (); if (!kh) rc = gpg_error (GPG_ERR_ENOMEM); else { nextone: rc = keydb_search (ctrl, kh, &desc, 1); if (!rc) { rc = keydb_get_cert (kh, r_cert); if (!rc && keyid) { ksba_sexp_t subj; rc = ksba_cert_get_subj_key_id (*r_cert, NULL, &subj); if (!rc) { if (cmp_simple_canon_sexp (keyid, subj)) { xfree (subj); goto nextone; } xfree (subj); /* Okay: Here we know that the certificate's subjectKeyIdentifier matches the requested one. */ } else if (gpg_err_code (rc) == GPG_ERR_NO_DATA) goto nextone; } } /* If we don't have the KEYID filter we need to check for ambiguous search results. Note, that it is somewhat reasonable to assume that a specification of a KEYID won't lead to ambiguous names. */ if (!rc && !keyid) { ksba_isotime_t notbefore = ""; const unsigned char *image = NULL; size_t length = 0; if (allow_ambiguous) { /* We want to return the newest certificate */ if (ksba_cert_get_validity (*r_cert, 0, notbefore)) *notbefore = '\0'; image = ksba_cert_get_image (*r_cert, &length); } next_ambiguous: rc = keydb_search (ctrl, kh, &desc, 1); if (rc == -1) rc = 0; else { if (!rc) { ksba_cert_t cert2 = NULL; ksba_isotime_t notbefore2 = ""; const unsigned char *image2 = NULL; size_t length2 = 0; int cmp = 0; if (!keydb_get_cert (kh, &cert2)) { if (gpgsm_certs_identical_p (*r_cert, cert2)) { ksba_cert_release (cert2); goto next_ambiguous; } if (allow_ambiguous) { if (ksba_cert_get_validity (cert2, 0, notbefore2)) *notbefore2 = '\0'; image2 = ksba_cert_get_image (cert2, &length2); cmp = strcmp (notbefore, notbefore2); /* use certificate image bits as last resort for stable ordering */ if (!cmp) cmp = memcmp (image, image2, length < length2 ? length : length2); if (!cmp) cmp = length < length2 ? -1 : length > length2 ? 1 : 0; if (cmp < 0) { ksba_cert_release (*r_cert); *r_cert = cert2; strcpy (notbefore, notbefore2); image = image2; length = length2; } else ksba_cert_release (cert2); goto next_ambiguous; } ksba_cert_release (cert2); } rc = gpg_error (GPG_ERR_AMBIGUOUS_NAME); } ksba_cert_release (*r_cert); *r_cert = NULL; } } } } keydb_release (kh); return rc == -1? gpg_error (GPG_ERR_NO_PUBKEY): rc; } diff --git a/sm/certreqgen-ui.c b/sm/certreqgen-ui.c index e508079b4..6ea481529 100644 --- a/sm/certreqgen-ui.c +++ b/sm/certreqgen-ui.c @@ -1,475 +1,474 @@ /* certreqgen-ui.c - Simple user interface for certreqgen.c * Copyright (C) 2007, 2010, 2011 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include -#include #include "gpgsm.h" #include #include "../common/i18n.h" #include "../common/ttyio.h" #include "../common/membuf.h" /* Prompt for lines and append them to MB. */ static void ask_mb_lines (membuf_t *mb, const char *prefix) { char *answer = NULL; do { xfree (answer); answer = tty_get ("> "); tty_kill_prompt (); trim_spaces (answer); if (*answer) { put_membuf_str (mb, prefix); put_membuf_str (mb, answer); put_membuf (mb, "\n", 1); } } while (*answer); xfree (answer); } /* Helper to store stuff in a membuf. */ void store_key_value_lf (membuf_t *mb, const char *key, const char *value) { put_membuf_str (mb, key); put_membuf_str (mb, value); put_membuf (mb, "\n", 1); } /* Helper tp store a membuf create by mb_ask_lines into MB. Returns -1 on error. */ int store_mb_lines (membuf_t *mb, membuf_t *lines) { char *p; if (get_membuf_len (lines)) { put_membuf (lines, "", 1); p = get_membuf (lines, NULL); if (!p) return -1; put_membuf_str (mb, p); xfree (p); } return 0; } /* Check whether we have a key for the key with HEXGRIP. Returns NULL if not or a string describing the type of the key (RSA, ELG, DSA, etc..). */ static const char * check_keygrip (ctrl_t ctrl, const char *hexgrip) { gpg_error_t err; ksba_sexp_t public; size_t publiclen; int algo; if (hexgrip[0] == '&') hexgrip++; err = gpgsm_agent_readkey (ctrl, 0, hexgrip, &public); if (err) return NULL; publiclen = gcry_sexp_canon_len (public, 0, NULL, NULL); algo = get_pk_algo_from_canon_sexp (public, publiclen); xfree (public); switch (algo) { case GCRY_PK_RSA: return "RSA"; case GCRY_PK_DSA: return "DSA"; case GCRY_PK_ELG: return "ELG"; case GCRY_PK_ECC: return "ECC"; case GCRY_PK_ECDSA: return "ECDSA"; case GCRY_PK_EDDSA: return "EdDSA"; default: return NULL; } } /* This function is used to create a certificate request from the command line. In the past the similar gpgsm-gencert.sh script has been used for it; however that scripts requires a full Unix shell and thus is not suitable for the Windows port. So here is the re-implementation. */ void gpgsm_gencertreq_tty (ctrl_t ctrl, estream_t output_stream) { gpg_error_t err; char *answer; int selection; estream_t fp = NULL; int method; char *keytype_buffer = NULL; const char *keytype; char *keygrip = NULL; unsigned int nbits; int minbits = 1024; int maxbits = 4096; int defbits = 3072; const char *keyusage; char *subject_name; membuf_t mb_email, mb_dns, mb_uri, mb_result; char *result = NULL; const char *s, *s2; int selfsigned; answer = NULL; init_membuf (&mb_email, 100); init_membuf (&mb_dns, 100); init_membuf (&mb_uri, 100); init_membuf (&mb_result, 512); again: /* Get the type of the key. */ tty_printf (_("Please select what kind of key you want:\n")); tty_printf (_(" (%d) RSA\n"), 1 ); tty_printf (_(" (%d) Existing key\n"), 2 ); tty_printf (_(" (%d) Existing key from card\n"), 3 ); do { xfree (answer); answer = tty_get (_("Your selection? ")); tty_kill_prompt (); selection = *answer? atoi (answer): 1; } while (!(selection >= 1 && selection <= 3)); method = selection; /* Get size of the key. */ if (method == 1) { keytype = "RSA"; for (;;) { xfree (answer); answer = tty_getf (_("What keysize do you want? (%u) "), defbits); tty_kill_prompt (); trim_spaces (answer); nbits = *answer? atoi (answer): defbits; if (nbits < minbits || nbits > maxbits) tty_printf(_("%s keysizes must be in the range %u-%u\n"), "RSA", minbits, maxbits); else break; /* Okay. */ } tty_printf (_("Requested keysize is %u bits\n"), nbits); /* We round it up so that it better matches the word size. */ if (( nbits % 64)) { nbits = ((nbits + 63) / 64) * 64; tty_printf (_("rounded up to %u bits\n"), nbits); } } else if (method == 2) { for (;;) { xfree (answer); answer = tty_get (_("Enter the keygrip: ")); tty_kill_prompt (); trim_spaces (answer); if (!*answer) goto again; else if (strlen (answer) != 40 && !(answer[0] == '&' && strlen (answer+1) == 40)) tty_printf (_("Not a valid keygrip (expecting 40 hex digits)\n")); else if (!(keytype = check_keygrip (ctrl, answer)) ) tty_printf (_("No key with this keygrip\n")); else break; /* Okay. */ } xfree (keygrip); keygrip = answer; answer = NULL; nbits = 1024; /* A dummy value is sufficient. */ } else /* method == 3 */ { char *serialno; strlist_t keypairlist, sl; int count; err = gpgsm_agent_scd_serialno (ctrl, &serialno); if (err) { tty_printf (_("error reading the card: %s\n"), gpg_strerror (err)); goto again; } tty_printf (_("Serial number of the card: %s\n"), serialno); xfree (serialno); err = gpgsm_agent_scd_keypairinfo (ctrl, &keypairlist); if (err) { tty_printf (_("error reading the card: %s\n"), gpg_strerror (err)); goto again; } do { tty_printf (_("Available keys:\n")); for (count=1,sl=keypairlist; sl; sl = sl->next, count++) { ksba_sexp_t pkey; gcry_sexp_t s_pkey; char *algostr = NULL; const char *keyref; int any = 0; keyref = strchr (sl->d, ' '); if (keyref) { keyref++; if (!gpgsm_agent_readkey (ctrl, 1, keyref, &pkey)) { if (!gcry_sexp_new (&s_pkey, pkey, 0, 0)) algostr = pubkey_algo_string (s_pkey, NULL); gcry_sexp_release (s_pkey); } xfree (pkey); } tty_printf (" (%d) %s %s", count, sl->d, algostr); if ((sl->flags & GCRY_PK_USAGE_CERT)) { tty_printf ("%scert", any?",":" ("); any = 1; } if ((sl->flags & GCRY_PK_USAGE_SIGN)) { tty_printf ("%ssign", any?",":" ("); any = 1; } if ((sl->flags & GCRY_PK_USAGE_AUTH)) { tty_printf ("%sauth", any?",":" ("); any = 1; } if ((sl->flags & GCRY_PK_USAGE_ENCR)) { tty_printf ("%sencr", any?",":" ("); any = 1; } tty_printf ("%s\n", any?")":""); xfree (algostr); } xfree (answer); answer = tty_get (_("Your selection? ")); tty_kill_prompt (); trim_spaces (answer); selection = atoi (answer); } while (!(selection > 0 && selection < count)); for (count=1,sl=keypairlist; sl; sl = sl->next, count++) if (count == selection) break; s = sl->d; while (*s && !spacep (s)) s++; while (spacep (s)) s++; xfree (keygrip); keygrip = NULL; xfree (keytype_buffer); keytype_buffer = xasprintf ("card:%s", s); free_strlist (keypairlist); keytype = keytype_buffer; nbits = 1024; /* A dummy value is sufficient. */ } /* Ask for the key usage. */ tty_printf (_("Possible actions for a %s key:\n"), "RSA"); tty_printf (_(" (%d) sign, encrypt\n"), 1 ); tty_printf (_(" (%d) sign\n"), 2 ); tty_printf (_(" (%d) encrypt\n"), 3 ); do { xfree (answer); answer = tty_get (_("Your selection? ")); tty_kill_prompt (); trim_spaces (answer); selection = *answer? atoi (answer): 1; switch (selection) { case 1: keyusage = "sign, encrypt"; break; case 2: keyusage = "sign"; break; case 3: keyusage = "encrypt"; break; default: keyusage = NULL; break; } } while (!keyusage); /* Get the subject name. */ do { size_t erroff, errlen; xfree (answer); answer = tty_get (_("Enter the X.509 subject name: ")); tty_kill_prompt (); trim_spaces (answer); if (!*answer) tty_printf (_("No subject name given\n")); else if ( (err = ksba_dn_teststr (answer, 0, &erroff, &errlen)) ) { if (gpg_err_code (err) == GPG_ERR_UNKNOWN_NAME) tty_printf (_("Invalid subject name label '%.*s'\n"), (int)errlen, answer+erroff); else { /* TRANSLATORS: The 22 in the second string is the length of the first string up to the "%s". Please adjust it do the length of your translation. The second string is merely passed to atoi so you can drop everything after the number. */ tty_printf (_("Invalid subject name '%s'\n"), answer); tty_printf ("%*s^\n", atoi (_("22 translator: see " "certreg-ui.c:gpgsm_gencertreq_tty")) + (int)erroff, ""); } *answer = 0; } } while (!*answer); subject_name = answer; answer = NULL; /* Get the email addresses. */ tty_printf (_("Enter email addresses")); tty_printf (_(" (end with an empty line):\n")); ask_mb_lines (&mb_email, "Name-Email: "); /* DNS names. */ tty_printf (_("Enter DNS names")); tty_printf (_(" (optional; end with an empty line):\n")); ask_mb_lines (&mb_dns, "Name-DNS: "); /* URIs. */ tty_printf (_("Enter URIs")); tty_printf (_(" (optional; end with an empty line):\n")); ask_mb_lines (&mb_uri, "Name-URI: "); /* Want a self-signed certificate? */ selfsigned = tty_get_answer_is_yes (_("Create self-signed certificate? (y/N) ")); /* Put it all together. */ store_key_value_lf (&mb_result, "Key-Type: ", keytype); { char numbuf[30]; snprintf (numbuf, sizeof numbuf, "%u", nbits); store_key_value_lf (&mb_result, "Key-Length: ", numbuf); } if (keygrip) store_key_value_lf (&mb_result, "Key-Grip: ", keygrip); store_key_value_lf (&mb_result, "Key-Usage: ", keyusage); if (selfsigned) store_key_value_lf (&mb_result, "Serial: ", "random"); store_key_value_lf (&mb_result, "Name-DN: ", subject_name); if (store_mb_lines (&mb_result, &mb_email)) goto mem_error; if (store_mb_lines (&mb_result, &mb_dns)) goto mem_error; if (store_mb_lines (&mb_result, &mb_uri)) goto mem_error; put_membuf (&mb_result, "", 1); result = get_membuf (&mb_result, NULL); if (!result) goto mem_error; tty_printf (_("These parameters are used:\n")); for (s=result; (s2 = strchr (s, '\n')); s = s2+1) tty_printf (" %.*s\n", (int)(s2-s), s); tty_printf ("\n"); if (!tty_get_answer_is_yes ("Proceed with creation? (y/N) ")) goto leave; /* Now create a parameter file and generate the key. */ fp = es_fopenmem (0, "w+"); if (!fp) { log_error (_("error creating temporary file: %s\n"), strerror (errno)); goto leave; } es_fputs (result, fp); es_rewind (fp); if (selfsigned) tty_printf ("%s", _("Now creating self-signed certificate. ")); else tty_printf ("%s", _("Now creating certificate request. ")); tty_printf ("%s", _("This may take a while ...\n")); { int save_pem = ctrl->create_pem; ctrl->create_pem = 1; /* Force creation of PEM. */ err = gpgsm_genkey (ctrl, fp, output_stream); ctrl->create_pem = save_pem; } if (!err) { if (selfsigned) tty_printf (_("Ready.\n")); else tty_printf (_("Ready. You should now send this request to your CA.\n")); } goto leave; mem_error: log_error (_("resource problem: out of core\n")); leave: es_fclose (fp); xfree (answer); xfree (subject_name); xfree (keytype_buffer); xfree (keygrip); xfree (get_membuf (&mb_email, NULL)); xfree (get_membuf (&mb_dns, NULL)); xfree (get_membuf (&mb_uri, NULL)); xfree (get_membuf (&mb_result, NULL)); xfree (result); } diff --git a/sm/certreqgen.c b/sm/certreqgen.c index 27a6a3458..75343385d 100644 --- a/sm/certreqgen.c +++ b/sm/certreqgen.c @@ -1,1572 +1,1575 @@ /* certreqgen.c - Generate a key and a certification [request] * Copyright (C) 2002, 2003, 2005, 2007, 2010, * 2011 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /* The format of the parameter file is described in the manual under "Unattended Usage". Here is an example: $ cat >foo < #include #include #include #include #include #include -#include #include "gpgsm.h" #include #include #include "keydb.h" #include "../common/i18n.h" #include "../common/membuf.h" enum para_name { pKEYTYPE, pKEYLENGTH, pKEYGRIP, pKEYCURVE, pKEYUSAGE, pNAMEDN, pNAMEEMAIL, pNAMEDNS, pNAMEURI, pSERIAL, pISSUERDN, pNOTBEFORE, pNOTAFTER, pSIGNINGKEY, pHASHALGO, pAUTHKEYID, pSUBJKEYID, pEXTENSION }; struct para_data_s { struct para_data_s *next; int lnr; enum para_name key; union { unsigned int usage; char value[1]; } u; }; struct reqgen_ctrl_s { int lnr; int dryrun; }; static const char oidstr_authorityKeyIdentifier[] = "2.5.29.35"; static const char oidstr_subjectKeyIdentifier[] = "2.5.29.14"; static const char oidstr_keyUsage[] = "2.5.29.15"; static const char oidstr_basicConstraints[] = "2.5.29.19"; static const char oidstr_standaloneCertificate[] = "1.3.6.1.4.1.11591.2.2.1"; static int proc_parameters (ctrl_t ctrl, struct para_data_s *para, estream_t out_fp, struct reqgen_ctrl_s *outctrl); static int create_request (ctrl_t ctrl, struct para_data_s *para, const char *carddirect, ksba_const_sexp_t public, ksba_const_sexp_t sigkey, ksba_writer_t writer); static void release_parameter_list (struct para_data_s *r) { struct para_data_s *r2; for (; r ; r = r2) { r2 = r->next; xfree(r); } } static struct para_data_s * get_parameter (struct para_data_s *para, enum para_name key, int seq) { struct para_data_s *r; for (r = para; r ; r = r->next) if ( r->key == key && !seq--) return r; return NULL; } static const char * get_parameter_value (struct para_data_s *para, enum para_name key, int seq) { struct para_data_s *r = get_parameter (para, key, seq); return (r && *r->u.value)? r->u.value : NULL; } static int get_parameter_algo (struct para_data_s *para, enum para_name key) { struct para_data_s *r = get_parameter (para, key, 0); if (!r) return -1; if (digitp (r->u.value)) return atoi( r->u.value ); return gcry_pk_map_name (r->u.value); } /* Parse the usage parameter. Returns 0 on success. Note that we only care about sign and encrypt and don't (yet) allow all the other X.509 usage to be specified; instead we will use a fixed mapping to the X.509 usage flags. */ static int parse_parameter_usage (struct para_data_s *para, enum para_name key) { struct para_data_s *r = get_parameter (para, key, 0); char *p, *pn; unsigned int use; if (!r) return 0; /* none (this is an optional parameter)*/ use = 0; pn = r->u.value; while ( (p = strsep (&pn, " \t,")) ) { if (!*p) ; else if ( !ascii_strcasecmp (p, "sign") ) use |= GCRY_PK_USAGE_SIGN; else if ( !ascii_strcasecmp (p, "encrypt") || !ascii_strcasecmp (p, "encr") ) use |= GCRY_PK_USAGE_ENCR; else if ( !ascii_strcasecmp (p, "cert") ) use |= GCRY_PK_USAGE_CERT; else { log_error ("line %d: invalid usage list\n", r?r->lnr:0); return -1; /* error */ } } r->u.usage = use; return 0; } static unsigned int get_parameter_uint (struct para_data_s *para, enum para_name key) { struct para_data_s *r = get_parameter (para, key, 0); if (!r) return 0; if (r->key == pKEYUSAGE) return r->u.usage; return (unsigned int)strtoul (r->u.value, NULL, 10); } /* Read the certificate generation parameters from FP and generate (all) certificate requests. */ static int read_parameters (ctrl_t ctrl, estream_t fp, estream_t out_fp) { static struct { const char *name; enum para_name key; int allow_dups; } keywords[] = { { "Key-Type", pKEYTYPE}, { "Key-Length", pKEYLENGTH }, { "Key-Grip", pKEYGRIP }, { "Key-Curve", pKEYCURVE }, { "Key-Usage", pKEYUSAGE }, { "Name-DN", pNAMEDN }, { "Name-Email", pNAMEEMAIL, 1 }, { "Name-DNS", pNAMEDNS, 1 }, { "Name-URI", pNAMEURI, 1 }, { "Serial", pSERIAL }, { "Issuer-DN", pISSUERDN }, { "Creation-Date", pNOTBEFORE }, { "Not-Before", pNOTBEFORE }, { "Expire-Date", pNOTAFTER }, { "Not-After", pNOTAFTER }, { "Signing-Key", pSIGNINGKEY }, { "Hash-Algo", pHASHALGO }, { "Authority-Key-Id", pAUTHKEYID }, { "Subject-Key-Id", pSUBJKEYID }, { "Extension", pEXTENSION, 1 }, { NULL, 0 } }; char line[1024], *p; const char *err = NULL; struct para_data_s *para, *r; int i, rc = 0, any = 0; struct reqgen_ctrl_s outctrl; memset (&outctrl, 0, sizeof (outctrl)); err = NULL; para = NULL; while (es_fgets (line, DIM(line)-1, fp) ) { char *keyword, *value; outctrl.lnr++; if (*line && line[strlen(line)-1] != '\n') { err = "line too long"; break; } for (p=line; spacep (p); p++) ; if (!*p || *p == '#') continue; keyword = p; if (*keyword == '%') { for (; *p && !ascii_isspace (*p); p++) ; if (*p) *p++ = 0; for (; ascii_isspace (*p); p++) ; value = p; trim_trailing_spaces (value); if (!ascii_strcasecmp (keyword, "%echo")) log_info ("%s\n", value); else if (!ascii_strcasecmp (keyword, "%dry-run")) outctrl.dryrun = 1; else if (!ascii_strcasecmp( keyword, "%commit")) { rc = proc_parameters (ctrl, para, out_fp, &outctrl); if (rc) goto leave; any = 1; release_parameter_list (para); para = NULL; } else log_info ("skipping control '%s' (%s)\n", keyword, value); continue; } if (!(p = strchr (p, ':')) || p == keyword) { err = "missing colon"; break; } if (*p) *p++ = 0; for (; spacep (p); p++) ; if (!*p) { err = "missing argument"; break; } value = p; trim_trailing_spaces (value); for (i=0; (keywords[i].name && ascii_strcasecmp (keywords[i].name, keyword)); i++) ; if (!keywords[i].name) { err = "unknown keyword"; break; } if (keywords[i].key != pKEYTYPE && !para) { err = "parameter block does not start with \"Key-Type\""; break; } if (keywords[i].key == pKEYTYPE && para) { rc = proc_parameters (ctrl, para, out_fp, &outctrl); if (rc) goto leave; any = 1; release_parameter_list (para); para = NULL; } else if (!keywords[i].allow_dups) { for (r = para; r && r->key != keywords[i].key; r = r->next) ; if (r) { err = "duplicate keyword"; break; } } r = xtrycalloc (1, sizeof *r + strlen( value )); if (!r) { err = "out of core"; break; } r->lnr = outctrl.lnr; r->key = keywords[i].key; strcpy (r->u.value, value); r->next = para; para = r; } if (err) { log_error ("line %d: %s\n", outctrl.lnr, err); rc = gpg_error (GPG_ERR_GENERAL); } else if (es_ferror(fp)) { log_error ("line %d: read error: %s\n", outctrl.lnr, strerror(errno) ); rc = gpg_error (GPG_ERR_GENERAL); } else if (para) { rc = proc_parameters (ctrl, para, out_fp, &outctrl); if (rc) goto leave; any = 1; } if (!rc && !any) rc = gpg_error (GPG_ERR_NO_DATA); leave: release_parameter_list (para); return rc; } /* check whether there are invalid characters in the email address S */ static int has_invalid_email_chars (const char *s) { int at_seen=0; static char valid_chars[] = "01234567890_-." "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; for (; *s; s++) { if (*s & 0x80) return 1; if (*s == '@') at_seen++; else if (!at_seen && !( !!strchr (valid_chars, *s) || *s == '+')) return 1; else if (at_seen && !strchr (valid_chars, *s)) return 1; } return at_seen != 1; } /* Check that all required parameters are given and perform the action */ static int proc_parameters (ctrl_t ctrl, struct para_data_s *para, estream_t out_fp, struct reqgen_ctrl_s *outctrl) { gpg_error_t err; struct para_data_s *r; const char *s, *string; int i; int algo; unsigned int nbits; char numbuf[20]; unsigned char keyparms[100]; int rc = 0; ksba_sexp_t public = NULL; ksba_sexp_t sigkey = NULL; int seq; size_t erroff, errlen; char *cardkeyid = NULL; /* Check that we have all required parameters; */ - assert (get_parameter (para, pKEYTYPE, 0)); + log_assert (get_parameter (para, pKEYTYPE, 0)); /* There is a problem with pkcs-10 on how to use ElGamal because it is expected that a PK algorithm can always be used for signing. Another problem is that on-card generated encryption keys may not be used for signing. */ algo = get_parameter_algo (para, pKEYTYPE); if (!algo && (s = get_parameter_value (para, pKEYTYPE, 0)) && *s) { /* Hack to allow creation of certificates directly from a smart card. For example: "Key-Type: card:OPENPGP.3". */ if (!strncmp (s, "card:", 5) && s[5]) cardkeyid = xtrystrdup (s+5); } if (algo < 1 && !cardkeyid) { r = get_parameter (para, pKEYTYPE, 0); if (r) log_error (_("line %d: invalid algorithm\n"), r?r->lnr:0); else log_error ("No Key-Type specified\n"); return gpg_error (GPG_ERR_INV_PARAMETER); } /* Check the keylength. NOTE: If you change this make sure that it macthes the gpgconflist item in gpgsm.c */ if (!get_parameter (para, pKEYLENGTH, 0)) nbits = 3072; else nbits = get_parameter_uint (para, pKEYLENGTH); if ((nbits < 1024 || nbits > 4096) && !cardkeyid) { /* The BSI specs dated 2002-11-25 don't allow lengths below 1024. */ r = get_parameter (para, pKEYLENGTH, 0); log_error (_("line %d: invalid key length %u (valid are %d to %d)\n"), r?r->lnr:0, nbits, 1024, 4096); xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } /* Check the usage. */ if (parse_parameter_usage (para, pKEYUSAGE)) { xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } /* Check that there is a subject name and that this DN fits our requirements. */ if (!(s=get_parameter_value (para, pNAMEDN, 0))) { r = get_parameter (para, pNAMEDN, 0); log_error (_("line %d: no subject name given\n"), r?r->lnr:0); xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } err = ksba_dn_teststr (s, 0, &erroff, &errlen); if (err) { r = get_parameter (para, pNAMEDN, 0); if (gpg_err_code (err) == GPG_ERR_UNKNOWN_NAME) log_error (_("line %d: invalid subject name label '%.*s'\n"), r?r->lnr:0, (int)errlen, s+erroff); else log_error (_("line %d: invalid subject name '%s' at pos %d\n"), r?r->lnr:0, s, (int)erroff); xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } /* Check that the optional email address is okay. */ for (seq=0; (s=get_parameter_value (para, pNAMEEMAIL, seq)); seq++) { if (has_invalid_email_chars (s) || *s == '@' || s[strlen(s)-1] == '@' || s[strlen(s)-1] == '.' || strstr(s, "..")) { r = get_parameter (para, pNAMEEMAIL, seq); log_error (_("line %d: not a valid email address\n"), r?r->lnr:0); xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } } /* Check the optional serial number. */ string = get_parameter_value (para, pSERIAL, 0); if (string) { if (!strcmp (string, "random")) ; /* Okay. */ else { for (s=string, i=0; hexdigitp (s); s++, i++) ; if (*s) { r = get_parameter (para, pSERIAL, 0); log_error (_("line %d: invalid serial number\n"), r?r->lnr:0); xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } } } /* Check the optional issuer DN. */ string = get_parameter_value (para, pISSUERDN, 0); if (string) { err = ksba_dn_teststr (string, 0, &erroff, &errlen); if (err) { r = get_parameter (para, pISSUERDN, 0); if (gpg_err_code (err) == GPG_ERR_UNKNOWN_NAME) log_error (_("line %d: invalid issuer name label '%.*s'\n"), r?r->lnr:0, (int)errlen, string+erroff); else log_error (_("line %d: invalid issuer name '%s' at pos %d\n"), r?r->lnr:0, string, (int)erroff); xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } } /* Check the optional creation date. */ string = get_parameter_value (para, pNOTBEFORE, 0); if (string && !string2isotime (NULL, string)) { r = get_parameter (para, pNOTBEFORE, 0); log_error (_("line %d: invalid date given\n"), r?r->lnr:0); xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } /* Check the optional expire date. */ string = get_parameter_value (para, pNOTAFTER, 0); if (string && !string2isotime (NULL, string)) { r = get_parameter (para, pNOTAFTER, 0); log_error (_("line %d: invalid date given\n"), r?r->lnr:0); xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } /* Get the optional signing key. */ string = get_parameter_value (para, pSIGNINGKEY, 0); if (string) { rc = gpgsm_agent_readkey (ctrl, 0, string, &sigkey); if (rc) { r = get_parameter (para, pKEYTYPE, 0); log_error (_("line %d: error getting signing key by keygrip '%s'" ": %s\n"), r?r->lnr:0, s, gpg_strerror (rc)); xfree (cardkeyid); return rc; } } /* Check the optional hash-algo. */ { int mdalgo; string = get_parameter_value (para, pHASHALGO, 0); if (string && !((mdalgo = gcry_md_map_name (string)) && (mdalgo == GCRY_MD_SHA1 || mdalgo == GCRY_MD_SHA256 || mdalgo == GCRY_MD_SHA384 || mdalgo == GCRY_MD_SHA512))) { r = get_parameter (para, pHASHALGO, 0); log_error (_("line %d: invalid hash algorithm given\n"), r?r->lnr:0); xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } } /* Check the optional AuthorityKeyId. */ string = get_parameter_value (para, pAUTHKEYID, 0); if (string && strcmp (string, "none")) { for (s=string, i=0; hexdigitp (s); s++, i++) ; if (*s || (i&1)) { r = get_parameter (para, pAUTHKEYID, 0); log_error (_("line %d: invalid authority-key-id\n"), r?r->lnr:0); xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } } /* Check the optional SubjectKeyId. */ string = get_parameter_value (para, pSUBJKEYID, 0); if (string && strcmp (string, "none")) { for (s=string, i=0; hexdigitp (s); s++, i++) ; if (*s || (i&1)) { r = get_parameter (para, pSUBJKEYID, 0); log_error (_("line %d: invalid subject-key-id\n"), r?r->lnr:0); xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } } /* Check the optional extensions. */ for (seq=0; (string=get_parameter_value (para, pEXTENSION, seq)); seq++) { int okay = 0; s = strpbrk (string, " \t:"); if (s) { s++; while (spacep (s)) s++; if (*s && strchr ("nNcC", *s)) { s++; while (spacep (s)) s++; if (*s == ':') s++; if (*s) { while (spacep (s)) s++; for (i=0; hexdigitp (s); s++, i++) ; if (!((*s && *s != ':') || !i || (i&1))) okay = 1; } } } if (!okay) { r = get_parameter (para, pEXTENSION, seq); log_error (_("line %d: invalid extension syntax\n"), r? r->lnr:0); xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } } /* Create or retrieve the public key. */ if (cardkeyid) /* Take the key from the current smart card. */ { rc = gpgsm_agent_readkey (ctrl, 1, cardkeyid, &public); if (rc) { r = get_parameter (para, pKEYTYPE, 0); log_error (_("line %d: error reading key '%s' from card: %s\n"), r?r->lnr:0, cardkeyid, gpg_strerror (rc)); xfree (sigkey); xfree (cardkeyid); return rc; } } else if ((s=get_parameter_value (para, pKEYGRIP, 0))) /* Use existing key.*/ { rc = gpgsm_agent_readkey (ctrl, 0, s, &public); if (rc) { r = get_parameter (para, pKEYTYPE, 0); log_error (_("line %d: error getting key by keygrip '%s': %s\n"), r->lnr, s, gpg_strerror (rc)); xfree (sigkey); xfree (cardkeyid); return rc; } } else if (!outctrl->dryrun) /* Generate new key. */ { if (algo == GCRY_PK_RSA) { sprintf (numbuf, "%u", nbits); snprintf ((char*)keyparms, DIM (keyparms), "(6:genkey(3:rsa(5:nbits%d:%s)))", (int)strlen (numbuf), numbuf); } else if (algo == GCRY_PK_ECC) { const char *curve = get_parameter_value (para, pKEYCURVE, 0); const char *flags; if (!strcmp (curve, "Ed25519")) flags = "(5:flags5:eddsa)"; else if (!strcmp (curve, "Curve25519")) flags = "(5:flags9:djb-tweak)"; else flags = ""; snprintf ((char*)keyparms, DIM (keyparms), "(6:genkey(3:ecc(5:curve%zu:%s)%s))", strlen (curve), curve, flags); } else { r = get_parameter (para, pKEYTYPE, 0); log_error (_("line %d: invalid algorithm\n"), r?r->lnr:0); xfree (sigkey); xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } rc = gpgsm_agent_genkey (ctrl, keyparms, &public); if (rc) { r = get_parameter (para, pKEYTYPE, 0); log_error (_("line %d: key generation failed: %s <%s>\n"), r?r->lnr:0, gpg_strerror (rc), gpg_strsource (rc)); xfree (sigkey); xfree (cardkeyid); return rc; } } if (!outctrl->dryrun) { gnupg_ksba_io_t b64writer = NULL; ksba_writer_t writer; int create_cert ; create_cert = !!get_parameter_value (para, pSERIAL, 0); ctrl->pem_name = create_cert? "CERTIFICATE" : "CERTIFICATE REQUEST"; rc = gnupg_ksba_create_writer (&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)), ctrl->pem_name, out_fp, &writer); if (rc) log_error ("can't create writer: %s\n", gpg_strerror (rc)); else { rc = create_request (ctrl, para, cardkeyid, public, sigkey, writer); if (!rc) { rc = gnupg_ksba_finish_writer (b64writer); if (rc) log_error ("write failed: %s\n", gpg_strerror (rc)); else { gpgsm_status (ctrl, STATUS_KEY_CREATED, "P"); log_info ("certificate%s created\n", create_cert?"":" request"); } } gnupg_ksba_destroy_writer (b64writer); } } xfree (sigkey); xfree (public); xfree (cardkeyid); return rc; } /* Parameters are checked, the key pair has been created. Now generate the request and write it out */ static int create_request (ctrl_t ctrl, struct para_data_s *para, const char *carddirect, ksba_const_sexp_t public, ksba_const_sexp_t sigkey, ksba_writer_t writer) { ksba_certreq_t cr; gpg_error_t err; gcry_md_hd_t md; ksba_stop_reason_t stopreason; int rc = 0; const char *s, *string; unsigned int use; int seq; char *buf, *p; size_t len; char numbuf[30]; ksba_isotime_t atime; int certmode = 0; int mdalgo; membuf_t tbsbuffer; membuf_t *tbsmb = NULL; size_t publiclen; size_t sigkeylen; int publicpkalgo; /* The gcrypt public key algo of the public key. */ int sigkeypkalgo; /* The gcrypt public key algo of the signing key. */ err = ksba_certreq_new (&cr); if (err) return err; publiclen = gcry_sexp_canon_len (public, 0, NULL, NULL); sigkeylen = sigkey? gcry_sexp_canon_len (sigkey, 0, NULL, NULL) : 0; publicpkalgo = get_pk_algo_from_canon_sexp (public, publiclen); sigkeypkalgo = sigkey? get_pk_algo_from_canon_sexp (public, publiclen) : 0; if (publicpkalgo == GCRY_PK_EDDSA) { mdalgo = GCRY_MD_SHA512; md = NULL; /* We sign the data and not a hash. */ init_membuf (&tbsbuffer, 2048); tbsmb = &tbsbuffer; ksba_certreq_set_hash_function (cr, (void (*)(void *, const void*,size_t))put_membuf, tbsmb); } else { if ((string = get_parameter_value (para, pHASHALGO, 0))) mdalgo = gcry_md_map_name (string); else mdalgo = GCRY_MD_SHA256; rc = gcry_md_open (&md, mdalgo, 0); if (rc) { log_error ("md_open failed: %s\n", gpg_strerror (rc)); goto leave; } if (DBG_HASHING) gcry_md_debug (md, "cr.cri"); ksba_certreq_set_hash_function (cr, HASH_FNC, md); } ksba_certreq_set_writer (cr, writer); err = ksba_certreq_add_subject (cr, get_parameter_value (para, pNAMEDN, 0)); if (err) { log_error ("error setting the subject's name: %s\n", gpg_strerror (err)); rc = err; goto leave; } for (seq=0; (s = get_parameter_value (para, pNAMEEMAIL, seq)); seq++) { buf = xtrymalloc (strlen (s) + 3); if (!buf) { rc = out_of_core (); goto leave; } *buf = '<'; strcpy (buf+1, s); strcat (buf+1, ">"); err = ksba_certreq_add_subject (cr, buf); xfree (buf); if (err) { log_error ("error setting the subject's alternate name: %s\n", gpg_strerror (err)); rc = err; goto leave; } } for (seq=0; (s = get_parameter_value (para, pNAMEDNS, seq)); seq++) { len = strlen (s); - assert (len); + log_assert (len); snprintf (numbuf, DIM(numbuf), "%u:", (unsigned int)len); buf = p = xtrymalloc (11 + strlen (numbuf) + len + 3); if (!buf) { rc = out_of_core (); goto leave; } p = stpcpy (p, "(8:dns-name"); p = stpcpy (p, numbuf); p = stpcpy (p, s); strcpy (p, ")"); err = ksba_certreq_add_subject (cr, buf); xfree (buf); if (err) { log_error ("error setting the subject's alternate name: %s\n", gpg_strerror (err)); rc = err; goto leave; } } for (seq=0; (s = get_parameter_value (para, pNAMEURI, seq)); seq++) { len = strlen (s); - assert (len); + log_assert (len); snprintf (numbuf, DIM(numbuf), "%u:", (unsigned int)len); buf = p = xtrymalloc (6 + strlen (numbuf) + len + 3); if (!buf) { rc = out_of_core (); goto leave; } p = stpcpy (p, "(3:uri"); p = stpcpy (p, numbuf); p = stpcpy (p, s); strcpy (p, ")"); err = ksba_certreq_add_subject (cr, buf); xfree (buf); if (err) { log_error ("error setting the subject's alternate name: %s\n", gpg_strerror (err)); rc = err; goto leave; } } err = ksba_certreq_set_public_key (cr, public); if (err) { log_error ("error setting the public key: %s\n", gpg_strerror (err)); rc = err; goto leave; } /* Set key usage flags. */ use = get_parameter_uint (para, pKEYUSAGE); if (use) { unsigned int mask, pos; unsigned char der[4]; der[0] = 0x03; der[1] = 0x02; der[2] = 0; der[3] = 0; if ((use & GCRY_PK_USAGE_SIGN)) { /* For signing only we encode the bits: KSBA_KEYUSAGE_DIGITAL_SIGNATURE KSBA_KEYUSAGE_NON_REPUDIATION = 0b11 -> 0b11000000 */ der[3] |= 0xc0; } if ((use & GCRY_PK_USAGE_ENCR)) { /* For encrypt only we encode the bits: KSBA_KEYUSAGE_KEY_ENCIPHERMENT KSBA_KEYUSAGE_DATA_ENCIPHERMENT = 0b1100 -> 0b00110000 */ der[3] |= 0x30; } if ((use & GCRY_PK_USAGE_CERT)) { /* For certify only we encode the bits: KSBA_KEYUSAGE_KEY_CERT_SIGN KSBA_KEYUSAGE_CRL_SIGN = 0b1100000 -> 0b00000110 */ der[3] |= 0x06; } /* Count number of unused bits. */ for (mask=1, pos=0; pos < 8 * sizeof mask; pos++, mask <<= 1) { if ((der[3] & mask)) break; der[2]++; } err = ksba_certreq_add_extension (cr, oidstr_keyUsage, 1, der, 4); if (err) { log_error ("error setting the key usage: %s\n", gpg_strerror (err)); rc = err; goto leave; } } /* See whether we want to create an X.509 certificate. */ string = get_parameter_value (para, pSERIAL, 0); if (string) { certmode = 1; /* Store the serial number. */ if (!strcmp (string, "random")) { char snbuf[3+8+1]; memcpy (snbuf, "(8:", 3); gcry_create_nonce (snbuf+3, 8); /* Clear high bit to guarantee a positive integer. */ snbuf[3] &= 0x7f; snbuf[3+8] = ')'; err = ksba_certreq_set_serial (cr, snbuf); } else { char *hexbuf; /* Allocate a buffer large enough to prefix the string with a '0' so to have an even number of digits. Prepend two further '0' so that the binary result will have a leading 0 byte and thus can't be the representation of a negative number. Note that ksba_certreq_set_serial strips all unneeded leading 0 bytes. */ hexbuf = p = xtrymalloc (2 + 1 + strlen (string) + 1); if (!hexbuf) { err = gpg_error_from_syserror (); goto leave; } if ((strlen (string) & 1)) *p++ = '0'; *p++ = '0'; *p++ = '0'; strcpy (p, string); for (p=hexbuf, len=0; p[0] && p[1]; p += 2) ((unsigned char*)hexbuf)[len++] = xtoi_2 (p); /* Now build the S-expression. */ snprintf (numbuf, DIM(numbuf), "%u:", (unsigned int)len); buf = p = xtrymalloc (1 + strlen (numbuf) + len + 1 + 1); if (!buf) { err = gpg_error_from_syserror (); xfree (hexbuf); goto leave; } p = stpcpy (stpcpy (buf, "("), numbuf); memcpy (p, hexbuf, len); p += len; strcpy (p, ")"); xfree (hexbuf); err = ksba_certreq_set_serial (cr, buf); xfree (buf); } if (err) { log_error ("error setting the serial number: %s\n", gpg_strerror (err)); goto leave; } /* Store the issuer DN. If no issuer DN is given and no signing key has been set we add the standalone extension and the basic constraints to mark it as a self-signed CA certificate. */ string = get_parameter_value (para, pISSUERDN, 0); if (string) { /* Issuer DN given. Note that this may be the same as the subject DN and thus this could as well be a self-signed certificate. However the caller needs to explicitly specify basicConstraints and so forth. */ err = ksba_certreq_set_issuer (cr, string); if (err) { log_error ("error setting the issuer DN: %s\n", gpg_strerror (err)); goto leave; } } else if (!string && !sigkey) { /* Self-signed certificate requested. Add basicConstraints and the custom GnuPG standalone extension. */ err = ksba_certreq_add_extension (cr, oidstr_basicConstraints, 1, "\x30\x03\x01\x01\xff", 5); if (err) goto leave; err = ksba_certreq_add_extension (cr, oidstr_standaloneCertificate, 0, "\x01\x01\xff", 3); if (err) goto leave; } /* Store the creation date. */ string = get_parameter_value (para, pNOTBEFORE, 0); if (string) { if (!string2isotime (atime, string)) BUG (); /* We already checked the value. */ } else gnupg_get_isotime (atime); err = ksba_certreq_set_validity (cr, 0, atime); if (err) { log_error ("error setting the creation date: %s\n", gpg_strerror (err)); goto leave; } /* Store the expire date. If it is not given, libksba inserts a default value. */ string = get_parameter_value (para, pNOTAFTER, 0); if (string) { if (!string2isotime (atime, string)) BUG (); /* We already checked the value. */ err = ksba_certreq_set_validity (cr, 1, atime); if (err) { log_error ("error setting the expire date: %s\n", gpg_strerror (err)); goto leave; } } /* Figure out the signing algorithm. If no sigkey has been given we set it to the public key to create a self-signed certificate. */ if (!sigkey) { sigkey = public; sigkeylen = publiclen; sigkeypkalgo = publicpkalgo; } /* Set the the digestinfo aka siginfo. */ { unsigned char *siginfo; err = transform_sigval (sigkey, sigkeylen, mdalgo, &siginfo, NULL); if (!err) { err = ksba_certreq_set_siginfo (cr, siginfo); xfree (siginfo); } if (err) { log_error ("error setting the siginfo: %s\n", gpg_strerror (err)); rc = err; goto leave; } } /* Insert the AuthorityKeyId. */ string = get_parameter_value (para, pAUTHKEYID, 0); if (string && !strcmp (string, "none")) ; /* Do not issue an AKI. */ else if (string) { char *hexbuf; /* Allocate a buffer for in-place conversion. We also add 4 extra bytes space for the tags and lengths fields. */ hexbuf = xtrymalloc (4 + strlen (string) + 1); if (!hexbuf) { err = gpg_error_from_syserror (); goto leave; } strcpy (hexbuf+4, string); for (p=hexbuf+4, len=0; p[0] && p[1]; p += 2) ((unsigned char*)hexbuf)[4+len++] = xtoi_2 (p); if (len > 125) { err = gpg_error (GPG_ERR_TOO_LARGE); xfree (hexbuf); goto leave; } hexbuf[0] = 0x30; /* Tag for a Sequence. */ hexbuf[1] = len+2; hexbuf[2] = 0x80; /* Context tag for an implicit Octet string. */ hexbuf[3] = len; err = ksba_certreq_add_extension (cr, oidstr_authorityKeyIdentifier, 0, hexbuf, 4+len); xfree (hexbuf); if (err) { log_error ("error setting the AKI: %s\n", gpg_strerror (err)); goto leave; } } else if (publicpkalgo == GCRY_PK_EDDSA || publicpkalgo == GCRY_PK_ECC) { /* For EdDSA and ECC we add the public key as default identifier. */ const unsigned char *q; size_t qlen, derlen; unsigned char *der; err = get_ecc_q_from_canon_sexp (public, publiclen, &q, &qlen); if (err) { log_error ("error getting Q from public key: %s\n", gpg_strerror (err)); goto leave; } if (publicpkalgo == GCRY_PK_EDDSA && qlen>32 && (qlen&1) && *q==0x40) { /* Skip our optional native encoding octet. */ q++; qlen--; } /* FIXME: For plain ECC we should better use a compressed * point. That requires an updated Libgcrypt. Without that * using nistp521 won't work due to the length check below. */ if (qlen > 125) { err = gpg_error (GPG_ERR_TOO_LARGE); goto leave; } derlen = 4 + qlen; der = xtrymalloc (derlen); if (!der) { err = gpg_error_from_syserror (); goto leave; } der[0] = 0x30; /* Sequence */ der[1] = qlen + 2; der[2] = 0x80; /* Context tag for an implict Octet String. */ der[3] = qlen; memcpy (der+4, q, qlen); err = ksba_certreq_add_extension (cr, oidstr_authorityKeyIdentifier, 0, der, derlen); xfree (der); if (err) { log_error ("error setting the AKI: %s\n", gpg_strerror (err)); goto leave; } } /* Insert the SubjectKeyId. */ string = get_parameter_value (para, pSUBJKEYID, 0); if (string && !strcmp (string, "none")) ; /* Do not issue an SKI. */ else if (string) { char *hexbuf; /* Allocate a buffer for in-place conversion. We also add 2 extra bytes space for the tag and length field. */ hexbuf = xtrymalloc (2 + strlen (string) + 1); if (!hexbuf) { err = gpg_error_from_syserror (); goto leave; } strcpy (hexbuf+2, string); for (p=hexbuf+2, len=0; p[0] && p[1]; p += 2) ((unsigned char*)hexbuf)[2+len++] = xtoi_2 (p); if (len > 127) { err = gpg_error (GPG_ERR_TOO_LARGE); xfree (hexbuf); goto leave; } hexbuf[0] = 0x04; /* Tag for an Octet string. */ hexbuf[1] = len; err = ksba_certreq_add_extension (cr, oidstr_subjectKeyIdentifier, 0, hexbuf, 2+len); xfree (hexbuf); if (err) { log_error ("error setting SKI: %s\n", gpg_strerror (err)); goto leave; } } else if (sigkeypkalgo == GCRY_PK_EDDSA || sigkeypkalgo == GCRY_PK_ECC) { /* For EdDSA and ECC we add the public key as default identifier. */ const unsigned char *q; size_t qlen, derlen; unsigned char *der; + /* FIXME: This assumes that the to-be-certified key uses the + * same algorithm as the certification key - this is not + * always the case; in fact it is common that they + * differ. */ err = get_ecc_q_from_canon_sexp (sigkey, sigkeylen, &q, &qlen); if (err) { log_error ("error getting Q from signature key: %s\n", gpg_strerror (err)); goto leave; } if (sigkeypkalgo == GCRY_PK_EDDSA && qlen>32 && (qlen&1) && *q==0x40) { /* Skip our optional native encoding octet. */ q++; qlen--; } if (qlen > 127) { err = gpg_error (GPG_ERR_TOO_LARGE); goto leave; } derlen = 2 + qlen; der = xtrymalloc (derlen); if (!der) { err = gpg_error_from_syserror (); goto leave; } der[0] = 0x04; /* Octet String */ der[1] = qlen; memcpy (der+2, q, qlen); err = ksba_certreq_add_extension (cr, oidstr_subjectKeyIdentifier, 0, der, derlen); xfree (der); if (err) { log_error ("error setting the SKI: %s\n", gpg_strerror (err)); goto leave; } } /* Insert additional extensions. */ for (seq=0; (string = get_parameter_value (para, pEXTENSION, seq)); seq++) { char *hexbuf; char *oidstr; int crit = 0; s = strpbrk (string, " \t:"); if (!s) { err = gpg_error (GPG_ERR_INTERNAL); goto leave; } oidstr = xtrymalloc (s - string + 1); if (!oidstr) { err = gpg_error_from_syserror (); goto leave; } memcpy (oidstr, string, (s-string)); oidstr[(s-string)] = 0; s++; while (spacep (s)) s++; if (!*s) { err = gpg_error (GPG_ERR_INTERNAL); xfree (oidstr); goto leave; } if (strchr ("cC", *s)) crit = 1; s++; while (spacep (s)) s++; if (*s == ':') s++; while (spacep (s)) s++; hexbuf = xtrystrdup (s); if (!hexbuf) { err = gpg_error_from_syserror (); xfree (oidstr); goto leave; } for (p=hexbuf, len=0; p[0] && p[1]; p += 2) ((unsigned char*)hexbuf)[len++] = xtoi_2 (p); err = ksba_certreq_add_extension (cr, oidstr, crit, hexbuf, len); xfree (oidstr); xfree (hexbuf); } } else { sigkey = public; sigkeylen = publiclen; sigkeypkalgo = publicpkalgo; } do { err = ksba_certreq_build (cr, &stopreason); if (err) { log_error ("ksba_certreq_build failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } if (stopreason == KSBA_SR_NEED_SIG) { gcry_sexp_t s_pkey; unsigned char grip[20]; char hexgrip[41]; unsigned char *sigval, *newsigval; size_t siglen; void *tbsdata; size_t tbsdatalen; rc = gcry_sexp_sscan (&s_pkey, NULL, (const char*)sigkey, sigkeylen); if (rc) { log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc)); goto leave; } if ( !gcry_pk_get_keygrip (s_pkey, grip) ) { rc = gpg_error (GPG_ERR_GENERAL); log_error ("can't figure out the keygrip\n"); gcry_sexp_release (s_pkey); goto leave; } gcry_sexp_release (s_pkey); bin2hex (grip, 20, hexgrip); if (!opt.quiet) log_info ("about to sign the %s for key: &%s\n", certmode? "certificate":"CSR", hexgrip); if (carddirect && !certmode) { if (tbsmb) { tbsdata = get_membuf (tbsmb, &tbsdatalen); tbsmb = NULL; if (!tbsdata) rc = gpg_error_from_syserror (); else rc = gpgsm_scd_pksign (ctrl, carddirect, NULL, tbsdata, tbsdatalen, 0, &sigval, &siglen); xfree (tbsdata); } else rc = gpgsm_scd_pksign (ctrl, carddirect, NULL, gcry_md_read (md, mdalgo), gcry_md_get_algo_dlen (mdalgo), mdalgo, &sigval, &siglen); } else { char *orig_codeset; char *desc; orig_codeset = i18n_switchto_utf8 (); desc = percent_plus_escape (_("To complete this certificate request please enter" " the passphrase for the key you just created once" " more.\n")); i18n_switchback (orig_codeset); if (tbsmb) { tbsdata = get_membuf (tbsmb, &tbsdatalen); tbsmb = NULL; if (!tbsdata) rc = gpg_error_from_syserror (); else rc = gpgsm_agent_pksign (ctrl, hexgrip, desc, tbsdata, tbsdatalen, 0, &sigval, &siglen); xfree (tbsdata); } else rc = gpgsm_agent_pksign (ctrl, hexgrip, desc, gcry_md_read(md, mdalgo), gcry_md_get_algo_dlen (mdalgo), mdalgo, &sigval, &siglen); xfree (desc); } if (rc) { log_error ("signing failed: %s\n", gpg_strerror (rc)); goto leave; } err = transform_sigval (sigval, siglen, mdalgo, &newsigval, NULL); xfree (sigval); if (!err) { err = ksba_certreq_set_sig_val (cr, newsigval); xfree (newsigval); } if (err) { log_error ("failed to store the sig_val: %s\n", gpg_strerror (err)); rc = err; goto leave; } } } while (stopreason != KSBA_SR_READY); leave: if (tbsmb) xfree (get_membuf (tbsmb, NULL)); gcry_md_close (md); ksba_certreq_release (cr); return rc; } /* Create a new key by reading the parameters from IN_FP. Multiple keys may be created */ int gpgsm_genkey (ctrl_t ctrl, estream_t in_stream, estream_t out_stream) { int rc; rc = read_parameters (ctrl, in_stream, out_stream); if (rc) { log_error ("error creating certificate request: %s <%s>\n", gpg_strerror (rc), gpg_strsource (rc)); goto leave; } leave: return rc; } diff --git a/sm/decrypt.c b/sm/decrypt.c index e79da570d..af9c2d684 100644 --- a/sm/decrypt.c +++ b/sm/decrypt.c @@ -1,1036 +1,1035 @@ /* 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 * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * SPDX-License-Identifier: GPL-3.0-or-later */ #include #include #include #include #include #include #include -#include #include "gpgsm.h" #include #include #include "keydb.h" #include "../common/i18n.h" #include "../common/tlv.h" #include "../common/compliance.h" struct decrypt_filter_parm_s { int algo; int mode; int blklen; gcry_cipher_hd_t hd; char iv[16]; size_t ivlen; int any_data; /* did we push anything through the filter at all? */ unsigned char lastblock[16]; /* to strip the padding we have to keep this one */ char helpblock[16]; /* needed because there is no block buffering in libgcrypt (yet) */ int helpblocklen; }; /* Return the hash algorithm's algo id from its name given in the * non-null termnated string in (buffer,buflen). Returns 0 on failure * or if the algo is not known. */ static char * string_from_gcry_buffer (gcry_buffer_t *buffer) { char *string; string = xtrymalloc (buffer->len + 1); if (!string) return NULL; memcpy (string, buffer->data, buffer->len); string[buffer->len] = 0; return string; } /* Helper to construct and hash the * ECC-CMS-SharedInfo ::= SEQUENCE { * keyInfo AlgorithmIdentifier, * entityUInfo [0] EXPLICIT OCTET STRING OPTIONAL, * suppPubInfo [2] EXPLICIT OCTET STRING } * as described in RFC-5753, 7.2. */ static gpg_error_t hash_ecc_cms_shared_info (gcry_md_hd_t hash_hd, const char *wrap_algo_str, unsigned int keylen, const void *ukm, unsigned int ukmlen) { gpg_error_t err; void *p; unsigned char *oid; size_t n, oidlen, toidlen, tkeyinfo, tukmlen, tsupppubinfo; unsigned char keylenbuf[6]; membuf_t mb = MEMBUF_ZERO; err = ksba_oid_from_str (wrap_algo_str, &oid, &oidlen); if (err) return err; toidlen = get_tlv_length (CLASS_UNIVERSAL, TAG_OBJECT_ID, 0, oidlen); tkeyinfo = get_tlv_length (CLASS_UNIVERSAL, TAG_SEQUENCE, 1, toidlen); tukmlen = ukm? get_tlv_length (CLASS_CONTEXT, 0, 1, ukmlen) : 0; keylen *= 8; keylenbuf[0] = TAG_OCTET_STRING; keylenbuf[1] = 4; keylenbuf[2] = (keylen >> 24); keylenbuf[3] = (keylen >> 16); keylenbuf[4] = (keylen >> 8); keylenbuf[5] = keylen; tsupppubinfo = get_tlv_length (CLASS_CONTEXT, 2, 1, sizeof keylenbuf); put_tlv_to_membuf (&mb, CLASS_UNIVERSAL, TAG_SEQUENCE, 1, tkeyinfo + tukmlen + tsupppubinfo); put_tlv_to_membuf (&mb, CLASS_UNIVERSAL, TAG_SEQUENCE, 1, toidlen); put_tlv_to_membuf (&mb, CLASS_UNIVERSAL, TAG_OBJECT_ID, 0, oidlen); put_membuf (&mb, oid, oidlen); ksba_free (oid); if (ukm) { put_tlv_to_membuf (&mb, CLASS_CONTEXT, 0, 1, ukmlen); put_membuf (&mb, ukm, ukmlen); } put_tlv_to_membuf (&mb, CLASS_CONTEXT, 2, 1, sizeof keylenbuf); put_membuf (&mb, keylenbuf, sizeof keylenbuf); p = get_membuf (&mb, &n); if (!p) return gpg_error_from_syserror (); gcry_md_write (hash_hd, p, n); xfree (p); return 0; } /* Derive a KEK (key wrapping key) using (SECRET,SECRETLEN), an * optional (UKM,ULMLEN), the wrap algorithm WRAP_ALGO_STR in decimal * dotted form, and the hash algorithm HASH_ALGO. On success a key of * length KEYLEN is stored at KEY. */ gpg_error_t ecdh_derive_kek (unsigned char *key, unsigned int keylen, int hash_algo, const char *wrap_algo_str, const void *secret, unsigned int secretlen, const void *ukm, unsigned int ukmlen) { gpg_error_t err = 0; unsigned int hashlen; gcry_md_hd_t hash_hd; unsigned char counter; unsigned int n, ncopy; hashlen = gcry_md_get_algo_dlen (hash_algo); if (!hashlen) return gpg_error (GPG_ERR_INV_ARG); err = gcry_md_open (&hash_hd, hash_algo, 0); if (err) return err; /* According to SEC1 3.6.1 we should check that * SECRETLEN + UKMLEN + 4 < maxhashlen * However, we have no practical limit on the hash length and thus * there is no point in checking this. The second check that * KEYLEN < hashlen*(2^32-1) * is obviously also not needed. */ for (n=0, counter=1; n < keylen; counter++) { if (counter > 1) gcry_md_reset (hash_hd); gcry_md_write (hash_hd, secret, secretlen); gcry_md_write (hash_hd, "\x00\x00\x00", 3); /* MSBs of counter */ gcry_md_write (hash_hd, &counter, 1); err = hash_ecc_cms_shared_info (hash_hd, wrap_algo_str, keylen, ukm, ukmlen); if (err) break; gcry_md_final (hash_hd); if (n + hashlen > keylen) ncopy = keylen - n; else ncopy = hashlen; memcpy (key+n, gcry_md_read (hash_hd, 0), ncopy); n += ncopy; } gcry_md_close (hash_hd); return err; } /* This function will modify SECRET. NBITS is the size of the curve * which which we took from the certificate. */ static gpg_error_t ecdh_decrypt (unsigned char *secret, size_t secretlen, unsigned int nbits, gcry_sexp_t enc_val, unsigned char **r_result, unsigned int *r_resultlen) { gpg_error_t err; gcry_buffer_t ioarray[4] = { {0}, {0}, {0}, {0} }; char *encr_algo_str = NULL; char *wrap_algo_str = NULL; int hash_algo, cipher_algo; const unsigned char *ukm; /* Alias for ioarray[2]. */ unsigned int ukmlen; const unsigned char *data; /* Alias for ioarray[3]. */ unsigned int datalen; unsigned int keylen; unsigned char key[32]; gcry_cipher_hd_t cipher_hd = NULL; unsigned char *result = NULL; unsigned int resultlen; *r_resultlen = 0; *r_result = NULL; /* Extract X from SECRET; this is the actual secret. Unless a * smartcard diretcly returns X, it must be in the format of: * * 04 || X || Y * 40 || X * 41 || X */ if (secretlen < 2) return gpg_error (GPG_ERR_BAD_DATA); if (secretlen == (nbits+7)/8) ; /* Matches curve length - this is already the X coordinate. */ else if (*secret == 0x04) { secretlen--; memmove (secret, secret+1, secretlen); if ((secretlen & 1)) return gpg_error (GPG_ERR_BAD_DATA); secretlen /= 2; } else if (*secret == 0x40 || *secret == 0x41) { secretlen--; memmove (secret, secret+1, secretlen); } else return gpg_error (GPG_ERR_BAD_DATA); if (!secretlen) return gpg_error (GPG_ERR_BAD_DATA); if (DBG_CRYPTO) log_printhex (secret, secretlen, "ECDH X ..:"); /* We have now the shared secret bytes in (SECRET,SECRETLEN). Now * we will compute the KEK using a value dervied from the secret * bytes. */ err = gcry_sexp_extract_param (enc_val, "enc-val", "&'encr-algo''wrap-algo''ukm'?s", ioarray+0, ioarray+1, ioarray+2, ioarray+3, NULL); if (err) { log_error ("extracting ECDH parameter failed: %s\n", gpg_strerror (err)); goto leave; } encr_algo_str = string_from_gcry_buffer (ioarray); if (!encr_algo_str) { err = gpg_error_from_syserror (); goto leave; } wrap_algo_str = string_from_gcry_buffer (ioarray+1); if (!wrap_algo_str) { err = gpg_error_from_syserror (); goto leave; } ukm = ioarray[2].data; ukmlen = ioarray[2].len; data = ioarray[3].data; datalen = ioarray[3].len; /* Check parameters. */ if (DBG_CRYPTO) { log_debug ("encr_algo: %s\n", encr_algo_str); log_debug ("wrap_algo: %s\n", wrap_algo_str); log_printhex (ukm, ukmlen, "ukm .....:"); log_printhex (data, datalen, "data ....:"); } if (!strcmp (encr_algo_str, "1.3.132.1.11.1")) { /* dhSinglePass-stdDH-sha256kdf-scheme */ hash_algo = GCRY_MD_SHA256; } else if (!strcmp (encr_algo_str, "1.3.132.1.11.2")) { /* dhSinglePass-stdDH-sha384kdf-scheme */ hash_algo = GCRY_MD_SHA384; } else if (!strcmp (encr_algo_str, "1.3.132.1.11.3")) { /* dhSinglePass-stdDH-sha512kdf-scheme */ hash_algo = GCRY_MD_SHA512; } else if (!strcmp (encr_algo_str, "1.3.133.16.840.63.0.2")) { /* dhSinglePass-stdDH-sha1kdf-scheme */ hash_algo = GCRY_MD_SHA1; } else { err = gpg_error (GPG_ERR_PUBKEY_ALGO); goto leave; } if (!strcmp (wrap_algo_str, "2.16.840.1.101.3.4.1.5")) { cipher_algo = GCRY_CIPHER_AES128; keylen = 16; } else if (!strcmp (wrap_algo_str, "2.16.840.1.101.3.4.1.25")) { cipher_algo = GCRY_CIPHER_AES192; keylen = 24; } else if (!strcmp (wrap_algo_str, "2.16.840.1.101.3.4.1.45")) { cipher_algo = GCRY_CIPHER_AES256; keylen = 32; } else { err = gpg_error (GPG_ERR_PUBKEY_ALGO); goto leave; } err = ecdh_derive_kek (key, keylen, hash_algo, wrap_algo_str, secret, secretlen, ukm, ukmlen); if (err) goto leave; if (DBG_CRYPTO) log_printhex (key, keylen, "KEK .....:"); /* Unwrap the key. */ if ((datalen % 8) || datalen < 16) { log_error ("can't use a shared secret of %u bytes for ecdh\n", datalen); err = gpg_error (GPG_ERR_BAD_DATA); goto leave; } resultlen = datalen - 8; result = xtrymalloc_secure (resultlen); if (!result) { err = gpg_error_from_syserror (); goto leave; } err = gcry_cipher_open (&cipher_hd, cipher_algo, GCRY_CIPHER_MODE_AESWRAP, 0); if (err) { log_error ("ecdh failed to initialize AESWRAP: %s\n", gpg_strerror (err)); goto leave; } err = gcry_cipher_setkey (cipher_hd, key, keylen); wipememory (key, sizeof key); if (err) { log_error ("ecdh failed in gcry_cipher_setkey: %s\n", gpg_strerror (err)); goto leave; } err = gcry_cipher_decrypt (cipher_hd, result, resultlen, data, datalen); if (err) { log_error ("ecdh failed in gcry_cipher_decrypt: %s\n",gpg_strerror (err)); goto leave; } *r_resultlen = resultlen; *r_result = result; result = NULL; leave: if (result) { wipememory (result, resultlen); xfree (result); } gcry_cipher_close (cipher_hd); xfree (encr_algo_str); xfree (wrap_algo_str); xfree (ioarray[0].data); xfree (ioarray[1].data); xfree (ioarray[2].data); xfree (ioarray[3].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. */ static int prepare_decryption (ctrl_t ctrl, const char *hexkeygrip, int pk_algo, unsigned int nbits, const char *desc, ksba_const_sexp_t enc_val, struct decrypt_filter_parm_s *parm) { char *seskey = NULL; size_t n, seskeylen; int rc; if (DBG_CRYPTO) log_printcanon ("decrypting:", enc_val, 0); 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:"); n=0; if (pk_algo == GCRY_PK_ECC) { 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 = ecdh_decrypt (seskey, seskeylen, nbits, s_enc_val, &decrypted, &decryptedlen); gcry_sexp_release (s_enc_val); if (rc) goto leave; xfree (seskey); seskey = decrypted; seskeylen = decryptedlen; } else if (seskeylen == 32 || seskeylen == 24 || seskeylen == 16) { /* Smells like an AES-128, 3-DES, or AES-256 key. This might * happen because a SC has already done the unpacking. A better * solution would be to test for this only after we triggered * the GPG_ERR_INV_SESSION_KEY. */ } else { if (n + 7 > seskeylen ) { rc = gpg_error (GPG_ERR_INV_SESSION_KEY); goto leave; } /* FIXME: Actually the leading zero is required but due to the way we encode the output in libgcrypt as an MPI we are not able to encode that leading zero. However, when using a Smartcard we are doing it the right way and therefore we have to skip the zero. This should be fixed in gpg-agent of course. */ if (!seskey[n]) n++; if (seskey[n] != 2 ) /* Wrong block type version. */ { rc = gpg_error (GPG_ERR_INV_SESSION_KEY); goto leave; } for (n++; n < seskeylen && seskey[n]; n++) /* Skip the random bytes. */ ; n++; /* and the zero byte */ if (n >= seskeylen ) { rc = gpg_error (GPG_ERR_INV_SESSION_KEY); goto leave; } } if (DBG_CRYPTO) log_printhex (seskey+n, seskeylen-n, "CEK .....:"); if (opt.verbose) log_info (_("%s.%s encrypted data\n"), gcry_cipher_algo_name (parm->algo), cipher_mode_to_string (parm->mode)); rc = gcry_cipher_open (&parm->hd, parm->algo, parm->mode, 0); if (rc) { log_error ("error creating decryptor: %s\n", gpg_strerror (rc)); goto leave; } rc = gcry_cipher_setkey (parm->hd, seskey+n, seskeylen-n); if (gpg_err_code (rc) == GPG_ERR_WEAK_KEY) { log_info (_("WARNING: message was encrypted with " "a weak key in the symmetric cipher.\n")); rc = 0; } if (rc) { log_error("key setup failed: %s\n", gpg_strerror(rc) ); goto leave; } gcry_cipher_setiv (parm->hd, parm->iv, parm->ivlen); leave: xfree (seskey); return rc; } /* This function is called by the KSBA writer just before the actual write is done. The function must take INLEN bytes from INBUF, decrypt it and store it inoutbuf which has a maximum size of maxoutlen. The valid bytes in outbuf should be return in outlen. Due to different buffer sizes or different length of input and output, it may happen that fewer bytes are processed or fewer bytes are written. */ static gpg_error_t decrypt_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; int blklen = parm->blklen; size_t orig_inlen = inlen; /* fixme: Should we issue an error when we have not seen one full block? */ if (!inlen) return gpg_error (GPG_ERR_BUG); if (maxoutlen < 2*parm->blklen) return gpg_error (GPG_ERR_BUG); /* Make some space because we will later need an extra block at the end. */ maxoutlen -= blklen; if (parm->helpblocklen) { int i, j; for (i=parm->helpblocklen,j=0; i < blklen && j < inlen; i++, j++) parm->helpblock[i] = ((const char*)inbuf)[j]; inlen -= j; if (blklen > maxoutlen) return gpg_error (GPG_ERR_BUG); if (i < blklen) { parm->helpblocklen = i; *outlen = 0; } else { parm->helpblocklen = 0; if (parm->any_data) { memcpy (outbuf, parm->lastblock, blklen); *outlen =blklen; } else *outlen = 0; gcry_cipher_decrypt (parm->hd, parm->lastblock, blklen, parm->helpblock, blklen); parm->any_data = 1; } *inused = orig_inlen - inlen; return 0; } if (inlen > maxoutlen) inlen = maxoutlen; if (inlen % blklen) { /* store the remainder away */ parm->helpblocklen = inlen%blklen; inlen = inlen/blklen*blklen; memcpy (parm->helpblock, (const char*)inbuf+inlen, parm->helpblocklen); } *inused = inlen + parm->helpblocklen; if (inlen) { - assert (inlen >= blklen); + log_assert (inlen >= blklen); if (parm->any_data) { gcry_cipher_decrypt (parm->hd, (char*)outbuf+blklen, inlen, inbuf, inlen); memcpy (outbuf, parm->lastblock, blklen); memcpy (parm->lastblock,(char*)outbuf+inlen, blklen); *outlen = inlen; } else { gcry_cipher_decrypt (parm->hd, outbuf, inlen, inbuf, inlen); memcpy (parm->lastblock, (char*)outbuf+inlen-blklen, blklen); *outlen = inlen - blklen; parm->any_data = 1; } } else *outlen = 0; return 0; } /* Perform a decrypt operation. */ int gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) { int rc; gnupg_ksba_io_t b64reader = NULL; gnupg_ksba_io_t b64writer = NULL; ksba_reader_t reader; ksba_writer_t writer; ksba_cms_t cms = NULL; ksba_stop_reason_t stopreason; KEYDB_HANDLE kh; int recp; estream_t in_fp = NULL; struct decrypt_filter_parm_s dfparm; memset (&dfparm, 0, sizeof dfparm); audit_set_type (ctrl->audit, AUDIT_TYPE_DECRYPT); kh = keydb_new (); if (!kh) { log_error (_("failed to allocate keyDB handle\n")); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } in_fp = es_fdopen_nc (in_fd, "rb"); if (!in_fp) { rc = gpg_error_from_syserror (); log_error ("fdopen() failed: %s\n", strerror (errno)); goto leave; } rc = gnupg_ksba_create_reader (&b64reader, ((ctrl->is_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->is_base64? GNUPG_KSBA_IO_BASE64 : 0) | (ctrl->autodetect_encoding? GNUPG_KSBA_IO_AUTODETECT : 0)), in_fp, &reader); if (rc) { log_error ("can't create reader: %s\n", gpg_strerror (rc)); goto leave; } rc = gnupg_ksba_create_writer (&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)), ctrl->pem_name, out_fp, &writer); if (rc) { log_error ("can't create writer: %s\n", gpg_strerror (rc)); goto leave; } rc = ksba_cms_new (&cms); if (rc) goto leave; rc = ksba_cms_set_reader_writer (cms, reader, writer); if (rc) { log_error ("ksba_cms_set_reader_writer failed: %s\n", gpg_strerror (rc)); goto leave; } audit_log (ctrl->audit, AUDIT_SETUP_READY); /* Parser loop. */ do { rc = ksba_cms_parse (cms, &stopreason); if (rc) { log_error ("ksba_cms_parse failed: %s\n", gpg_strerror (rc)); goto leave; } if (stopreason == KSBA_SR_BEGIN_DATA || stopreason == KSBA_SR_DETACHED_DATA) { 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); algoid = ksba_cms_get_content_oid (cms, 2/* encryption algo*/); algo = gcry_cipher_map_name (algoid); mode = gcry_cipher_mode_from_oid (algoid); if (!algo || !mode) { rc = gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); log_error ("unsupported algorithm '%s'\n", algoid? algoid:"?"); if (algoid && !strcmp (algoid, "1.2.840.113549.3.2")) log_info (_("(this is the RC2 algorithm)\n")); else if (!algoid) log_info (_("(this does not seem to be an encrypted" " message)\n")); { char numbuf[50]; sprintf (numbuf, "%d", rc); gpgsm_status2 (ctrl, STATUS_ERROR, "decrypt.algorithm", numbuf, algoid?algoid:"?", NULL); audit_log_s (ctrl->audit, AUDIT_BAD_DATA_CIPHER_ALGO, algoid); } /* If it seems that this is not an encrypted message we return a more sensible error code. */ if (!algoid) rc = gpg_error (GPG_ERR_NO_DATA); goto leave; } /* Check compliance. */ if (! gnupg_cipher_is_allowed (opt.compliance, 0, algo, mode)) { log_error (_("cipher algorithm '%s'" " may not be used in %s mode\n"), gcry_cipher_algo_name (algo), gnupg_compliance_option_string (opt.compliance)); rc = gpg_error (GPG_ERR_CIPHER_ALGO); goto leave; } /* For CMS, CO_DE_VS demands CBC mode. */ 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 = ksba_cms_get_content_enc_iv (cms, dfparm.iv, sizeof (dfparm.iv), &dfparm.ivlen); if (rc) { log_error ("error getting IV: %s\n", gpg_strerror (rc)); goto leave; } for (recp=0; !any_key; recp++) { char *issuer; ksba_sexp_t serial; ksba_sexp_t enc_val; char *hexkeygrip = NULL; char *pkalgostr = NULL; char *pkfpr = NULL; char *desc = NULL; char kidbuf[16+1]; int tmp_rc; ksba_cert_t cert = NULL; unsigned int nbits; int pk_algo = 0; *kidbuf = 0; tmp_rc = ksba_cms_get_issuer_serial (cms, recp, &issuer, &serial); 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)); else { if (opt.verbose) { log_info ("recp %d - issuer: '%s'\n", recp, issuer? issuer:"[NONE]"); log_info ("recp %d - serial: ", recp); gpgsm_dump_serial (serial); log_printf ("\n"); } if (ctrl->audit) { char *tmpstr = gpgsm_format_sn_issuer (serial, issuer); audit_log_s (ctrl->audit, AUDIT_RECP_NAME, tmpstr); xfree (tmpstr); } keydb_search_reset (kh); rc = keydb_search_issuer_sn (ctrl, kh, issuer, serial); if (rc) { log_error ("failed to find the certificate: %s\n", gpg_strerror(rc)); goto oops; } rc = keydb_get_cert (kh, &cert); if (rc) { log_error ("failed to get cert: %s\n", gpg_strerror (rc)); goto oops; } /* Print the ENC_TO status line. Note that we can do so only if we have the certificate. This is in contrast to gpg where the keyID is commonly included in the encrypted messages. It is too cumbersome to retrieve the used algorithm, thus we don't print it for now. We also record the keyid for later use. */ { unsigned long kid[2]; kid[0] = gpgsm_get_short_fingerprint (cert, kid+1); snprintf (kidbuf, sizeof kidbuf, "%08lX%08lX", kid[1], kid[0]); gpgsm_status2 (ctrl, STATUS_ENC_TO, kidbuf, "0", "0", NULL); } /* Put the certificate into the audit log. */ audit_log_cert (ctrl->audit, AUDIT_SAVE_CERT, cert, 0); /* Just in case there is a problem with the own certificate we print this message - should never happen of course */ rc = gpgsm_cert_use_decrypt_p (cert); if (rc) { char numbuf[50]; sprintf (numbuf, "%d", rc); gpgsm_status2 (ctrl, STATUS_ERROR, "decrypt.keyusage", numbuf, NULL); rc = 0; } hexkeygrip = gpgsm_get_keygrip_hexstring (cert); desc = gpgsm_format_keydesc (cert); pkfpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); pkalgostr = gpgsm_pubkey_algo_string (cert, NULL); pk_algo = gpgsm_get_key_algo_info (cert, &nbits); if (!opt.quiet) log_info (_("encrypted to %s key %s\n"), pkalgostr, pkfpr); /* Check compliance. */ if (!gnupg_pk_is_allowed (opt.compliance, PK_USE_DECRYPTION, pk_algo, 0, NULL, nbits, NULL)) { char kidstr[10+1]; snprintf (kidstr, sizeof kidstr, "0x%08lX", gpgsm_get_short_fingerprint (cert, NULL)); log_info (_("key %s is not suitable for decryption" " in %s mode\n"), kidstr, gnupg_compliance_option_string(opt.compliance)); rc = gpg_error (GPG_ERR_PUBKEY_ALGO); goto oops; } /* 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)); oops: if (rc) { /* We cannot check compliance of certs that we * don't have. */ is_de_vs = 0; } xfree (issuer); xfree (serial); ksba_cert_release (cert); } if (!hexkeygrip || !pk_algo) ; else if (!(enc_val = ksba_cms_get_enc_val (cms, recp))) log_error ("recp %d - error getting encrypted session key\n", recp); else { rc = prepare_decryption (ctrl, hexkeygrip, pk_algo, nbits, desc, enc_val, &dfparm); xfree (enc_val); if (rc) { log_info ("decrypting session key failed: %s\n", gpg_strerror (rc)); if (gpg_err_code (rc) == GPG_ERR_NO_SECKEY && *kidbuf) gpgsm_status2 (ctrl, STATUS_NO_SECKEY, kidbuf, NULL); } else { /* setup the bulk decrypter */ any_key = 1; ksba_writer_set_filter (writer, decrypt_filter, &dfparm); if (is_de_vs) gpgsm_status (ctrl, STATUS_DECRYPTION_COMPLIANCE_MODE, gnupg_status_compliance_flag (CO_DE_VS)); } audit_log_ok (ctrl->audit, AUDIT_RECP_RESULT, rc); } xfree (pkalgostr); xfree (pkfpr); xfree (hexkeygrip); xfree (desc); } /* If we write an audit log add the unused recipients to the log as well. */ if (ctrl->audit && any_key) { for (;; recp++) { char *issuer; ksba_sexp_t serial; int tmp_rc; tmp_rc = ksba_cms_get_issuer_serial (cms, recp, &issuer, &serial); if (tmp_rc == -1) 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)); else { char *tmpstr = gpgsm_format_sn_issuer (serial, issuer); audit_log_s (ctrl->audit, AUDIT_RECP_NAME, tmpstr); xfree (tmpstr); xfree (issuer); xfree (serial); } } } if (!any_key) { if (!rc) rc = gpg_error (GPG_ERR_NO_SECKEY); goto leave; } } else if (stopreason == KSBA_SR_END_DATA) { ksba_writer_set_filter (writer, NULL, NULL); if (dfparm.any_data) { /* write the last block with padding removed */ int i, npadding = dfparm.lastblock[dfparm.blklen-1]; if (!npadding || npadding > dfparm.blklen) { log_error ("invalid padding with value %d\n", npadding); rc = gpg_error (GPG_ERR_INV_DATA); goto leave; } rc = ksba_writer_write (writer, dfparm.lastblock, dfparm.blklen - npadding); if (rc) goto leave; for (i=dfparm.blklen - npadding; i < dfparm.blklen; i++) { if (dfparm.lastblock[i] != npadding) { log_error ("inconsistent padding\n"); rc = gpg_error (GPG_ERR_INV_DATA); goto leave; } } } } } while (stopreason != KSBA_SR_READY); rc = gnupg_ksba_finish_writer (b64writer); if (rc) { log_error ("write failed: %s\n", gpg_strerror (rc)); goto leave; } gpgsm_status (ctrl, STATUS_DECRYPTION_OKAY, NULL); leave: audit_log_ok (ctrl->audit, AUDIT_DECRYPTION_RESULT, rc); if (rc) { gpgsm_status (ctrl, STATUS_DECRYPTION_FAILED, NULL); log_error ("message decryption failed: %s <%s>\n", gpg_strerror (rc), gpg_strsource (rc)); } ksba_cms_release (cms); gnupg_ksba_destroy_reader (b64reader); gnupg_ksba_destroy_writer (b64writer); keydb_release (kh); es_fclose (in_fp); if (dfparm.hd) gcry_cipher_close (dfparm.hd); return rc; } diff --git a/sm/delete.c b/sm/delete.c index b370406de..9ba3fa137 100644 --- a/sm/delete.c +++ b/sm/delete.c @@ -1,181 +1,180 @@ /* delete.c - Delete certificates from the keybox. * Copyright (C) 2002, 2009 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include -#include #include "gpgsm.h" #include #include #include "keydb.h" #include "../common/i18n.h" /* Delete a certificate or an secret key from a key database. */ static int delete_one (ctrl_t ctrl, const char *username) { int rc = 0; KEYDB_SEARCH_DESC desc; KEYDB_HANDLE kh = NULL; ksba_cert_t cert = NULL; int duplicates = 0; int is_ephem = 0; rc = classify_user_id (username, &desc, 0); if (rc) { log_error (_("certificate '%s' not found: %s\n"), username, gpg_strerror (rc)); gpgsm_status2 (ctrl, STATUS_DELETE_PROBLEM, "1", NULL); goto leave; } kh = keydb_new (); if (!kh) { log_error ("keydb_new failed\n"); goto leave; } /* If the key is specified in a unique way, include ephemeral keys in the search. */ if ( desc.mode == KEYDB_SEARCH_MODE_FPR || desc.mode == KEYDB_SEARCH_MODE_KEYGRIP ) { is_ephem = 1; keydb_set_ephemeral (kh, 1); } rc = keydb_search (ctrl, kh, &desc, 1); if (!rc) rc = keydb_get_cert (kh, &cert); if (!rc && !is_ephem) { unsigned char fpr[20]; gpgsm_get_fingerprint (cert, 0, fpr, NULL); next_ambigious: rc = keydb_search (ctrl, kh, &desc, 1); if (rc == -1) rc = 0; else if (!rc) { ksba_cert_t cert2 = NULL; unsigned char fpr2[20]; /* We ignore all duplicated certificates which might have been inserted due to program bugs. */ if (!keydb_get_cert (kh, &cert2)) { gpgsm_get_fingerprint (cert2, 0, fpr2, NULL); ksba_cert_release (cert2); if (!memcmp (fpr, fpr2, 20)) { duplicates++; goto next_ambigious; } } rc = gpg_error (GPG_ERR_AMBIGUOUS_NAME); } } if (rc) { if (rc == -1) rc = gpg_error (GPG_ERR_NO_PUBKEY); log_error (_("certificate '%s' not found: %s\n"), username, gpg_strerror (rc)); gpgsm_status2 (ctrl, STATUS_DELETE_PROBLEM, "3", NULL); goto leave; } /* We need to search again to get back to the right position. Neo * that the lock is kept until the KH is released. */ rc = keydb_lock (kh); if (rc) { log_error (_("error locking keybox: %s\n"), gpg_strerror (rc)); goto leave; } do { keydb_search_reset (kh); rc = keydb_search (ctrl, kh, &desc, 1); if (rc) { log_error ("problem re-searching certificate: %s\n", gpg_strerror (rc)); goto leave; } rc = keydb_delete (kh); if (rc) goto leave; if (opt.verbose) { if (duplicates) log_info (_("duplicated certificate '%s' deleted\n"), username); else log_info (_("certificate '%s' deleted\n"), username); } } while (duplicates--); leave: keydb_release (kh); ksba_cert_release (cert); return rc; } /* Delete the certificates specified by NAMES. */ int gpgsm_delete (ctrl_t ctrl, strlist_t names) { int rc; if (!names) { log_error ("nothing to delete\n"); return gpg_error (GPG_ERR_NO_DATA); } for (; names; names=names->next ) { rc = delete_one (ctrl, names->d); if (rc) { log_error (_("deleting certificate \"%s\" failed: %s\n"), names->d, gpg_strerror (rc) ); return rc; } } return 0; } diff --git a/sm/encrypt.c b/sm/encrypt.c index be4f19fef..449b3b4f8 100644 --- a/sm/encrypt.c +++ b/sm/encrypt.c @@ -1,855 +1,854 @@ /* encrypt.c - Encrypt a message * Copyright (C) 2001, 2003, 2004, 2007, 2008, * 2010 Free Software Foundation, Inc. * Copyright (C) 2001-2019 Werner Koch * Copyright (C) 2015-2020 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * SPDX-License-Identifier: GPL-3.0-or-later */ #include #include #include #include #include #include #include -#include #include "gpgsm.h" #include #include #include "keydb.h" #include "../common/i18n.h" #include "../common/compliance.h" struct dek_s { const char *algoid; int algo; gcry_cipher_hd_t chd; char key[32]; int keylen; char iv[32]; int ivlen; }; typedef struct dek_s *DEK; /* Callback parameters for the encryption. */ struct encrypt_cb_parm_s { estream_t fp; DEK dek; int eof_seen; int ready; int readerror; int bufsize; unsigned char *buffer; int buflen; }; /* Initialize the data encryption key (session key). */ static int init_dek (DEK dek) { int rc=0, mode, i; dek->algo = gcry_cipher_map_name (dek->algoid); mode = gcry_cipher_mode_from_oid (dek->algoid); if (!dek->algo || !mode) { log_error ("unsupported algorithm '%s'\n", dek->algoid); return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); } /* Extra check for algorithms we consider to be too weak for encryption, although we support them for decryption. Note that there is another check below discriminating on the key length. */ switch (dek->algo) { case GCRY_CIPHER_DES: case GCRY_CIPHER_RFC2268_40: log_error ("cipher algorithm '%s' not allowed: too weak\n", gnupg_cipher_algo_name (dek->algo)); return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); default: break; } dek->keylen = gcry_cipher_get_algo_keylen (dek->algo); if (!dek->keylen || dek->keylen > sizeof (dek->key)) return gpg_error (GPG_ERR_BUG); dek->ivlen = gcry_cipher_get_algo_blklen (dek->algo); if (!dek->ivlen || dek->ivlen > sizeof (dek->iv)) return gpg_error (GPG_ERR_BUG); /* Make sure we don't use weak keys. */ if (dek->keylen < 100/8) { log_error ("key length of '%s' too small\n", dek->algoid); return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); } rc = gcry_cipher_open (&dek->chd, dek->algo, mode, GCRY_CIPHER_SECURE); if (rc) { log_error ("failed to create cipher context: %s\n", gpg_strerror (rc)); return rc; } for (i=0; i < 8; i++) { gcry_randomize (dek->key, dek->keylen, GCRY_STRONG_RANDOM ); rc = gcry_cipher_setkey (dek->chd, dek->key, dek->keylen); if (gpg_err_code (rc) != GPG_ERR_WEAK_KEY) break; log_info(_("weak key created - retrying\n") ); } if (rc) { log_error ("failed to set the key: %s\n", gpg_strerror (rc)); gcry_cipher_close (dek->chd); dek->chd = NULL; return rc; } gcry_create_nonce (dek->iv, dek->ivlen); rc = gcry_cipher_setiv (dek->chd, dek->iv, dek->ivlen); if (rc) { log_error ("failed to set the IV: %s\n", gpg_strerror (rc)); gcry_cipher_close (dek->chd); dek->chd = NULL; return rc; } return 0; } /* Encode an RSA session key. */ static int encode_session_key (DEK dek, gcry_sexp_t * r_data) { gcry_sexp_t data; char *p; int rc; p = xtrymalloc (64 + 2 * dek->keylen); if (!p) return gpg_error_from_syserror (); strcpy (p, "(data\n (flags pkcs1)\n (value #"); bin2hex (dek->key, dek->keylen, p + strlen (p)); strcat (p, "#))\n"); rc = gcry_sexp_sscan (&data, NULL, p, strlen (p)); xfree (p); *r_data = data; return rc; } /* Encrypt DEK using ECDH. S_PKEY is the public key. On success the * result is stored at R_ENCVAL. Example of a public key: * * (public-key (ecc (curve "1.3.132.0.34") (q #04B0[...]B8#))) * */ static gpg_error_t ecdh_encrypt (DEK dek, gcry_sexp_t s_pkey, gcry_sexp_t *r_encval) { gpg_error_t err; gcry_sexp_t l1; char *curvebuf = NULL; const char *curve; unsigned int curvebits; const char *encr_algo_str; const char *wrap_algo_str; int hash_algo, cipher_algo; unsigned int keylen; unsigned char key[32]; gcry_sexp_t s_data = NULL; gcry_sexp_t s_encr = NULL; gcry_buffer_t ioarray[2] = { {0}, {0} }; unsigned char *secret; /* Alias for ioarray[0]. */ unsigned int secretlen; unsigned char *pubkey; /* Alias for ioarray[1]. */ unsigned int pubkeylen; gcry_cipher_hd_t cipher_hd = NULL; unsigned char *result = NULL; unsigned int resultlen; *r_encval = NULL; /* Figure out the encryption and wrap algo OIDs. */ /* Get the curve name if any, */ l1 = gcry_sexp_find_token (s_pkey, "curve", 0); if (l1) { curvebuf = gcry_sexp_nth_string (l1, 1); gcry_sexp_release (l1); } if (!curvebuf) { err = gpg_error (GPG_ERR_INV_CURVE); log_error ("%s: invalid public key: no curve\n", __func__); goto leave; } /* We need to use our OpenPGP mapping to turn a curve name into its * canonical numerical OID. We also use this to get the size of the * curve which we need to figure out a suitable hash algo. We * should have a Libgcrypt function to do this; see bug report #4926. */ curve = openpgp_curve_to_oid (curvebuf, &curvebits, NULL); if (!curve) { err = gpg_error (GPG_ERR_UNKNOWN_CURVE); log_error ("%s: invalid public key: %s\n", __func__, gpg_strerror (err)); goto leave; } xfree (curvebuf); curvebuf = NULL; /* Our mapping matches the recommended algorithms from RFC-5753 but * not supporting the short curves which would require 3DES. */ if (curvebits < 255) { err = gpg_error (GPG_ERR_UNKNOWN_CURVE); log_error ("%s: curve '%s' is not supported\n", __func__, curve); goto leave; } else if (opt.force_ecdh_sha1kdf) { /* dhSinglePass-stdDH-sha1kdf-scheme */ encr_algo_str = "1.3.133.16.840.63.0.2"; wrap_algo_str = "2.16.840.1.101.3.4.1.45"; hash_algo = GCRY_MD_SHA1; cipher_algo = GCRY_CIPHER_AES256; keylen = 32; } else if (curvebits <= 256) { /* dhSinglePass-stdDH-sha256kdf-scheme */ encr_algo_str = "1.3.132.1.11.1"; wrap_algo_str = "2.16.840.1.101.3.4.1.5"; hash_algo = GCRY_MD_SHA256; cipher_algo = GCRY_CIPHER_AES128; keylen = 16; } else if (curvebits <= 384) { /* dhSinglePass-stdDH-sha384kdf-scheme */ encr_algo_str = "1.3.132.1.11.2"; wrap_algo_str = "2.16.840.1.101.3.4.1.25"; hash_algo = GCRY_MD_SHA384; cipher_algo = GCRY_CIPHER_AES256; keylen = 24; } else { /* dhSinglePass-stdDH-sha512kdf-scheme*/ encr_algo_str = "1.3.132.1.11.3"; wrap_algo_str = "2.16.840.1.101.3.4.1.45"; hash_algo = GCRY_MD_SHA512; cipher_algo = GCRY_CIPHER_AES256; keylen = 32; } /* Create a secret and an ephemeral key. */ { char *k; k = gcry_random_bytes_secure ((curvebits+7)/8, GCRY_STRONG_RANDOM); if (DBG_CRYPTO) log_printhex (k, (curvebits+7)/8, "ephm. k .:"); err = gcry_sexp_build (&s_data, NULL, "%b", (int)(curvebits+7)/8, k); xfree (k); } if (err) { log_error ("%s: error building ephemeral secret: %s\n", __func__, gpg_strerror (err)); goto leave; } err = gcry_pk_encrypt (&s_encr, s_data, s_pkey); if (err) { log_error ("%s: error encrypting ephemeral secret: %s\n", __func__, gpg_strerror (err)); goto leave; } err = gcry_sexp_extract_param (s_encr, NULL, "&se", &ioarray+0, ioarray+1, NULL); if (err) { log_error ("%s: error extracting ephemeral key and secret: %s\n", __func__, gpg_strerror (err)); goto leave; } secret = ioarray[0].data; secretlen = ioarray[0].len; pubkey = ioarray[1].data; pubkeylen = ioarray[1].len; if (DBG_CRYPTO) { log_printhex (pubkey, pubkeylen, "pubkey ..:"); log_printhex (secret, secretlen, "secret ..:"); } /* Extract X coordinate from SECRET. */ if (secretlen < 5) /* 5 because N could be reduced to (n-1)/2. */ err = gpg_error (GPG_ERR_BAD_DATA); else if (*secret == 0x04) { secretlen--; memmove (secret, secret+1, secretlen); if ((secretlen & 1)) { err = gpg_error (GPG_ERR_BAD_DATA); goto leave; } secretlen /= 2; } else if (*secret == 0x40 || *secret == 0x41) { secretlen--; memmove (secret, secret+1, secretlen); } else err = gpg_error (GPG_ERR_BAD_DATA); if (err) goto leave; if (DBG_CRYPTO) log_printhex (secret, secretlen, "ECDH X ..:"); err = ecdh_derive_kek (key, keylen, hash_algo, wrap_algo_str, secret, secretlen, NULL, 0); if (err) goto leave; if (DBG_CRYPTO) log_printhex (key, keylen, "KEK .....:"); /* Wrap the key. */ if ((dek->keylen % 8) || dek->keylen < 16) { log_error ("%s: can't use a session key of %u bytes\n", __func__, dek->keylen); err = gpg_error (GPG_ERR_BAD_DATA); goto leave; } resultlen = dek->keylen + 8; result = xtrymalloc_secure (resultlen); if (!result) { err = gpg_error_from_syserror (); goto leave; } err = gcry_cipher_open (&cipher_hd, cipher_algo, GCRY_CIPHER_MODE_AESWRAP, 0); if (err) { log_error ("%s: failed to initialize AESWRAP: %s\n", __func__, gpg_strerror (err)); goto leave; } err = gcry_cipher_setkey (cipher_hd, key, keylen); wipememory (key, sizeof key); if (err) { log_error ("%s: failed in gcry_cipher_setkey: %s\n", __func__, gpg_strerror (err)); goto leave; } err = gcry_cipher_encrypt (cipher_hd, result, resultlen, dek->key, dek->keylen); if (err) { log_error ("%s: failed in gcry_cipher_encrypt: %s\n", __func__, gpg_strerror (err)); goto leave; } if (DBG_CRYPTO) log_printhex (result, resultlen, "w(CEK) ..:"); err = gcry_sexp_build (r_encval, NULL, "(enc-val(ecdh(e%b)(s%b)(encr-algo%s)(wrap-algo%s)))", (int)pubkeylen, pubkey, (int)resultlen, result, encr_algo_str, wrap_algo_str, NULL); if (err) log_error ("%s: failed building final S-exp: %s\n", __func__, gpg_strerror (err)); leave: gcry_cipher_close (cipher_hd); wipememory (key, sizeof key); xfree (result); xfree (ioarray[0].data); xfree (ioarray[1].data); gcry_sexp_release (s_data); gcry_sexp_release (s_encr); xfree (curvebuf); return err; } /* Encrypt the DEK under the key contained in CERT and return it as a * canonical S-expressions at ENCVAL. PK_ALGO is the public key * algorithm which the caller has already retrieved from CERT. */ static int encrypt_dek (const DEK dek, ksba_cert_t cert, int pk_algo, unsigned char **encval) { gcry_sexp_t s_ciph, s_data, s_pkey; int rc; ksba_sexp_t buf; size_t len; *encval = NULL; /* get the key from the cert */ buf = ksba_cert_get_public_key (cert); if (!buf) { log_error ("no public key for recipient\n"); return gpg_error (GPG_ERR_NO_PUBKEY); } len = gcry_sexp_canon_len (buf, 0, NULL, NULL); if (!len) { log_error ("libksba did not return a proper S-Exp\n"); return gpg_error (GPG_ERR_BUG); } rc = gcry_sexp_sscan (&s_pkey, NULL, (char*)buf, len); xfree (buf); buf = NULL; if (rc) { log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc)); return rc; } if (DBG_CRYPTO) { log_printsexp (" pubkey:", s_pkey); log_printhex (dek->key, dek->keylen, "CEK .....:"); } /* Put the encoded cleartext into a simple list. */ s_data = NULL; /* (avoid compiler warning) */ if (pk_algo == GCRY_PK_ECC) { rc = ecdh_encrypt (dek, s_pkey, &s_ciph); } else { rc = encode_session_key (dek, &s_data); if (rc) { log_error ("encode_session_key failed: %s\n", gpg_strerror (rc)); return rc; } if (DBG_CRYPTO) log_printsexp (" data:", s_data); /* pass it to libgcrypt */ rc = gcry_pk_encrypt (&s_ciph, s_data, s_pkey); } gcry_sexp_release (s_data); gcry_sexp_release (s_pkey); if (DBG_CRYPTO) log_printsexp ("enc-val:", s_ciph); /* Reformat it. */ if (!rc) { rc = make_canon_sexp (s_ciph, encval, NULL); gcry_sexp_release (s_ciph); } return rc; } /* do the actual encryption */ static int encrypt_cb (void *cb_value, char *buffer, size_t count, size_t *nread) { struct encrypt_cb_parm_s *parm = cb_value; int blklen = parm->dek->ivlen; unsigned char *p; size_t n; *nread = 0; if (!buffer) return -1; /* not supported */ if (parm->ready) return -1; if (count < blklen) BUG (); if (!parm->eof_seen) { /* fillup the buffer */ p = parm->buffer; for (n=parm->buflen; n < parm->bufsize; n++) { int c = es_getc (parm->fp); if (c == EOF) { if (es_ferror (parm->fp)) { parm->readerror = errno; return -1; } parm->eof_seen = 1; break; } p[n] = c; } parm->buflen = n; } n = parm->buflen < count? parm->buflen : count; n = n/blklen * blklen; if (n) { /* encrypt the stuff */ gcry_cipher_encrypt (parm->dek->chd, buffer, n, parm->buffer, n); *nread = n; /* Who cares about cycles, take the easy way and shift the buffer */ parm->buflen -= n; memmove (parm->buffer, parm->buffer+n, parm->buflen); } else if (parm->eof_seen) { /* no complete block but eof: add padding */ /* fixme: we should try to do this also in the above code path */ int i, npad = blklen - (parm->buflen % blklen); p = parm->buffer; for (n=parm->buflen, i=0; n < parm->bufsize && i < npad; n++, i++) p[n] = npad; gcry_cipher_encrypt (parm->dek->chd, buffer, n, parm->buffer, n); *nread = n; parm->ready = 1; } return 0; } /* Perform an encrypt operation. Encrypt the data received on DATA-FD and write it to OUT_FP. The recipients are take from the certificate given in recplist; if this is NULL it will be encrypted for a default recipient */ int gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) { int rc = 0; gnupg_ksba_io_t b64writer = NULL; gpg_error_t err; ksba_writer_t writer; ksba_reader_t reader = NULL; ksba_cms_t cms = NULL; ksba_stop_reason_t stopreason; KEYDB_HANDLE kh = NULL; struct encrypt_cb_parm_s encparm; DEK dek = NULL; int recpno; estream_t data_fp = NULL; certlist_t cl; int count; int compliant; memset (&encparm, 0, sizeof encparm); audit_set_type (ctrl->audit, AUDIT_TYPE_ENCRYPT); /* Check that the certificate list is not empty and that at least one certificate is not flagged as encrypt_to; i.e. is a real recipient. */ for (cl = recplist; cl; cl = cl->next) if (!cl->is_encrypt_to) break; if (!cl) { log_error(_("no valid recipients given\n")); gpgsm_status (ctrl, STATUS_NO_RECP, "0"); audit_log_i (ctrl->audit, AUDIT_GOT_RECIPIENTS, 0); rc = gpg_error (GPG_ERR_NO_PUBKEY); goto leave; } for (count = 0, cl = recplist; cl; cl = cl->next) count++; audit_log_i (ctrl->audit, AUDIT_GOT_RECIPIENTS, count); kh = keydb_new (); if (!kh) { log_error (_("failed to allocate keyDB handle\n")); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } /* Fixme: We should use the unlocked version of the es functions. */ data_fp = es_fdopen_nc (data_fd, "rb"); if (!data_fp) { rc = gpg_error_from_syserror (); log_error ("fdopen() failed: %s\n", strerror (errno)); goto leave; } err = ksba_reader_new (&reader); if (err) rc = err; if (!rc) rc = ksba_reader_set_cb (reader, encrypt_cb, &encparm); if (rc) goto leave; encparm.fp = data_fp; ctrl->pem_name = "ENCRYPTED MESSAGE"; rc = gnupg_ksba_create_writer (&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)), ctrl->pem_name, out_fp, &writer); if (rc) { log_error ("can't create writer: %s\n", gpg_strerror (rc)); goto leave; } err = ksba_cms_new (&cms); if (err) { rc = err; goto leave; } err = ksba_cms_set_reader_writer (cms, reader, writer); if (err) { log_error ("ksba_cms_set_reader_writer failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } audit_log (ctrl->audit, AUDIT_GOT_DATA); /* We are going to create enveloped data with uninterpreted data as inner content */ err = ksba_cms_set_content_type (cms, 0, KSBA_CT_ENVELOPED_DATA); if (!err) err = ksba_cms_set_content_type (cms, 1, KSBA_CT_DATA); if (err) { log_error ("ksba_cms_set_content_type failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } /* Check compliance. */ if (!gnupg_cipher_is_allowed (opt.compliance, 1, gcry_cipher_map_name (opt.def_cipher_algoid), gcry_cipher_mode_from_oid (opt.def_cipher_algoid))) { log_error (_("cipher algorithm '%s' may not be used in %s mode\n"), opt.def_cipher_algoid, gnupg_compliance_option_string (opt.compliance)); rc = gpg_error (GPG_ERR_CIPHER_ALGO); goto leave; } if (!gnupg_rng_is_compliant (opt.compliance)) { rc = gpg_error (GPG_ERR_FORBIDDEN); log_error (_("%s is not compliant with %s mode\n"), "RNG", gnupg_compliance_option_string (opt.compliance)); gpgsm_status_with_error (ctrl, STATUS_ERROR, "random-compliance", rc); goto leave; } /* Create a session key */ dek = xtrycalloc_secure (1, sizeof *dek); if (!dek) rc = out_of_core (); else { dek->algoid = opt.def_cipher_algoid; rc = init_dek (dek); } if (rc) { log_error ("failed to create the session key: %s\n", gpg_strerror (rc)); goto leave; } err = ksba_cms_set_content_enc_algo (cms, dek->algoid, dek->iv, dek->ivlen); if (err) { log_error ("ksba_cms_set_content_enc_algo failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } encparm.dek = dek; /* Use a ~8k (AES) or ~4k (3DES) buffer */ encparm.bufsize = 500 * dek->ivlen; encparm.buffer = xtrymalloc (encparm.bufsize); if (!encparm.buffer) { rc = out_of_core (); goto leave; } audit_log_s (ctrl->audit, AUDIT_SESSION_KEY, dek->algoid); compliant = gnupg_cipher_is_compliant (CO_DE_VS, dek->algo, GCRY_CIPHER_MODE_CBC); /* Gather certificates of recipients, encrypt the session key for each and store them in the CMS object */ for (recpno = 0, cl = recplist; cl; recpno++, cl = cl->next) { unsigned char *encval; unsigned int nbits; int pk_algo; /* Check compliance. */ pk_algo = gpgsm_get_key_algo_info (cl->cert, &nbits); if (!gnupg_pk_is_compliant (opt.compliance, pk_algo, 0, NULL, nbits, NULL)) { char kidstr[10+1]; snprintf (kidstr, sizeof kidstr, "0x%08lX", gpgsm_get_short_fingerprint (cl->cert, NULL)); log_info (_("WARNING: key %s is not suitable for encryption" " in %s mode\n"), kidstr, gnupg_compliance_option_string (opt.compliance)); } /* Fixme: When adding ECC we need to provide the curvename and * the key to gnupg_pk_is_compliant. */ if (compliant && !gnupg_pk_is_compliant (CO_DE_VS, pk_algo, 0, NULL, nbits, NULL)) compliant = 0; rc = encrypt_dek (dek, cl->cert, pk_algo, &encval); if (rc) { audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, rc); log_error ("encryption failed for recipient no. %d: %s\n", recpno, gpg_strerror (rc)); goto leave; } err = ksba_cms_add_recipient (cms, cl->cert); if (err) { audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, err); log_error ("ksba_cms_add_recipient failed: %s\n", gpg_strerror (err)); rc = err; xfree (encval); goto leave; } err = ksba_cms_set_enc_val (cms, recpno, encval); xfree (encval); audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, err); if (err) { log_error ("ksba_cms_set_enc_val failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } } if (compliant) gpgsm_status (ctrl, STATUS_ENCRYPTION_COMPLIANCE_MODE, gnupg_status_compliance_flag (CO_DE_VS)); /* Main control loop for encryption. */ recpno = 0; do { err = ksba_cms_build (cms, &stopreason); if (err) { log_debug ("ksba_cms_build failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } } while (stopreason != KSBA_SR_READY); if (encparm.readerror) { log_error ("error reading input: %s\n", strerror (encparm.readerror)); rc = gpg_error (gpg_err_code_from_errno (encparm.readerror)); goto leave; } rc = gnupg_ksba_finish_writer (b64writer); if (rc) { log_error ("write failed: %s\n", gpg_strerror (rc)); goto leave; } audit_log (ctrl->audit, AUDIT_ENCRYPTION_DONE); log_info ("encrypted data created\n"); leave: ksba_cms_release (cms); gnupg_ksba_destroy_writer (b64writer); ksba_reader_release (reader); keydb_release (kh); xfree (dek); es_fclose (data_fp); xfree (encparm.buffer); return rc; } diff --git a/sm/export.c b/sm/export.c index 50489304a..115d79b9e 100644 --- a/sm/export.c +++ b/sm/export.c @@ -1,774 +1,773 @@ /* export.c - Export certificates and private keys. * Copyright (C) 2002, 2003, 2004, 2007, 2009, * 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include -#include #include "gpgsm.h" #include #include #include "keydb.h" #include "../common/exechelp.h" #include "../common/i18n.h" #include "../common/sysutils.h" #include "minip12.h" /* A table to store a fingerprint as used in a duplicates table. We don't need to hash here because a fingerprint is already a perfect hash value. This we use the most significant bits to index the table and then use a linked list for the overflow. Possible enhancement for very large number of certificates: Add a second level table and then resort to a linked list. */ struct duptable_s { struct duptable_s *next; /* Note that we only need to store 19 bytes because the first byte is implicitly given by the table index (we require at least 8 bits). */ unsigned char fpr[19]; }; typedef struct duptable_s *duptable_t; #define DUPTABLE_BITS 12 #define DUPTABLE_SIZE (1 << DUPTABLE_BITS) static void print_short_info (ksba_cert_t cert, estream_t stream); static gpg_error_t export_p12 (ctrl_t ctrl, const unsigned char *certimg, size_t certimglen, const char *prompt, const char *keygrip, int rawmode, void **r_result, size_t *r_resultlen); /* Create a table used to indetify duplicated certificates. */ static duptable_t * create_duptable (void) { return xtrycalloc (DUPTABLE_SIZE, sizeof (duptable_t)); } static void destroy_duptable (duptable_t *table) { int idx; duptable_t t, t2; if (table) { for (idx=0; idx < DUPTABLE_SIZE; idx++) for (t = table[idx]; t; t = t2) { t2 = t->next; xfree (t); } xfree (table); } } /* Insert the 20 byte fingerprint FPR into TABLE. Sets EXITS to true if the fingerprint already exists in the table. */ static gpg_error_t insert_duptable (duptable_t *table, unsigned char *fpr, int *exists) { size_t idx; duptable_t t; *exists = 0; idx = fpr[0]; #if DUPTABLE_BITS > 16 || DUPTABLE_BITS < 8 #error cannot handle a table larger than 16 bits or smaller than 8 bits #elif DUPTABLE_BITS > 8 idx <<= (DUPTABLE_BITS - 8); idx |= (fpr[1] & ~(~0U << 4)); #endif for (t = table[idx]; t; t = t->next) if (!memcmp (t->fpr, fpr+1, 19)) break; if (t) { *exists = 1; return 0; } /* Insert that fingerprint. */ t = xtrymalloc (sizeof *t); if (!t) return gpg_error_from_syserror (); memcpy (t->fpr, fpr+1, 19); t->next = table[idx]; table[idx] = t; return 0; } /* Export all certificates or just those given in NAMES. The output is written to STREAM. */ void gpgsm_export (ctrl_t ctrl, strlist_t names, estream_t stream) { KEYDB_HANDLE hd = NULL; KEYDB_SEARCH_DESC *desc = NULL; int ndesc; gnupg_ksba_io_t b64writer = NULL; ksba_writer_t writer; strlist_t sl; ksba_cert_t cert = NULL; int rc=0; int count = 0; int i; duptable_t *dtable; dtable = create_duptable (); if (!dtable) { log_error ("creating duplicates table failed: %s\n", strerror (errno)); goto leave; } hd = keydb_new (); if (!hd) { log_error ("keydb_new failed\n"); goto leave; } if (!names) ndesc = 1; else { for (sl=names, ndesc=0; sl; sl = sl->next, ndesc++) ; } desc = xtrycalloc (ndesc, sizeof *desc); if (!ndesc) { log_error ("allocating memory for export failed: %s\n", gpg_strerror (out_of_core ())); goto leave; } if (!names) desc[0].mode = KEYDB_SEARCH_MODE_FIRST; else { for (ndesc=0, sl=names; sl; sl = sl->next) { rc = classify_user_id (sl->d, desc+ndesc, 0); if (rc) { log_error ("key '%s' not found: %s\n", sl->d, gpg_strerror (rc)); rc = 0; } else ndesc++; } } /* If all specifications are done by fingerprint or keygrip, we switch to ephemeral mode so that _all_ currently available and matching certificates are exported. */ if (names && ndesc) { for (i=0; (i < ndesc && (desc[i].mode == KEYDB_SEARCH_MODE_FPR || desc[i].mode == KEYDB_SEARCH_MODE_KEYGRIP)); i++) ; if (i == ndesc) keydb_set_ephemeral (hd, 1); } while (!(rc = keydb_search (ctrl, hd, desc, ndesc))) { unsigned char fpr[20]; int exists; if (!names) desc[0].mode = KEYDB_SEARCH_MODE_NEXT; rc = keydb_get_cert (hd, &cert); if (rc) { log_error ("keydb_get_cert failed: %s\n", gpg_strerror (rc)); goto leave; } gpgsm_get_fingerprint (cert, 0, fpr, NULL); rc = insert_duptable (dtable, fpr, &exists); if (rc) { log_error ("inserting into duplicates table failed: %s\n", gpg_strerror (rc)); goto leave; } if (!exists && count && !ctrl->create_pem) { log_info ("exporting more than one certificate " "is not possible in binary mode\n"); log_info ("ignoring other certificates\n"); break; } if (!exists) { const unsigned char *image; size_t imagelen; image = ksba_cert_get_image (cert, &imagelen); if (!image) { log_error ("ksba_cert_get_image failed\n"); goto leave; } if (ctrl->create_pem) { if (count) es_putc ('\n', stream); print_short_info (cert, stream); es_putc ('\n', stream); } count++; if (!b64writer) { ctrl->pem_name = "CERTIFICATE"; rc = gnupg_ksba_create_writer (&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 :0)), ctrl->pem_name, stream, &writer); if (rc) { log_error ("can't create writer: %s\n", gpg_strerror (rc)); goto leave; } } rc = ksba_writer_write (writer, image, imagelen); if (rc) { log_error ("write error: %s\n", gpg_strerror (rc)); goto leave; } if (ctrl->create_pem) { /* We want one certificate per PEM block */ rc = gnupg_ksba_finish_writer (b64writer); if (rc) { log_error ("write failed: %s\n", gpg_strerror (rc)); goto leave; } gnupg_ksba_destroy_writer (b64writer); b64writer = NULL; } } ksba_cert_release (cert); cert = NULL; } if (rc && rc != -1) log_error ("keydb_search failed: %s\n", gpg_strerror (rc)); else if (b64writer) { rc = gnupg_ksba_finish_writer (b64writer); if (rc) { log_error ("write failed: %s\n", gpg_strerror (rc)); goto leave; } } leave: gnupg_ksba_destroy_writer (b64writer); ksba_cert_release (cert); xfree (desc); keydb_release (hd); destroy_duptable (dtable); } /* Export a certificate and its private key. RAWMODE controls the actual output: 0 - Private key and certifciate in PKCS#12 format 1 - Only unencrypted private key in PKCS#8 format 2 - Only unencrypted private key in PKCS#1 format */ void gpgsm_p12_export (ctrl_t ctrl, const char *name, estream_t stream, int rawmode) { gpg_error_t err = 0; KEYDB_HANDLE hd; KEYDB_SEARCH_DESC *desc = NULL; gnupg_ksba_io_t b64writer = NULL; ksba_writer_t writer; ksba_cert_t cert = NULL; const unsigned char *image; size_t imagelen; char *keygrip = NULL; char *prompt; void *data; size_t datalen; hd = keydb_new (); if (!hd) { log_error ("keydb_new failed\n"); goto leave; } desc = xtrycalloc (1, sizeof *desc); if (!desc) { log_error ("allocating memory for export failed: %s\n", gpg_strerror (out_of_core ())); goto leave; } err = classify_user_id (name, desc, 0); if (err) { log_error ("key '%s' not found: %s\n", name, gpg_strerror (err)); goto leave; } /* Lookup the certificate and make sure that it is unique. */ err = keydb_search (ctrl, hd, desc, 1); if (!err) { err = keydb_get_cert (hd, &cert); if (err) { log_error ("keydb_get_cert failed: %s\n", gpg_strerror (err)); goto leave; } next_ambiguous: err = keydb_search (ctrl, hd, desc, 1); if (!err) { ksba_cert_t cert2 = NULL; if (!keydb_get_cert (hd, &cert2)) { if (gpgsm_certs_identical_p (cert, cert2)) { ksba_cert_release (cert2); goto next_ambiguous; } ksba_cert_release (cert2); } err = gpg_error (GPG_ERR_AMBIGUOUS_NAME); } else if (err == -1 || gpg_err_code (err) == GPG_ERR_EOF) err = 0; if (err) { log_error ("key '%s' not found: %s\n", name, gpg_strerror (err)); goto leave; } } keygrip = gpgsm_get_keygrip_hexstring (cert); if (!keygrip || gpgsm_agent_havekey (ctrl, keygrip)) { /* Note, that the !keygrip case indicates a bad certificate. */ err = gpg_error (GPG_ERR_NO_SECKEY); log_error ("can't export key '%s': %s\n", name, gpg_strerror (err)); goto leave; } image = ksba_cert_get_image (cert, &imagelen); if (!image) { log_error ("ksba_cert_get_image failed\n"); goto leave; } if (ctrl->create_pem) { print_short_info (cert, stream); es_putc ('\n', stream); } if (opt.p12_charset && ctrl->create_pem && !rawmode) { es_fprintf (stream, "The passphrase is %s encoded.\n\n", opt.p12_charset); } if (rawmode == 0) ctrl->pem_name = "PKCS12"; else if (gpgsm_get_key_algo_info (cert, NULL) == GCRY_PK_ECC) ctrl->pem_name = "EC PRIVATE KEY"; else if (rawmode == 1) ctrl->pem_name = "PRIVATE KEY"; else ctrl->pem_name = "RSA PRIVATE KEY"; err = gnupg_ksba_create_writer (&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)), ctrl->pem_name, stream, &writer); if (err) { log_error ("can't create writer: %s\n", gpg_strerror (err)); goto leave; } prompt = gpgsm_format_keydesc (cert); err = export_p12 (ctrl, image, imagelen, prompt, keygrip, rawmode, &data, &datalen); xfree (prompt); if (err) goto leave; err = ksba_writer_write (writer, data, datalen); xfree (data); if (err) { log_error ("write failed: %s\n", gpg_strerror (err)); goto leave; } if (ctrl->create_pem) { /* We want one certificate per PEM block */ err = gnupg_ksba_finish_writer (b64writer); if (err) { log_error ("write failed: %s\n", gpg_strerror (err)); goto leave; } gnupg_ksba_destroy_writer (b64writer); b64writer = NULL; } ksba_cert_release (cert); cert = NULL; leave: gnupg_ksba_destroy_writer (b64writer); ksba_cert_release (cert); xfree (keygrip); xfree (desc); keydb_release (hd); } /* Print some info about the certifciate CERT to FP or STREAM */ static void print_short_info (ksba_cert_t cert, estream_t stream) { char *p; ksba_sexp_t sexp; int idx; for (idx=0; (p = ksba_cert_get_issuer (cert, idx)); idx++) { es_fputs ((!idx ? "Issuer ...: " : "\n aka ...: "), stream); gpgsm_es_print_name (stream, p); xfree (p); } es_putc ('\n', stream); es_fputs ("Serial ...: ", stream); sexp = ksba_cert_get_serial (cert); if (sexp) { int len; const unsigned char *s = sexp; if (*s == '(') { s++; for (len=0; *s && *s != ':' && digitp (s); s++) len = len*10 + atoi_1 (s); if (*s == ':') es_write_hexstring (stream, s+1, len, 0, NULL); } xfree (sexp); } es_putc ('\n', stream); for (idx=0; (p = ksba_cert_get_subject (cert, idx)); idx++) { es_fputs ((!idx ? "Subject ..: " : "\n aka ..: "), stream); gpgsm_es_print_name (stream, p); xfree (p); } es_putc ('\n', stream); p = gpgsm_get_keygrip_hexstring (cert); if (p) { es_fprintf (stream, "Keygrip ..: %s\n", p); xfree (p); } } /* Parse a private key S-expression and return a malloced array with the RSA parameters in pkcs#12 order. The caller needs to deep-release this array. */ static gcry_mpi_t * sexp_to_kparms (gcry_sexp_t sexp) { gcry_sexp_t list, l2; const char *name; const char *s; size_t n; int idx; gcry_mpi_t *array; list = gcry_sexp_find_token (sexp, "private-key", 0 ); if(!list) return NULL; l2 = gcry_sexp_cadr (list); gcry_sexp_release (list); list = l2; name = gcry_sexp_nth_data (list, 0, &n); if (name && n == 3 && !memcmp (name, "rsa", 3)) { /* Parameter names used with RSA in the pkcs#12 order. */ const char *elems = "nedqp--u"; array = xtrycalloc (strlen(elems) + 1, sizeof *array); if (!array) { gcry_sexp_release (list); return NULL; } for (idx=0, s=elems; *s; s++, idx++ ) { if (*s == '-') continue; /* Computed below */ l2 = gcry_sexp_find_token (list, s, 1); if (l2) { array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); gcry_sexp_release (l2); } if (!array[idx]) /* Required parameter not found or invalid. */ { for (idx=0; array[idx]; idx++) gcry_mpi_release (array[idx]); xfree (array); gcry_sexp_release (list); return NULL; } } array[5] = gcry_mpi_snew (0); /* compute d mod (q-1) */ gcry_mpi_sub_ui (array[5], array[3], 1); gcry_mpi_mod (array[5], array[2], array[5]); array[6] = gcry_mpi_snew (0); /* compute d mod (p-1) */ gcry_mpi_sub_ui (array[6], array[4], 1); gcry_mpi_mod (array[6], array[2], array[6]); } else if (name && n == 3 && !memcmp (name, "ecc", 3)) { array = xtrycalloc (3 + 1, sizeof *array); if (!array) { gcry_sexp_release (list); return NULL; } if (gcry_sexp_extract_param (list, NULL, "/'curve'qd", array+0, array+1, array+2, NULL)) { xfree (array); array = NULL; /* Error. */ } } else { array = NULL; } gcry_sexp_release (list); return array; } static gpg_error_t export_p12 (ctrl_t ctrl, const unsigned char *certimg, size_t certimglen, const char *prompt, const char *keygrip, int rawmode, void **r_result, size_t *r_resultlen) { gpg_error_t err = 0; void *kek = NULL; size_t keklen; unsigned char *wrappedkey = NULL; size_t wrappedkeylen; gcry_cipher_hd_t cipherhd = NULL; gcry_sexp_t s_skey = NULL; gcry_mpi_t *kparms = NULL; unsigned char *key = NULL; size_t keylen; char *passphrase = NULL; unsigned char *result = NULL; size_t resultlen; int i; *r_result = NULL; /* Get the current KEK. */ err = gpgsm_agent_keywrap_key (ctrl, 1, &kek, &keklen); if (err) { log_error ("error getting the KEK: %s\n", gpg_strerror (err)); goto leave; } /* Receive the wrapped key from the agent. */ err = gpgsm_agent_export_key (ctrl, keygrip, prompt, &wrappedkey, &wrappedkeylen); if (err) goto leave; /* Unwrap the key. */ err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_AESWRAP, 0); if (err) goto leave; err = gcry_cipher_setkey (cipherhd, kek, keklen); if (err) goto leave; xfree (kek); kek = NULL; if (wrappedkeylen < 24) { err = gpg_error (GPG_ERR_INV_LENGTH); goto leave; } keylen = wrappedkeylen - 8; key = xtrymalloc_secure (keylen); if (!key) { err = gpg_error_from_syserror (); goto leave; } err = gcry_cipher_decrypt (cipherhd, key, keylen, wrappedkey, wrappedkeylen); if (err) goto leave; xfree (wrappedkey); wrappedkey = NULL; gcry_cipher_close (cipherhd); cipherhd = NULL; /* Convert to a gcrypt S-expression. */ err = gcry_sexp_create (&s_skey, key, keylen, 0, xfree_fnc); if (err) goto leave; key = NULL; /* Key is now owned by S_KEY. */ /* Get the parameters from the S-expression. */ kparms = sexp_to_kparms (s_skey); gcry_sexp_release (s_skey); s_skey = NULL; if (!kparms) { log_error ("error converting key parameters\n"); err = GPG_ERR_BAD_SECKEY; goto leave; } if (rawmode) { /* Export in raw mode, that is only the pkcs#1/#8 private key. */ result = p12_raw_build (kparms, rawmode, &resultlen); if (!result) err = gpg_error (GPG_ERR_GENERAL); } else { err = gpgsm_agent_ask_passphrase (ctrl, i18n_utf8 ("Please enter the passphrase to protect the " "new PKCS#12 object."), 1, &passphrase); if (err) goto leave; result = p12_build (kparms, certimg, certimglen, passphrase, opt.p12_charset, &resultlen); xfree (passphrase); passphrase = NULL; if (!result) err = gpg_error (GPG_ERR_GENERAL); } leave: xfree (key); gcry_sexp_release (s_skey); if (kparms) { for (i=0; kparms[i]; i++) gcry_mpi_release (kparms[i]); xfree (kparms); } gcry_cipher_close (cipherhd); xfree (wrappedkey); xfree (kek); if (gpg_err_code (err) == GPG_ERR_BAD_PASSPHRASE) { /* During export this is the passphrase used to unprotect the key and not the pkcs#12 thing as in export. Therefore we can issue the regular passphrase status. FIXME: replace the all zero keyid by a regular one. */ gpgsm_status (ctrl, STATUS_BAD_PASSPHRASE, "0000000000000000"); } if (err) { xfree (result); } else { *r_result = result; *r_resultlen = resultlen; } return err; } diff --git a/sm/fingerprint.c b/sm/fingerprint.c index c3c37c2d6..70ca0e9ab 100644 --- a/sm/fingerprint.c +++ b/sm/fingerprint.c @@ -1,475 +1,474 @@ /* fingerprint.c - Get the fingerprint * Copyright (C) 2001 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include -#include #include "gpgsm.h" #include #include #include "../common/host2net.h" /* Return the fingerprint of the certificate (we can't put this into libksba because we need libgcrypt support). The caller must provide an array of sufficient length or NULL so that the function allocates the array. If r_len is not NULL, the length of the digest is returned; well, this can also be done by using gcry_md_get_algo_dlen(). If algo is 0, a SHA-1 will be used. If there is a problem , the function does never return NULL but a digest of all 0xff. */ unsigned char * gpgsm_get_fingerprint (ksba_cert_t cert, int algo, unsigned char *array, int *r_len) { gcry_md_hd_t md; int rc, len; if (!algo) algo = GCRY_MD_SHA1; len = gcry_md_get_algo_dlen (algo); - assert (len); + log_assert (len); if (!array) array = xmalloc (len); if (r_len) *r_len = len; /* Fist check whether we have cached the fingerprint. */ if (algo == GCRY_MD_SHA1) { size_t buflen; - assert (len >= 20); + log_assert (len >= 20); if (!ksba_cert_get_user_data (cert, "sha1-fingerprint", array, len, &buflen) && buflen == 20) return array; } /* No, need to compute it. */ rc = gcry_md_open (&md, algo, 0); if (rc) { log_error ("md_open failed: %s\n", gpg_strerror (rc)); memset (array, 0xff, len); /* better return an invalid fpr than NULL */ return array; } rc = ksba_cert_hash (cert, 0, HASH_FNC, md); if (rc) { log_error ("ksba_cert_hash failed: %s\n", gpg_strerror (rc)); gcry_md_close (md); memset (array, 0xff, len); /* better return an invalid fpr than NULL */ return array; } gcry_md_final (md); memcpy (array, gcry_md_read(md, algo), len ); gcry_md_close (md); /* Cache an SHA-1 fingerprint. */ if ( algo == GCRY_MD_SHA1 ) ksba_cert_set_user_data (cert, "sha1-fingerprint", array, 20); return array; } /* Return an allocated buffer with the formatted fingerprint */ char * gpgsm_get_fingerprint_string (ksba_cert_t cert, int algo) { unsigned char digest[MAX_DIGEST_LEN]; char *buf; int len; if (!algo) algo = GCRY_MD_SHA1; len = gcry_md_get_algo_dlen (algo); - assert (len <= MAX_DIGEST_LEN ); + log_assert (len <= MAX_DIGEST_LEN ); gpgsm_get_fingerprint (cert, algo, digest, NULL); buf = xmalloc (len*3+1); bin2hexcolon (digest, len, buf); return buf; } /* Return an allocated buffer with the formatted fingerprint as one large hexnumber */ char * gpgsm_get_fingerprint_hexstring (ksba_cert_t cert, int algo) { unsigned char digest[MAX_DIGEST_LEN]; char *buf; int len; if (!algo) algo = GCRY_MD_SHA1; len = gcry_md_get_algo_dlen (algo); - assert (len <= MAX_DIGEST_LEN ); + log_assert (len <= MAX_DIGEST_LEN ); gpgsm_get_fingerprint (cert, algo, digest, NULL); buf = xmalloc (len*2+1); bin2hex (digest, len, buf); return buf; } /* Return a certificate ID. These are the last 4 bytes of the SHA-1 fingerprint. If R_HIGH is not NULL the next 4 bytes are stored there. */ unsigned long gpgsm_get_short_fingerprint (ksba_cert_t cert, unsigned long *r_high) { unsigned char digest[20]; gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, digest, NULL); if (r_high) *r_high = buf32_to_ulong (digest+12); return buf32_to_ulong (digest + 16); } /* Return the so called KEYGRIP which is the SHA-1 hash of the public key parameters expressed as an canonical encoded S-Exp. ARRAY must be 20 bytes long. Returns ARRAY or a newly allocated buffer if ARRAY was given as NULL. May return NULL on error. */ unsigned char * gpgsm_get_keygrip (ksba_cert_t cert, unsigned char *array) { gcry_sexp_t s_pkey; int rc; ksba_sexp_t p; size_t n; p = ksba_cert_get_public_key (cert); if (!p) return NULL; /* oops */ if (DBG_X509) log_debug ("get_keygrip for public key\n"); n = gcry_sexp_canon_len (p, 0, NULL, NULL); if (!n) { log_error ("libksba did not return a proper S-Exp\n"); return NULL; } rc = gcry_sexp_sscan ( &s_pkey, NULL, (char*)p, n); xfree (p); if (rc) { log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc)); return NULL; } array = gcry_pk_get_keygrip (s_pkey, array); gcry_sexp_release (s_pkey); if (!array) { log_error ("can't calculate keygrip\n"); return NULL; } if (DBG_X509) log_printhex (array, 20, "keygrip:"); return array; } /* Return an allocated buffer with the keygrip of CERT encoded as a hexstring. NULL is returned in case of error. */ char * gpgsm_get_keygrip_hexstring (ksba_cert_t cert) { unsigned char grip[20]; char *buf; if (!gpgsm_get_keygrip (cert, grip)) return NULL; buf = xtrymalloc (20*2+1); if (buf) bin2hex (grip, 20, buf); return buf; } /* Return the PK algorithm used by CERT as well as the length in bits * of the public key at NBITS. If R_CURVE is not NULL and an ECC * algorithm is used the name or OID of the curve is stored there; the * caller needs to free this value. */ int gpgsm_get_key_algo_info2 (ksba_cert_t cert, unsigned int *nbits, char **r_curve) { gcry_sexp_t s_pkey; int rc; ksba_sexp_t p; size_t n; gcry_sexp_t l1, l2; const char *curve; const char *name; char namebuf[128]; if (nbits) *nbits = 0; if (r_curve) *r_curve = NULL; p = ksba_cert_get_public_key (cert); if (!p) return 0; n = gcry_sexp_canon_len (p, 0, NULL, NULL); if (!n) { xfree (p); return 0; } rc = gcry_sexp_sscan (&s_pkey, NULL, (char *)p, n); xfree (p); if (rc) return 0; if (nbits) *nbits = gcry_pk_get_nbits (s_pkey); /* Breaking the algorithm out of the S-exp is a bit of a challenge ... */ l1 = gcry_sexp_find_token (s_pkey, "public-key", 0); if (!l1) { gcry_sexp_release (s_pkey); return 0; } if (r_curve) { curve = gcry_pk_get_curve (l1, 0, NULL); if (curve) { name = openpgp_oid_to_curve (openpgp_curve_to_oid (curve, NULL, NULL), 0); *r_curve = xtrystrdup (name? name : curve); if (!*r_curve) { gcry_sexp_release (l1); gcry_sexp_release (s_pkey); return 0; /* Out of core. */ } } } l2 = gcry_sexp_cadr (l1); gcry_sexp_release (l1); l1 = l2; name = gcry_sexp_nth_data (l1, 0, &n); if (name) { if (n > sizeof namebuf -1) n = sizeof namebuf -1; memcpy (namebuf, name, n); namebuf[n] = 0; } else *namebuf = 0; gcry_sexp_release (l1); gcry_sexp_release (s_pkey); return gcry_pk_map_name (namebuf); } int gpgsm_get_key_algo_info (ksba_cert_t cert, unsigned int *nbits) { return gpgsm_get_key_algo_info2 (cert, nbits, NULL); } -/* This is a wrapper around pubkey_algo_string which takesa KSA - * certitificate instead of a Gcrypt public key. Note that this +/* This is a wrapper around pubkey_algo_string which takes a KSBA + * certificate instead of a Gcrypt public key. Note that this * function may return NULL on error. */ char * gpgsm_pubkey_algo_string (ksba_cert_t cert, int *r_algoid) { gpg_error_t err; gcry_sexp_t s_pkey; ksba_sexp_t p; size_t n; enum gcry_pk_algos algoid; char *algostr; p = ksba_cert_get_public_key (cert); if (!p) return NULL; n = gcry_sexp_canon_len (p, 0, NULL, NULL); if (!n) { xfree (p); return NULL; } err = gcry_sexp_sscan (&s_pkey, NULL, (char *)p, n); xfree (p); if (err) return NULL; algostr = pubkey_algo_string (s_pkey, r_algoid? &algoid : NULL); if (algostr && r_algoid) *r_algoid = algoid; gcry_sexp_release (s_pkey); return algostr; } /* If KEY is an RSA key, return its modulus. For non-RSA keys or on * error return NULL. */ gcry_mpi_t gpgsm_get_rsa_modulus (ksba_cert_t cert) { gpg_error_t err; gcry_sexp_t key; gcry_sexp_t list = NULL; gcry_sexp_t l2 = NULL; char *name = NULL; gcry_mpi_t modulus = NULL; { ksba_sexp_t ckey; size_t n; ckey = ksba_cert_get_public_key (cert); if (!ckey) return NULL; n = gcry_sexp_canon_len (ckey, 0, NULL, NULL); if (!n) { xfree (ckey); return NULL; } err = gcry_sexp_sscan (&key, NULL, (char *)ckey, n); xfree (ckey); if (err) return NULL; } list = gcry_sexp_find_token (key, "public-key", 0); if (!list) list = gcry_sexp_find_token (key, "private-key", 0); if (!list) list = gcry_sexp_find_token (key, "protected-private-key", 0); if (!list) list = gcry_sexp_find_token (key, "shadowed-private-key", 0); gcry_sexp_release (key); if (!list) return NULL; /* No suitable key. */ l2 = gcry_sexp_cadr (list); gcry_sexp_release (list); list = l2; l2 = NULL; name = gcry_sexp_nth_string (list, 0); if (!name) ; else if (gcry_pk_map_name (name) == GCRY_PK_RSA) { l2 = gcry_sexp_find_token (list, "n", 1); if (l2) modulus = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); } gcry_free (name); gcry_sexp_release (l2); gcry_sexp_release (list); return modulus; } /* For certain purposes we need a certificate id which has an upper limit of the size. We use the hash of the issuer name and the serial number for this. In most cases the serial number is not that large and the resulting string can be passed on an assuan command line. Everything is hexencoded with the serialnumber delimited from the hash by a dot. The caller must free the string. */ char * gpgsm_get_certid (ksba_cert_t cert) { ksba_sexp_t serial; char *p; char *endp; unsigned char hash[20]; unsigned long n; char *certid; int i; p = ksba_cert_get_issuer (cert, 0); if (!p) return NULL; /* Ooops: No issuer */ gcry_md_hash_buffer (GCRY_MD_SHA1, hash, p, strlen (p)); xfree (p); serial = ksba_cert_get_serial (cert); if (!serial) return NULL; /* oops: no serial number */ p = (char *)serial; if (*p != '(') { log_error ("Ooops: invalid serial number\n"); xfree (serial); return NULL; } p++; n = strtoul (p, &endp, 10); p = endp; if (*p != ':') { log_error ("Ooops: invalid serial number (no colon)\n"); xfree (serial); return NULL; } p++; certid = xtrymalloc ( 40 + 1 + n*2 + 1); if (!certid) { xfree (serial); return NULL; /* out of core */ } for (i=0, endp = certid; i < 20; i++, endp += 2 ) sprintf (endp, "%02X", hash[i]); *endp++ = '.'; for (i=0; i < n; i++, endp += 2) sprintf (endp, "%02X", ((unsigned char*)p)[i]); *endp = 0; xfree (serial); return certid; } diff --git a/sm/import.c b/sm/import.c index 3f4d192c2..a4dd671f8 100644 --- a/sm/import.c +++ b/sm/import.c @@ -1,986 +1,985 @@ /* import.c - Import certificates * Copyright (C) 2001, 2003, 2004, 2009, 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include -#include #include #include "gpgsm.h" #include #include #include "keydb.h" #include "../common/exechelp.h" #include "../common/i18n.h" #include "../common/sysutils.h" #include "../kbx/keybox.h" /* for KEYBOX_FLAG_* */ #include "../common/membuf.h" #include "minip12.h" /* The arbitrary limit of one PKCS#12 object. */ #define MAX_P12OBJ_SIZE 128 /*kb*/ struct stats_s { unsigned long count; unsigned long imported; unsigned long unchanged; unsigned long not_imported; unsigned long secret_read; unsigned long secret_imported; unsigned long secret_dups; }; struct rsa_secret_key_s { gcry_mpi_t n; /* public modulus */ gcry_mpi_t e; /* public exponent */ gcry_mpi_t d; /* exponent */ gcry_mpi_t p; /* prime p. */ gcry_mpi_t q; /* prime q. */ gcry_mpi_t u; /* inverse of p mod q. */ }; static gpg_error_t parse_p12 (ctrl_t ctrl, ksba_reader_t reader, struct stats_s *stats); static void print_imported_status (ctrl_t ctrl, ksba_cert_t cert, int new_cert) { char *fpr; fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); if (new_cert) gpgsm_status2 (ctrl, STATUS_IMPORTED, fpr, "[X.509]", NULL); gpgsm_status2 (ctrl, STATUS_IMPORT_OK, new_cert? "1":"0", fpr, NULL); xfree (fpr); } /* Print an IMPORT_PROBLEM status. REASON is one of: 0 := "No specific reason given". 1 := "Invalid Certificate". 2 := "Issuer Certificate missing". 3 := "Certificate Chain too long". 4 := "Error storing certificate". */ static void print_import_problem (ctrl_t ctrl, ksba_cert_t cert, int reason) { char *fpr = NULL; char buf[25]; int i; sprintf (buf, "%d", reason); if (cert) { fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); /* detetect an error (all high) value */ for (i=0; fpr[i] == 'F'; i++) ; if (!fpr[i]) { xfree (fpr); fpr = NULL; } } gpgsm_status2 (ctrl, STATUS_IMPORT_PROBLEM, buf, fpr, NULL); xfree (fpr); } void print_imported_summary (ctrl_t ctrl, struct stats_s *stats) { char buf[14*25]; if (!opt.quiet) { log_info (_("total number processed: %lu\n"), stats->count); if (stats->imported) { log_info (_(" imported: %lu"), stats->imported ); log_printf ("\n"); } if (stats->unchanged) log_info (_(" unchanged: %lu\n"), stats->unchanged); if (stats->secret_read) log_info (_(" secret keys read: %lu\n"), stats->secret_read ); if (stats->secret_imported) log_info (_(" secret keys imported: %lu\n"), stats->secret_imported ); if (stats->secret_dups) log_info (_(" secret keys unchanged: %lu\n"), stats->secret_dups ); if (stats->not_imported) log_info (_(" not imported: %lu\n"), stats->not_imported); } sprintf(buf, "%lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", stats->count, 0l /*stats->no_user_id*/, stats->imported, 0l /*stats->imported_rsa*/, stats->unchanged, 0l /*stats->n_uids*/, 0l /*stats->n_subk*/, 0l /*stats->n_sigs*/, 0l /*stats->n_revoc*/, stats->secret_read, stats->secret_imported, stats->secret_dups, 0l /*stats->skipped_new_keys*/, stats->not_imported ); gpgsm_status (ctrl, STATUS_IMPORT_RES, buf); } static void check_and_store (ctrl_t ctrl, struct stats_s *stats, ksba_cert_t cert, int depth) { int rc; if (stats) stats->count++; if ( depth >= 50 ) { log_error (_("certificate chain too long\n")); if (stats) stats->not_imported++; print_import_problem (ctrl, cert, 3); return; } /* Some basic checks, but don't care about missing certificates; this is so that we are able to import entire certificate chains w/o requiring a special order (i.e. root-CA first). This used to be different but because gpgsm_verify even imports certificates without any checks, it doesn't matter much and the code gets much cleaner. A housekeeping function to remove certificates w/o an anchor would be nice, though. Optionally we do a full validation in addition to the basic test. */ rc = gpgsm_basic_cert_check (ctrl, cert); if (!rc && ctrl->with_validation) rc = gpgsm_validate_chain (ctrl, cert, "", NULL, 0, NULL, 0, NULL); if (!rc || (!ctrl->with_validation && (gpg_err_code (rc) == GPG_ERR_MISSING_CERT || gpg_err_code (rc) == GPG_ERR_MISSING_ISSUER_CERT))) { int existed; if (!keydb_store_cert (ctrl, cert, 0, &existed)) { ksba_cert_t next = NULL; if (!existed) { print_imported_status (ctrl, cert, 1); if (stats) stats->imported++; } else { print_imported_status (ctrl, cert, 0); if (stats) stats->unchanged++; } if (opt.verbose > 1 && existed) { if (depth) log_info ("issuer certificate already in DB\n"); else log_info ("certificate already in DB\n"); } else if (opt.verbose && !existed) { if (depth) log_info ("issuer certificate imported\n"); else log_info ("certificate imported\n"); } /* Now lets walk up the chain and import all certificates up the chain. This is required in case we already stored parent certificates in the ephemeral keybox. Do not update the statistics, though. */ if (!gpgsm_walk_cert_chain (ctrl, cert, &next)) { check_and_store (ctrl, NULL, next, depth+1); ksba_cert_release (next); } } else { log_error (_("error storing certificate\n")); if (stats) stats->not_imported++; print_import_problem (ctrl, cert, 4); } } else { log_error (_("basic certificate checks failed - not imported\n")); if (stats) stats->not_imported++; /* We keep the test for GPG_ERR_MISSING_CERT only in case GPG_ERR_MISSING_CERT has been used instead of the newer GPG_ERR_MISSING_ISSUER_CERT. */ print_import_problem (ctrl, cert, gpg_err_code (rc) == GPG_ERR_MISSING_ISSUER_CERT? 2 : gpg_err_code (rc) == GPG_ERR_MISSING_CERT? 2 : gpg_err_code (rc) == GPG_ERR_BAD_CERT? 1 : 0); } } static int import_one (ctrl_t ctrl, struct stats_s *stats, int in_fd) { int rc; gnupg_ksba_io_t b64reader = NULL; ksba_reader_t reader; ksba_cert_t cert = NULL; ksba_cms_t cms = NULL; estream_t fp = NULL; ksba_content_type_t ct; int any = 0; fp = es_fdopen_nc (in_fd, "rb"); if (!fp) { rc = gpg_error_from_syserror (); log_error ("fdopen() failed: %s\n", strerror (errno)); goto leave; } rc = gnupg_ksba_create_reader (&b64reader, ((ctrl->is_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->is_base64? GNUPG_KSBA_IO_BASE64 : 0) | (ctrl->autodetect_encoding? GNUPG_KSBA_IO_AUTODETECT : 0) | GNUPG_KSBA_IO_MULTIPEM), fp, &reader); if (rc) { log_error ("can't create reader: %s\n", gpg_strerror (rc)); goto leave; } /* We need to loop here to handle multiple PEM objects in one file. */ do { ksba_cms_release (cms); cms = NULL; ksba_cert_release (cert); cert = NULL; ct = ksba_cms_identify (reader); if (ct == KSBA_CT_SIGNED_DATA) { /* This is probably a signed-only message - import the certs */ ksba_stop_reason_t stopreason; int i; rc = ksba_cms_new (&cms); if (rc) goto leave; rc = ksba_cms_set_reader_writer (cms, reader, NULL); if (rc) { log_error ("ksba_cms_set_reader_writer failed: %s\n", gpg_strerror (rc)); goto leave; } do { rc = ksba_cms_parse (cms, &stopreason); if (rc) { log_error ("ksba_cms_parse failed: %s\n", gpg_strerror (rc)); goto leave; } if (stopreason == KSBA_SR_BEGIN_DATA) log_info ("not a certs-only message\n"); } while (stopreason != KSBA_SR_READY); for (i=0; (cert=ksba_cms_get_cert (cms, i)); i++) { check_and_store (ctrl, stats, cert, 0); ksba_cert_release (cert); cert = NULL; } if (!i) log_error ("no certificate found\n"); else any = 1; } else if (ct == KSBA_CT_PKCS12) { /* This seems to be a pkcs12 message. */ rc = parse_p12 (ctrl, reader, stats); if (!rc) any = 1; } else if (ct == KSBA_CT_NONE) { /* Failed to identify this message - assume a certificate */ rc = ksba_cert_new (&cert); if (rc) goto leave; rc = ksba_cert_read_der (cert, reader); if (rc) goto leave; check_and_store (ctrl, stats, cert, 0); any = 1; } else { log_error ("can't extract certificates from input\n"); rc = gpg_error (GPG_ERR_NO_DATA); } ksba_reader_clear (reader, NULL, NULL); } while (!gnupg_ksba_reader_eof_seen (b64reader)); leave: if (any && gpg_err_code (rc) == GPG_ERR_EOF) rc = 0; ksba_cms_release (cms); ksba_cert_release (cert); gnupg_ksba_destroy_reader (b64reader); es_fclose (fp); return rc; } /* Re-import certifciates. IN_FD is a list of linefeed delimited fingerprints t re-import. The actual re-import is done by clearing the ephemeral flag. */ static int reimport_one (ctrl_t ctrl, struct stats_s *stats, int in_fd) { gpg_error_t err = 0; estream_t fp = NULL; char line[100]; /* Sufficient for a fingerprint. */ KEYDB_HANDLE kh; KEYDB_SEARCH_DESC desc; ksba_cert_t cert = NULL; unsigned int flags; kh = keydb_new (); if (!kh) { err = gpg_error (GPG_ERR_ENOMEM);; log_error (_("failed to allocate keyDB handle\n")); goto leave; } keydb_set_ephemeral (kh, 1); fp = es_fdopen_nc (in_fd, "r"); if (!fp) { err = gpg_error_from_syserror (); log_error ("es_fdopen(%d) failed: %s\n", in_fd, gpg_strerror (err)); goto leave; } while (es_fgets (line, DIM(line)-1, fp) ) { if (*line && line[strlen(line)-1] != '\n') { err = gpg_error (GPG_ERR_LINE_TOO_LONG); goto leave; } trim_spaces (line); if (!*line) continue; stats->count++; err = classify_user_id (line, &desc, 0); if (err) { print_import_problem (ctrl, NULL, 0); stats->not_imported++; continue; } keydb_search_reset (kh); err = keydb_search (ctrl, kh, &desc, 1); if (err) { print_import_problem (ctrl, NULL, 0); stats->not_imported++; continue; } ksba_cert_release (cert); cert = NULL; err = keydb_get_cert (kh, &cert); if (err) { log_error ("keydb_get_cert() failed: %s\n", gpg_strerror (err)); print_import_problem (ctrl, NULL, 1); stats->not_imported++; continue; } err = keydb_get_flags (kh, KEYBOX_FLAG_BLOB, 0, &flags); if (err) { log_error (_("error getting stored flags: %s\n"), gpg_strerror (err)); print_imported_status (ctrl, cert, 0); stats->not_imported++; continue; } if ( !(flags & KEYBOX_FLAG_BLOB_EPHEMERAL) ) { print_imported_status (ctrl, cert, 0); stats->unchanged++; continue; } err = keydb_set_cert_flags (ctrl, cert, 1, KEYBOX_FLAG_BLOB, 0, KEYBOX_FLAG_BLOB_EPHEMERAL, 0); if (err) { log_error ("clearing ephemeral flag failed: %s\n", gpg_strerror (err)); print_import_problem (ctrl, cert, 0); stats->not_imported++; continue; } print_imported_status (ctrl, cert, 1); stats->imported++; } err = 0; if (es_ferror (fp)) { err = gpg_error_from_syserror (); log_error ("error reading fd %d: %s\n", in_fd, gpg_strerror (err)); goto leave; } leave: ksba_cert_release (cert); keydb_release (kh); es_fclose (fp); return err; } int gpgsm_import (ctrl_t ctrl, int in_fd, int reimport_mode) { int rc; struct stats_s stats; memset (&stats, 0, sizeof stats); if (reimport_mode) rc = reimport_one (ctrl, &stats, in_fd); else rc = import_one (ctrl, &stats, in_fd); print_imported_summary (ctrl, &stats); /* If we never printed an error message do it now so that a command line invocation will return with an error (log_error keeps a global errorcount) */ if (rc && !log_get_errorcount (0)) log_error (_("error importing certificate: %s\n"), gpg_strerror (rc)); return rc; } int gpgsm_import_files (ctrl_t ctrl, int nfiles, char **files, int (*of)(const char *fname)) { int rc = 0; struct stats_s stats; memset (&stats, 0, sizeof stats); if (!nfiles) rc = import_one (ctrl, &stats, 0); else { for (; nfiles && !rc ; nfiles--, files++) { int fd = of (*files); rc = import_one (ctrl, &stats, fd); close (fd); if (rc == -1) rc = 0; } } print_imported_summary (ctrl, &stats); /* If we never printed an error message do it now so that a command line invocation will return with an error (log_error keeps a global errorcount) */ if (rc && !log_get_errorcount (0)) log_error (_("error importing certificate: %s\n"), gpg_strerror (rc)); return rc; } /* Check that the RSA secret key SKEY is valid. Swap parameters to the libgcrypt standard. */ static gpg_error_t rsa_key_check (struct rsa_secret_key_s *skey) { int err = 0; gcry_mpi_t t = gcry_mpi_snew (0); gcry_mpi_t t1 = gcry_mpi_snew (0); gcry_mpi_t t2 = gcry_mpi_snew (0); gcry_mpi_t phi = gcry_mpi_snew (0); /* Check that n == p * q. */ gcry_mpi_mul (t, skey->p, skey->q); if (gcry_mpi_cmp( t, skey->n) ) { log_error ("RSA oops: n != p * q\n"); err++; } /* Check that p is less than q. */ if (gcry_mpi_cmp (skey->p, skey->q) > 0) { gcry_mpi_t tmp; log_info ("swapping secret primes\n"); tmp = gcry_mpi_copy (skey->p); gcry_mpi_set (skey->p, skey->q); gcry_mpi_set (skey->q, tmp); gcry_mpi_release (tmp); /* Recompute u. */ gcry_mpi_invm (skey->u, skey->p, skey->q); } /* Check that e divides neither p-1 nor q-1. */ gcry_mpi_sub_ui (t, skey->p, 1 ); gcry_mpi_div (NULL, t, t, skey->e, 0); if (!gcry_mpi_cmp_ui( t, 0) ) { log_error ("RSA oops: e divides p-1\n"); err++; } gcry_mpi_sub_ui (t, skey->q, 1); gcry_mpi_div (NULL, t, t, skey->e, 0); if (!gcry_mpi_cmp_ui( t, 0)) { log_info ("RSA oops: e divides q-1\n" ); err++; } /* Check that d is correct. */ gcry_mpi_sub_ui (t1, skey->p, 1); gcry_mpi_sub_ui (t2, skey->q, 1); gcry_mpi_mul (phi, t1, t2); gcry_mpi_invm (t, skey->e, phi); if (gcry_mpi_cmp (t, skey->d)) { /* No: try universal exponent. */ gcry_mpi_gcd (t, t1, t2); gcry_mpi_div (t, NULL, phi, t, 0); gcry_mpi_invm (t, skey->e, t); if (gcry_mpi_cmp (t, skey->d)) { log_error ("RSA oops: bad secret exponent\n"); err++; } } /* Check for correctness of u. */ gcry_mpi_invm (t, skey->p, skey->q); if (gcry_mpi_cmp (t, skey->u)) { log_info ("RSA oops: bad u parameter\n"); err++; } if (err) log_info ("RSA secret key check failed\n"); gcry_mpi_release (t); gcry_mpi_release (t1); gcry_mpi_release (t2); gcry_mpi_release (phi); return err? gpg_error (GPG_ERR_BAD_SECKEY):0; } /* Object passed to store_cert_cb. */ struct store_cert_parm_s { gpg_error_t err; /* First error seen. */ struct stats_s *stats; /* The stats object. */ ctrl_t ctrl; /* The control object. */ }; /* Helper to store the DER encoded certificate CERTDATA of length CERTDATALEN. */ static void store_cert_cb (void *opaque, const unsigned char *certdata, size_t certdatalen) { struct store_cert_parm_s *parm = opaque; gpg_error_t err; ksba_cert_t cert; err = ksba_cert_new (&cert); if (err) { if (!parm->err) parm->err = err; return; } err = ksba_cert_init_from_mem (cert, certdata, certdatalen); if (err) { log_error ("failed to parse a certificate: %s\n", gpg_strerror (err)); if (!parm->err) parm->err = err; } else check_and_store (parm->ctrl, parm->stats, cert, 0); ksba_cert_release (cert); } /* Assume that the reader is at a pkcs#12 message and try to import certificates from that stupid format. We will transfer secret keys to the agent. */ static gpg_error_t parse_p12 (ctrl_t ctrl, ksba_reader_t reader, struct stats_s *stats) { gpg_error_t err = 0; char buffer[1024]; size_t ntotal, nread; membuf_t p12mbuf; char *p12buffer = NULL; size_t p12buflen; size_t p12bufoff; gcry_mpi_t *kparms = NULL; struct rsa_secret_key_s sk; char *passphrase = NULL; unsigned char *key = NULL; size_t keylen; void *kek = NULL; size_t keklen; unsigned char *wrappedkey = NULL; size_t wrappedkeylen; gcry_cipher_hd_t cipherhd = NULL; gcry_sexp_t s_key = NULL; unsigned char grip[20]; int bad_pass = 0; char *curve = NULL; int i; struct store_cert_parm_s store_cert_parm; memset (&store_cert_parm, 0, sizeof store_cert_parm); store_cert_parm.ctrl = ctrl; store_cert_parm.stats = stats; init_membuf (&p12mbuf, 4096); ntotal = 0; while (!(err = ksba_reader_read (reader, buffer, sizeof buffer, &nread))) { if (ntotal >= MAX_P12OBJ_SIZE*1024) { /* Arbitrary limit to avoid DoS attacks. */ err = gpg_error (GPG_ERR_TOO_LARGE); log_error ("pkcs#12 object is larger than %dk\n", MAX_P12OBJ_SIZE); break; } put_membuf (&p12mbuf, buffer, nread); ntotal += nread; } if (gpg_err_code (err) == GPG_ERR_EOF) err = 0; if (!err) { p12buffer = get_membuf (&p12mbuf, &p12buflen); if (!p12buffer) err = gpg_error_from_syserror (); } if (err) { log_error (_("error reading input: %s\n"), gpg_strerror (err)); goto leave; } /* GnuPG 2.0.4 accidentally created binary P12 files with the string "The passphrase is %s encoded.\n\n" prepended to the ASN.1 data. We fix that here. */ if (p12buflen > 29 && !memcmp (p12buffer, "The passphrase is ", 18)) { for (p12bufoff=18; p12bufoff < p12buflen && p12buffer[p12bufoff] != '\n'; p12bufoff++) ; p12bufoff++; if (p12bufoff < p12buflen && p12buffer[p12bufoff] == '\n') p12bufoff++; } else p12bufoff = 0; err = gpgsm_agent_ask_passphrase (ctrl, i18n_utf8 ("Please enter the passphrase to unprotect the PKCS#12 object."), 0, &passphrase); if (err) goto leave; kparms = p12_parse (p12buffer + p12bufoff, p12buflen - p12bufoff, passphrase, store_cert_cb, &store_cert_parm, &bad_pass, &curve); xfree (passphrase); passphrase = NULL; if (!kparms) { log_error ("error parsing or decrypting the PKCS#12 file\n"); err = gpg_error (GPG_ERR_INV_OBJ); goto leave; } if (curve) { gcry_ctx_t ecctx = NULL; /* log_debug ("curve: %s\n", curve); */ /* gcry_log_debugmpi ("MPI[0]", kparms[0]); */ /* We need to get the public key. */ err = gcry_mpi_ec_new (&ecctx, NULL, curve); if (err) { log_error ("error creating context for curve '%s': %s\n", curve, gpg_strerror (err)); goto leave; } err = gcry_mpi_ec_set_mpi ("d", kparms[0], ecctx); if (err) { log_error ("error setting 'd' into context of curve '%s': %s\n", curve, gpg_strerror (err)); gcry_ctx_release (ecctx); goto leave; } kparms[1] = gcry_mpi_ec_get_mpi ("q", ecctx, 1); if (!kparms[1]) { log_error ("error computing 'q' from 'd' for curve '%s'\n", curve); gcry_ctx_release (ecctx); goto leave; } gcry_ctx_release (ecctx); err = gcry_sexp_build (&s_key, NULL, "(private-key(ecc(curve %s)(q%m)(d%m)))", curve, kparms[1], kparms[0], NULL); } else /* RSA */ { /* print_mpi (" n", kparms[0]); */ /* print_mpi (" e", kparms[1]); */ /* print_mpi (" d", kparms[2]); */ /* print_mpi (" p", kparms[3]); */ /* print_mpi (" q", kparms[4]); */ /* print_mpi ("dmp1", kparms[5]); */ /* print_mpi ("dmq1", kparms[6]); */ /* print_mpi (" u", kparms[7]); */ sk.n = kparms[0]; sk.e = kparms[1]; sk.d = kparms[2]; sk.q = kparms[3]; sk.p = kparms[4]; sk.u = kparms[7]; err = rsa_key_check (&sk); if (err) goto leave; /* print_mpi (" n", sk.n); */ /* print_mpi (" e", sk.e); */ /* print_mpi (" d", sk.d); */ /* print_mpi (" p", sk.p); */ /* print_mpi (" q", sk.q); */ /* print_mpi (" u", sk.u); */ /* Create an S-expression from the parameters. */ err = gcry_sexp_build (&s_key, NULL, "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))", sk.n, sk.e, sk.d, sk.p, sk.q, sk.u, NULL); } /* The next is very ugly - we really should not rely on our * knowledge of p12_parse internals. */ for (i=0; i < 8; i++) gcry_mpi_release (kparms[i]); gcry_free (kparms); kparms = NULL; if (err) { log_error ("failed to create S-expression from key: %s\n", gpg_strerror (err)); goto leave; } /* Compute the keygrip. */ if (!gcry_pk_get_keygrip (s_key, grip)) { err = gpg_error (GPG_ERR_GENERAL); log_error ("can't calculate keygrip\n"); goto leave; } log_printhex (grip, 20, "keygrip:"); /* Convert to canonical encoding using a function which pads it to a multiple of 64 bits. We need this padding for AESWRAP. */ err = make_canon_sexp_pad (s_key, 1, &key, &keylen); if (err) { log_error ("error creating canonical S-expression\n"); goto leave; } gcry_sexp_release (s_key); s_key = NULL; /* Get the current KEK. */ err = gpgsm_agent_keywrap_key (ctrl, 0, &kek, &keklen); if (err) { log_error ("error getting the KEK: %s\n", gpg_strerror (err)); goto leave; } /* Wrap the key. */ err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_AESWRAP, 0); if (err) goto leave; err = gcry_cipher_setkey (cipherhd, kek, keklen); if (err) goto leave; xfree (kek); kek = NULL; wrappedkeylen = keylen + 8; wrappedkey = xtrymalloc (wrappedkeylen); if (!wrappedkey) { err = gpg_error_from_syserror (); goto leave; } err = gcry_cipher_encrypt (cipherhd, wrappedkey, wrappedkeylen, key, keylen); if (err) goto leave; xfree (key); key = NULL; gcry_cipher_close (cipherhd); cipherhd = NULL; /* Send the wrapped key to the agent. */ err = gpgsm_agent_import_key (ctrl, wrappedkey, wrappedkeylen); if (!err) { stats->count++; stats->secret_read++; stats->secret_imported++; } else if ( gpg_err_code (err) == GPG_ERR_EEXIST ) { err = 0; stats->count++; stats->secret_read++; stats->secret_dups++; } /* If we did not get an error from storing the secret key we return a possible error from parsing the certificates. We do this after storing the secret keys so that a bad certificate does not inhibit our chance to store the secret key. */ if (!err && store_cert_parm.err) err = store_cert_parm.err; leave: if (kparms) { for (i=0; i < 8; i++) gcry_mpi_release (kparms[i]); gcry_free (kparms); kparms = NULL; } xfree (key); gcry_sexp_release (s_key); xfree (passphrase); gcry_cipher_close (cipherhd); xfree (wrappedkey); xfree (kek); xfree (get_membuf (&p12mbuf, NULL)); xfree (p12buffer); xfree (curve); if (bad_pass) { /* We only write a plain error code and not direct BAD_PASSPHRASE because the pkcs12 parser might issue this message multiple times, BAD_PASSPHRASE in general requires a keyID and parts of the import might actually succeed so that IMPORT_PROBLEM is also not appropriate. */ gpgsm_status_with_err_code (ctrl, STATUS_ERROR, "import.parsep12", GPG_ERR_BAD_PASSPHRASE); } return err; } diff --git a/sm/keylist.c b/sm/keylist.c index 39ae8e0f4..1a47be084 100644 --- a/sm/keylist.c +++ b/sm/keylist.c @@ -1,1692 +1,1691 @@ /* keylist.c - Print certificates in various formats. * Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2008, 2009, * 2010, 2011 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include -#include #include "gpgsm.h" #include #include #include "keydb.h" #include "../kbx/keybox.h" /* for KEYBOX_FLAG_* */ #include "../common/i18n.h" #include "../common/tlv.h" #include "../common/compliance.h" #include "../common/pkscreening.h" struct list_external_parm_s { ctrl_t ctrl; estream_t fp; int print_header; int with_colons; int with_chain; int raw_mode; }; /* This table is to map Extended Key Usage OIDs to human readable names. */ struct { const char *oid; const char *name; } key_purpose_map[] = { { "1.3.6.1.5.5.7.3.1", "serverAuth" }, { "1.3.6.1.5.5.7.3.2", "clientAuth" }, { "1.3.6.1.5.5.7.3.3", "codeSigning" }, { "1.3.6.1.5.5.7.3.4", "emailProtection" }, { "1.3.6.1.5.5.7.3.5", "ipsecEndSystem" }, { "1.3.6.1.5.5.7.3.6", "ipsecTunnel" }, { "1.3.6.1.5.5.7.3.7", "ipsecUser" }, { "1.3.6.1.5.5.7.3.8", "timeStamping" }, { "1.3.6.1.5.5.7.3.9", "ocspSigning" }, { "1.3.6.1.5.5.7.3.10", "dvcs" }, { "1.3.6.1.5.5.7.3.11", "sbgpCertAAServerAuth" }, { "1.3.6.1.5.5.7.3.13", "eapOverPPP" }, { "1.3.6.1.5.5.7.3.14", "wlanSSID" }, { "2.16.840.1.113730.4.1", "serverGatedCrypto.ns" }, /* Netscape. */ { "1.3.6.1.4.1.311.10.3.3", "serverGatedCrypto.ms"}, /* Microsoft. */ { "1.3.6.1.5.5.7.48.1.5", "ocspNoCheck" }, { NULL, NULL } }; /* Do not print this extension in the list of extensions. This is set for oids which are already available via ksba functions. */ #define OID_FLAG_SKIP 1 /* The extension is a simple UTF8String and should be printed. */ #define OID_FLAG_UTF8 2 /* The extension can be trnted as a hex string. */ #define OID_FLAG_HEX 4 /* A table mapping OIDs to a descriptive string. */ static struct { char *oid; char *name; unsigned int flag; /* A flag as described above. */ } oidtranstbl[] = { /* Algorithms. */ { "1.2.840.10040.4.1", "dsa" }, { "1.2.840.10040.4.3", "dsaWithSha1" }, { "1.2.840.113549.1.1.1", "rsaEncryption" }, { "1.2.840.113549.1.1.2", "md2WithRSAEncryption" }, { "1.2.840.113549.1.1.3", "md4WithRSAEncryption" }, { "1.2.840.113549.1.1.4", "md5WithRSAEncryption" }, { "1.2.840.113549.1.1.5", "sha1WithRSAEncryption" }, { "1.2.840.113549.1.1.7", "rsaOAEP" }, { "1.2.840.113549.1.1.8", "rsaOAEP-MGF" }, { "1.2.840.113549.1.1.9", "rsaOAEP-pSpecified" }, { "1.2.840.113549.1.1.10", "rsaPSS" }, { "1.2.840.113549.1.1.11", "sha256WithRSAEncryption" }, { "1.2.840.113549.1.1.12", "sha384WithRSAEncryption" }, { "1.2.840.113549.1.1.13", "sha512WithRSAEncryption" }, { "1.3.14.3.2.26", "sha1" }, { "1.3.14.3.2.29", "sha-1WithRSAEncryption" }, { "1.3.36.3.3.1.2", "rsaSignatureWithripemd160" }, /* Telesec extensions. */ { "0.2.262.1.10.12.0", "certExtensionLiabilityLimitationExt" }, { "0.2.262.1.10.12.1", "telesecCertIdExt" }, { "0.2.262.1.10.12.2", "telesecPolicyIdentifier" }, { "0.2.262.1.10.12.3", "telesecPolicyQualifierID" }, { "0.2.262.1.10.12.4", "telesecCRLFilteredExt" }, { "0.2.262.1.10.12.5", "telesecCRLFilterExt"}, { "0.2.262.1.10.12.6", "telesecNamingAuthorityExt" }, #define OIDSTR_restriction \ "1.3.36.8.3.8" { OIDSTR_restriction, "restriction", OID_FLAG_UTF8 }, /* PKIX private extensions. */ { "1.3.6.1.5.5.7.1.1", "authorityInfoAccess" }, { "1.3.6.1.5.5.7.1.2", "biometricInfo" }, { "1.3.6.1.5.5.7.1.3", "qcStatements" }, { "1.3.6.1.5.5.7.1.4", "acAuditIdentity" }, { "1.3.6.1.5.5.7.1.5", "acTargeting" }, { "1.3.6.1.5.5.7.1.6", "acAaControls" }, { "1.3.6.1.5.5.7.1.7", "sbgp-ipAddrBlock" }, { "1.3.6.1.5.5.7.1.8", "sbgp-autonomousSysNum" }, { "1.3.6.1.5.5.7.1.9", "sbgp-routerIdentifier" }, { "1.3.6.1.5.5.7.1.10", "acProxying" }, { "1.3.6.1.5.5.7.1.11", "subjectInfoAccess" }, { "1.3.6.1.5.5.7.48.1", "ocsp" }, { "1.3.6.1.5.5.7.48.2", "caIssuers" }, { "1.3.6.1.5.5.7.48.3", "timeStamping" }, { "1.3.6.1.5.5.7.48.5", "caRepository" }, /* X.509 id-ce */ { "2.5.29.14", "subjectKeyIdentifier", OID_FLAG_SKIP}, { "2.5.29.15", "keyUsage", OID_FLAG_SKIP}, { "2.5.29.16", "privateKeyUsagePeriod" }, { "2.5.29.17", "subjectAltName", OID_FLAG_SKIP}, { "2.5.29.18", "issuerAltName", OID_FLAG_SKIP}, { "2.5.29.19", "basicConstraints", OID_FLAG_SKIP}, { "2.5.29.20", "cRLNumber" }, { "2.5.29.21", "cRLReason" }, { "2.5.29.22", "expirationDate" }, { "2.5.29.23", "instructionCode" }, { "2.5.29.24", "invalidityDate" }, { "2.5.29.27", "deltaCRLIndicator" }, { "2.5.29.28", "issuingDistributionPoint" }, { "2.5.29.29", "certificateIssuer" }, { "2.5.29.30", "nameConstraints" }, { "2.5.29.31", "cRLDistributionPoints", OID_FLAG_SKIP}, { "2.5.29.32", "certificatePolicies", OID_FLAG_SKIP}, { "2.5.29.32.0", "anyPolicy" }, { "2.5.29.33", "policyMappings" }, { "2.5.29.35", "authorityKeyIdentifier", OID_FLAG_SKIP}, { "2.5.29.36", "policyConstraints" }, { "2.5.29.37", "extKeyUsage", OID_FLAG_SKIP}, { "2.5.29.46", "freshestCRL" }, { "2.5.29.54", "inhibitAnyPolicy" }, /* Netscape certificate extensions. */ { "2.16.840.1.113730.1.1", "netscape-cert-type" }, { "2.16.840.1.113730.1.2", "netscape-base-url" }, { "2.16.840.1.113730.1.3", "netscape-revocation-url" }, { "2.16.840.1.113730.1.4", "netscape-ca-revocation-url" }, { "2.16.840.1.113730.1.7", "netscape-cert-renewal-url" }, { "2.16.840.1.113730.1.8", "netscape-ca-policy-url" }, { "2.16.840.1.113730.1.9", "netscape-homePage-url" }, { "2.16.840.1.113730.1.10", "netscape-entitylogo" }, { "2.16.840.1.113730.1.11", "netscape-userPicture" }, { "2.16.840.1.113730.1.12", "netscape-ssl-server-name" }, { "2.16.840.1.113730.1.13", "netscape-comment" }, /* GnuPG extensions */ { "1.3.6.1.4.1.11591.2.1.1", "pkaAddress" }, { "1.3.6.1.4.1.11591.2.2.1", "standaloneCertificate" }, { "1.3.6.1.4.1.11591.2.2.2", "wellKnownPrivateKey" }, /* Extensions used by the Bundesnetzagentur. */ { "1.3.6.1.4.1.8301.3.5", "validityModel" }, /* Yubikey extensions for attestation certificates. */ { "1.3.6.1.4.1.41482.3.3", "yubikey-firmware-version", OID_FLAG_HEX }, { "1.3.6.1.4.1.41482.3.7", "yubikey-serial-number", OID_FLAG_HEX }, { "1.3.6.1.4.1.41482.3.8", "yubikey-pin-touch-policy", OID_FLAG_HEX }, { "1.3.6.1.4.1.41482.3.9", "yubikey-formfactor", OID_FLAG_HEX }, { NULL } }; /* Return the description for OID; if no description is available NULL is returned. */ static const char * get_oid_desc (const char *oid, unsigned int *flag) { int i; if (oid) for (i=0; oidtranstbl[i].oid; i++) if (!strcmp (oidtranstbl[i].oid, oid)) { if (flag) *flag = oidtranstbl[i].flag; return oidtranstbl[i].name; } if (flag) *flag = 0; return NULL; } static void print_key_data (ksba_cert_t cert, estream_t fp) { #if 0 int n = pk ? pubkey_get_npkey( pk->pubkey_algo ) : 0; int i; for(i=0; i < n; i++ ) { es_fprintf (fp, "pkd:%d:%u:", i, mpi_get_nbits( pk->pkey[i] ) ); mpi_print(stdout, pk->pkey[i], 1 ); putchar(':'); putchar('\n'); } #else (void)cert; (void)fp; #endif } /* Various public key screenings. (Right now just ROCA). With * COLON_MODE set the output is formatted for use in the compliance * field of a colon listing. */ static void print_pk_screening (ksba_cert_t cert, int colon_mode, estream_t fp) { gpg_error_t err; gcry_mpi_t modulus; modulus = gpgsm_get_rsa_modulus (cert); if (modulus) { err = screen_key_for_roca (modulus); if (!err) ; else if (gpg_err_code (err) == GPG_ERR_TRUE) { if (colon_mode) es_fprintf (fp, colon_mode > 1? " %d":"%d", 6001); else es_fprintf (fp, " screening: ROCA vulnerability detected\n"); } else if (!colon_mode) es_fprintf (fp, " screening: [ROCA check failed: %s]\n", gpg_strerror (err)); gcry_mpi_release (modulus); } } static void print_capabilities (ksba_cert_t cert, estream_t fp) { gpg_error_t err; unsigned int use; size_t buflen; char buffer[1]; err = ksba_cert_get_user_data (cert, "is_qualified", &buffer, sizeof (buffer), &buflen); if (!err && buflen) { if (*buffer) es_putc ('q', fp); } else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) ; /* Don't know - will not get marked as 'q' */ else log_debug ("get_user_data(is_qualified) failed: %s\n", gpg_strerror (err)); err = ksba_cert_get_key_usage (cert, &use); if (gpg_err_code (err) == GPG_ERR_NO_DATA) { es_putc ('e', fp); es_putc ('s', fp); es_putc ('c', fp); es_putc ('E', fp); es_putc ('S', fp); es_putc ('C', fp); return; } if (err) { log_error (_("error getting key usage information: %s\n"), gpg_strerror (err)); return; } if ((use & (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT))) es_putc ('e', fp); if ((use & (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION))) es_putc ('s', fp); if ((use & KSBA_KEYUSAGE_KEY_CERT_SIGN)) es_putc ('c', fp); if ((use & (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT))) es_putc ('E', fp); if ((use & (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION))) es_putc ('S', fp); if ((use & KSBA_KEYUSAGE_KEY_CERT_SIGN)) es_putc ('C', fp); } static void print_time (gnupg_isotime_t t, estream_t fp) { if (!t || !*t) ; else es_fputs (t, fp); } /* Return an allocated string with the email address extracted from a DN. Note hat we use this code also in ../kbx/keybox-blob.c. */ static char * email_kludge (const char *name) { const char *p, *string; unsigned char *buf; int n; string = name; for (;;) { p = strstr (string, "1.2.840.113549.1.9.1=#"); if (!p) return NULL; if (p == name || (p > string+1 && p[-1] == ',' && p[-2] != '\\')) { name = p + 22; break; } string = p + 22; } /* This looks pretty much like an email address in the subject's DN we use this to add an additional user ID entry. This way, OpenSSL generated keys get a nicer and usable listing. */ for (n=0, p=name; hexdigitp (p) && hexdigitp (p+1); p +=2, n++) ; if (!n) return NULL; buf = xtrymalloc (n+3); if (!buf) return NULL; /* oops, out of core */ *buf = '<'; for (n=1, p=name; hexdigitp (p); p +=2, n++) buf[n] = xtoi_2 (p); buf[n++] = '>'; buf[n] = 0; return (char*)buf; } /* Print the compliance flags to field 18. ALGO is the gcrypt algo * number. NBITS is the length of the key in bits. */ static void print_compliance_flags (ksba_cert_t cert, int algo, unsigned int nbits, estream_t fp) { int indent = 0; int hashalgo; /* Note that we do not need to test for PK_ALGO_FLAG_RSAPSS because * that is not a property of the key but one of the created * signature. */ if (gnupg_pk_is_compliant (CO_DE_VS, algo, 0, NULL, nbits, NULL)) { hashalgo = gcry_md_map_name (ksba_cert_get_digest_algo (cert)); if (gnupg_digest_is_compliant (CO_DE_VS, hashalgo)) { es_fputs (gnupg_status_compliance_flag (CO_DE_VS), fp); indent = 1; } } if (opt.with_key_screening) print_pk_screening (cert, 1+indent, fp); } /* List one certificate in colon mode */ static void list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity, estream_t fp, int have_secret) { int rc; int idx; char truststring[2]; char *p; ksba_sexp_t sexp; char *fpr; ksba_isotime_t t; gpg_error_t valerr; int algo; unsigned int nbits; char *curve = NULL; const char *chain_id; char *chain_id_buffer = NULL; int is_root = 0; char *kludge_uid; if (ctrl->with_validation) valerr = gpgsm_validate_chain (ctrl, cert, "", NULL, 1, NULL, 0, NULL); else valerr = 0; /* We need to get the fingerprint and the chaining ID in advance. */ fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); { ksba_cert_t next; rc = gpgsm_walk_cert_chain (ctrl, cert, &next); if (!rc) /* We known the issuer's certificate. */ { p = gpgsm_get_fingerprint_hexstring (next, GCRY_MD_SHA1); chain_id_buffer = p; chain_id = chain_id_buffer; ksba_cert_release (next); } else if (rc == -1) /* We have reached the root certificate. */ { chain_id = fpr; is_root = 1; } else chain_id = NULL; } es_fputs (have_secret? "crs:":"crt:", fp); /* Note: We can't use multiple flags, like "ei", because the validation check does only return one error. */ truststring[0] = 0; truststring[1] = 0; if ((validity & VALIDITY_REVOKED) || gpg_err_code (valerr) == GPG_ERR_CERT_REVOKED) *truststring = 'r'; else if (gpg_err_code (valerr) == GPG_ERR_CERT_EXPIRED) *truststring = 'e'; else { /* Lets also check whether the certificate under question expired. This is merely a hack until we found a proper way to store the expiration flag in the keybox. */ ksba_isotime_t current_time, not_after; gnupg_get_isotime (current_time); if (!opt.ignore_expiration && !ksba_cert_get_validity (cert, 1, not_after) && *not_after && strcmp (current_time, not_after) > 0 ) *truststring = 'e'; else if (valerr) { if (gpgsm_cert_has_well_known_private_key (cert)) *truststring = 'w'; /* Well, this is dummy CA. */ else *truststring = 'i'; } else if (ctrl->with_validation && !is_root) *truststring = 'f'; } /* If we have no truststring yet (i.e. the certificate might be good) and this is a root certificate, we ask the agent whether this is a trusted root certificate. */ if (!*truststring && is_root) { struct rootca_flags_s dummy_flags; if (gpgsm_cert_has_well_known_private_key (cert)) *truststring = 'w'; /* Well, this is dummy CA. */ else { rc = gpgsm_agent_istrusted (ctrl, cert, NULL, &dummy_flags); if (!rc) *truststring = 'u'; /* Yes, we trust this one (ultimately). */ else if (gpg_err_code (rc) == GPG_ERR_NOT_TRUSTED) *truststring = 'n'; /* No, we do not trust this one. */ /* (in case of an error we can't tell anything.) */ } } if (*truststring) es_fputs (truststring, fp); algo = gpgsm_get_key_algo_info2 (cert, &nbits, &curve); es_fprintf (fp, ":%u:%d:%s:", nbits, algo, fpr+24); ksba_cert_get_validity (cert, 0, t); print_time (t, fp); es_putc (':', fp); ksba_cert_get_validity (cert, 1, t); print_time ( t, fp); es_putc (':', fp); /* Field 8, serial number: */ if ((sexp = ksba_cert_get_serial (cert))) { int len; const unsigned char *s = sexp; if (*s == '(') { s++; for (len=0; *s && *s != ':' && digitp (s); s++) len = len*10 + atoi_1 (s); if (*s == ':') for (s++; len; len--, s++) es_fprintf (fp,"%02X", *s); } xfree (sexp); } es_putc (':', fp); /* Field 9, ownertrust - not used here */ es_putc (':', fp); /* field 10, old user ID - we use it here for the issuer DN */ if ((p = ksba_cert_get_issuer (cert,0))) { es_write_sanitized (fp, p, strlen (p), ":", NULL); xfree (p); } es_putc (':', fp); /* Field 11, signature class - not used */ es_putc (':', fp); /* Field 12, capabilities: */ print_capabilities (cert, fp); es_putc (':', fp); /* Field 13, not used: */ es_putc (':', fp); /* Field 14, not used: */ es_putc (':', fp); if (have_secret || ctrl->with_secret) { char *cardsn; p = gpgsm_get_keygrip_hexstring (cert); if (!gpgsm_agent_keyinfo (ctrl, p, &cardsn) && (cardsn || ctrl->with_secret)) { /* Field 15: Token serial number or secret key indicator. */ if (cardsn) es_fputs (cardsn, fp); else if (ctrl->with_secret) es_putc ('+', fp); } xfree (cardsn); xfree (p); } es_putc (':', fp); /* End of field 15. */ es_putc (':', fp); /* End of field 16. */ if (curve) es_fputs (curve, fp); es_putc (':', fp); /* End of field 17. */ print_compliance_flags (cert, algo, nbits, fp); es_putc (':', fp); /* End of field 18. */ es_putc ('\n', fp); /* FPR record */ es_fprintf (fp, "fpr:::::::::%s:::", fpr); /* Print chaining ID (field 13)*/ if (chain_id) es_fputs (chain_id, fp); es_putc (':', fp); es_putc ('\n', fp); xfree (fpr); fpr = NULL; chain_id = NULL; xfree (chain_id_buffer); chain_id_buffer = NULL; /* Always print the keygrip. */ if ( (p = gpgsm_get_keygrip_hexstring (cert))) { es_fprintf (fp, "grp:::::::::%s:\n", p); xfree (p); } if (opt.with_key_data) print_key_data (cert, fp); kludge_uid = NULL; for (idx=0; (p = ksba_cert_get_subject (cert,idx)); idx++) { /* In the case that the same email address is in the subject DN as well as in an alternate subject name we avoid printing it a second time. */ if (kludge_uid && !strcmp (kludge_uid, p)) continue; es_fprintf (fp, "uid:%s::::::::", truststring); es_write_sanitized (fp, p, strlen (p), ":", NULL); es_putc (':', fp); es_putc (':', fp); es_putc ('\n', fp); if (!idx) { /* It would be better to get the faked email address from the keydb. But as long as we don't have a way to pass the meta data back, we just check it the same way as the code used to create the keybox meta data does */ kludge_uid = email_kludge (p); if (kludge_uid) { es_fprintf (fp, "uid:%s::::::::", truststring); es_write_sanitized (fp, kludge_uid, strlen (kludge_uid), ":", NULL); es_putc (':', fp); es_putc (':', fp); es_putc ('\n', fp); } } xfree (p); } xfree (kludge_uid); xfree (curve); } static void print_name_raw (estream_t fp, const char *string) { if (!string) es_fputs ("[error]", fp); else es_write_sanitized (fp, string, strlen (string), NULL, NULL); } static void print_names_raw (estream_t fp, int indent, ksba_name_t name) { int idx; const char *s; int indent_all; if ((indent_all = (indent < 0))) indent = - indent; if (!name) { es_fputs ("none\n", fp); return; } for (idx=0; (s = ksba_name_enum (name, idx)); idx++) { char *p = ksba_name_get_uri (name, idx); es_fprintf (fp, "%*s", idx||indent_all?indent:0, ""); es_write_sanitized (fp, p?p:s, strlen (p?p:s), NULL, NULL); es_putc ('\n', fp); xfree (p); } } static void print_utf8_extn_raw (estream_t fp, int indent, const unsigned char *der, size_t derlen) { gpg_error_t err; int class, tag, constructed, ndef; size_t objlen, hdrlen; if (indent < 0) indent = - indent; err = parse_ber_header (&der, &derlen, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > derlen || tag != TAG_UTF8_STRING)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) { es_fprintf (fp, "%*s[%s]\n", indent, "", gpg_strerror (err)); return; } es_fprintf (fp, "%*s(%.*s)\n", indent, "", (int)objlen, der); } static void print_utf8_extn (estream_t fp, int indent, const unsigned char *der, size_t derlen) { gpg_error_t err; int class, tag, constructed, ndef; size_t objlen, hdrlen; int indent_all; if ((indent_all = (indent < 0))) indent = - indent; err = parse_ber_header (&der, &derlen, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > derlen || tag != TAG_UTF8_STRING)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) { es_fprintf (fp, "%*s[%s%s]\n", indent_all? indent:0, "", _("Error - "), gpg_strerror (err)); return; } es_fprintf (fp, "%*s\"", indent_all? indent:0, ""); /* Fixme: we should implement word wrapping */ es_write_sanitized (fp, der, objlen, "\"", NULL); es_fputs ("\"\n", fp); } /* Print the extension described by (DER,DERLEN) in hex. */ static void print_hex_extn (estream_t fp, int indent, const unsigned char *der, size_t derlen) { if (indent < 0) indent = - indent; es_fprintf (fp, "%*s(", indent, ""); for (; derlen; der++, derlen--) es_fprintf (fp, "%02X%s", *der, derlen > 1? " ":""); es_fprintf (fp, ")\n"); } /* List one certificate in raw mode useful to have a closer look at the certificate. This one does no beautification and only minimal output sanitation. It is mainly useful for debugging. */ static void list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd, ksba_cert_t cert, estream_t fp, int have_secret, int with_validation) { gpg_error_t err; size_t off, len; ksba_sexp_t sexp, keyid; char *dn; ksba_isotime_t t; int idx, i; int is_ca, chainlen; unsigned int kusage; char *string, *p, *pend; const char *oid, *s; ksba_name_t name, name2; unsigned int reason; const unsigned char *cert_der = NULL; (void)have_secret; es_fprintf (fp, " ID: 0x%08lX\n", gpgsm_get_short_fingerprint (cert, NULL)); sexp = ksba_cert_get_serial (cert); es_fputs (" S/N: ", fp); gpgsm_print_serial (fp, sexp); es_putc ('\n', fp); es_fputs (" (dec): ", fp); gpgsm_print_serial_decimal (fp, sexp); es_putc ('\n', fp); ksba_free (sexp); dn = ksba_cert_get_issuer (cert, 0); es_fputs (" Issuer: ", fp); print_name_raw (fp, dn); ksba_free (dn); es_putc ('\n', fp); for (idx=1; (dn = ksba_cert_get_issuer (cert, idx)); idx++) { es_fputs (" aka: ", fp); print_name_raw (fp, dn); ksba_free (dn); es_putc ('\n', fp); } dn = ksba_cert_get_subject (cert, 0); es_fputs (" Subject: ", fp); print_name_raw (fp, dn); ksba_free (dn); es_putc ('\n', fp); for (idx=1; (dn = ksba_cert_get_subject (cert, idx)); idx++) { es_fputs (" aka: ", fp); print_name_raw (fp, dn); ksba_free (dn); es_putc ('\n', fp); } dn = gpgsm_get_fingerprint_string (cert, 0); es_fprintf (fp, " sha1_fpr: %s\n", dn?dn:"error"); xfree (dn); dn = gpgsm_get_fingerprint_string (cert, GCRY_MD_MD5); es_fprintf (fp, " md5_fpr: %s\n", dn?dn:"error"); xfree (dn); dn = gpgsm_get_certid (cert); es_fprintf (fp, " certid: %s\n", dn?dn:"error"); xfree (dn); dn = gpgsm_get_keygrip_hexstring (cert); es_fprintf (fp, " keygrip: %s\n", dn?dn:"error"); xfree (dn); ksba_cert_get_validity (cert, 0, t); es_fputs (" notBefore: ", fp); gpgsm_print_time (fp, t); es_putc ('\n', fp); es_fputs (" notAfter: ", fp); ksba_cert_get_validity (cert, 1, t); gpgsm_print_time (fp, t); es_putc ('\n', fp); oid = ksba_cert_get_digest_algo (cert); s = get_oid_desc (oid, 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); } /* subjectKeyIdentifier */ es_fputs (" subjKeyId: ", fp); err = ksba_cert_get_subj_key_id (cert, NULL, &keyid); if (!err || gpg_err_code (err) == GPG_ERR_NO_DATA) { if (gpg_err_code (err) == GPG_ERR_NO_DATA) es_fputs ("[none]\n", fp); else { gpgsm_print_serial (fp, keyid); ksba_free (keyid); es_putc ('\n', fp); } } else es_fputs ("[?]\n", fp); /* authorityKeyIdentifier */ es_fputs (" authKeyId: ", fp); err = ksba_cert_get_auth_key_id (cert, &keyid, &name, &sexp); if (!err || gpg_err_code (err) == GPG_ERR_NO_DATA) { if (gpg_err_code (err) == GPG_ERR_NO_DATA || !name) es_fputs ("[none]\n", fp); else { gpgsm_print_serial (fp, sexp); ksba_free (sexp); es_putc ('\n', fp); print_names_raw (fp, -15, name); ksba_name_release (name); } if (keyid) { es_fputs (" authKeyId.ki: ", fp); gpgsm_print_serial (fp, keyid); ksba_free (keyid); es_putc ('\n', fp); } } else es_fputs ("[?]\n", fp); es_fputs (" keyUsage:", fp); err = ksba_cert_get_key_usage (cert, &kusage); if (gpg_err_code (err) != GPG_ERR_NO_DATA) { if (err) es_fprintf (fp, " [error: %s]", gpg_strerror (err)); else { if ( (kusage & KSBA_KEYUSAGE_DIGITAL_SIGNATURE)) es_fputs (" digitalSignature", fp); if ( (kusage & KSBA_KEYUSAGE_NON_REPUDIATION)) es_fputs (" nonRepudiation", fp); if ( (kusage & KSBA_KEYUSAGE_KEY_ENCIPHERMENT)) es_fputs (" keyEncipherment", fp); if ( (kusage & KSBA_KEYUSAGE_DATA_ENCIPHERMENT)) es_fputs (" dataEncipherment", fp); if ( (kusage & KSBA_KEYUSAGE_KEY_AGREEMENT)) es_fputs (" keyAgreement", fp); if ( (kusage & KSBA_KEYUSAGE_KEY_CERT_SIGN)) es_fputs (" certSign", fp); if ( (kusage & KSBA_KEYUSAGE_CRL_SIGN)) es_fputs (" crlSign", fp); if ( (kusage & KSBA_KEYUSAGE_ENCIPHER_ONLY)) es_fputs (" encipherOnly", fp); if ( (kusage & KSBA_KEYUSAGE_DECIPHER_ONLY)) es_fputs (" decipherOnly", fp); } es_putc ('\n', fp); } else es_fputs (" [none]\n", fp); es_fputs (" extKeyUsage: ", fp); err = ksba_cert_get_ext_key_usages (cert, &string); if (gpg_err_code (err) != GPG_ERR_NO_DATA) { if (err) es_fprintf (fp, "[error: %s]", gpg_strerror (err)); else { p = string; while (p && (pend=strchr (p, ':'))) { *pend++ = 0; for (i=0; key_purpose_map[i].oid; i++) if ( !strcmp (key_purpose_map[i].oid, p) ) break; es_fputs (key_purpose_map[i].oid?key_purpose_map[i].name:p, fp); p = pend; if (*p != 'C') es_fputs (" (suggested)", fp); if ((p = strchr (p, '\n'))) { p++; es_fputs ("\n ", fp); } } xfree (string); } es_putc ('\n', fp); } else es_fputs ("[none]\n", fp); es_fputs (" policies: ", fp); err = ksba_cert_get_cert_policies (cert, &string); if (gpg_err_code (err) != GPG_ERR_NO_DATA) { if (err) es_fprintf (fp, "[error: %s]", gpg_strerror (err)); else { p = string; while (p && (pend=strchr (p, ':'))) { *pend++ = 0; for (i=0; key_purpose_map[i].oid; i++) if ( !strcmp (key_purpose_map[i].oid, p) ) break; es_fputs (p, fp); p = pend; if (*p == 'C') es_fputs (" (critical)", fp); if ((p = strchr (p, '\n'))) { p++; es_fputs ("\n ", fp); } } xfree (string); } es_putc ('\n', fp); } else es_fputs ("[none]\n", fp); es_fputs (" chainLength: ", fp); err = ksba_cert_is_ca (cert, &is_ca, &chainlen); if (err || is_ca) { if (gpg_err_code (err) == GPG_ERR_NO_VALUE ) es_fprintf (fp, "[none]"); else if (err) es_fprintf (fp, "[error: %s]", gpg_strerror (err)); else if (chainlen == -1) es_fputs ("unlimited", fp); else es_fprintf (fp, "%d", chainlen); es_putc ('\n', fp); } else es_fputs ("not a CA\n", fp); /* CRL distribution point */ for (idx=0; !(err=ksba_cert_get_crl_dist_point (cert, idx, &name, &name2, &reason)) ;idx++) { es_fputs (" crlDP: ", fp); print_names_raw (fp, 15, name); if (reason) { es_fputs (" reason: ", fp); if ( (reason & KSBA_CRLREASON_UNSPECIFIED)) es_fputs (" unused", fp); if ( (reason & KSBA_CRLREASON_KEY_COMPROMISE)) es_fputs (" keyCompromise", fp); if ( (reason & KSBA_CRLREASON_CA_COMPROMISE)) es_fputs (" caCompromise", fp); if ( (reason & KSBA_CRLREASON_AFFILIATION_CHANGED)) es_fputs (" affiliationChanged", fp); if ( (reason & KSBA_CRLREASON_SUPERSEDED)) es_fputs (" superseded", fp); if ( (reason & KSBA_CRLREASON_CESSATION_OF_OPERATION)) es_fputs (" cessationOfOperation", fp); if ( (reason & KSBA_CRLREASON_CERTIFICATE_HOLD)) es_fputs (" certificateHold", fp); es_putc ('\n', fp); } es_fputs (" issuer: ", fp); print_names_raw (fp, 23, name2); ksba_name_release (name); ksba_name_release (name2); } if (err && gpg_err_code (err) != GPG_ERR_EOF && gpg_err_code (err) != GPG_ERR_NO_VALUE) es_fputs (" crlDP: [error]\n", fp); else if (!idx) es_fputs (" crlDP: [none]\n", fp); /* authorityInfoAccess. */ for (idx=0; !(err=ksba_cert_get_authority_info_access (cert, idx, &string, &name)); idx++) { es_fputs (" authInfo: ", fp); s = get_oid_desc (string, NULL); es_fprintf (fp, "%s%s%s%s\n", string, s?" (":"", s?s:"", s?")":""); print_names_raw (fp, -15, name); ksba_name_release (name); ksba_free (string); } if (err && gpg_err_code (err) != GPG_ERR_EOF && gpg_err_code (err) != GPG_ERR_NO_VALUE) es_fputs (" authInfo: [error]\n", fp); else if (!idx) es_fputs (" authInfo: [none]\n", fp); /* subjectInfoAccess. */ for (idx=0; !(err=ksba_cert_get_subject_info_access (cert, idx, &string, &name)); idx++) { es_fputs (" subjectInfo: ", fp); s = get_oid_desc (string, NULL); es_fprintf (fp, "%s%s%s%s\n", string, s?" (":"", s?s:"", s?")":""); print_names_raw (fp, -15, name); ksba_name_release (name); ksba_free (string); } if (err && gpg_err_code (err) != GPG_ERR_EOF && gpg_err_code (err) != GPG_ERR_NO_VALUE) es_fputs (" subjInfo: [error]\n", fp); else if (!idx) es_fputs (" subjInfo: [none]\n", fp); for (idx=0; !(err=ksba_cert_get_extension (cert, idx, &oid, &i, &off, &len));idx++) { unsigned int flag; s = get_oid_desc (oid, &flag); if ((flag & OID_FLAG_SKIP)) continue; es_fprintf (fp, " %s: %s%s%s%s", i? "critExtn":" extn", oid, s?" (":"", s?s:"", s?")":""); if ((flag & OID_FLAG_UTF8)) { if (!cert_der) cert_der = ksba_cert_get_image (cert, NULL); log_assert (cert_der); es_fprintf (fp, "\n"); print_utf8_extn_raw (fp, -15, cert_der+off, len); } else if ((flag & OID_FLAG_HEX)) { if (!cert_der) cert_der = ksba_cert_get_image (cert, NULL); log_assert (cert_der); es_fprintf (fp, "\n"); print_hex_extn (fp, -15, cert_der+off, len); } else es_fprintf (fp, " [%d octets]\n", (int)len); } if (with_validation) { err = gpgsm_validate_chain (ctrl, cert, "", NULL, 1, fp, 0, NULL); if (!err) es_fprintf (fp, " [certificate is good]\n"); else es_fprintf (fp, " [certificate is bad: %s]\n", gpg_strerror (err)); } if (hd) { unsigned int blobflags; err = keydb_get_flags (hd, KEYBOX_FLAG_BLOB, 0, &blobflags); if (err) es_fprintf (fp, " [error getting keyflags: %s]\n",gpg_strerror (err)); else if ((blobflags & KEYBOX_FLAG_BLOB_EPHEMERAL)) es_fprintf (fp, " [stored as ephemeral]\n"); } } /* List one certificate in standard mode */ static void list_cert_std (ctrl_t ctrl, ksba_cert_t cert, estream_t fp, int have_secret, int with_validation) { gpg_error_t err; ksba_sexp_t sexp; char *dn; ksba_isotime_t t; int idx, i; int is_ca, chainlen; unsigned int kusage; char *string, *p, *pend; size_t off, len; const char *oid; const unsigned char *cert_der = NULL; es_fprintf (fp, " ID: 0x%08lX\n", gpgsm_get_short_fingerprint (cert, NULL)); sexp = ksba_cert_get_serial (cert); es_fputs (" S/N: ", fp); gpgsm_print_serial (fp, sexp); es_putc ('\n', fp); es_fputs (" (dec): ", fp); gpgsm_print_serial_decimal (fp, sexp); es_putc ('\n', fp); ksba_free (sexp); dn = ksba_cert_get_issuer (cert, 0); es_fputs (" Issuer: ", fp); gpgsm_es_print_name (fp, dn); ksba_free (dn); es_putc ('\n', fp); for (idx=1; (dn = ksba_cert_get_issuer (cert, idx)); idx++) { es_fputs (" aka: ", fp); gpgsm_es_print_name (fp, dn); ksba_free (dn); es_putc ('\n', fp); } dn = ksba_cert_get_subject (cert, 0); es_fputs (" Subject: ", fp); gpgsm_es_print_name (fp, dn); ksba_free (dn); es_putc ('\n', fp); for (idx=1; (dn = ksba_cert_get_subject (cert, idx)); idx++) { es_fputs (" aka: ", fp); gpgsm_es_print_name (fp, dn); ksba_free (dn); es_putc ('\n', fp); } ksba_cert_get_validity (cert, 0, t); es_fputs (" validity: ", fp); gpgsm_print_time (fp, t); es_fputs (" through ", fp); ksba_cert_get_validity (cert, 1, t); gpgsm_print_time (fp, t); es_putc ('\n', fp); { char *algostr; algostr = gpgsm_pubkey_algo_string (cert, NULL); es_fprintf (fp, " key type: %s\n", algostr? algostr : "[error]"); xfree (algostr); } err = ksba_cert_get_key_usage (cert, &kusage); if (gpg_err_code (err) != GPG_ERR_NO_DATA) { es_fputs (" key usage:", fp); if (err) es_fprintf (fp, " [error: %s]", gpg_strerror (err)); else { if ( (kusage & KSBA_KEYUSAGE_DIGITAL_SIGNATURE)) es_fputs (" digitalSignature", fp); if ( (kusage & KSBA_KEYUSAGE_NON_REPUDIATION)) es_fputs (" nonRepudiation", fp); if ( (kusage & KSBA_KEYUSAGE_KEY_ENCIPHERMENT)) es_fputs (" keyEncipherment", fp); if ( (kusage & KSBA_KEYUSAGE_DATA_ENCIPHERMENT)) es_fputs (" dataEncipherment", fp); if ( (kusage & KSBA_KEYUSAGE_KEY_AGREEMENT)) es_fputs (" keyAgreement", fp); if ( (kusage & KSBA_KEYUSAGE_KEY_CERT_SIGN)) es_fputs (" certSign", fp); if ( (kusage & KSBA_KEYUSAGE_CRL_SIGN)) es_fputs (" crlSign", fp); if ( (kusage & KSBA_KEYUSAGE_ENCIPHER_ONLY)) es_fputs (" encipherOnly", fp); if ( (kusage & KSBA_KEYUSAGE_DECIPHER_ONLY)) es_fputs (" decipherOnly", fp); } es_putc ('\n', fp); } err = ksba_cert_get_ext_key_usages (cert, &string); if (gpg_err_code (err) != GPG_ERR_NO_DATA) { es_fputs ("ext key usage: ", fp); if (err) es_fprintf (fp, "[error: %s]", gpg_strerror (err)); else { p = string; while (p && (pend=strchr (p, ':'))) { *pend++ = 0; for (i=0; key_purpose_map[i].oid; i++) if ( !strcmp (key_purpose_map[i].oid, p) ) break; es_fputs (key_purpose_map[i].oid?key_purpose_map[i].name:p, fp); p = pend; if (*p != 'C') es_fputs (" (suggested)", fp); if ((p = strchr (p, '\n'))) { p++; es_fputs (", ", fp); } } xfree (string); } es_putc ('\n', fp); } /* Print restrictions. */ for (idx=0; !(err=ksba_cert_get_extension (cert, idx, &oid, NULL, &off, &len));idx++) { if (!strcmp (oid, OIDSTR_restriction) ) { if (!cert_der) cert_der = ksba_cert_get_image (cert, NULL); - assert (cert_der); + log_assert (cert_der); es_fputs (" restriction: ", fp); print_utf8_extn (fp, 15, cert_der+off, len); } } /* Print policies. */ err = ksba_cert_get_cert_policies (cert, &string); if (gpg_err_code (err) != GPG_ERR_NO_DATA) { es_fputs (" policies: ", fp); if (err) es_fprintf (fp, "[error: %s]", gpg_strerror (err)); else { for (p=string; *p; p++) { if (*p == '\n') *p = ','; } es_write_sanitized (fp, string, strlen (string), NULL, NULL); xfree (string); } es_putc ('\n', fp); } err = ksba_cert_is_ca (cert, &is_ca, &chainlen); if (err || is_ca) { es_fputs (" chain length: ", fp); if (gpg_err_code (err) == GPG_ERR_NO_VALUE ) es_fprintf (fp, "none"); else if (err) es_fprintf (fp, "[error: %s]", gpg_strerror (err)); else if (chainlen == -1) es_fputs ("unlimited", fp); else es_fprintf (fp, "%d", chainlen); es_putc ('\n', fp); } if (opt.with_md5_fingerprint) { dn = gpgsm_get_fingerprint_string (cert, GCRY_MD_MD5); es_fprintf (fp, " md5 fpr: %s\n", dn?dn:"error"); xfree (dn); } dn = gpgsm_get_fingerprint_string (cert, 0); es_fprintf (fp, " fingerprint: %s\n", dn?dn:"error"); xfree (dn); if (opt.with_keygrip) { dn = gpgsm_get_keygrip_hexstring (cert); if (dn) { es_fprintf (fp, " keygrip: %s\n", dn); xfree (dn); } } if (opt.with_key_screening) print_pk_screening (cert, 0, fp); if (have_secret) { char *cardsn; p = gpgsm_get_keygrip_hexstring (cert); if (!gpgsm_agent_keyinfo (ctrl, p, &cardsn) && cardsn) es_fprintf (fp, " card s/n: %s\n", cardsn); xfree (cardsn); xfree (p); } if (with_validation) { gpg_error_t tmperr; size_t buflen; char buffer[1]; err = gpgsm_validate_chain (ctrl, cert, "", NULL, 1, fp, 0, NULL); tmperr = ksba_cert_get_user_data (cert, "is_qualified", &buffer, sizeof (buffer), &buflen); if (!tmperr && buflen) { if (*buffer) es_fputs (" [qualified]\n", fp); } else if (gpg_err_code (tmperr) == GPG_ERR_NOT_FOUND) ; /* Don't know - will not get marked as 'q' */ else log_debug ("get_user_data(is_qualified) failed: %s\n", gpg_strerror (tmperr)); if (!err) es_fprintf (fp, " [certificate is good]\n"); else es_fprintf (fp, " [certificate is bad: %s]\n", gpg_strerror (err)); } if (opt.debug) es_fflush (fp); } /* Same as standard mode list all certifying certs too. */ static void list_cert_chain (ctrl_t ctrl, KEYDB_HANDLE hd, ksba_cert_t cert, int raw_mode, estream_t fp, int with_validation) { ksba_cert_t next = NULL; if (raw_mode) list_cert_raw (ctrl, hd, cert, fp, 0, with_validation); else list_cert_std (ctrl, cert, fp, 0, with_validation); ksba_cert_ref (cert); while (!gpgsm_walk_cert_chain (ctrl, cert, &next)) { ksba_cert_release (cert); es_fputs ("Certified by\n", fp); if (raw_mode) list_cert_raw (ctrl, hd, next, fp, 0, with_validation); else list_cert_std (ctrl, next, fp, 0, with_validation); cert = next; } ksba_cert_release (cert); es_putc ('\n', fp); } /* List all internal keys or just the keys given as NAMES. MODE is a bit vector to specify what keys are to be included; see gpgsm_list_keys (below) for details. If RAW_MODE is true, the raw output mode will be used instead of the standard beautified one. */ static gpg_error_t list_internal_keys (ctrl_t ctrl, strlist_t names, estream_t fp, unsigned int mode, int raw_mode) { KEYDB_HANDLE hd; KEYDB_SEARCH_DESC *desc = NULL; strlist_t sl; int ndesc; ksba_cert_t cert = NULL; ksba_cert_t lastcert = NULL; gpg_error_t rc = 0; const char *lastresname, *resname; int have_secret; int want_ephemeral = ctrl->with_ephemeral_keys; hd = keydb_new (); if (!hd) { log_error ("keydb_new failed\n"); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } if (!names) ndesc = 1; else { for (sl=names, ndesc=0; sl; sl = sl->next, ndesc++) ; } desc = xtrycalloc (ndesc, sizeof *desc); if (!ndesc) { rc = gpg_error_from_syserror (); log_error ("out of core\n"); goto leave; } if (!names) desc[0].mode = KEYDB_SEARCH_MODE_FIRST; else { for (ndesc=0, sl=names; sl; sl = sl->next) { rc = classify_user_id (sl->d, desc+ndesc, 0); if (rc) { log_error ("key '%s' not found: %s\n", sl->d, gpg_strerror (rc)); rc = 0; } else ndesc++; } } /* If all specifications are done by fingerprint or keygrip, we switch to ephemeral mode so that _all_ currently available and matching certificates are listed. */ if (!want_ephemeral && names && ndesc) { int i; for (i=0; (i < ndesc && (desc[i].mode == KEYDB_SEARCH_MODE_FPR || desc[i].mode == KEYDB_SEARCH_MODE_KEYGRIP)); i++) ; if (i == ndesc) want_ephemeral = 1; } if (want_ephemeral) keydb_set_ephemeral (hd, 1); /* It would be nice to see which of the given users did actually match one in the keyring. To implement this we need to have a found flag for each entry in desc and to set this we must check all those entries after a match to mark all matched one - currently we stop at the first match. To do this we need an extra flag to enable this feature so */ /* Suppress duplicates at least when they follow each other. */ lastresname = NULL; while (!(rc = keydb_search (ctrl, hd, desc, ndesc))) { unsigned int validity; if (!names) desc[0].mode = KEYDB_SEARCH_MODE_NEXT; rc = keydb_get_flags (hd, KEYBOX_FLAG_VALIDITY, 0, &validity); if (rc) { log_error ("keydb_get_flags failed: %s\n", gpg_strerror (rc)); goto leave; } rc = keydb_get_cert (hd, &cert); if (rc) { log_error ("keydb_get_cert failed: %s\n", gpg_strerror (rc)); goto leave; } /* Skip duplicated certificates, at least if they follow each others. This works best if a single key is searched for and expected. FIXME: Non-sequential duplicates remain. */ if (gpgsm_certs_identical_p (cert, lastcert)) { ksba_cert_release (cert); cert = NULL; continue; } resname = keydb_get_resource_name (hd); if (lastresname != resname ) { int i; if (ctrl->no_server) { es_fprintf (fp, "%s\n", resname ); for (i=strlen(resname); i; i-- ) es_putc ('-', fp); es_putc ('\n', fp); lastresname = resname; } } have_secret = 0; if (mode) { char *p = gpgsm_get_keygrip_hexstring (cert); if (p) { rc = gpgsm_agent_havekey (ctrl, p); if (!rc) have_secret = 1; else if ( gpg_err_code (rc) != GPG_ERR_NO_SECKEY) goto leave; rc = 0; xfree (p); } } if (!mode || ((mode & 1) && !have_secret) || ((mode & 2) && have_secret) ) { if (ctrl->with_colons) list_cert_colon (ctrl, cert, validity, fp, have_secret); else if (ctrl->with_chain) list_cert_chain (ctrl, hd, cert, raw_mode, fp, ctrl->with_validation); else { if (raw_mode) list_cert_raw (ctrl, hd, cert, fp, have_secret, ctrl->with_validation); else list_cert_std (ctrl, cert, fp, have_secret, ctrl->with_validation); es_putc ('\n', fp); } } ksba_cert_release (lastcert); lastcert = cert; cert = NULL; } if (gpg_err_code (rc) == GPG_ERR_EOF || rc == -1 ) rc = 0; if (rc) log_error ("keydb_search failed: %s\n", gpg_strerror (rc)); leave: ksba_cert_release (cert); ksba_cert_release (lastcert); xfree (desc); keydb_release (hd); return rc; } static void list_external_cb (void *cb_value, ksba_cert_t cert) { struct list_external_parm_s *parm = cb_value; if (keydb_store_cert (parm->ctrl, cert, 1, NULL)) log_error ("error storing certificate as ephemeral\n"); if (parm->print_header) { const char *resname = "[external keys]"; int i; es_fprintf (parm->fp, "%s\n", resname ); for (i=strlen(resname); i; i-- ) es_putc('-', parm->fp); es_putc ('\n', parm->fp); parm->print_header = 0; } if (parm->with_colons) list_cert_colon (parm->ctrl, cert, 0, parm->fp, 0); else if (parm->with_chain) list_cert_chain (parm->ctrl, NULL, cert, parm->raw_mode, parm->fp, 0); else { if (parm->raw_mode) list_cert_raw (parm->ctrl, NULL, cert, parm->fp, 0, 0); else list_cert_std (parm->ctrl, cert, parm->fp, 0, 0); es_putc ('\n', parm->fp); } } /* List external keys similar to internal one. Note: mode does not make sense here because it would be unwise to list external secret keys */ static gpg_error_t list_external_keys (ctrl_t ctrl, strlist_t names, estream_t fp, int raw_mode) { int rc; struct list_external_parm_s parm; parm.fp = fp; parm.ctrl = ctrl, parm.print_header = ctrl->no_server; parm.with_colons = ctrl->with_colons; parm.with_chain = ctrl->with_chain; parm.raw_mode = raw_mode; rc = gpgsm_dirmngr_lookup (ctrl, names, NULL, 0, list_external_cb, &parm); if (gpg_err_code (rc) == GPG_ERR_EOF || rc == -1 || gpg_err_code (rc) == GPG_ERR_NOT_FOUND) rc = 0; /* "Not found" is not an error here. */ if (rc) log_error ("listing external keys failed: %s\n", gpg_strerror (rc)); return rc; } /* List all keys or just the key given as NAMES. MODE controls the operation mode: Bit 0-2: 0 = list all public keys but don't flag secret ones 1 = list only public keys 2 = list only secret keys 3 = list secret and public keys Bit 6: list internal keys Bit 7: list external keys Bit 8: Do a raw format dump. */ gpg_error_t gpgsm_list_keys (ctrl_t ctrl, strlist_t names, estream_t fp, unsigned int mode) { gpg_error_t err = 0; if ((mode & (1<<6))) err = list_internal_keys (ctrl, names, fp, (mode & 3), (mode&256)); if (!err && (mode & (1<<7))) err = list_external_keys (ctrl, names, fp, (mode&256)); return err; } diff --git a/sm/minip12.c b/sm/minip12.c index 1e8a49eb5..26d77f426 100644 --- a/sm/minip12.c +++ b/sm/minip12.c @@ -1,2853 +1,2852 @@ /* minip12.c - A minimal pkcs-12 implementation. * Copyright (C) 2002, 2003, 2004, 2006, 2011 Free Software Foundation, Inc. * Copyright (C) 2014 Werner Koch * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /* 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 #include #endif #include #include #include -#include #include #include #include #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 #define DIM(v) (sizeof(v)/sizeof((v)[0])) #endif static unsigned char const oid_data[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01 }; static unsigned char const oid_encryptedData[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x06 }; static unsigned char const oid_pkcs_12_keyBag[11] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x0A, 0x01, 0x01 }; static unsigned char const oid_pkcs_12_pkcs_8ShroudedKeyBag[11] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x0A, 0x01, 0x02 }; static unsigned char const oid_pkcs_12_CertBag[11] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x0A, 0x01, 0x03 }; static unsigned char const oid_pkcs_12_CrlBag[11] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x0A, 0x01, 0x04 }; static unsigned char const oid_pbeWithSHAAnd3_KeyTripleDES_CBC[10] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x03 }; static unsigned char const oid_pbeWithSHAAnd40BitRC2_CBC[10] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x06 }; static unsigned char const oid_x509Certificate_for_pkcs_12[10] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x16, 0x01 }; static unsigned char const oid_pkcs5PBKDF2[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x05, 0x0C }; static unsigned char const oid_pkcs5PBES2[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x05, 0x0D }; static unsigned char const oid_aes128_CBC[9] = { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x02 }; 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, 0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x03, 0x30, 0x0E, 0x04, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x02, 0x02, 0x08, 0x00 }; #define DATA_3DESITER2048_SALT_OFF 18 static unsigned char const data_rc2iter2048[30] = { 0x30, 0x1C, 0x06, 0x0A, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x06, 0x30, 0x0E, 0x04, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x02, 0x02, 0x08, 0x00 }; #define DATA_RC2ITER2048_SALT_OFF 18 static unsigned char const data_mactemplate[51] = { 0x30, 0x31, 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x04, 0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x02, 0x02, 0x08, 0x00 }; #define DATA_MACTEMPLATE_MAC_OFF 17 #define DATA_MACTEMPLATE_SALT_OFF 39 static unsigned char const data_attrtemplate[106] = { 0x31, 0x7c, 0x30, 0x55, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x14, 0x31, 0x48, 0x1e, 0x46, 0x00, 0x47, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x50, 0x00, 0x47, 0x00, 0x20, 0x00, 0x65, 0x00, 0x78, 0x00, 0x70, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x74, 0x00, 0x65, 0x00, 0x64, 0x00, 0x20, 0x00, 0x63, 0x00, 0x65, 0x00, 0x72, 0x00, 0x74, 0x00, 0x69, 0x00, 0x66, 0x00, 0x69, 0x00, 0x63, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x20, 0x00, 0x66, 0x00, 0x66, 0x00, 0x66, 0x00, 0x66, 0x00, 0x66, 0x00, 0x66, 0x00, 0x66, 0x00, 0x66, 0x30, 0x23, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x15, 0x31, 0x16, 0x04, 0x14 }; /* Need to append SHA-1 digest. */ #define DATA_ATTRTEMPLATE_KEYID_OFF 73 struct buffer_s { unsigned char *buffer; size_t length; }; struct tag_info { int class; int is_constructed; unsigned long tag; unsigned long length; /* length part of the TLV */ int nhdr; int ndef; /* It is an indefinite length */ }; /* 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 exhaust the length of the provided buffer. */ static int parse_tag (unsigned char const **buffer, size_t *size, struct tag_info *ti) { int c; unsigned long tag; const unsigned char *buf = *buffer; size_t length = *size; ti->length = 0; ti->ndef = 0; ti->nhdr = 0; /* Get the tag */ if (!length) return -1; /* premature eof */ c = *buf++; length--; ti->nhdr++; ti->class = (c & 0xc0) >> 6; ti->is_constructed = !!(c & 0x20); tag = c & 0x1f; if (tag == 0x1f) { tag = 0; do { tag <<= 7; if (!length) return -1; /* premature eof */ c = *buf++; length--; ti->nhdr++; tag |= c & 0x7f; } while (c & 0x80); } ti->tag = tag; /* Get the length */ if (!length) return -1; /* prematureeof */ c = *buf++; length--; ti->nhdr++; if ( !(c & 0x80) ) ti->length = c; else if (c == 0x80) ti->ndef = 1; else if (c == 0xff) return -1; /* forbidden length value */ else { unsigned long len = 0; int count = c & 0x7f; for (; count; count--) { len <<= 8; if (!length) return -1; /* premature_eof */ c = *buf++; length--; ti->nhdr++; len |= c & 0xff; } ti->length = len; } if (ti->class == CLASS_UNIVERSAL && !ti->tag) ti->length = 0; if (ti->length > length) return -1; /* data larger than buffer. */ *buffer = buf; *size = length; return 0; } /* Given an ASN.1 chunk of a structure like: 24 NDEF: OCTET STRING -- This is not passed to us 04 1: OCTET STRING -- INPUT point s to here : 30 04 1: OCTET STRING : 80 [...] 04 2: OCTET STRING : 00 00 : } -- This denotes a Null tag and are the last -- two bytes in INPUT. Create a new buffer with the content of that octet string. INPUT is the original buffer with a length as stored at LENGTH. Returns NULL on error or a new malloced buffer with the length of this new buffer stored at LENGTH and the number of bytes parsed from input are added to the value stored at INPUT_CONSUMED. INPUT_CONSUMED is allowed to be passed as NULL if the caller is not interested in this value. */ static unsigned char * cram_octet_string (const unsigned char *input, size_t *length, size_t *input_consumed) { const unsigned char *s = input; size_t n = *length; unsigned char *output, *d; struct tag_info ti; /* Allocate output buf. We know that it won't be longer than the input buffer. */ d = output = gcry_malloc (n); if (!output) goto bailout; for (;;) { if (parse_tag (&s, &n, &ti)) goto bailout; if (ti.class == CLASS_UNIVERSAL && ti.tag == TAG_OCTET_STRING && !ti.ndef && !ti.is_constructed) { memcpy (d, s, ti.length); s += ti.length; d += ti.length; n -= ti.length; } else if (ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed) break; /* Ready */ else goto bailout; } *length = d - output; if (input_consumed) *input_consumed += s - input; return output; bailout: if (input_consumed) *input_consumed += s - input; gcry_free (output); return NULL; } static int string_to_key (int id, char *salt, size_t saltlen, int iter, const char *pw, int req_keylen, unsigned char *keybuf) { int rc, i, j; gcry_md_hd_t md; gcry_mpi_t num_b1 = NULL; int pwlen; unsigned char hash[20], buf_b[64], buf_i[128], *p; size_t cur_keylen; size_t n; cur_keylen = 0; pwlen = strlen (pw); if (pwlen > 63/2) { log_error ("password too long\n"); return -1; } if (saltlen < 8) { log_error ("salt too short\n"); return -1; } /* Store salt and password in BUF_I */ p = buf_i; for(i=0; i < 64; i++) *p++ = salt [i%saltlen]; for(i=j=0; i < 64; i += 2) { *p++ = 0; *p++ = pw[j]; if (++j > pwlen) /* Note, that we include the trailing zero */ j = 0; } for (;;) { rc = gcry_md_open (&md, GCRY_MD_SHA1, 0); if (rc) { log_error ( "gcry_md_open failed: %s\n", gpg_strerror (rc)); return rc; } for(i=0; i < 64; i++) gcry_md_putc (md, id); gcry_md_write (md, buf_i, 128); memcpy (hash, gcry_md_read (md, 0), 20); gcry_md_close (md); for (i=1; i < iter; i++) gcry_md_hash_buffer (GCRY_MD_SHA1, hash, hash, 20); for (i=0; i < 20 && cur_keylen < req_keylen; i++) keybuf[cur_keylen++] = hash[i]; if (cur_keylen == req_keylen) { gcry_mpi_release (num_b1); return 0; /* ready */ } /* need more bytes. */ for(i=0; i < 64; i++) buf_b[i] = hash[i % 20]; rc = gcry_mpi_scan (&num_b1, GCRYMPI_FMT_USG, buf_b, 64, &n); if (rc) { log_error ( "gcry_mpi_scan failed: %s\n", gpg_strerror (rc)); return -1; } gcry_mpi_add_ui (num_b1, num_b1, 1); for (i=0; i < 128; i += 64) { gcry_mpi_t num_ij; rc = gcry_mpi_scan (&num_ij, GCRYMPI_FMT_USG, buf_i + i, 64, &n); if (rc) { log_error ( "gcry_mpi_scan failed: %s\n", gpg_strerror (rc)); return -1; } gcry_mpi_add (num_ij, num_ij, num_b1); gcry_mpi_clear_highbit (num_ij, 64*8); rc = gcry_mpi_print (GCRYMPI_FMT_USG, buf_i + i, 64, &n, num_ij); if (rc) { log_error ( "gcry_mpi_print failed: %s\n", gpg_strerror (rc)); return -1; } gcry_mpi_release (num_ij); } } } static int set_key_iv (gcry_cipher_hd_t chd, char *salt, size_t saltlen, int iter, const char *pw, int keybytes) { 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); if (rc) { log_error ( "gcry_cipher_setkey failed: %s\n", gpg_strerror (rc)); return -1; } if (string_to_key (2, salt, saltlen, iter, pw, 8, keybuf)) return -1; rc = gcry_cipher_setiv (chd, keybuf, 8); if (rc) { log_error ("gcry_cipher_setiv failed: %s\n", gpg_strerror (rc)); return -1; } return 0; } static int set_key_iv_pbes2 (gcry_cipher_hd_t chd, char *salt, size_t saltlen, int iter, const void *iv, size_t ivlen, const char *pw, int algo) { unsigned char *keybuf; size_t keylen; int rc; keylen = gcry_cipher_get_algo_keylen (algo); if (!keylen) return -1; keybuf = gcry_malloc_secure (keylen); if (!keybuf) return -1; rc = gcry_kdf_derive (pw, strlen (pw), GCRY_KDF_PBKDF2, GCRY_MD_SHA1, salt, saltlen, iter, keylen, keybuf); if (rc) { log_error ("gcry_kdf_derive failed: %s\n", gpg_strerror (rc)); gcry_free (keybuf); return -1; } rc = gcry_cipher_setkey (chd, keybuf, keylen); gcry_free (keybuf); if (rc) { log_error ("gcry_cipher_setkey failed: %s\n", gpg_strerror (rc)); return -1; } rc = gcry_cipher_setiv (chd, iv, ivlen); if (rc) { log_error ("gcry_cipher_setiv failed: %s\n", gpg_strerror (rc)); return -1; } return 0; } static void crypt_block (unsigned char *buffer, size_t length, char *salt, size_t saltlen, int iter, const void *iv, size_t ivlen, const char *pw, int cipher_algo, int encrypt) { gcry_cipher_hd_t chd; int rc; rc = gcry_cipher_open (&chd, cipher_algo, GCRY_CIPHER_MODE_CBC, 0); if (rc) { log_error ( "gcry_cipher_open failed: %s\n", gpg_strerror(rc)); wipememory (buffer, length); return; } if (cipher_algo == GCRY_CIPHER_AES128 ? set_key_iv_pbes2 (chd, salt, saltlen, iter, iv, ivlen, pw, cipher_algo) : set_key_iv (chd, salt, saltlen, iter, pw, cipher_algo == GCRY_CIPHER_RFC2268_40? 5:24)) { wipememory (buffer, length); goto leave; } rc = encrypt? gcry_cipher_encrypt (chd, buffer, length, NULL, 0) : gcry_cipher_decrypt (chd, buffer, length, NULL, 0); if (rc) { wipememory (buffer, length); log_error ("%scrytion failed (%zu bytes): %s\n", encrypt?"en":"de", length, gpg_strerror (rc)); goto leave; } leave: gcry_cipher_close (chd); } /* Decrypt a block of data and try several encodings of the key. CIPHERTEXT is the encrypted data of size LENGTH bytes; PLAINTEXT is a buffer of the same size to receive the decryption result. SALT, SALTLEN, ITER and PW are the information required for decryption and CIPHER_ALGO is the algorithm id to use. CHECK_FNC is a function called with the plaintext and used to check whether the decryption succeeded; i.e. that a correct passphrase has been given. That function shall return true if the decryption has likely succeeded. */ static void decrypt_block (const void *ciphertext, unsigned char *plaintext, size_t length, char *salt, size_t saltlen, int iter, const void *iv, size_t ivlen, const char *pw, int cipher_algo, int (*check_fnc) (const void *, size_t)) { static const char * const charsets[] = { "", /* No conversion - use the UTF-8 passphrase direct. */ "ISO-8859-1", "ISO-8859-15", "ISO-8859-2", "ISO-8859-3", "ISO-8859-4", "ISO-8859-5", "ISO-8859-6", "ISO-8859-7", "ISO-8859-8", "ISO-8859-9", "KOI8-R", "IBM437", "IBM850", "EUC-JP", "BIG5", NULL }; int charsetidx = 0; char *convertedpw = NULL; /* Malloced and converted password or NULL. */ size_t convertedpwsize = 0; /* Allocated length. */ for (charsetidx=0; charsets[charsetidx]; charsetidx++) { if (*charsets[charsetidx]) { jnlib_iconv_t cd; const char *inptr; char *outptr; size_t inbytes, outbytes; if (!convertedpw) { /* We assume one byte encodings. Thus we can allocate the buffer of the same size as the original passphrase; the result will actually be shorter then. */ convertedpwsize = strlen (pw) + 1; convertedpw = gcry_malloc_secure (convertedpwsize); if (!convertedpw) { log_info ("out of secure memory while" " converting passphrase\n"); break; /* Give up. */ } } cd = jnlib_iconv_open (charsets[charsetidx], "utf-8"); if (cd == (jnlib_iconv_t)(-1)) continue; inptr = pw; inbytes = strlen (pw); outptr = convertedpw; outbytes = convertedpwsize - 1; if ( jnlib_iconv (cd, (const char **)&inptr, &inbytes, &outptr, &outbytes) == (size_t)-1) { jnlib_iconv_close (cd); continue; } *outptr = 0; jnlib_iconv_close (cd); log_info ("decryption failed; trying charset '%s'\n", charsets[charsetidx]); } memcpy (plaintext, ciphertext, length); crypt_block (plaintext, length, salt, saltlen, iter, iv, ivlen, convertedpw? convertedpw:pw, cipher_algo, 0); if (check_fnc (plaintext, length)) break; /* Decryption succeeded. */ } gcry_free (convertedpw); } /* Return true if the decryption of an bag_encrypted_data object has likely succeeded. */ static int bag_decrypted_data_p (const void *plaintext, size_t length) { struct tag_info ti; const unsigned char *p = plaintext; size_t n = length; /* { */ /* # warning debug code is enabled */ /* FILE *fp = fopen ("tmp-minip12-plain-data.der", "wb"); */ /* if (!fp || fwrite (p, n, 1, fp) != 1) */ /* exit (2); */ /* fclose (fp); */ /* } */ if (parse_tag (&p, &n, &ti)) return 0; if (ti.class || ti.tag != TAG_SEQUENCE) return 0; if (parse_tag (&p, &n, &ti)) return 0; 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) { struct tag_info ti; const unsigned char *p = buffer; const unsigned char *p_start = buffer; size_t n = length; const char *where; char salt[20]; size_t saltlen; 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; if (r_result) *r_result = NULL; where = "start"; 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.tag != TAG_SEQUENCE) goto bailout; where = "bag.encryptedData.version"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.tag != TAG_INTEGER || ti.length != 1 || *p != 0) goto bailout; p++; n--; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.tag != TAG_SEQUENCE) goto bailout; where = "bag.encryptedData.data"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.tag != TAG_OBJECT_ID || ti.length != DIM(oid_data) || memcmp (p, oid_data, DIM(oid_data))) goto bailout; p += DIM(oid_data); n -= DIM(oid_data); where = "bag.encryptedData.keyinfo"; 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_pbeWithSHAAnd40BitRC2_CBC) && !memcmp (p, oid_pbeWithSHAAnd40BitRC2_CBC, DIM(oid_pbeWithSHAAnd40BitRC2_CBC))) { p += DIM(oid_pbeWithSHAAnd40BitRC2_CBC); n -= DIM(oid_pbeWithSHAAnd40BitRC2_CBC); } else if (!ti.class && ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC) && !memcmp (p, oid_pbeWithSHAAnd3_KeyTripleDES_CBC, DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC))) { p += DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC); n -= DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC); is_3des = 1; } else if (!ti.class && ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_pkcs5PBES2) && !memcmp (p, oid_pkcs5PBES2, ti.length)) { p += ti.length; n -= ti.length; is_pbes2 = 1; } else goto bailout; if (is_pbes2) { where = "pkcs5PBES2-params"; 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; if (parse_tag (&p, &n, &ti)) goto bailout; if (!(!ti.class && ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_pkcs5PBKDF2) && !memcmp (p, oid_pkcs5PBKDF2, ti.length))) goto bailout; /* Not PBKDF2. */ p += ti.length; n -= ti.length; 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_OCTET_STRING && ti.length >= 8 && ti.length < sizeof salt)) goto bailout; /* No salt or unsupported length. */ saltlen = ti.length; memcpy (salt, p, saltlen); p += saltlen; n -= saltlen; if (parse_tag (&p, &n, &ti)) goto bailout; if (!(!ti.class && ti.tag == TAG_INTEGER && ti.length)) goto bailout; /* No valid iteration count. */ for (iter=0; ti.length; ti.length--) { iter <<= 8; iter |= (*p++) & 0xff; n--; } /* Note: We don't support the optional parameters but assume that the algorithmIdentifier follows. */ 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_aes128_CBC) && !memcmp (p, oid_aes128_CBC, ti.length))) goto bailout; /* Not AES-128. */ p += ti.length; n -= ti.length; if (parse_tag (&p, &n, &ti)) goto bailout; if (!(!ti.class && ti.tag == TAG_OCTET_STRING && ti.length == sizeof iv)) goto bailout; /* Bad IV. */ memcpy (iv, p, sizeof iv); p += sizeof iv; n -= sizeof iv; } else { where = "rc2or3des-params"; 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_OCTET_STRING || ti.length < 8 || ti.length > 20 ) goto bailout; saltlen = ti.length; memcpy (salt, p, saltlen); p += saltlen; n -= saltlen; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_INTEGER || !ti.length ) goto bailout; for (iter=0; ti.length; ti.length--) { iter <<= 8; iter |= (*p++) & 0xff; n--; } } where = "rc2or3desoraes-ciphertext"; if (parse_tag (&p, &n, &ti)) goto bailout; consumed = p - p_start; if (ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed && ti.ndef) { /* Mozilla exported certs now come with single byte chunks of octet strings. (Mozilla Firefox 1.0.4). Arghh. */ where = "cram-rc2or3des-ciphertext"; 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. */ ti.length = n; } else if (ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.length ) ; else goto bailout; log_info ("%lu bytes of %s encrypted text\n",ti.length, is_pbes2?"AES128":is_3des?"3DES":"RC2"); plain = gcry_malloc_secure (ti.length); if (!plain) { log_error ("error allocating decryption buffer\n"); goto bailout; } decrypt_block (p, plain, ti.length, salt, saltlen, iter, iv, is_pbes2?16:0, pw, is_pbes2 ? GCRY_CIPHER_AES128 : is_3des ? GCRY_CIPHER_3DES : GCRY_CIPHER_RFC2268_40, bag_decrypted_data_p); n = ti.length; startoffset = 0; p_start = p = plain; where = "outer.outer.seq"; if (parse_tag (&p, &n, &ti)) { bad_pass = 1; goto bailout; } if (ti.class || ti.tag != TAG_SEQUENCE) { bad_pass = 1; goto bailout; } if (parse_tag (&p, &n, &ti)) { bad_pass = 1; goto bailout; } /* Loop over all certificates inside the bag. */ while (n) { int iscrlbag = 0; int iskeybag = 0; where = "certbag.nextcert"; if (ti.class || ti.tag != TAG_SEQUENCE) goto bailout; where = "certbag.objectidentifier"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_OBJECT_ID) goto bailout; 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); } else if ( ti.length == DIM(oid_pkcs_12_CrlBag) && !memcmp (p, oid_pkcs_12_CrlBag, DIM(oid_pkcs_12_CrlBag))) { p += DIM(oid_pkcs_12_CrlBag); n -= DIM(oid_pkcs_12_CrlBag); iscrlbag = 1; } else if ( ti.length == DIM(oid_pkcs_12_keyBag) && !memcmp (p, oid_pkcs_12_keyBag, DIM(oid_pkcs_12_keyBag))) { /* The TrustedMIME plugin for MS Outlook started to create files with just one outer 3DES encrypted container and inside the certificates as well as the key. */ p += DIM(oid_pkcs_12_keyBag); n -= DIM(oid_pkcs_12_keyBag); iskeybag = 1; } else goto bailout; where = "certbag.before.certheader"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class != CLASS_CONTEXT || ti.tag) goto bailout; if (iscrlbag) { log_info ("skipping unsupported crlBag\n"); p += ti.length; n -= ti.length; } else if (iskeybag && (result || !r_result)) { log_info ("one keyBag already processed; skipping this one\n"); p += ti.length; n -= ti.length; } else if (iskeybag) { int len; log_info ("processing simple keyBag\n"); /* Fixme: This code is duplicated from parse_bag_data. */ 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 || ti.length != 1 || *p) goto bailout; p++; n--; if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE) goto bailout; len = ti.length; if (parse_tag (&p, &n, &ti)) goto bailout; 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))) goto bailout; p += DIM (oid_rsaEncryption); n -= DIM (oid_rsaEncryption); if (len < ti.length) goto bailout; len -= ti.length; if (n < len) goto bailout; p += len; n -= len; if ( parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_OCTET_STRING) goto bailout; if ( parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE) goto bailout; len = ti.length; result = gcry_calloc (10, sizeof *result); if (!result) { log_error ( "error allocating result array\n"); goto bailout; } result_count = 0; where = "reading.keybag.key-parameters"; for (result_count = 0; len && result_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 (!result_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, ti.length, NULL); if (rc) { log_error ("error parsing key parameter: %s\n", gpg_strerror (rc)); goto bailout; } result_count++; } p += ti.length; n -= ti.length; } if (len) goto bailout; } else { log_info ("processing certBag\n"); 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); 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. */ if (certcb) certcb (certcbarg, p, ti.length); p += ti.length; n -= ti.length; } /* Ugly hack to cope with the padding: Forget about the rest if that is less or equal to the cipher's block length. We can reasonable assume that all valid data will be longer than just one block. */ if (n <= (is_pbes2? 16:8)) n = 0; /* Skip the optional SET with the pkcs12 cert attributes. */ if (n) { where = "bag.attributes"; if (parse_tag (&p, &n, &ti)) goto bailout; if (!ti.class && ti.tag == TAG_SEQUENCE) ; /* No attributes. */ else if (!ti.class && ti.tag == TAG_SET && !ti.ndef) { /* The optional SET. */ p += ti.length; n -= ti.length; if (n <= (is_pbes2?16:8)) n = 0; if (n && parse_tag (&p, &n, &ti)) goto bailout; } else goto bailout; } } if (r_consumed) *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) { /* 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; } /* Return true if the decryption of a bag_data object has likely succeeded. */ static int bag_data_p (const void *plaintext, size_t length) { struct tag_info ti; const unsigned char *p = plaintext; size_t n = length; /* { */ /* # warning debug code is enabled */ /* FILE *fp = fopen ("tmp-minip12-plain-key.der", "wb"); */ /* if (!fp || fwrite (p, n, 1, fp) != 1) */ /* exit (2); */ /* fclose (fp); */ /* } */ if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE) return 0; if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER || ti.length != 1 || *p) return 0; return 1; } static gcry_mpi_t * parse_bag_data (const unsigned char *buffer, size_t length, int startoffset, size_t *r_consumed, char **r_curve, const char *pw) { int rc; struct tag_info ti; const unsigned char *p = buffer; const unsigned char *p_start = buffer; size_t n = length; const char *where; char salt[20]; size_t saltlen; char iv[16]; 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; char *curve = NULL; where = "start"; 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 = "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"; 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_SEQUENCE) goto bailout; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class == 0 && ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC) && !memcmp (p, oid_pbeWithSHAAnd3_KeyTripleDES_CBC, DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC))) { p += DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC); n -= DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC); } else if (ti.class == 0 && ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_pkcs5PBES2) && !memcmp (p, oid_pkcs5PBES2, DIM(oid_pkcs5PBES2))) { p += DIM(oid_pkcs5PBES2); n -= DIM(oid_pkcs5PBES2); is_pbes2 = 1; } else goto bailout; if (is_pbes2) { where = "pkcs5PBES2-params"; 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; if (parse_tag (&p, &n, &ti)) goto bailout; if (!(!ti.class && ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_pkcs5PBKDF2) && !memcmp (p, oid_pkcs5PBKDF2, ti.length))) goto bailout; /* Not PBKDF2. */ p += ti.length; n -= ti.length; 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_OCTET_STRING && ti.length >= 8 && ti.length < sizeof salt)) goto bailout; /* No salt or unsupported length. */ saltlen = ti.length; memcpy (salt, p, saltlen); p += saltlen; n -= saltlen; if (parse_tag (&p, &n, &ti)) goto bailout; if (!(!ti.class && ti.tag == TAG_INTEGER && ti.length)) goto bailout; /* No valid iteration count. */ for (iter=0; ti.length; ti.length--) { iter <<= 8; iter |= (*p++) & 0xff; n--; } /* Note: We don't support the optional parameters but assume that the algorithmIdentifier follows. */ 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_aes128_CBC) && !memcmp (p, oid_aes128_CBC, ti.length))) goto bailout; /* Not AES-128. */ p += ti.length; n -= ti.length; if (parse_tag (&p, &n, &ti)) goto bailout; if (!(!ti.class && ti.tag == TAG_OCTET_STRING && ti.length == sizeof iv)) goto bailout; /* Bad IV. */ memcpy (iv, p, sizeof iv); p += sizeof iv; n -= sizeof iv; } else { where = "3des-params"; 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_OCTET_STRING || ti.length < 8 || ti.length > 20) goto bailout; saltlen = ti.length; memcpy (salt, p, saltlen); p += saltlen; n -= saltlen; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_INTEGER || !ti.length ) goto bailout; for (iter=0; ti.length; ti.length--) { iter <<= 8; iter |= (*p++) & 0xff; n--; } } where = "3desoraes-ciphertext"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.class || ti.tag != TAG_OCTET_STRING || !ti.length ) goto bailout; log_info ("%lu bytes of %s encrypted text\n", ti.length, is_pbes2? "AES128":"3DES"); plain = gcry_malloc_secure (ti.length); if (!plain) { log_error ("error allocating decryption buffer\n"); goto bailout; } consumed += p - p_start + ti.length; decrypt_block (p, plain, ti.length, salt, saltlen, iter, iv, is_pbes2? 16:0, pw, is_pbes2? GCRY_CIPHER_AES128 : GCRY_CIPHER_3DES, bag_data_p); n = ti.length; startoffset = 0; p_start = p = plain; where = "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 || ti.length != 1 || *p) goto bailout; p++; n--; if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE) goto bailout; len = ti.length; if (parse_tag (&p, &n, &ti)) goto bailout; if (len < ti.nhdr) goto bailout; len -= ti.nhdr; 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; curve = ksba_oid_to_str (p, ti.length); if (!curve) goto bailout; /* log_debug ("OID of curve is: %s\n", curve); */ p += ti.length; n -= ti.length; } else goto bailout; if (len < ti.length) goto bailout; len -= ti.length; if (n < len) goto bailout; p += len; n -= len; if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_OCTET_STRING) goto bailout; if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE) goto bailout; len = ti.length; result = gcry_calloc (10, sizeof *result); if (!result) { log_error ( "error allocating result array\n"); goto bailout; } result_count = 0; where = "reading.key-parameters"; if (curve) /* ECC case. */ { 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 (ti.length != 1 && *p != 1) { 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="); */ rc = gcry_mpi_scan (result, GCRYMPI_FMT_USG, p, ti.length, NULL); if (rc) { log_error ("error parsing key parameter: %s\n", gpg_strerror (rc)); goto bailout; } p += ti.length; n -= ti.length; len = 0; /* Skip the rest. */ } else /* RSA case */ { for (result_count=0; len && result_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 (!result_count && ti.length == 1 && !*p) ; /* ignore the very first one if it is a 0 */ else { 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++; } p += ti.length; n -= ti.length; } } if (len) goto bailout; goto leave; bailout: gcry_free (plain); if (result) { for (i=0; result[i]; i++) gcry_mpi_release (result[i]); gcry_free (result); } log_error ( "data error at \"%s\", offset %u\n", where, (unsigned int)((p - buffer) + startoffset)); result = NULL; leave: if (r_curve && result) { *r_curve = curve; curve = NULL; } else if (r_curve) *r_curve = NULL; ksba_free (curve); gcry_free (cram_buffer); if (r_consumed) *r_consumed = consumed; return result; } /* Parse a PKCS12 object and return an array of MPI representing the secret key parameters. This is a very limited implementation in 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. */ 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, char **r_curve) { struct tag_info ti; const unsigned char *p = buffer; const unsigned char *p_start = buffer; size_t n = length; const char *where; int bagseqlength, len; int bagseqndef, lenndef; gcry_mpi_t *result = NULL; unsigned char *cram_buffer = NULL; char *curve = NULL; *r_badpass = 0; where = "pfx"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.tag != TAG_SEQUENCE) goto bailout; where = "pfxVersion"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.tag != TAG_INTEGER || ti.length != 1 || *p != 3) goto bailout; p++; n--; where = "authSave"; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.tag != TAG_SEQUENCE) goto bailout; if (parse_tag (&p, &n, &ti)) goto bailout; if (ti.tag != TAG_OBJECT_ID || ti.length != DIM(oid_data) || memcmp (p, oid_data, DIM(oid_data))) goto bailout; p += DIM(oid_data); n -= DIM(oid_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 != 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 octet strings. (Mozilla Firefox 1.0.4). Arghh. */ where = "cram-bags"; cram_buffer = cram_octet_string ( p, &n, NULL); if (!cram_buffer) goto bailout; p = p_start = cram_buffer; } where = "bags"; if (parse_tag (&p, &n, &ti)) goto bailout; 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)); */ where = "bag-sequence"; if (parse_tag (&p, &n, &ti)) goto bailout; if (bagseqndef && ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed) break; /* Ready */ if (ti.class != CLASS_UNIVERSAL || ti.tag != TAG_SEQUENCE) goto bailout; if (!bagseqndef) { if (bagseqlength < ti.nhdr) goto bailout; bagseqlength -= ti.nhdr; if (bagseqlength < ti.length) goto bailout; bagseqlength -= ti.length; } lenndef = ti.ndef; len = ti.length; if (parse_tag (&p, &n, &ti)) goto bailout; if (lenndef) len = ti.nhdr; else len -= ti.nhdr; 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)) goto bailout; if (lenndef) len += consumed; } 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, &curve, pw); if (!result) goto bailout; if (lenndef) len += consumed; } } else { log_info ("unknown bag type - skipped\n"); p += ti.length; n -= ti.length; } if (len < 0 || len > n) goto bailout; p += len; n -= len; if (lenndef) { /* Need to skip the Null Tag. */ if (parse_tag (&p, &n, &ti)) goto bailout; if (!(ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed)) goto bailout; } } gcry_free (cram_buffer); *r_curve = curve; return result; bailout: log_error ("error at \"%s\", offset %u\n", where, (unsigned int)(p - p_start)); if (result) { int i; for (i=0; result[i]; i++) gcry_mpi_release (result[i]); gcry_free (result); } gcry_free (cram_buffer); gcry_free (curve); *r_curve = NULL; return NULL; } static size_t compute_tag_length (size_t n) { int needed = 0; if (n < 128) needed += 2; /* tag and one length byte */ else if (n < 256) needed += 3; /* tag, number of length bytes, 1 length byte */ else if (n < 65536) needed += 4; /* tag, number of length bytes, 2 length bytes */ else { log_error ("object too larger to encode\n"); return 0; } return needed; } static unsigned char * store_tag_length (unsigned char *p, int tag, size_t n) { if (tag == TAG_SEQUENCE) tag |= 0x20; /* constructed */ *p++ = tag; if (n < 128) *p++ = n; else if (n < 256) { *p++ = 0x81; *p++ = n; } else if (n < 65536) { *p++ = 0x82; *p++ = n >> 8; *p++ = n; } return p; } /* Create the final PKCS-12 object from the sequences contained in SEQLIST. PW is the password. That array is terminated with an NULL object. */ static unsigned char * create_final (struct buffer_s *sequences, const char *pw, size_t *r_length) { int i; size_t needed = 0; size_t len[8], n; unsigned char *macstart; size_t maclen; unsigned char *result, *p; size_t resultlen; char salt[8]; unsigned char keybuf[20]; gcry_md_hd_t md; int rc; int with_mac = 1; /* 9 steps to create the pkcs#12 Krampf. */ /* 8. The MAC. */ /* We add this at step 0. */ /* 7. All the buffers. */ for (i=0; sequences[i].buffer; i++) needed += sequences[i].length; /* 6. This goes into a sequences. */ len[6] = needed; n = compute_tag_length (needed); needed += n; /* 5. Encapsulate all in an octet string. */ len[5] = needed; n = compute_tag_length (needed); needed += n; /* 4. And tag it with [0]. */ len[4] = needed; n = compute_tag_length (needed); needed += n; /* 3. Prepend an data OID. */ needed += 2 + DIM (oid_data); /* 2. Put all into a sequences. */ len[2] = needed; n = compute_tag_length (needed); needed += n; /* 1. Prepend the version integer 3. */ needed += 3; /* 0. And the final outer sequence. */ if (with_mac) needed += DIM (data_mactemplate); len[0] = needed; n = compute_tag_length (needed); needed += n; /* Allocate a buffer. */ result = gcry_malloc (needed); if (!result) { log_error ("error allocating buffer\n"); return NULL; } p = result; /* 0. Store the very outer sequence. */ p = store_tag_length (p, TAG_SEQUENCE, len[0]); /* 1. Store the version integer 3. */ *p++ = TAG_INTEGER; *p++ = 1; *p++ = 3; /* 2. Store another sequence. */ p = store_tag_length (p, TAG_SEQUENCE, len[2]); /* 3. Store the data OID. */ p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_data)); memcpy (p, oid_data, DIM (oid_data)); p += DIM (oid_data); /* 4. Next comes a context tag. */ p = store_tag_length (p, 0xa0, len[4]); /* 5. And an octet string. */ p = store_tag_length (p, TAG_OCTET_STRING, len[5]); /* 6. And the inner sequence. */ macstart = p; p = store_tag_length (p, TAG_SEQUENCE, len[6]); /* 7. Append all the buffers. */ for (i=0; sequences[i].buffer; i++) { memcpy (p, sequences[i].buffer, sequences[i].length); p += sequences[i].length; } if (with_mac) { /* Intermezzo to compute the MAC. */ maclen = p - macstart; gcry_randomize (salt, 8, GCRY_STRONG_RANDOM); if (string_to_key (3, salt, 8, 2048, pw, 20, keybuf)) { gcry_free (result); return NULL; } rc = gcry_md_open (&md, GCRY_MD_SHA1, GCRY_MD_FLAG_HMAC); if (rc) { log_error ("gcry_md_open failed: %s\n", gpg_strerror (rc)); gcry_free (result); return NULL; } rc = gcry_md_setkey (md, keybuf, 20); if (rc) { log_error ("gcry_md_setkey failed: %s\n", gpg_strerror (rc)); gcry_md_close (md); gcry_free (result); return NULL; } gcry_md_write (md, macstart, maclen); /* 8. Append the MAC template and fix it up. */ memcpy (p, data_mactemplate, DIM (data_mactemplate)); memcpy (p + DATA_MACTEMPLATE_SALT_OFF, salt, 8); memcpy (p + DATA_MACTEMPLATE_MAC_OFF, gcry_md_read (md, 0), 20); p += DIM (data_mactemplate); gcry_md_close (md); } /* Ready. */ resultlen = p - result; if (needed != resultlen) log_debug ("length mismatch: %lu, %lu\n", (unsigned long)needed, (unsigned long)resultlen); *r_length = resultlen; return result; } /* Build a DER encoded SEQUENCE with the key: * * 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_rsa_key_sequence (gcry_mpi_t *kparms, int mode, size_t *r_length) { int rc, i; size_t needed, n; unsigned char *plain, *p; size_t plainlen; size_t outseqlen, oidseqlen, octstrlen, inseqlen; needed = 3; /* The version integer with value 0. */ for (i=0; kparms[i]; i++) { n = 0; rc = gcry_mpi_print (GCRYMPI_FMT_STD, NULL, 0, &n, kparms[i]); if (rc) { log_error ("error formatting parameter: %s\n", gpg_strerror (rc)); return NULL; } needed += n; n = compute_tag_length (n); if (!n) return NULL; needed += n; } if (i != 8) { log_error ("invalid parameters for p12_build\n"); return NULL; } /* Now this all goes into a sequence. */ inseqlen = needed; n = compute_tag_length (needed); if (!n) return NULL; needed += n; if (mode != 2) { /* Encapsulate all into an octet string. */ octstrlen = needed; n = compute_tag_length (needed); if (!n) return NULL; needed += n; /* Prepend the object identifier sequence. */ oidseqlen = 2 + DIM (oid_rsaEncryption) + 2; needed += 2 + oidseqlen; /* The version number. */ needed += 3; /* And finally put the whole thing into a sequence. */ outseqlen = needed; n = compute_tag_length (needed); if (!n) return NULL; needed += n; } /* allocate 8 extra bytes for padding */ plain = gcry_malloc_secure (needed+8); if (!plain) { log_error ("error allocating encryption buffer\n"); return NULL; } /* And now fill the plaintext buffer. */ p = plain; if (mode != 2) { p = store_tag_length (p, TAG_SEQUENCE, outseqlen); /* Store version. */ *p++ = TAG_INTEGER; *p++ = 1; *p++ = 0; /* Store object identifier sequence. */ p = store_tag_length (p, TAG_SEQUENCE, oidseqlen); p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_rsaEncryption)); memcpy (p, oid_rsaEncryption, DIM (oid_rsaEncryption)); p += DIM (oid_rsaEncryption); *p++ = TAG_NULL; *p++ = 0; /* Start with the octet string. */ p = store_tag_length (p, TAG_OCTET_STRING, octstrlen); } p = store_tag_length (p, TAG_SEQUENCE, inseqlen); /* Store the key parameters. */ *p++ = TAG_INTEGER; *p++ = 1; *p++ = 0; for (i=0; kparms[i]; i++) { n = 0; rc = gcry_mpi_print (GCRYMPI_FMT_STD, NULL, 0, &n, kparms[i]); if (rc) { log_error ("oops: error formatting parameter: %s\n", gpg_strerror (rc)); gcry_free (plain); return NULL; } p = store_tag_length (p, TAG_INTEGER, n); n = plain + needed - p; rc = gcry_mpi_print (GCRYMPI_FMT_STD, p, n, &n, kparms[i]); if (rc) { log_error ("oops: error storing parameter: %s\n", gpg_strerror (rc)); gcry_free (plain); return NULL; } p += n; } plainlen = p - plain; - assert (needed == plainlen); + log_assert (needed == plainlen); if (!mode) { /* Append some pad characters; we already allocated extra space. */ n = 8 - plainlen % 8; for (i=0; i < n; i++, plainlen++) *p++ = n; } *r_length = plainlen; return plain; } /* 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 pgublic 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, const unsigned char *sha1hash, const char *keyidstr, size_t *r_length) { size_t len[11], needed; unsigned char *p, *keybag; size_t keybaglen; /* Walk 11 steps down to collect the info: */ /* 10. The data goes into an octet string. */ needed = compute_tag_length (buflen); needed += buflen; /* 9. Prepend the algorithm identifier. */ needed += DIM (data_3desiter2048); /* 8. Put a sequence around. */ len[8] = needed; needed += compute_tag_length (needed); /* 7. Prepend a [0] tag. */ len[7] = needed; needed += compute_tag_length (needed); /* 6b. The attributes which are appended at the end. */ if (sha1hash) needed += DIM (data_attrtemplate) + 20; /* 6. Prepend the shroudedKeyBag OID. */ needed += 2 + DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag); /* 5+4. Put all into two sequences. */ len[5] = needed; needed += compute_tag_length ( needed); len[4] = needed; needed += compute_tag_length (needed); /* 3. This all goes into an octet string. */ len[3] = needed; needed += compute_tag_length (needed); /* 2. Prepend another [0] tag. */ len[2] = needed; needed += compute_tag_length (needed); /* 1. Prepend the data OID. */ needed += 2 + DIM (oid_data); /* 0. Prepend another sequence. */ len[0] = needed; needed += compute_tag_length (needed); /* Now that we have all length information, allocate a buffer. */ p = keybag = gcry_malloc (needed); if (!keybag) { log_error ("error allocating buffer\n"); return NULL; } /* Walk 11 steps up to store the data. */ /* 0. Store the first sequence. */ p = store_tag_length (p, TAG_SEQUENCE, len[0]); /* 1. Store the data OID. */ p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_data)); memcpy (p, oid_data, DIM (oid_data)); p += DIM (oid_data); /* 2. Store a [0] tag. */ p = store_tag_length (p, 0xa0, len[2]); /* 3. And an octet string. */ p = store_tag_length (p, TAG_OCTET_STRING, len[3]); /* 4+5. Two sequences. */ p = store_tag_length (p, TAG_SEQUENCE, len[4]); p = store_tag_length (p, TAG_SEQUENCE, len[5]); /* 6. Store the shroudedKeyBag OID. */ p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag)); memcpy (p, oid_pkcs_12_pkcs_8ShroudedKeyBag, DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag)); p += DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag); /* 7. Store a [0] tag. */ p = store_tag_length (p, 0xa0, len[7]); /* 8. Store a sequence. */ p = store_tag_length (p, TAG_SEQUENCE, len[8]); /* 9. Now for the pre-encoded algorithm identifier and the salt. */ memcpy (p, data_3desiter2048, DIM (data_3desiter2048)); memcpy (p + DATA_3DESITER2048_SALT_OFF, salt, 8); p += DIM (data_3desiter2048); /* 10. And the octet string with the encrypted data. */ p = store_tag_length (p, TAG_OCTET_STRING, buflen); memcpy (p, buffer, buflen); p += buflen; /* Append the attributes whose length we calculated at step 2b. */ if (sha1hash) { int i; memcpy (p, data_attrtemplate, DIM (data_attrtemplate)); for (i=0; i < 8; i++) p[DATA_ATTRTEMPLATE_KEYID_OFF+2*i+1] = keyidstr[i]; p += DIM (data_attrtemplate); memcpy (p, sha1hash, 20); p += 20; } keybaglen = p - keybag; if (needed != keybaglen) log_debug ("length mismatch: %lu, %lu\n", (unsigned long)needed, (unsigned long)keybaglen); *r_length = keybaglen; return keybag; } static unsigned char * build_cert_bag (unsigned char *buffer, size_t buflen, char *salt, size_t *r_length) { size_t len[9], needed; unsigned char *p, *certbag; size_t certbaglen; /* Walk 9 steps down to collect the info: */ /* 8. The data goes into an octet string. */ needed = compute_tag_length (buflen); needed += buflen; /* 7. The algorithm identifier. */ needed += DIM (data_rc2iter2048); /* 6. The data OID. */ needed += 2 + DIM (oid_data); /* 5. A sequence. */ len[5] = needed; needed += compute_tag_length ( needed); /* 4. An integer. */ needed += 3; /* 3. A sequence. */ len[3] = needed; needed += compute_tag_length (needed); /* 2. A [0] tag. */ len[2] = needed; needed += compute_tag_length (needed); /* 1. The encryptedData OID. */ needed += 2 + DIM (oid_encryptedData); /* 0. The first sequence. */ len[0] = needed; needed += compute_tag_length (needed); /* Now that we have all length information, allocate a buffer. */ p = certbag = gcry_malloc (needed); if (!certbag) { log_error ("error allocating buffer\n"); return NULL; } /* Walk 9 steps up to store the data. */ /* 0. Store the first sequence. */ p = store_tag_length (p, TAG_SEQUENCE, len[0]); /* 1. Store the encryptedData OID. */ p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_encryptedData)); memcpy (p, oid_encryptedData, DIM (oid_encryptedData)); p += DIM (oid_encryptedData); /* 2. Store a [0] tag. */ p = store_tag_length (p, 0xa0, len[2]); /* 3. Store a sequence. */ p = store_tag_length (p, TAG_SEQUENCE, len[3]); /* 4. Store the integer 0. */ *p++ = TAG_INTEGER; *p++ = 1; *p++ = 0; /* 5. Store a sequence. */ p = store_tag_length (p, TAG_SEQUENCE, len[5]); /* 6. Store the data OID. */ p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_data)); memcpy (p, oid_data, DIM (oid_data)); p += DIM (oid_data); /* 7. Now for the pre-encoded algorithm identifier and the salt. */ memcpy (p, data_rc2iter2048, DIM (data_rc2iter2048)); memcpy (p + DATA_RC2ITER2048_SALT_OFF, salt, 8); p += DIM (data_rc2iter2048); /* 8. And finally the [0] tag with the encrypted data. */ p = store_tag_length (p, 0x80, buflen); memcpy (p, buffer, buflen); p += buflen; certbaglen = p - certbag; if (needed != certbaglen) log_debug ("length mismatch: %lu, %lu\n", (unsigned long)needed, (unsigned long)certbaglen); *r_length = certbaglen; return certbag; } static unsigned char * build_cert_sequence (const unsigned char *buffer, size_t buflen, const unsigned char *sha1hash, const char *keyidstr, size_t *r_length) { size_t len[8], needed, n; unsigned char *p, *certseq; size_t certseqlen; int i; - assert (strlen (keyidstr) == 8); + log_assert (strlen (keyidstr) == 8); /* Walk 8 steps down to collect the info: */ /* 7. The data goes into an octet string. */ needed = compute_tag_length (buflen); needed += buflen; /* 6. A [0] tag. */ len[6] = needed; needed += compute_tag_length (needed); /* 5. An OID. */ needed += 2 + DIM (oid_x509Certificate_for_pkcs_12); /* 4. A sequence. */ len[4] = needed; needed += compute_tag_length (needed); /* 3. A [0] tag. */ len[3] = needed; needed += compute_tag_length (needed); /* 2b. The attributes which are appended at the end. */ if (sha1hash) needed += DIM (data_attrtemplate) + 20; /* 2. An OID. */ needed += 2 + DIM (oid_pkcs_12_CertBag); /* 1. A sequence. */ len[1] = needed; needed += compute_tag_length (needed); /* 0. The first sequence. */ len[0] = needed; needed += compute_tag_length (needed); /* Now that we have all length information, allocate a buffer. */ p = certseq = gcry_malloc (needed + 8 /*(for padding)*/); if (!certseq) { log_error ("error allocating buffer\n"); return NULL; } /* Walk 8 steps up to store the data. */ /* 0. Store the first sequence. */ p = store_tag_length (p, TAG_SEQUENCE, len[0]); /* 1. Store the second sequence. */ p = store_tag_length (p, TAG_SEQUENCE, len[1]); /* 2. Store the pkcs12-cert-bag OID. */ p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_pkcs_12_CertBag)); memcpy (p, oid_pkcs_12_CertBag, DIM (oid_pkcs_12_CertBag)); p += DIM (oid_pkcs_12_CertBag); /* 3. Store a [0] tag. */ p = store_tag_length (p, 0xa0, len[3]); /* 4. Store a sequence. */ p = store_tag_length (p, TAG_SEQUENCE, len[4]); /* 5. Store the x509Certificate OID. */ p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_x509Certificate_for_pkcs_12)); memcpy (p, oid_x509Certificate_for_pkcs_12, DIM (oid_x509Certificate_for_pkcs_12)); p += DIM (oid_x509Certificate_for_pkcs_12); /* 6. Store a [0] tag. */ p = store_tag_length (p, 0xa0, len[6]); /* 7. And the octet string with the actual certificate. */ p = store_tag_length (p, TAG_OCTET_STRING, buflen); memcpy (p, buffer, buflen); p += buflen; /* Append the attributes whose length we calculated at step 2b. */ if (sha1hash) { memcpy (p, data_attrtemplate, DIM (data_attrtemplate)); for (i=0; i < 8; i++) p[DATA_ATTRTEMPLATE_KEYID_OFF+2*i+1] = keyidstr[i]; p += DIM (data_attrtemplate); memcpy (p, sha1hash, 20); p += 20; } certseqlen = p - certseq; if (needed != certseqlen) log_debug ("length mismatch: %lu, %lu\n", (unsigned long)needed, (unsigned long)certseqlen); /* Append some pad characters; we already allocated extra space. */ n = 8 - certseqlen % 8; for (i=0; i < n; i++, certseqlen++) *p++ = n; *r_length = certseqlen; return certseq; } /* Expect the RSA key parameters in KPARMS and a password in PW. Create a PKCS structure from it and return it as well as the length in R_LENGTH; return NULL in case of an error. If CHARSET is not NULL, re-encode PW to that character set. */ unsigned char * p12_build (gcry_mpi_t *kparms, const void *cert, size_t certlen, const char *pw, const char *charset, size_t *r_length) { unsigned char *buffer = NULL; size_t n, buflen; char salt[8]; struct buffer_s seqlist[3]; int seqlistidx = 0; unsigned char sha1hash[20]; char keyidstr[8+1]; char *pwbuf = NULL; size_t pwbufsize = 0; n = buflen = 0; /* (avoid compiler warning). */ memset (sha1hash, 0, 20); *keyidstr = 0; if (charset && pw && *pw) { jnlib_iconv_t cd; const char *inptr; char *outptr; size_t inbytes, outbytes; /* We assume that the converted passphrase is at max 2 times longer than its utf-8 encoding. */ pwbufsize = strlen (pw)*2 + 1; pwbuf = gcry_malloc_secure (pwbufsize); if (!pwbuf) { log_error ("out of secure memory while converting passphrase\n"); goto failure; } cd = jnlib_iconv_open (charset, "utf-8"); if (cd == (jnlib_iconv_t)(-1)) { log_error ("can't convert passphrase to" " requested charset '%s': %s\n", charset, strerror (errno)); goto failure; } inptr = pw; inbytes = strlen (pw); outptr = pwbuf; outbytes = pwbufsize - 1; if ( jnlib_iconv (cd, (const char **)&inptr, &inbytes, &outptr, &outbytes) == (size_t)-1) { log_error ("error converting passphrase to" " requested charset '%s': %s\n", charset, strerror (errno)); jnlib_iconv_close (cd); goto failure; } *outptr = 0; jnlib_iconv_close (cd); pw = pwbuf; } if (cert && certlen) { /* Calculate the hash value we need for the bag attributes. */ gcry_md_hash_buffer (GCRY_MD_SHA1, sha1hash, cert, certlen); sprintf (keyidstr, "%02x%02x%02x%02x", sha1hash[16], sha1hash[17], sha1hash[18], sha1hash[19]); /* Encode the certificate. */ buffer = build_cert_sequence (cert, certlen, sha1hash, keyidstr, &buflen); if (!buffer) goto failure; /* Encrypt it. */ gcry_randomize (salt, 8, GCRY_STRONG_RANDOM); crypt_block (buffer, buflen, salt, 8, 2048, NULL, 0, pw, GCRY_CIPHER_RFC2268_40, 1); /* Encode the encrypted stuff into a bag. */ seqlist[seqlistidx].buffer = build_cert_bag (buffer, buflen, salt, &n); seqlist[seqlistidx].length = n; gcry_free (buffer); buffer = NULL; if (!seqlist[seqlistidx].buffer) goto failure; seqlistidx++; } if (kparms) { /* Encode the key. */ 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; /* Encrypt it. */ gcry_randomize (salt, 8, GCRY_STRONG_RANDOM); crypt_block (buffer, buflen, salt, 8, 2048, NULL, 0, pw, GCRY_CIPHER_3DES, 1); /* Encode the encrypted stuff into a bag. */ if (cert && certlen) seqlist[seqlistidx].buffer = build_key_bag (buffer, buflen, salt, sha1hash, keyidstr, &n); else seqlist[seqlistidx].buffer = build_key_bag (buffer, buflen, salt, NULL, NULL, &n); seqlist[seqlistidx].length = n; gcry_free (buffer); buffer = NULL; if (!seqlist[seqlistidx].buffer) goto failure; seqlistidx++; } seqlist[seqlistidx].buffer = NULL; seqlist[seqlistidx].length = 0; buffer = create_final (seqlist, pw, &buflen); failure: if (pwbuf) { /* Note that wipememory is not really needed due to the use of gcry_malloc_secure. */ wipememory (pwbuf, pwbufsize); gcry_free (pwbuf); } for ( ; seqlistidx; seqlistidx--) gcry_free (seqlist[seqlistidx].buffer); *r_length = buffer? buflen : 0; return buffer; } /* This is actually not a pkcs#12 function but one which creates an unencrypted a 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; 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; } diff --git a/sm/qualified.c b/sm/qualified.c index 70d03aed3..a1d6fe9bf 100644 --- a/sm/qualified.c +++ b/sm/qualified.c @@ -1,325 +1,324 @@ /* qualified.c - Routines related to qualified signatures * Copyright (C) 2005, 2007 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include -#include #include #include "gpgsm.h" #include "../common/i18n.h" #include /* We open the file only once and keep the open file pointer as well as the name of the file here. Note that, a listname not equal to NULL indicates that this module has been initialized and if the LISTFP is also NULL, no list of qualified signatures exists. */ static char *listname; static FILE *listfp; /* Read the trustlist and return entry by entry. KEY must point to a buffer of at least 41 characters. COUNTRY shall be a buffer of at least 3 characters to receive the country code of that qualified signature (i.e. "de" for German and "be" for Belgium). Reading a valid entry returns 0, EOF is indicated by GPG_ERR_EOF and any other error condition is indicated by the appropriate error code. */ static gpg_error_t read_list (char *key, char *country, int *lnr) { gpg_error_t err; int c, i, j; char *p, line[256]; *key = 0; *country = 0; if (!listname) { listname = make_filename (gnupg_sysconfdir (), "qualified.txt", NULL); listfp = fopen (listname, "r"); if (!listfp && errno != ENOENT) { err = gpg_error_from_syserror (); log_error (_("can't open '%s': %s\n"), listname, gpg_strerror (err)); return err; } } if (!listfp) return gpg_error (GPG_ERR_EOF); do { if (!fgets (line, DIM(line)-1, listfp) ) { if (feof (listfp)) return gpg_error (GPG_ERR_EOF); return gpg_error_from_syserror (); } if (!*line || line[strlen(line)-1] != '\n') { /* Eat until end of line. */ while ( (c=getc (listfp)) != EOF && c != '\n') ; return gpg_error (*line? GPG_ERR_LINE_TOO_LONG : GPG_ERR_INCOMPLETE_LINE); } ++*lnr; /* Allow for empty lines and spaces */ for (p=line; spacep (p); p++) ; } while (!*p || *p == '\n' || *p == '#'); for (i=j=0; (p[i] == ':' || hexdigitp (p+i)) && j < 40; i++) if ( p[i] != ':' ) key[j++] = p[i] >= 'a'? (p[i] & 0xdf): p[i]; key[j] = 0; if (j != 40 || !(spacep (p+i) || p[i] == '\n')) { log_error (_("invalid formatted fingerprint in '%s', line %d\n"), listname, *lnr); return gpg_error (GPG_ERR_BAD_DATA); } - assert (p[i]); + log_assert (p[i]); i++; while (spacep (p+i)) i++; if ( p[i] >= 'a' && p[i] <= 'z' && p[i+1] >= 'a' && p[i+1] <= 'z' && (spacep (p+i+2) || p[i+2] == '\n')) { country[0] = p[i]; country[1] = p[i+1]; country[2] = 0; } else { log_error (_("invalid country code in '%s', line %d\n"), listname, *lnr); return gpg_error (GPG_ERR_BAD_DATA); } return 0; } /* Check whether the certificate CERT is included in the list of qualified certificates. This list is similar to the "trustlist.txt" as maintained by gpg-agent and includes fingerprints of root certificates to be used for qualified (legally binding like handwritten) signatures. We keep this list system wide and not per user because it is not a decision of the user. Returns: 0 if the certificate is included. GPG_ERR_NOT_FOUND if it is not in the list or any other error (e.g. if no list of qualified signatures is available. If COUNTRY has not been passed as NULL a string with a maximum length of 2 will be copied into it; thus the caller needs to provide a buffer of length 3. */ gpg_error_t gpgsm_is_in_qualified_list (ctrl_t ctrl, ksba_cert_t cert, char *country) { gpg_error_t err; char *fpr; char key[41]; char mycountry[3]; int lnr = 0; (void)ctrl; if (country) *country = 0; fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); if (!fpr) return gpg_error (GPG_ERR_GENERAL); if (listfp) { /* W32ce has no rewind, thus we use the equivalent code. */ fseek (listfp, 0, SEEK_SET); clearerr (listfp); } while (!(err = read_list (key, mycountry, &lnr))) { if (!strcmp (key, fpr)) break; } if (gpg_err_code (err) == GPG_ERR_EOF) err = gpg_error (GPG_ERR_NOT_FOUND); if (!err && country) strcpy (country, mycountry); xfree (fpr); return err; } /* We know that CERT is a qualified certificate. Ask the user for consent to actually create a signature using this certificate. Returns: 0 for yes, GPG_ERR_CANCEL for no or any other error code. */ gpg_error_t gpgsm_qualified_consent (ctrl_t ctrl, ksba_cert_t cert) { gpg_error_t err; char *name, *subject, *buffer, *p; const char *s; char *orig_codeset = NULL; name = ksba_cert_get_subject (cert, 0); if (!name) return gpg_error (GPG_ERR_GENERAL); subject = gpgsm_format_name2 (name, 0); ksba_free (name); name = NULL; orig_codeset = i18n_switchto_utf8 (); if (asprintf (&name, _("You are about to create a signature using your " "certificate:\n" "\"%s\"\n" "This will create a qualified signature by law " "equated to a handwritten signature.\n\n%s%s" "Are you really sure that you want to do this?"), subject? subject:"?", opt.qualsig_approval? "": _("Note, that this software is not officially approved " "to create or verify such signatures.\n"), opt.qualsig_approval? "":"\n" ) < 0 ) err = gpg_error_from_syserror (); else err = 0; i18n_switchback (orig_codeset); xfree (subject); if (err) return err; buffer = p = xtrymalloc (strlen (name) * 3 + 1); if (!buffer) { err = gpg_error_from_syserror (); free (name); return err; } for (s=name; *s; s++) { if (*s < ' ' || *s == '+') { sprintf (p, "%%%02X", *(unsigned char *)s); p += 3; } else if (*s == ' ') *p++ = '+'; else *p++ = *s; } *p = 0; free (name); err = gpgsm_agent_get_confirmation (ctrl, buffer); xfree (buffer); return err; } /* Popup a prompt to inform the user that the signature created is not a qualified one. This is of course only done if we know that we have been approved. */ gpg_error_t gpgsm_not_qualified_warning (ctrl_t ctrl, ksba_cert_t cert) { gpg_error_t err; char *name, *subject, *buffer, *p; const char *s; char *orig_codeset; if (!opt.qualsig_approval) return 0; name = ksba_cert_get_subject (cert, 0); if (!name) return gpg_error (GPG_ERR_GENERAL); subject = gpgsm_format_name2 (name, 0); ksba_free (name); name = NULL; orig_codeset = i18n_switchto_utf8 (); if (asprintf (&name, _("You are about to create a signature using your " "certificate:\n" "\"%s\"\n" "Note, that this certificate will NOT create a " "qualified signature!"), subject? subject:"?") < 0 ) err = gpg_error_from_syserror (); else err = 0; i18n_switchback (orig_codeset); xfree (subject); if (err) return err; buffer = p = xtrymalloc (strlen (name) * 3 + 1); if (!buffer) { err = gpg_error_from_syserror (); free (name); return err; } for (s=name; *s; s++) { if (*s < ' ' || *s == '+') { sprintf (p, "%%%02X", *(unsigned char *)s); p += 3; } else if (*s == ' ') *p++ = '+'; else *p++ = *s; } *p = 0; free (name); err = gpgsm_agent_get_confirmation (ctrl, buffer); xfree (buffer); return err; } diff --git a/sm/sign.c b/sm/sign.c index 0b1266200..8a65d58c1 100644 --- a/sm/sign.c +++ b/sm/sign.c @@ -1,963 +1,962 @@ /* sign.c - Sign a message * Copyright (C) 2001, 2002, 2003, 2008, * 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include -#include #include "gpgsm.h" #include #include #include "keydb.h" #include "../common/i18n.h" /* Hash the data and return if something was hashed. Return -1 on error. */ static int hash_data (int fd, gcry_md_hd_t md) { estream_t fp; char buffer[4096]; int nread; int rc = 0; fp = es_fdopen_nc (fd, "rb"); if (!fp) { log_error ("fdopen(%d) failed: %s\n", fd, strerror (errno)); return -1; } do { nread = es_fread (buffer, 1, DIM(buffer), fp); gcry_md_write (md, buffer, nread); } while (nread); if (es_ferror (fp)) { log_error ("read error on fd %d: %s\n", fd, strerror (errno)); rc = -1; } es_fclose (fp); return rc; } static int hash_and_copy_data (int fd, gcry_md_hd_t md, ksba_writer_t writer) { gpg_error_t err; estream_t fp; char buffer[4096]; int nread; int rc = 0; int any = 0; fp = es_fdopen_nc (fd, "rb"); if (!fp) { gpg_error_t tmperr = gpg_error_from_syserror (); log_error ("fdopen(%d) failed: %s\n", fd, strerror (errno)); return tmperr; } do { nread = es_fread (buffer, 1, DIM(buffer), fp); if (nread) { any = 1; gcry_md_write (md, buffer, nread); err = ksba_writer_write_octet_string (writer, buffer, nread, 0); if (err) { log_error ("write failed: %s\n", gpg_strerror (err)); rc = err; } } } while (nread && !rc); if (es_ferror (fp)) { rc = gpg_error_from_syserror (); log_error ("read error on fd %d: %s\n", fd, strerror (errno)); } es_fclose (fp); if (!any) { /* We can't allow signing an empty message because it does not make much sense and more seriously, ksba_cms_build has already written the tag for data and now expects an octet string and an octet string of size 0 is illegal. */ log_error ("cannot sign an empty message\n"); rc = gpg_error (GPG_ERR_NO_DATA); } if (!rc) { err = ksba_writer_write_octet_string (writer, NULL, 0, 1); if (err) { log_error ("write failed: %s\n", gpg_strerror (err)); rc = err; } } return rc; } /* Get the default certificate which is defined as the first certificate capable of signing returned by the keyDB and has a secret key available. */ int gpgsm_get_default_cert (ctrl_t ctrl, ksba_cert_t *r_cert) { KEYDB_HANDLE hd; ksba_cert_t cert = NULL; int rc; char *p; hd = keydb_new (); if (!hd) return gpg_error (GPG_ERR_GENERAL); rc = keydb_search_first (ctrl, hd); if (rc) { keydb_release (hd); return rc; } do { rc = keydb_get_cert (hd, &cert); if (rc) { log_error ("keydb_get_cert failed: %s\n", gpg_strerror (rc)); keydb_release (hd); return rc; } if (!gpgsm_cert_use_sign_p (cert, 1)) { p = gpgsm_get_keygrip_hexstring (cert); if (p) { if (!gpgsm_agent_havekey (ctrl, p)) { xfree (p); keydb_release (hd); *r_cert = cert; return 0; /* got it */ } xfree (p); } } ksba_cert_release (cert); cert = NULL; } while (!(rc = keydb_search_next (ctrl, hd))); if (rc && rc != -1) log_error ("keydb_search_next failed: %s\n", gpg_strerror (rc)); ksba_cert_release (cert); keydb_release (hd); return rc; } static ksba_cert_t get_default_signer (ctrl_t ctrl) { KEYDB_SEARCH_DESC desc; ksba_cert_t cert = NULL; KEYDB_HANDLE kh = NULL; int rc; if (!opt.local_user) { rc = gpgsm_get_default_cert (ctrl, &cert); if (rc) { if (rc != -1) log_debug ("failed to find default certificate: %s\n", gpg_strerror (rc)); return NULL; } return cert; } rc = classify_user_id (opt.local_user, &desc, 0); if (rc) { log_error ("failed to find default signer: %s\n", gpg_strerror (rc)); return NULL; } kh = keydb_new (); if (!kh) return NULL; rc = keydb_search (ctrl, kh, &desc, 1); if (rc) { log_debug ("failed to find default certificate: rc=%d\n", rc); } else { rc = keydb_get_cert (kh, &cert); if (rc) { log_debug ("failed to get cert: rc=%d\n", rc); } } keydb_release (kh); return cert; } /* Depending on the options in CTRL add the certificate CERT as well as other certificate up in the chain to the Root-CA to the CMS object. */ static int add_certificate_list (ctrl_t ctrl, ksba_cms_t cms, ksba_cert_t cert) { gpg_error_t err; int rc = 0; ksba_cert_t next = NULL; int n; int not_root = 0; ksba_cert_ref (cert); n = ctrl->include_certs; if (n == -2) { not_root = 1; n = -1; } if (n < 0 || n > 50) n = 50; /* We better apply an upper bound */ /* First add my own certificate unless we don't want any certificate included at all. */ if (n) { if (not_root && gpgsm_is_root_cert (cert)) err = 0; else err = ksba_cms_add_cert (cms, cert); if (err) goto ksba_failure; if (n>0) n--; } /* Walk the chain to include all other certificates. Note that a -1 used for N makes sure that there is no limit and all certs get included. */ while ( n-- && !(rc = gpgsm_walk_cert_chain (ctrl, cert, &next)) ) { if (not_root && gpgsm_is_root_cert (next)) err = 0; else err = ksba_cms_add_cert (cms, next); ksba_cert_release (cert); cert = next; next = NULL; if (err) goto ksba_failure; } ksba_cert_release (cert); return rc == -1? 0: rc; ksba_failure: ksba_cert_release (cert); log_error ("ksba_cms_add_cert failed: %s\n", gpg_strerror (err)); return err; } #if KSBA_VERSION_NUMBER >= 0x010400 && 0 /* 1.4.0 */ static gpg_error_t add_signed_attribute (ksba_cms_t cms, const char *attrstr) { gpg_error_t err; char **fields = NULL; const char *s; int i; unsigned char *der = NULL; size_t derlen; fields = strtokenize (attrstr, ":"); if (!fields) { err = gpg_error_from_syserror (); log_error ("strtokenize failed: %s\n", gpg_strerror (err)); goto leave; } for (i=0; fields[i]; i++) ; if (i != 3) { err = gpg_error (GPG_ERR_SYNTAX); log_error ("invalid attribute specification '%s': %s\n", attrstr, i < 3 ? "not enough fields":"too many fields"); goto leave; } if (!ascii_strcasecmp (fields[1], "u")) { err = 0; goto leave; /* Skip unsigned attributes. */ } if (ascii_strcasecmp (fields[1], "s")) { err = gpg_error (GPG_ERR_SYNTAX); log_error ("invalid attribute specification '%s': %s\n", attrstr, "type is not 's' or 'u'"); goto leave; } /* Check that the OID is valid. */ err = ksba_oid_from_str (fields[0], &der, &derlen); if (err) { log_error ("invalid attribute specification '%s': %s\n", attrstr, gpg_strerror (err)); goto leave; } xfree (der); der = NULL; if (strchr (fields[2], '/')) { /* FIXME: read from file. */ } else /* Directly given in hex. */ { for (i=0, s = fields[2]; hexdigitp (s); s++, i++) ; if (*s || !i || (i&1)) { log_error ("invalid attribute specification '%s': %s\n", attrstr, "invalid hex encoding of the data"); err = gpg_error (GPG_ERR_SYNTAX); goto leave; } der = xtrystrdup (fields[2]); if (!der) { err = gpg_error_from_syserror (); log_error ("malloc failed: %s\n", gpg_strerror (err)); goto leave; } for (s=fields[2], derlen=0; s[0] && s[1]; s += 2) der[derlen++] = xtoi_2 (s); } /* Store the data in the CMS object for all signers. */ err = ksba_cms_add_attribute (cms, -1, fields[0], 0, der, derlen); if (err) { log_error ("invalid attribute specification '%s': %s\n", attrstr, gpg_strerror (err)); goto leave; } leave: xfree (der); xfree (fields); return err; } #endif /*ksba >= 1.4.0 */ /* Perform a sign operation. Sign the data received on DATA-FD in embedded mode or in detached mode when DETACHED is true. Write the signature to OUT_FP. The keys used to sign are taken from SIGNERLIST or the default one will be used if the value of this argument is NULL. */ int gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, int data_fd, int detached, estream_t out_fp) { int i, rc; gpg_error_t err; gnupg_ksba_io_t b64writer = NULL; ksba_writer_t writer; ksba_cms_t cms = NULL; ksba_stop_reason_t stopreason; KEYDB_HANDLE kh = NULL; gcry_md_hd_t data_md = NULL; int signer; const char *algoid; int algo; ksba_isotime_t signed_at; certlist_t cl; int release_signerlist = 0; audit_set_type (ctrl->audit, AUDIT_TYPE_SIGN); kh = keydb_new (); if (!kh) { log_error (_("failed to allocate keyDB handle\n")); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } if (!gnupg_rng_is_compliant (opt.compliance)) { rc = gpg_error (GPG_ERR_FORBIDDEN); log_error (_("%s is not compliant with %s mode\n"), "RNG", gnupg_compliance_option_string (opt.compliance)); gpgsm_status_with_error (ctrl, STATUS_ERROR, "random-compliance", rc); goto leave; } ctrl->pem_name = "SIGNED MESSAGE"; rc = gnupg_ksba_create_writer (&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)), ctrl->pem_name, out_fp, &writer); if (rc) { log_error ("can't create writer: %s\n", gpg_strerror (rc)); goto leave; } err = ksba_cms_new (&cms); if (err) { rc = err; goto leave; } err = ksba_cms_set_reader_writer (cms, NULL, writer); if (err) { log_debug ("ksba_cms_set_reader_writer failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } /* We are going to create signed data with data as encap. content. * In authenticode mode we use spcIndirectDataContext instead. */ err = ksba_cms_set_content_type (cms, 0, KSBA_CT_SIGNED_DATA); if (!err) err = ksba_cms_set_content_type (cms, 1, #if KSBA_VERSION_NUMBER >= 0x010400 && 0 opt.authenticode? KSBA_CT_SPC_IND_DATA_CTX : #endif KSBA_CT_DATA ); if (err) { log_debug ("ksba_cms_set_content_type failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } /* If no list of signers is given, use the default certificate. */ if (!signerlist) { ksba_cert_t cert = get_default_signer (ctrl); if (!cert) { log_error ("no default signer found\n"); gpgsm_status2 (ctrl, STATUS_INV_SGNR, get_inv_recpsgnr_code (GPG_ERR_NO_SECKEY), NULL); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } /* Although we don't check for ambiguous specification we will check that the signer's certificate is usable and valid. */ rc = gpgsm_cert_use_sign_p (cert, 0); if (!rc) rc = gpgsm_validate_chain (ctrl, cert, "", NULL, 0, NULL, 0, NULL); if (rc) { char *tmpfpr; tmpfpr = gpgsm_get_fingerprint_hexstring (cert, 0); gpgsm_status2 (ctrl, STATUS_INV_SGNR, get_inv_recpsgnr_code (rc), tmpfpr, NULL); xfree (tmpfpr); goto leave; } /* That one is fine - create signerlist. */ signerlist = xtrycalloc (1, sizeof *signerlist); if (!signerlist) { rc = out_of_core (); ksba_cert_release (cert); goto leave; } signerlist->cert = cert; release_signerlist = 1; } /* Figure out the hash algorithm to use. We do not want to use the one for the certificate but if possible an OID for the plain algorithm. */ if (opt.forced_digest_algo && opt.verbose) log_info ("user requested hash algorithm %d\n", opt.forced_digest_algo); for (i=0, cl=signerlist; cl; cl = cl->next, i++) { const char *oid; unsigned int nbits; int pk_algo; pk_algo = gpgsm_get_key_algo_info (cl->cert, &nbits); cl->pk_algo = pk_algo; if (opt.forced_digest_algo) { oid = NULL; cl->hash_algo = opt.forced_digest_algo; } else { if (pk_algo == GCRY_PK_ECC) { /* Map the Curve to a corresponding hash algo. */ if (nbits <= 256) oid = "2.16.840.1.101.3.4.2.1"; /* sha256 */ else if (nbits <= 384) oid = "2.16.840.1.101.3.4.2.2"; /* sha384 */ else oid = "2.16.840.1.101.3.4.2.3"; /* sha512 */ } else { /* For RSA we reuse the hash algo used by the certificate. */ oid = ksba_cert_get_digest_algo (cl->cert); } cl->hash_algo = oid ? gcry_md_map_name (oid) : 0; } switch (cl->hash_algo) { case GCRY_MD_SHA1: oid = "1.3.14.3.2.26"; break; case GCRY_MD_RMD160: oid = "1.3.36.3.2.1"; break; case GCRY_MD_SHA224: oid = "2.16.840.1.101.3.4.2.4"; break; case GCRY_MD_SHA256: oid = "2.16.840.1.101.3.4.2.1"; break; case GCRY_MD_SHA384: oid = "2.16.840.1.101.3.4.2.2"; break; case GCRY_MD_SHA512: oid = "2.16.840.1.101.3.4.2.3"; break; /* case GCRY_MD_WHIRLPOOL: oid = "No OID yet"; break; */ case GCRY_MD_MD5: /* We don't want to use MD5. */ case 0: /* No algorithm found in cert. */ default: /* Other algorithms. */ log_info (_("hash algorithm %d (%s) for signer %d not supported;" " using %s\n"), cl->hash_algo, oid? oid: "?", i, gcry_md_algo_name (GCRY_MD_SHA1)); cl->hash_algo = GCRY_MD_SHA1; oid = "1.3.14.3.2.26"; break; } cl->hash_algo_oid = oid; /* Check compliance. */ if (! gnupg_digest_is_allowed (opt.compliance, 1, cl->hash_algo)) { log_error (_("digest algorithm '%s' may not be used in %s mode\n"), gcry_md_algo_name (cl->hash_algo), gnupg_compliance_option_string (opt.compliance)); err = gpg_error (GPG_ERR_DIGEST_ALGO); goto leave; } if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_SIGNING, pk_algo, 0, NULL, nbits, NULL)) { char kidstr[10+1]; snprintf (kidstr, sizeof kidstr, "0x%08lX", gpgsm_get_short_fingerprint (cl->cert, NULL)); log_error (_("key %s may not be used for signing in %s mode\n"), kidstr, gnupg_compliance_option_string (opt.compliance)); err = gpg_error (GPG_ERR_PUBKEY_ALGO); goto leave; } } if (opt.verbose > 1 || opt.debug) { for (i=0, cl=signerlist; cl; cl = cl->next, i++) log_info (_("hash algorithm used for signer %d: %s (%s)\n"), i, gcry_md_algo_name (cl->hash_algo), cl->hash_algo_oid); } /* Gather certificates of signers and store them in the CMS object. */ for (cl=signerlist; cl; cl = cl->next) { rc = gpgsm_cert_use_sign_p (cl->cert, 0); if (rc) goto leave; err = ksba_cms_add_signer (cms, cl->cert); if (err) { log_error ("ksba_cms_add_signer failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } rc = add_certificate_list (ctrl, cms, cl->cert); if (rc) { log_error ("failed to store list of certificates: %s\n", gpg_strerror(rc)); goto leave; } /* Set the hash algorithm we are going to use */ err = ksba_cms_add_digest_algo (cms, cl->hash_algo_oid); if (err) { log_debug ("ksba_cms_add_digest_algo failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } } /* Check whether one of the certificates is qualified. Note that we already validated the certificate and thus the user data stored flag must be available. */ if (!opt.no_chain_validation) { for (cl=signerlist; cl; cl = cl->next) { size_t buflen; char buffer[1]; err = ksba_cert_get_user_data (cl->cert, "is_qualified", &buffer, sizeof (buffer), &buflen); if (err || !buflen) { log_error (_("checking for qualified certificate failed: %s\n"), gpg_strerror (err)); rc = err; goto leave; } if (*buffer) err = gpgsm_qualified_consent (ctrl, cl->cert); else err = gpgsm_not_qualified_warning (ctrl, cl->cert); if (err) { rc = err; goto leave; } } } /* Prepare hashing (actually we are figuring out what we have set above). */ rc = gcry_md_open (&data_md, 0, 0); if (rc) { log_error ("md_open failed: %s\n", gpg_strerror (rc)); goto leave; } if (DBG_HASHING) gcry_md_debug (data_md, "sign.data"); for (i=0; (algoid=ksba_cms_get_digest_algo_list (cms, i)); i++) { algo = gcry_md_map_name (algoid); if (!algo) { log_error ("unknown hash algorithm '%s'\n", algoid? algoid:"?"); rc = gpg_error (GPG_ERR_BUG); goto leave; } gcry_md_enable (data_md, algo); audit_log_i (ctrl->audit, AUDIT_DATA_HASH_ALGO, algo); } audit_log (ctrl->audit, AUDIT_SETUP_READY); if (detached) { /* We hash the data right now so that we can store the message digest. ksba_cms_build() takes this as an flag that detached data is expected. */ unsigned char *digest; size_t digest_len; if (!hash_data (data_fd, data_md)) audit_log (ctrl->audit, AUDIT_GOT_DATA); for (cl=signerlist,signer=0; cl; cl = cl->next, signer++) { digest = gcry_md_read (data_md, cl->hash_algo); digest_len = gcry_md_get_algo_dlen (cl->hash_algo); if ( !digest || !digest_len ) { log_error ("problem getting the hash of the data\n"); rc = gpg_error (GPG_ERR_BUG); goto leave; } err = ksba_cms_set_message_digest (cms, signer, digest, digest_len); if (err) { log_error ("ksba_cms_set_message_digest failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } } } gnupg_get_isotime (signed_at); for (cl=signerlist,signer=0; cl; cl = cl->next, signer++) { err = ksba_cms_set_signing_time (cms, signer, signed_at); if (err) { log_error ("ksba_cms_set_signing_time failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } } /* We can add signed attributes only when build against libksba 1.4. */ #if KSBA_VERSION_NUMBER >= 0x010400 && 0 /* 1.4.0 */ { strlist_t sl; for (sl = opt.attributes; sl; sl = sl->next) if ((err = add_signed_attribute (cms, sl->d))) goto leave; } #else if (opt.attributes) log_info ("Note: option --attribute is ignored by this version\n"); #endif /*ksba >= 1.4.0 */ /* We need to write at least a minimal list of our capabilities to try to convince some MUAs to use 3DES and not the crippled RC2. Our list is: aes128-CBC des-EDE3-CBC */ err = ksba_cms_add_smime_capability (cms, "2.16.840.1.101.3.4.1.2", NULL, 0); if (!err) err = ksba_cms_add_smime_capability (cms, "1.2.840.113549.3.7", NULL, 0); if (err) { log_error ("ksba_cms_add_smime_capability failed: %s\n", gpg_strerror (err)); goto leave; } /* Main building loop. */ do { err = ksba_cms_build (cms, &stopreason); if (err) { log_debug ("ksba_cms_build failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } if (stopreason == KSBA_SR_BEGIN_DATA) { /* Hash the data and store the message digest. */ unsigned char *digest; size_t digest_len; - assert (!detached); + log_assert (!detached); rc = hash_and_copy_data (data_fd, data_md, writer); if (rc) goto leave; audit_log (ctrl->audit, AUDIT_GOT_DATA); for (cl=signerlist,signer=0; cl; cl = cl->next, signer++) { digest = gcry_md_read (data_md, cl->hash_algo); digest_len = gcry_md_get_algo_dlen (cl->hash_algo); if ( !digest || !digest_len ) { log_error ("problem getting the hash of the data\n"); rc = gpg_error (GPG_ERR_BUG); goto leave; } err = ksba_cms_set_message_digest (cms, signer, digest, digest_len); if (err) { log_error ("ksba_cms_set_message_digest failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } } } else if (stopreason == KSBA_SR_NEED_SIG) { /* Compute the signature for all signers. */ gcry_md_hd_t md; rc = gcry_md_open (&md, 0, 0); if (rc) { log_error ("md_open failed: %s\n", gpg_strerror (rc)); goto leave; } if (DBG_HASHING) gcry_md_debug (md, "sign.attr"); ksba_cms_set_hash_function (cms, HASH_FNC, md); for (cl=signerlist,signer=0; cl; cl = cl->next, signer++) { unsigned char *sigval = NULL; char *buf, *fpr; audit_log_i (ctrl->audit, AUDIT_NEW_SIG, signer); if (signer) gcry_md_reset (md); { certlist_t cl_tmp; for (cl_tmp=signerlist; cl_tmp; cl_tmp = cl_tmp->next) { gcry_md_enable (md, cl_tmp->hash_algo); audit_log_i (ctrl->audit, AUDIT_ATTR_HASH_ALGO, cl_tmp->hash_algo); } } rc = ksba_cms_hash_signed_attrs (cms, signer); if (rc) { log_debug ("hashing signed attrs failed: %s\n", gpg_strerror (rc)); gcry_md_close (md); goto leave; } rc = gpgsm_create_cms_signature (ctrl, cl->cert, md, cl->hash_algo, &sigval); if (rc) { audit_log_cert (ctrl->audit, AUDIT_SIGNED_BY, cl->cert, rc); gcry_md_close (md); goto leave; } err = ksba_cms_set_sig_val (cms, signer, sigval); xfree (sigval); if (err) { audit_log_cert (ctrl->audit, AUDIT_SIGNED_BY, cl->cert, err); log_error ("failed to store the signature: %s\n", gpg_strerror (err)); rc = err; gcry_md_close (md); goto leave; } /* write a status message */ fpr = gpgsm_get_fingerprint_hexstring (cl->cert, GCRY_MD_SHA1); if (!fpr) { rc = gpg_error (GPG_ERR_ENOMEM); gcry_md_close (md); goto leave; } rc = 0; if (opt.verbose) { char *pkalgostr = gpgsm_pubkey_algo_string (cl->cert, NULL); log_info (_("%s/%s signature using %s key %s\n"), pubkey_algo_to_string (cl->pk_algo), gcry_md_algo_name (cl->hash_algo), pkalgostr, fpr); xfree (pkalgostr); } buf = xtryasprintf ("%c %d %d 00 %s %s", detached? 'D':'S', cl->pk_algo, cl->hash_algo, signed_at, fpr); if (!buf) rc = gpg_error_from_syserror (); xfree (fpr); if (rc) { gcry_md_close (md); goto leave; } gpgsm_status (ctrl, STATUS_SIG_CREATED, buf); xfree (buf); audit_log_cert (ctrl->audit, AUDIT_SIGNED_BY, cl->cert, 0); } gcry_md_close (md); } } while (stopreason != KSBA_SR_READY); rc = gnupg_ksba_finish_writer (b64writer); if (rc) { log_error ("write failed: %s\n", gpg_strerror (rc)); goto leave; } audit_log (ctrl->audit, AUDIT_SIGNING_DONE); log_info ("signature created\n"); leave: if (rc) log_error ("error creating signature: %s <%s>\n", gpg_strerror (rc), gpg_strsource (rc) ); if (release_signerlist) gpgsm_release_certlist (signerlist); ksba_cms_release (cms); gnupg_ksba_destroy_writer (b64writer); keydb_release (kh); gcry_md_close (data_md); return rc; } diff --git a/sm/verify.c b/sm/verify.c index 824767bef..e95734770 100644 --- a/sm/verify.c +++ b/sm/verify.c @@ -1,734 +1,733 @@ /* verify.c - Verify a messages signature * Copyright (C) 2001, 2002, 2003, 2007, * 2010 Free Software Foundation, Inc. * Copyright (C) 2001-2019 Werner Koch * Copyright (C) 2015-2020 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * SPDX-License-Identifier: GPL-3.0-or-later */ #include #include #include #include #include #include #include -#include #include "gpgsm.h" #include #include #include "keydb.h" #include "../common/i18n.h" #include "../common/compliance.h" static char * strtimestamp_r (ksba_isotime_t atime) { char *buffer = xmalloc (15); if (!atime || !*atime) strcpy (buffer, "none"); else sprintf (buffer, "%.4s-%.2s-%.2s", atime, atime+4, atime+6); return buffer; } /* Hash the data for a detached signature. Returns 0 on success. */ static gpg_error_t hash_data (int fd, gcry_md_hd_t md) { gpg_error_t err = 0; estream_t fp; char buffer[4096]; int nread; fp = es_fdopen_nc (fd, "rb"); if (!fp) { err = gpg_error_from_syserror (); log_error ("fdopen(%d) failed: %s\n", fd, gpg_strerror (err)); return err; } do { nread = es_fread (buffer, 1, DIM(buffer), fp); gcry_md_write (md, buffer, nread); } while (nread); if (es_ferror (fp)) { err = gpg_error_from_syserror (); log_error ("read error on fd %d: %s\n", fd, gpg_strerror (err)); } es_fclose (fp); return err; } /* Perform a verify operation. To verify detached signatures, DATA_FD must be different than -1. With OUT_FP given and a non-detached signature, the signed material is written to that stream. */ int gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) { int i, rc; gnupg_ksba_io_t b64reader = NULL; gnupg_ksba_io_t b64writer = NULL; ksba_reader_t reader; ksba_writer_t writer = NULL; ksba_cms_t cms = NULL; ksba_stop_reason_t stopreason; ksba_cert_t cert; KEYDB_HANDLE kh; gcry_md_hd_t data_md = NULL; int signer; const char *algoid; int algo; int is_detached; estream_t in_fp = NULL; char *p; audit_set_type (ctrl->audit, AUDIT_TYPE_VERIFY); kh = keydb_new (); if (!kh) { log_error (_("failed to allocate keyDB handle\n")); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } in_fp = es_fdopen_nc (in_fd, "rb"); if (!in_fp) { rc = gpg_error_from_syserror (); log_error ("fdopen() failed: %s\n", strerror (errno)); goto leave; } rc = gnupg_ksba_create_reader (&b64reader, ((ctrl->is_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->is_base64? GNUPG_KSBA_IO_BASE64 : 0) | (ctrl->autodetect_encoding? GNUPG_KSBA_IO_AUTODETECT : 0)), in_fp, &reader); if (rc) { log_error ("can't create reader: %s\n", gpg_strerror (rc)); goto leave; } if (out_fp) { rc = gnupg_ksba_create_writer (&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)), ctrl->pem_name, out_fp, &writer); if (rc) { log_error ("can't create writer: %s\n", gpg_strerror (rc)); goto leave; } } rc = ksba_cms_new (&cms); if (rc) goto leave; rc = ksba_cms_set_reader_writer (cms, reader, writer); if (rc) { log_error ("ksba_cms_set_reader_writer failed: %s\n", gpg_strerror (rc)); goto leave; } rc = gcry_md_open (&data_md, 0, 0); if (rc) { log_error ("md_open failed: %s\n", gpg_strerror (rc)); goto leave; } if (DBG_HASHING) gcry_md_debug (data_md, "vrfy.data"); audit_log (ctrl->audit, AUDIT_SETUP_READY); is_detached = 0; do { rc = ksba_cms_parse (cms, &stopreason); if (rc) { log_error ("ksba_cms_parse failed: %s\n", gpg_strerror (rc)); goto leave; } if (stopreason == KSBA_SR_NEED_HASH) { is_detached = 1; audit_log (ctrl->audit, AUDIT_DETACHED_SIGNATURE); if (opt.verbose) log_info ("detached signature\n"); } if (stopreason == KSBA_SR_NEED_HASH || stopreason == KSBA_SR_BEGIN_DATA) { audit_log (ctrl->audit, AUDIT_GOT_DATA); /* We are now able to enable the hash algorithms */ for (i=0; (algoid=ksba_cms_get_digest_algo_list (cms, i)); i++) { algo = gcry_md_map_name (algoid); if (!algo) { log_error ("unknown hash algorithm '%s'\n", algoid? algoid:"?"); if (algoid && ( !strcmp (algoid, "1.2.840.113549.1.1.2") ||!strcmp (algoid, "1.2.840.113549.2.2"))) log_info (_("(this is the MD2 algorithm)\n")); audit_log_s (ctrl->audit, AUDIT_BAD_DATA_HASH_ALGO, algoid); } else { if (DBG_X509) log_debug ("enabling hash algorithm %d (%s)\n", algo, algoid? algoid:""); gcry_md_enable (data_md, algo); audit_log_i (ctrl->audit, AUDIT_DATA_HASH_ALGO, algo); } } if (opt.extra_digest_algo) { if (DBG_X509) log_debug ("enabling extra hash algorithm %d\n", opt.extra_digest_algo); gcry_md_enable (data_md, opt.extra_digest_algo); audit_log_i (ctrl->audit, AUDIT_DATA_HASH_ALGO, opt.extra_digest_algo); } if (is_detached) { if (data_fd == -1) { log_info ("detached signature w/o data " "- assuming certs-only\n"); audit_log (ctrl->audit, AUDIT_CERT_ONLY_SIG); } else audit_log_ok (ctrl->audit, AUDIT_DATA_HASHING, hash_data (data_fd, data_md)); } else { ksba_cms_set_hash_function (cms, HASH_FNC, data_md); } } else if (stopreason == KSBA_SR_END_DATA) { /* The data bas been hashed */ audit_log_ok (ctrl->audit, AUDIT_DATA_HASHING, 0); } } while (stopreason != KSBA_SR_READY); if (b64writer) { rc = gnupg_ksba_finish_writer (b64writer); if (rc) { log_error ("write failed: %s\n", gpg_strerror (rc)); audit_log_ok (ctrl->audit, AUDIT_WRITE_ERROR, rc); goto leave; } } if (data_fd != -1 && !is_detached) { log_error ("data given for a non-detached signature\n"); rc = gpg_error (GPG_ERR_CONFLICT); audit_log (ctrl->audit, AUDIT_USAGE_ERROR); goto leave; } for (i=0; (cert=ksba_cms_get_cert (cms, i)); i++) { /* Fixme: it might be better to check the validity of the certificate first before entering it into the DB. This way we would avoid cluttering the DB with invalid certificates. */ audit_log_cert (ctrl->audit, AUDIT_SAVE_CERT, cert, keydb_store_cert (ctrl, cert, 0, NULL)); ksba_cert_release (cert); } cert = NULL; for (signer=0; ; signer++) { char *issuer = NULL; gcry_sexp_t sigval = NULL; ksba_isotime_t sigtime, keyexptime; ksba_sexp_t serial; char *msgdigest = NULL; size_t msgdigestlen; char *ctattr; int sigval_hash_algo; int info_pkalgo; unsigned int nbits; int pkalgo; char *pkalgostr = NULL; char *pkfpr = NULL; unsigned int pkalgoflags, verifyflags; rc = ksba_cms_get_issuer_serial (cms, signer, &issuer, &serial); if (!signer && gpg_err_code (rc) == GPG_ERR_NO_DATA && data_fd == -1 && is_detached) { log_info ("certs-only message accepted\n"); rc = 0; break; } if (rc) { if (signer && rc == -1) rc = 0; break; } gpgsm_status (ctrl, STATUS_NEWSIG, NULL); audit_log_i (ctrl->audit, AUDIT_NEW_SIG, signer); if (DBG_X509) { log_debug ("signer %d - issuer: '%s'\n", signer, issuer? issuer:"[NONE]"); log_debug ("signer %d - serial: ", signer); gpgsm_dump_serial (serial); log_printf ("\n"); } if (ctrl->audit) { char *tmpstr = gpgsm_format_sn_issuer (serial, issuer); audit_log_s (ctrl->audit, AUDIT_SIG_NAME, tmpstr); xfree (tmpstr); } rc = ksba_cms_get_signing_time (cms, signer, sigtime); if (gpg_err_code (rc) == GPG_ERR_NO_DATA) *sigtime = 0; else if (rc) { log_error ("error getting signing time: %s\n", gpg_strerror (rc)); *sigtime = 0; /* (we can't encode an error in the time string.) */ } rc = ksba_cms_get_message_digest (cms, signer, &msgdigest, &msgdigestlen); if (!rc) { algoid = ksba_cms_get_digest_algo (cms, signer); algo = gcry_md_map_name (algoid); if (DBG_X509) log_debug ("signer %d - digest algo: %d\n", signer, algo); if (! gcry_md_is_enabled (data_md, algo)) { log_error ("digest algo %d (%s) has not been enabled\n", algo, algoid?algoid:""); audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "unsupported"); goto next_signer; } } else if (gpg_err_code (rc) == GPG_ERR_NO_DATA) { - assert (!msgdigest); + log_assert (!msgdigest); rc = 0; algoid = NULL; algo = 0; } else /* real error */ { audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "error"); break; } rc = ksba_cms_get_sigattr_oids (cms, signer, "1.2.840.113549.1.9.3", &ctattr); if (!rc) { const char *s; if (DBG_X509) log_debug ("signer %d - content-type attribute: %s", signer, ctattr); s = ksba_cms_get_content_oid (cms, 1); if (!s || strcmp (ctattr, s)) { log_error ("content-type attribute does not match " "actual content-type\n"); ksba_free (ctattr); ctattr = NULL; audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad"); goto next_signer; } ksba_free (ctattr); ctattr = NULL; } else if (rc != -1) { log_error ("error getting content-type attribute: %s\n", gpg_strerror (rc)); audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad"); goto next_signer; } rc = 0; sigval = gpgsm_ksba_cms_get_sig_val (cms, signer); if (!sigval) { log_error ("no signature value available\n"); audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad"); goto next_signer; } sigval_hash_algo = gpgsm_get_hash_algo_from_sigval (sigval, &pkalgoflags); if (DBG_X509) { log_debug ("signer %d - signature available (sigval hash=%d pkaf=%u)", signer, sigval_hash_algo, pkalgoflags); } if (!sigval_hash_algo) sigval_hash_algo = algo; /* Fallback used e.g. with old libksba. */ /* Find the certificate of the signer */ keydb_search_reset (kh); rc = keydb_search_issuer_sn (ctrl, kh, issuer, serial); if (rc) { if (rc == -1) { log_error ("certificate not found\n"); rc = gpg_error (GPG_ERR_NO_PUBKEY); } else log_error ("failed to find the certificate: %s\n", gpg_strerror(rc)); { char numbuf[50]; sprintf (numbuf, "%d", rc); gpgsm_status2 (ctrl, STATUS_ERROR, "verify.findkey", numbuf, NULL); } audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "no-cert"); goto next_signer; } rc = keydb_get_cert (kh, &cert); if (rc) { log_error ("failed to get cert: %s\n", gpg_strerror (rc)); audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "error"); goto next_signer; } pkfpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); pkalgostr = gpgsm_pubkey_algo_string (cert, NULL); pkalgo = gpgsm_get_key_algo_info (cert, &nbits); /* Remap the ECC algo to the algo we use. Note that EdDSA has * already been mapped. */ if (pkalgo == GCRY_PK_ECC) pkalgo = GCRY_PK_ECDSA; /* Print infos about the signature. */ log_info (_("Signature made ")); if (*sigtime) { /* We take the freedom as noted in RFC3339 to use a space * instead of the "T" delimiter between date and time. We * also append a separate UTC instead of a "Z" or "+00:00" * suffix because that makes it clear to everyone what kind * of time this is. */ dump_isotime (sigtime); log_printf (" UTC"); } else log_printf (_("[date not given]")); log_info (_(" using %s key %s\n"), pkalgostr, pkfpr); if (opt.verbose) { log_info (_("algorithm:")); log_printf (" %s + %s", pubkey_algo_to_string (pkalgo), gcry_md_algo_name (sigval_hash_algo)); if (algo != sigval_hash_algo) log_printf (" (%s)", gcry_md_algo_name (algo)); log_printf ("\n"); } audit_log_i (ctrl->audit, AUDIT_DATA_HASH_ALGO, algo); /* Check compliance. */ if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_VERIFICATION, pkalgo, pkalgoflags, NULL, nbits, NULL)) { char kidstr[10+1]; snprintf (kidstr, sizeof kidstr, "0x%08lX", gpgsm_get_short_fingerprint (cert, NULL)); log_error (_("key %s may not be used for signing in %s mode\n"), kidstr, gnupg_compliance_option_string (opt.compliance)); goto next_signer; } if (! gnupg_digest_is_allowed (opt.compliance, 0, sigval_hash_algo)) { log_error (_("digest algorithm '%s' may not be used in %s mode\n"), gcry_md_algo_name (sigval_hash_algo), gnupg_compliance_option_string (opt.compliance)); goto next_signer; } /* Check compliance with CO_DE_VS. */ if (gnupg_pk_is_compliant (CO_DE_VS, pkalgo, pkalgoflags, NULL, nbits, NULL) && gnupg_digest_is_compliant (CO_DE_VS, sigval_hash_algo)) gpgsm_status (ctrl, STATUS_VERIFICATION_COMPLIANCE_MODE, gnupg_status_compliance_flag (CO_DE_VS)); /* Now we can check the signature. */ if (msgdigest) { /* Signed attributes are available. */ gcry_md_hd_t md; unsigned char *s; /* Check that the message digest in the signed attributes matches the one we calculated on the data. */ s = gcry_md_read (data_md, algo); if ( !s || !msgdigestlen || gcry_md_get_algo_dlen (algo) != msgdigestlen || memcmp (s, msgdigest, msgdigestlen) ) { char *fpr; log_error (_("invalid signature: message digest attribute " "does not match computed one\n")); if (DBG_X509) { if (msgdigest) log_printhex (msgdigest, msgdigestlen, "message: "); if (s) log_printhex (s, gcry_md_get_algo_dlen (algo), "computed: "); } fpr = gpgsm_fpr_and_name_for_status (cert); gpgsm_status (ctrl, STATUS_BADSIG, fpr); xfree (fpr); audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad"); goto next_signer; } audit_log_i (ctrl->audit, AUDIT_ATTR_HASH_ALGO, sigval_hash_algo); rc = gcry_md_open (&md, sigval_hash_algo, 0); if (rc) { log_error ("md_open failed: %s\n", gpg_strerror (rc)); audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "error"); goto next_signer; } if (DBG_HASHING) gcry_md_debug (md, "vrfy.attr"); ksba_cms_set_hash_function (cms, HASH_FNC, md); rc = ksba_cms_hash_signed_attrs (cms, signer); if (rc) { log_error ("hashing signed attrs failed: %s\n", gpg_strerror (rc)); gcry_md_close (md); audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "error"); goto next_signer; } rc = gpgsm_check_cms_signature (cert, sigval, md, sigval_hash_algo, pkalgoflags, &info_pkalgo); gcry_md_close (md); } else { rc = gpgsm_check_cms_signature (cert, sigval, data_md, algo, pkalgoflags, &info_pkalgo); } if (rc) { char *fpr; log_error ("invalid signature: %s\n", gpg_strerror (rc)); fpr = gpgsm_fpr_and_name_for_status (cert); gpgsm_status (ctrl, STATUS_BADSIG, fpr); xfree (fpr); audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad"); goto next_signer; } rc = gpgsm_cert_use_verify_p (cert); /*(this displays an info message)*/ if (rc) { gpgsm_status_with_err_code (ctrl, STATUS_ERROR, "verify.keyusage", gpg_err_code (rc)); rc = 0; } if (DBG_X509) log_debug ("signature okay - checking certs\n"); audit_log (ctrl->audit, AUDIT_VALIDATE_CHAIN); rc = gpgsm_validate_chain (ctrl, cert, *sigtime? sigtime : "19700101T000000", keyexptime, 0, NULL, 0, &verifyflags); { char *fpr, *buf, *tstr; fpr = gpgsm_fpr_and_name_for_status (cert); if (gpg_err_code (rc) == GPG_ERR_CERT_EXPIRED) { gpgsm_status (ctrl, STATUS_EXPKEYSIG, fpr); rc = 0; } else gpgsm_status (ctrl, STATUS_GOODSIG, fpr); xfree (fpr); /* FIXME: INFO_PKALGO correctly shows ECDSA but PKALGO is then * ECC. We should use the ECDSA here and need to find a way to * figure this oult without using the bodus assumtion in * gpgsm_check_cms_signature that ECC is alwas ECDSA. */ fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); tstr = strtimestamp_r (sigtime); buf = xasprintf ("%s %s %s %s 0 0 %d %d 00", fpr, tstr, *sigtime? sigtime : "0", *keyexptime? keyexptime : "0", info_pkalgo, algo); xfree (tstr); xfree (fpr); gpgsm_status (ctrl, STATUS_VALIDSIG, buf); xfree (buf); } audit_log_ok (ctrl->audit, AUDIT_CHAIN_STATUS, rc); if (rc) /* of validate_chain */ { log_error ("invalid certification chain: %s\n", gpg_strerror (rc)); if (gpg_err_code (rc) == GPG_ERR_BAD_CERT_CHAIN || gpg_err_code (rc) == GPG_ERR_BAD_CERT || gpg_err_code (rc) == GPG_ERR_BAD_CA_CERT || gpg_err_code (rc) == GPG_ERR_CERT_REVOKED) gpgsm_status_with_err_code (ctrl, STATUS_TRUST_NEVER, NULL, gpg_err_code (rc)); else gpgsm_status_with_err_code (ctrl, STATUS_TRUST_UNDEFINED, NULL, gpg_err_code (rc)); audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "bad"); goto next_signer; } audit_log_s (ctrl->audit, AUDIT_SIG_STATUS, "good"); for (i=0; (p = ksba_cert_get_subject (cert, i)); i++) { log_info (!i? _("Good signature from") : _(" aka")); log_printf (" \""); gpgsm_es_print_name (log_get_stream (), p); log_printf ("\"\n"); ksba_free (p); } /* Print a note if this is a qualified signature. */ { size_t qualbuflen; char qualbuffer[1]; rc = ksba_cert_get_user_data (cert, "is_qualified", &qualbuffer, sizeof (qualbuffer), &qualbuflen); if (!rc && qualbuflen) { if (*qualbuffer) { log_info (_("This is a qualified signature\n")); if (!opt.qualsig_approval) log_info (_("Note, that this software is not officially approved " "to create or verify such signatures.\n")); } } else if (gpg_err_code (rc) != GPG_ERR_NOT_FOUND) log_error ("get_user_data(is_qualified) failed: %s\n", gpg_strerror (rc)); } gpgsm_status (ctrl, STATUS_TRUST_FULLY, (verifyflags & VALIDATE_FLAG_STEED)? "0 steed": (verifyflags & VALIDATE_FLAG_CHAIN_MODEL)? "0 chain": "0 shell"); next_signer: rc = 0; xfree (issuer); xfree (serial); gcry_sexp_release (sigval); xfree (msgdigest); xfree (pkalgostr); xfree (pkfpr); ksba_cert_release (cert); cert = NULL; } rc = 0; leave: ksba_cms_release (cms); gnupg_ksba_destroy_reader (b64reader); gnupg_ksba_destroy_writer (b64writer); keydb_release (kh); gcry_md_close (data_md); es_fclose (in_fp); if (rc) { char numbuf[50]; sprintf (numbuf, "%d", rc ); gpgsm_status2 (ctrl, STATUS_ERROR, "verify.leave", numbuf, NULL); } return rc; }