diff --git a/common/Makefile.am b/common/Makefile.am index 83d82ac1f..fcbe7ea66 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -1,227 +1,228 @@ # Makefile for common gnupg modules # Copyright (C) 2001, 2003, 2007, 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 . ## Process this file with automake to produce Makefile.in EXTRA_DIST = mkstrtable.awk exaudit.awk exstatus.awk ChangeLog-2011 \ audit-events.h status-codes.h ChangeLog.jnlib \ ChangeLog-2011.include w32info-rc.h.in gnupg.ico \ all-tests.scm noinst_LIBRARIES = libcommon.a libcommonpth.a libgpgrl.a if !HAVE_W32CE_SYSTEM noinst_LIBRARIES += libsimple-pwquery.a endif noinst_PROGRAMS = $(module_tests) $(module_maint_tests) TESTS = $(module_tests) BUILT_SOURCES = audit-events.h status-codes.h MAINTAINERCLEANFILES = audit-events.h status-codes.h AM_CPPFLAGS = AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(LIBASSUAN_CFLAGS) $(KSBA_CFLAGS) include $(top_srcdir)/am/cmacros.am common_sources = \ common-defs.h \ util.h utilproto.h fwddecl.h i18n.c i18n.h \ types.h host2net.h dynload.h w32help.h \ mapstrings.c stringhelp.c stringhelp.h \ strlist.c strlist.h \ utf8conv.c utf8conv.h \ argparse.c argparse.h \ logging.c logging.h \ dotlock.c dotlock.h \ mischelp.c mischelp.h \ status.c status.h\ shareddefs.h \ openpgpdefs.h \ gc-opt-flags.h \ keyserver.h \ sexp-parse.h \ tlv.c tlv.h \ init.c init.h \ sexputil.c \ sysutils.c sysutils.h \ homedir.c \ gettime.c gettime.h \ yesno.c \ b64enc.c b64dec.c zb32.c zb32.h \ convert.c \ percent.c \ mbox-util.c mbox-util.h \ miscellaneous.c \ xasprintf.c \ xreadline.c \ membuf.c membuf.h \ ccparray.c ccparray.h \ iobuf.c iobuf.h \ ttyio.c ttyio.h \ asshelp.c asshelp2.c asshelp.h \ exechelp.h \ signal.c \ audit.c audit.h \ localename.c \ session-env.c session-env.h \ userids.c userids.h \ openpgp-oid.c \ ssh-utils.c ssh-utils.h \ agent-opt.c \ helpfile.c \ mkdir_p.c mkdir_p.h \ strlist.c strlist.h \ exectool.c exectool.h \ server-help.c server-help.h \ name-value.c name-value.h \ recsel.c recsel.h \ - ksba-io-support.c ksba-io-support.h + ksba-io-support.c ksba-io-support.h \ + compliance.c compliance.h if HAVE_W32_SYSTEM common_sources += w32-reg.c endif # To make the code easier to read we have split home some code into # separate source files. if HAVE_W32_SYSTEM if HAVE_W32CE_SYSTEM common_sources += exechelp-w32ce.c else common_sources += exechelp-w32.c endif else common_sources += exechelp-posix.c endif # Sources only useful without NPTH. without_npth_sources = \ get-passphrase.c get-passphrase.h # Sources only useful with NPTH. with_npth_sources = \ call-gpg.c call-gpg.h libcommon_a_SOURCES = $(common_sources) $(without_npth_sources) libcommon_a_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) -DWITHOUT_NPTH=1 libcommonpth_a_SOURCES = $(common_sources) $(with_npth_sources) libcommonpth_a_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS) if !HAVE_W32CE_SYSTEM libsimple_pwquery_a_SOURCES = \ simple-pwquery.c simple-pwquery.h asshelp.c asshelp.h libsimple_pwquery_a_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) endif libgpgrl_a_SOURCES = \ gpgrlhelp.c if MAINTAINER_MODE # Note: Due to the dependency on Makefile, the file will always be # rebuilt, so we allow this only in maintainer mode. # Create the audit-events.h include file from audit.h # Note: We create the target file in the source directory because it # is a distributed built source. If we would not do that we may end # up with two files and then it is not clear which version of the # files will be picked up. audit-events.h: Makefile.am mkstrtable.awk exaudit.awk audit.h $(AWK) -f $(srcdir)/exaudit.awk $(srcdir)/audit.h \ | $(AWK) -f $(srcdir)/mkstrtable.awk -v textidx=3 -v nogettext=1 \ -v namespace=eventstr_ > $(srcdir)/audit-events.h # Create the status-codes.h include file from status.h status-codes.h: Makefile.am mkstrtable.awk exstatus.awk status.h $(AWK) -f $(srcdir)/exstatus.awk $(srcdir)/status.h \ | $(AWK) -f $(srcdir)/mkstrtable.awk -v textidx=3 -v nogettext=1 \ -v namespace=statusstr_ > $(srcdir)/status-codes.h endif # # Module tests # module_tests = t-stringhelp t-timestuff \ t-convert t-percent t-gettime t-sysutils t-sexputil \ t-session-env t-openpgp-oid t-ssh-utils \ t-mapstrings t-zb32 t-mbox-util t-iobuf t-strlist \ t-name-value t-ccparray t-recsel if !HAVE_W32CE_SYSTEM module_tests += t-exechelp t-exectool endif if HAVE_W32_SYSTEM module_tests += t-w32-reg endif if MAINTAINER_MODE module_maint_tests = t-helpfile t-b64 else module_maint_tests = endif t_extra_src = t-support.h t_common_cflags = $(KSBA_CFLAGS) $(LIBGCRYPT_CFLAGS) \ $(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS) $(INCICONV) t_common_ldadd = libcommon.a \ $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \ $(LIBINTL) $(LIBICONV) # Common tests t_stringhelp_SOURCES = t-stringhelp.c $(t_extra_src) t_stringhelp_LDADD = $(t_common_ldadd) t_timestuff_SOURCES = t-timestuff.c $(t_extra_src) t_timestuff_LDADD = $(t_common_ldadd) t_convert_LDADD = $(t_common_ldadd) t_percent_LDADD = $(t_common_ldadd) t_gettime_LDADD = $(t_common_ldadd) t_sysutils_LDADD = $(t_common_ldadd) t_helpfile_LDADD = $(t_common_ldadd) t_sexputil_LDADD = $(t_common_ldadd) t_b64_LDADD = $(t_common_ldadd) t_exechelp_LDADD = $(t_common_ldadd) t_exectool_LDADD = $(t_common_ldadd) t_session_env_LDADD = $(t_common_ldadd) t_openpgp_oid_LDADD = $(t_common_ldadd) t_ssh_utils_LDADD = $(t_common_ldadd) t_mapstrings_LDADD = $(t_common_ldadd) t_zb32_SOURCES = t-zb32.c $(t_extra_src) t_zb32_LDADD = $(t_common_ldadd) t_mbox_util_LDADD = $(t_common_ldadd) t_iobuf_LDADD = $(t_common_ldadd) t_strlist_LDADD = $(t_common_ldadd) t_name_value_LDADD = $(t_common_ldadd) t_ccparray_LDADD = $(t_common_ldadd) t_recsel_LDADD = $(t_common_ldadd) # System specific test if HAVE_W32_SYSTEM t_w32_reg_SOURCES = t-w32-reg.c $(t_extra_src) t_w32_reg_LDADD = $(t_common_ldadd) endif # All programs should depend on the created libs. $(PROGRAMS) : libcommon.a libcommonpth.a diff --git a/common/compliance.c b/common/compliance.c new file mode 100644 index 000000000..73c7ad724 --- /dev/null +++ b/common/compliance.c @@ -0,0 +1,144 @@ +/* compliance.c - Functions for compliance modi + * Copyright (C) 2017 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file 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 "openpgpdefs.h" +#include "logging.h" +#include "util.h" +#include "compliance.h" + +/* Return true if ALGO with a key of KEYLENGTH is compliant to the + * give COMPLIANCE mode. If KEY is not NULL, various bits of + * information will be extracted from it. If CURVENAME is not NULL, it + * is assumed to be the already computed. ALGO may be either an + * OpenPGP-style pubkey_algo_t, or a gcrypt-style enum gcry_pk_algos, + * both are compatible from the point of view of this function. */ +int +gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo, + gcry_mpi_t key[], unsigned int keylength, const char *curvename) +{ + enum { is_rsa, is_pgp5, is_elg_sign, is_ecc } algotype; + int result; + + switch (algo) + { + case PUBKEY_ALGO_RSA: + case PUBKEY_ALGO_RSA_E: + case PUBKEY_ALGO_RSA_S: + algotype = is_rsa; + break; + + case PUBKEY_ALGO_ELGAMAL_E: + case PUBKEY_ALGO_DSA: + algotype = is_pgp5; + break; + + case PUBKEY_ALGO_ECDH: + case PUBKEY_ALGO_ECDSA: + case PUBKEY_ALGO_EDDSA: + algotype = is_ecc; + break; + + case PUBKEY_ALGO_ELGAMAL: + algotype = is_elg_sign; + break; + + default: /* Unknown. */ + return 0; + } + + if (compliance == CO_DE_VS) + { + char *curve = NULL; + + switch (algotype) + { + case is_pgp5: + result = 0; + break; + + case is_rsa: + result = (keylength >= 2048); + break; + + case is_ecc: + if (!curvename && key) + { + curve = openpgp_oid_to_str (key[0]); + curvename = openpgp_oid_to_curve (curve, 0); + if (!curvename) + curvename = curve; + } + + result = (curvename + && algo != PUBKEY_ALGO_EDDSA + && (!strcmp (curvename, "brainpoolP256r1") + || !strcmp (curvename, "brainpoolP384r1") + || !strcmp (curvename, "brainpoolP512r1"))); + break; + + default: + result = 0; + } + xfree (curve); + } + else if (algotype == is_elg_sign) + { + /* An Elgamal signing key is only RFC-2440 compliant. */ + result = (compliance == CO_RFC2440); + } + else + { + result = 1; /* Assume compliance. */ + } + + return result; +} + + +const char * +gnupg_status_compliance_flag (enum gnupg_compliance_mode compliance) +{ + switch (compliance) + { + case CO_GNUPG: + return "8"; + case CO_RFC4880: + case CO_RFC2440: + case CO_PGP6: + case CO_PGP7: + case CO_PGP8: + log_assert (!"no status code assigned for this compliance mode"); + case CO_DE_VS: + return "23"; + } + log_assert (!"invalid compliance mode"); +} diff --git a/common/compliance.h b/common/compliance.h new file mode 100644 index 000000000..123bd1b50 --- /dev/null +++ b/common/compliance.h @@ -0,0 +1,47 @@ +/* compliance.h - Definitions for compliance modi + * Copyright (C) 2017 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef GNUPG_COMMON_COMPLIANCE_H +#define GNUPG_COMMON_COMPLIANCE_H + +#include +#include "openpgpdefs.h" + +enum gnupg_compliance_mode + { + CO_GNUPG, CO_RFC4880, CO_RFC2440, + CO_PGP6, CO_PGP7, CO_PGP8, CO_DE_VS + }; + +int gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo, + gcry_mpi_t key[], unsigned int keylength, + const char *curvename); +const char *gnupg_status_compliance_flag (enum gnupg_compliance_mode compliance); + +#endif /*GNUPG_COMMON_COMPLIANCE_H*/ diff --git a/g10/keylist.c b/g10/keylist.c index e2b8fef05..4848bab64 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -1,1961 +1,1967 @@ /* keylist.c - Print information about OpenPGP keys * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, * 2008, 2010, 2012 Free Software Foundation, Inc. * Copyright (C) 2013, 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 . */ #include #include #include #include #include #ifdef HAVE_DOSISH_SYSTEM # include /* for setmode() */ #endif #include "gpg.h" #include "options.h" #include "packet.h" #include "../common/status.h" #include "keydb.h" #include "photoid.h" #include "../common/util.h" #include "../common/ttyio.h" #include "trustdb.h" #include "main.h" #include "../common/i18n.h" #include "../common/status.h" #include "call-agent.h" #include "../common/mbox-util.h" #include "../common/zb32.h" #include "tofu.h" +#include "../common/compliance.h" static void list_all (ctrl_t, int, int); static void list_one (ctrl_t ctrl, strlist_t names, int secret, int mark_secret); static void locate_one (ctrl_t ctrl, strlist_t names); static void print_card_serialno (const char *serialno); struct keylist_context { int check_sigs; /* If set signatures shall be verified. */ int good_sigs; /* Counter used if CHECK_SIGS is set. */ int inv_sigs; /* Counter used if CHECK_SIGS is set. */ int no_key; /* Counter used if CHECK_SIGS is set. */ int oth_err; /* Counter used if CHECK_SIGS is set. */ int no_validity; /* Do not show validity. */ }; static void list_keyblock (ctrl_t ctrl, kbnode_t keyblock, int secret, int has_secret, int fpr, struct keylist_context *listctx); /* The stream used to write attribute packets to. */ static estream_t attrib_fp; /* Release resources from a keylist context. */ static void keylist_context_release (struct keylist_context *listctx) { (void)listctx; /* Nothing to release. */ } /* List the keys. If list is NULL, all available keys are listed. With LOCATE_MODE set the locate algorithm is used to find a key. */ void public_key_list (ctrl_t ctrl, strlist_t list, int locate_mode) { #ifndef NO_TRUST_MODELS if (opt.with_colons) { byte trust_model, marginals, completes, cert_depth, min_cert_level; ulong created, nextcheck; read_trust_options (ctrl, &trust_model, &created, &nextcheck, &marginals, &completes, &cert_depth, &min_cert_level); es_fprintf (es_stdout, "tru:"); if (nextcheck && nextcheck <= make_timestamp ()) es_fprintf (es_stdout, "o"); if (trust_model != opt.trust_model) es_fprintf (es_stdout, "t"); if (opt.trust_model == TM_PGP || opt.trust_model == TM_CLASSIC || opt.trust_model == TM_TOFU_PGP) { if (marginals != opt.marginals_needed) es_fprintf (es_stdout, "m"); if (completes != opt.completes_needed) es_fprintf (es_stdout, "c"); if (cert_depth != opt.max_cert_depth) es_fprintf (es_stdout, "d"); if (min_cert_level != opt.min_cert_level) es_fprintf (es_stdout, "l"); } es_fprintf (es_stdout, ":%d:%lu:%lu", trust_model, created, nextcheck); /* Only show marginals, completes, and cert_depth in the classic or PGP trust models since they are not meaningful otherwise. */ if (trust_model == TM_PGP || trust_model == TM_CLASSIC) es_fprintf (es_stdout, ":%d:%d:%d", marginals, completes, cert_depth); es_fprintf (es_stdout, "\n"); } #endif /*!NO_TRUST_MODELS*/ /* We need to do the stale check right here because it might need to update the keyring while we already have the keyring open. This is very bad for W32 because of a sharing violation. For real OSes it might lead to false results if we are later listing a keyring which is associated with the inode of a deleted file. */ check_trustdb_stale (ctrl); #ifdef USE_TOFU tofu_begin_batch_update (ctrl); #endif if (locate_mode) locate_one (ctrl, list); else if (!list) list_all (ctrl, 0, opt.with_secret); else list_one (ctrl, list, 0, opt.with_secret); #ifdef USE_TOFU tofu_end_batch_update (ctrl); #endif } void secret_key_list (ctrl_t ctrl, strlist_t list) { (void)ctrl; check_trustdb_stale (ctrl); if (!list) list_all (ctrl, 1, 0); else /* List by user id */ list_one (ctrl, list, 1, 0); } char * format_seckey_info (ctrl_t ctrl, PKT_public_key *pk) { u32 keyid[2]; char *p; char pkstrbuf[PUBKEY_STRING_SIZE]; char *info; keyid_from_pk (pk, keyid); p = get_user_id_native (ctrl, keyid); info = xtryasprintf ("sec %s/%s %s %s", pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr (keyid), datestr_from_pk (pk), p); xfree (p); return info; } void print_seckey_info (ctrl_t ctrl, PKT_public_key *pk) { char *p = format_seckey_info (ctrl, pk); tty_printf ("\n%s\n", p); xfree (p); } /* Print information about the public key. With FP passed as NULL, the tty output interface is used, otherwise output is directted to the given stream. */ void print_pubkey_info (ctrl_t ctrl, estream_t fp, PKT_public_key *pk) { u32 keyid[2]; char *p; char pkstrbuf[PUBKEY_STRING_SIZE]; keyid_from_pk (pk, keyid); /* If the pk was chosen by a particular user ID, that is the one to print. */ if (pk->user_id) p = utf8_to_native (pk->user_id->name, pk->user_id->len, 0); else p = get_user_id_native (ctrl, keyid); if (fp) tty_printf ("\n"); tty_fprintf (fp, "%s %s/%s %s %s\n", pk->flags.primary? "pub":"sub", pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr (keyid), datestr_from_pk (pk), p); xfree (p); } /* Print basic information of a secret key including the card serial number information. */ #ifdef ENABLE_CARD_SUPPORT void print_card_key_info (estream_t fp, kbnode_t keyblock) { kbnode_t node; char *hexgrip; char *serialno; int s2k_char; char pkstrbuf[PUBKEY_STRING_SIZE]; int indent; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { int rc; PKT_public_key *pk = node->pkt->pkt.public_key; serialno = NULL; rc = hexkeygrip_from_pk (pk, &hexgrip); if (rc) { log_error ("error computing a keygrip: %s\n", gpg_strerror (rc)); s2k_char = '?'; } else if (!agent_get_keyinfo (NULL, hexgrip, &serialno, NULL)) s2k_char = serialno? '>':' '; else s2k_char = '#'; /* Key not found. */ tty_fprintf (fp, "%s%c %s/%s %n", node->pkt->pkttype == PKT_PUBLIC_KEY ? "sec" : "ssb", s2k_char, pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr_from_pk (pk), &indent); tty_fprintf (fp, _("created: %s"), datestr_from_pk (pk)); tty_fprintf (fp, " "); tty_fprintf (fp, _("expires: %s"), expirestr_from_pk (pk)); if (serialno) { tty_fprintf (fp, "\n%*s%s", indent, "", _("card-no: ")); if (strlen (serialno) == 32 && !strncmp (serialno, "D27600012401", 12)) { /* This is an OpenPGP card. Print the relevant part. */ /* Example: D2760001240101010001000003470000 */ /* xxxxyyyyyyyy */ tty_fprintf (fp, "%.*s %.*s", 4, serialno+16, 8, serialno+20); } else tty_fprintf (fp, "%s", serialno); } tty_fprintf (fp, "\n"); xfree (hexgrip); xfree (serialno); } } } #endif /*ENABLE_CARD_SUPPORT*/ /* Flags = 0x01 hashed 0x02 critical. */ static void status_one_subpacket (sigsubpkttype_t type, size_t len, int flags, const byte * buf) { char status[40]; /* Don't print these. */ if (len > 256) return; snprintf (status, sizeof status, "%d %u %u ", type, flags, (unsigned int) len); write_status_text_and_buffer (STATUS_SIG_SUBPACKET, status, buf, len, 0); } /* Print a policy URL. Allowed values for MODE are: * -1 - print to the TTY * 0 - print to stdout. * 1 - use log_info and emit status messages. * 2 - emit only status messages. */ void show_policy_url (PKT_signature * sig, int indent, int mode) { const byte *p; size_t len; int seq = 0, crit; estream_t fp = mode < 0? NULL : mode ? log_get_stream () : es_stdout; while ((p = enum_sig_subpkt (sig->hashed, SIGSUBPKT_POLICY, &len, &seq, &crit))) { if (mode != 2) { const char *str; tty_fprintf (fp, "%*s", indent, ""); if (crit) str = _("Critical signature policy: "); else str = _("Signature policy: "); if (mode > 0) log_info ("%s", str); else tty_fprintf (fp, "%s", str); tty_print_utf8_string2 (fp, p, len, 0); tty_fprintf (fp, "\n"); } if (mode > 0) write_status_buffer (STATUS_POLICY_URL, p, len, 0); } } /* Print a keyserver URL. Allowed values for MODE are: * -1 - print to the TTY * 0 - print to stdout. * 1 - use log_info and emit status messages. * 2 - emit only status messages. */ void show_keyserver_url (PKT_signature * sig, int indent, int mode) { const byte *p; size_t len; int seq = 0, crit; estream_t fp = mode < 0? NULL : mode ? log_get_stream () : es_stdout; while ((p = enum_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_KS, &len, &seq, &crit))) { if (mode != 2) { const char *str; tty_fprintf (fp, "%*s", indent, ""); if (crit) str = _("Critical preferred keyserver: "); else str = _("Preferred keyserver: "); if (mode > 0) log_info ("%s", str); else tty_fprintf (es_stdout, "%s", str); tty_print_utf8_string2 (fp, p, len, 0); tty_fprintf (fp, "\n"); } if (mode > 0) status_one_subpacket (SIGSUBPKT_PREF_KS, len, (crit ? 0x02 : 0) | 0x01, p); } } /* Print notation data. Allowed values for MODE are: * -1 - print to the TTY * 0 - print to stdout. * 1 - use log_info and emit status messages. * 2 - emit only status messages. * * Defined bits in WHICH: * 1 - standard notations * 2 - user notations */ void show_notation (PKT_signature * sig, int indent, int mode, int which) { estream_t fp = mode < 0? NULL : mode ? log_get_stream () : es_stdout; notation_t nd, notations; if (which == 0) which = 3; notations = sig_to_notation (sig); /* There may be multiple notations in the same sig. */ for (nd = notations; nd; nd = nd->next) { if (mode != 2) { int has_at = !!strchr (nd->name, '@'); if ((which & 1 && !has_at) || (which & 2 && has_at)) { const char *str; tty_fprintf (fp, "%*s", indent, ""); if (nd->flags.critical) str = _("Critical signature notation: "); else str = _("Signature notation: "); if (mode > 0) log_info ("%s", str); else tty_fprintf (es_stdout, "%s", str); /* This is all UTF8 */ tty_print_utf8_string2 (fp, nd->name, strlen (nd->name), 0); tty_fprintf (fp, "="); tty_print_utf8_string2 (fp, nd->value, strlen (nd->value), 0); /* (We need to use log_printf so that the next call to a log function does not insert an extra LF.) */ if (mode > 0) log_printf ("\n"); else tty_fprintf (fp, "\n"); } } if (mode > 0) { write_status_buffer (STATUS_NOTATION_NAME, nd->name, strlen (nd->name), 0); if (nd->flags.critical || nd->flags.human) write_status_text (STATUS_NOTATION_FLAGS, nd->flags.critical && nd->flags.human? "1 1" : nd->flags.critical? "1 0" : "0 1"); write_status_buffer (STATUS_NOTATION_DATA, nd->value, strlen (nd->value), 50); } } free_notation (notations); } static void print_signature_stats (struct keylist_context *s) { if (!s->check_sigs) return; /* Signature checking was not requested. */ /* Better flush stdout so that the stats are always printed after * the output. */ es_fflush (es_stdout); if (s->good_sigs) log_info (ngettext("%d good signature\n", "%d good signatures\n", s->good_sigs), s->good_sigs); if (s->inv_sigs) log_info (ngettext("%d bad signature\n", "%d bad signatures\n", s->inv_sigs), s->inv_sigs); if (s->no_key) log_info (ngettext("%d signature not checked due to a missing key\n", "%d signatures not checked due to missing keys\n", s->no_key), s->no_key); if (s->oth_err) log_info (ngettext("%d signature not checked due to an error\n", "%d signatures not checked due to errors\n", s->oth_err), s->oth_err); } /* List all keys. If SECRET is true only secret keys are listed. If MARK_SECRET is true secret keys are indicated in a public key listing. */ static void list_all (ctrl_t ctrl, int secret, int mark_secret) { KEYDB_HANDLE hd; KBNODE keyblock = NULL; int rc = 0; int any_secret; const char *lastresname, *resname; struct keylist_context listctx; memset (&listctx, 0, sizeof (listctx)); if (opt.check_sigs) listctx.check_sigs = 1; hd = keydb_new (); if (!hd) rc = gpg_error_from_syserror (); else rc = keydb_search_first (hd); if (rc) { if (gpg_err_code (rc) != GPG_ERR_NOT_FOUND) log_error ("keydb_search_first failed: %s\n", gpg_strerror (rc)); goto leave; } lastresname = NULL; do { rc = keydb_get_keyblock (hd, &keyblock); if (rc) { if (gpg_err_code (rc) == GPG_ERR_LEGACY_KEY) continue; /* Skip legacy keys. */ log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (rc)); goto leave; } if (secret || mark_secret) any_secret = !agent_probe_any_secret_key (NULL, keyblock); else any_secret = 0; if (secret && !any_secret) ; /* Secret key listing requested but this isn't one. */ else { if (!opt.with_colons) { resname = keydb_get_resource_name (hd); if (lastresname != resname) { int i; es_fprintf (es_stdout, "%s\n", resname); for (i = strlen (resname); i; i--) es_putc ('-', es_stdout); es_putc ('\n', es_stdout); lastresname = resname; } } merge_keys_and_selfsig (ctrl, keyblock); list_keyblock (ctrl, keyblock, secret, any_secret, opt.fingerprint, &listctx); } release_kbnode (keyblock); keyblock = NULL; } while (!(rc = keydb_search_next (hd))); es_fflush (es_stdout); if (rc && gpg_err_code (rc) != GPG_ERR_NOT_FOUND) log_error ("keydb_search_next failed: %s\n", gpg_strerror (rc)); if (keydb_get_skipped_counter (hd)) log_info (ngettext("Warning: %lu key skipped due to its large size\n", "Warning: %lu keys skipped due to their large sizes\n", keydb_get_skipped_counter (hd)), keydb_get_skipped_counter (hd)); if (opt.check_sigs && !opt.with_colons) print_signature_stats (&listctx); leave: keylist_context_release (&listctx); release_kbnode (keyblock); keydb_release (hd); } static void list_one (ctrl_t ctrl, strlist_t names, int secret, int mark_secret) { int rc = 0; KBNODE keyblock = NULL; GETKEY_CTX ctx; const char *resname; const char *keyring_str = _("Keyring"); int i; struct keylist_context listctx; memset (&listctx, 0, sizeof (listctx)); if (!secret && opt.check_sigs) listctx.check_sigs = 1; /* fixme: using the bynames function has the disadvantage that we * don't know whether one of the names given was not found. OTOH, * this function has the advantage to list the names in the * sequence as defined by the keyDB and does not duplicate * outputs. A solution could be do test whether all given have * been listed (this needs a way to use the keyDB search * functions) or to have the search function return indicators for * found names. Yet another way is to use the keydb search * facilities directly. */ rc = getkey_bynames (ctrl, &ctx, NULL, names, secret, &keyblock); if (rc) { log_error ("error reading key: %s\n", gpg_strerror (rc)); getkey_end (ctrl, ctx); return; } do { if ((opt.list_options & LIST_SHOW_KEYRING) && !opt.with_colons) { resname = keydb_get_resource_name (get_ctx_handle (ctx)); es_fprintf (es_stdout, "%s: %s\n", keyring_str, resname); for (i = strlen (resname) + strlen (keyring_str) + 2; i; i--) es_putc ('-', es_stdout); es_putc ('\n', es_stdout); } list_keyblock (ctrl, keyblock, secret, mark_secret, opt.fingerprint, &listctx); release_kbnode (keyblock); } while (!getkey_next (ctrl, ctx, NULL, &keyblock)); getkey_end (ctrl, ctx); if (opt.check_sigs && !opt.with_colons) print_signature_stats (&listctx); keylist_context_release (&listctx); } static void locate_one (ctrl_t ctrl, strlist_t names) { int rc = 0; strlist_t sl; GETKEY_CTX ctx = NULL; KBNODE keyblock = NULL; struct keylist_context listctx; memset (&listctx, 0, sizeof (listctx)); if (opt.check_sigs) listctx.check_sigs = 1; for (sl = names; sl; sl = sl->next) { rc = get_best_pubkey_byname (ctrl, &ctx, NULL, sl->d, &keyblock, 1, 0); if (rc) { if (gpg_err_code (rc) != GPG_ERR_NO_PUBKEY) log_error ("error reading key: %s\n", gpg_strerror (rc)); else if (opt.verbose) log_info (_("key \"%s\" not found: %s\n"), sl->d, gpg_strerror (rc)); } else { do { list_keyblock (ctrl, keyblock, 0, 0, opt.fingerprint, &listctx); release_kbnode (keyblock); } while (ctx && !getkey_next (ctrl, ctx, NULL, &keyblock)); getkey_end (ctrl, ctx); ctx = NULL; } } if (opt.check_sigs && !opt.with_colons) print_signature_stats (&listctx); keylist_context_release (&listctx); } static void print_key_data (PKT_public_key * pk) { int n = pk ? pubkey_get_npkey (pk->pubkey_algo) : 0; int i; for (i = 0; i < n; i++) { es_fprintf (es_stdout, "pkd:%d:%u:", i, mpi_get_nbits (pk->pkey[i])); mpi_print (es_stdout, pk->pkey[i], 1); es_putc (':', es_stdout); es_putc ('\n', es_stdout); } } static void print_capabilities (ctrl_t ctrl, PKT_public_key *pk, KBNODE keyblock) { unsigned int use = pk->pubkey_usage; int c_printed = 0; if (use & PUBKEY_USAGE_ENC) es_putc ('e', es_stdout); if (use & PUBKEY_USAGE_SIG) { es_putc ('s', es_stdout); if (pk->flags.primary) { es_putc ('c', es_stdout); /* The PUBKEY_USAGE_CERT flag was introduced later and we used to always print 'c' for a primary key. To avoid any regression here we better track whether we printed 'c' already. */ c_printed = 1; } } if ((use & PUBKEY_USAGE_CERT) && !c_printed) es_putc ('c', es_stdout); if ((use & PUBKEY_USAGE_AUTH)) es_putc ('a', es_stdout); if ((use & PUBKEY_USAGE_UNKNOWN)) es_putc ('?', es_stdout); if (keyblock) { /* Figure out the usable capabilities. */ KBNODE k; int enc = 0, sign = 0, cert = 0, auth = 0, disabled = 0; for (k = keyblock; k; k = k->next) { if (k->pkt->pkttype == PKT_PUBLIC_KEY || k->pkt->pkttype == PKT_PUBLIC_SUBKEY) { pk = k->pkt->pkt.public_key; if (pk->flags.primary) disabled = pk_is_disabled (pk); if (pk->flags.valid && !pk->flags.revoked && !pk->has_expired) { if (pk->pubkey_usage & PUBKEY_USAGE_ENC) enc = 1; if (pk->pubkey_usage & PUBKEY_USAGE_SIG) { sign = 1; if (pk->flags.primary) cert = 1; } if (pk->pubkey_usage & PUBKEY_USAGE_CERT) cert = 1; if ((pk->pubkey_usage & PUBKEY_USAGE_AUTH)) auth = 1; } } } if (enc) es_putc ('E', es_stdout); if (sign) es_putc ('S', es_stdout); if (cert) es_putc ('C', es_stdout); if (auth) es_putc ('A', es_stdout); if (disabled) es_putc ('D', es_stdout); } es_putc (':', es_stdout); } /* FLAGS: 0x01 hashed 0x02 critical */ static void print_one_subpacket (sigsubpkttype_t type, size_t len, int flags, const byte * buf) { size_t i; es_fprintf (es_stdout, "spk:%d:%u:%u:", type, flags, (unsigned int) len); for (i = 0; i < len; i++) { /* printable ascii other than : and % */ if (buf[i] >= 32 && buf[i] <= 126 && buf[i] != ':' && buf[i] != '%') es_fprintf (es_stdout, "%c", buf[i]); else es_fprintf (es_stdout, "%%%02X", buf[i]); } es_fprintf (es_stdout, "\n"); } void print_subpackets_colon (PKT_signature * sig) { byte *i; log_assert (opt.show_subpackets); for (i = opt.show_subpackets; *i; i++) { const byte *p; size_t len; int seq, crit; seq = 0; while ((p = enum_sig_subpkt (sig->hashed, *i, &len, &seq, &crit))) print_one_subpacket (*i, len, 0x01 | (crit ? 0x02 : 0), p); seq = 0; while ((p = enum_sig_subpkt (sig->unhashed, *i, &len, &seq, &crit))) print_one_subpacket (*i, len, 0x00 | (crit ? 0x02 : 0), p); } } void dump_attribs (const PKT_user_id *uid, PKT_public_key *pk) { int i; if (!attrib_fp) return; for (i = 0; i < uid->numattribs; i++) { if (is_status_enabled ()) { byte array[MAX_FINGERPRINT_LEN], *p; char buf[(MAX_FINGERPRINT_LEN * 2) + 90]; size_t j, n; if (!pk) BUG (); fingerprint_from_pk (pk, array, &n); p = array; for (j = 0; j < n; j++, p++) sprintf (buf + 2 * j, "%02X", *p); sprintf (buf + strlen (buf), " %lu %u %u %u %lu %lu %u", (ulong) uid->attribs[i].len, uid->attribs[i].type, i + 1, uid->numattribs, (ulong) uid->created, (ulong) uid->expiredate, ((uid->flags.primary ? 0x01 : 0) | (uid->flags.revoked ? 0x02 : 0) | (uid->flags.expired ? 0x04 : 0))); write_status_text (STATUS_ATTRIBUTE, buf); } es_fwrite (uid->attribs[i].data, uid->attribs[i].len, 1, attrib_fp); es_fflush (attrib_fp); } } static void list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr, struct keylist_context *listctx) { int rc; KBNODE kbctx; KBNODE node; PKT_public_key *pk; int skip_sigs = 0; char *hexgrip = NULL; char *serialno = NULL; /* Get the keyid from the keyblock. */ node = find_kbnode (keyblock, PKT_PUBLIC_KEY); if (!node) { log_error ("Oops; key lost!\n"); dump_kbnode (keyblock); return; } pk = node->pkt->pkt.public_key; if (secret || opt.with_keygrip) { rc = hexkeygrip_from_pk (pk, &hexgrip); if (rc) log_error ("error computing a keygrip: %s\n", gpg_strerror (rc)); } if (secret) { /* Encode some info about the secret key in SECRET. */ if (!agent_get_keyinfo (NULL, hexgrip, &serialno, NULL)) secret = serialno? 3 : 1; else secret = 2; /* Key not found. */ } if (!listctx->no_validity) check_trustdb_stale (ctrl); /* Print the "pub" line and in KF_NONE mode the fingerprint. */ print_key_line (ctrl, es_stdout, pk, secret); if (fpr) print_fingerprint (ctrl, NULL, pk, 0); if (opt.with_keygrip && hexgrip) es_fprintf (es_stdout, " Keygrip = %s\n", hexgrip); if (serialno) print_card_serialno (serialno); if (opt.with_key_data) print_key_data (pk); for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));) { if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; int indent; int kl = opt.keyid_format == KF_NONE? 10 : keystrlen (); if ((uid->flags.expired || uid->flags.revoked) && !(opt.list_options & LIST_SHOW_UNUSABLE_UIDS)) { skip_sigs = 1; continue; } else skip_sigs = 0; if (attrib_fp && uid->attrib_data != NULL) dump_attribs (uid, pk); if ((uid->flags.revoked || uid->flags.expired) || ((opt.list_options & LIST_SHOW_UID_VALIDITY) && !listctx->no_validity)) { const char *validity; validity = uid_trust_string_fixed (ctrl, pk, uid); indent = ((kl + (opt.legacy_list_mode? 9:11)) - atoi (uid_trust_string_fixed (ctrl, NULL, NULL))); if (indent < 0 || indent > 40) indent = 0; es_fprintf (es_stdout, "uid%*s%s ", indent, "", validity); } else { indent = kl + (opt.legacy_list_mode? 10:12); es_fprintf (es_stdout, "uid%*s", indent, ""); } print_utf8_buffer (es_stdout, uid->name, uid->len); es_putc ('\n', es_stdout); if (opt.with_wkd_hash) { char *mbox, *hash, *p; char hashbuf[32]; mbox = mailbox_from_userid (uid->name); if (mbox && (p = strchr (mbox, '@'))) { *p++ = 0; gcry_md_hash_buffer (GCRY_MD_SHA1, hashbuf, mbox, strlen (mbox)); hash = zb32_encode (hashbuf, 8*20); if (hash) { es_fprintf (es_stdout, " %*s%s@%s\n", indent, "", hash, p); xfree (hash); } } xfree (mbox); } if ((opt.list_options & LIST_SHOW_PHOTOS) && uid->attribs != NULL) show_photos (ctrl, uid->attribs, uid->numattribs, pk, uid); } else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { PKT_public_key *pk2 = node->pkt->pkt.public_key; if ((pk2->flags.revoked || pk2->has_expired) && !(opt.list_options & LIST_SHOW_UNUSABLE_SUBKEYS)) { skip_sigs = 1; continue; } else skip_sigs = 0; xfree (serialno); serialno = NULL; xfree (hexgrip); hexgrip = NULL; if (secret || opt.with_keygrip) { rc = hexkeygrip_from_pk (pk2, &hexgrip); if (rc) log_error ("error computing a keygrip: %s\n", gpg_strerror (rc)); } if (secret) { if (!agent_get_keyinfo (NULL, hexgrip, &serialno, NULL)) secret = serialno? 3 : 1; else secret = 2; /* Key not found. */ } /* Print the "sub" line. */ print_key_line (ctrl, es_stdout, pk2, secret); if (fpr > 1 || opt.with_subkey_fingerprint) { print_fingerprint (ctrl, NULL, pk2, 0); if (serialno) print_card_serialno (serialno); } if (opt.with_keygrip && hexgrip) es_fprintf (es_stdout, " Keygrip = %s\n", hexgrip); if (opt.with_key_data) print_key_data (pk2); } else if (opt.list_sigs && node->pkt->pkttype == PKT_SIGNATURE && !skip_sigs) { PKT_signature *sig = node->pkt->pkt.signature; int sigrc; char *sigstr; if (listctx->check_sigs) { rc = check_key_signature (ctrl, keyblock, node, NULL); switch (gpg_err_code (rc)) { case 0: listctx->good_sigs++; sigrc = '!'; break; case GPG_ERR_BAD_SIGNATURE: listctx->inv_sigs++; sigrc = '-'; break; case GPG_ERR_NO_PUBKEY: case GPG_ERR_UNUSABLE_PUBKEY: listctx->no_key++; continue; default: listctx->oth_err++; sigrc = '%'; break; } /* TODO: Make sure a cached sig record here still has the pk that issued it. See also keyedit.c:print_and_check_one_sig */ } else { rc = 0; sigrc = ' '; } if (sig->sig_class == 0x20 || sig->sig_class == 0x28 || sig->sig_class == 0x30) sigstr = "rev"; else if ((sig->sig_class & ~3) == 0x10) sigstr = "sig"; else if (sig->sig_class == 0x18) sigstr = "sig"; else if (sig->sig_class == 0x1F) sigstr = "sig"; else { es_fprintf (es_stdout, "sig " "[unexpected signature class 0x%02x]\n", sig->sig_class); continue; } es_fputs (sigstr, es_stdout); es_fprintf (es_stdout, "%c%c %c%c%c%c%c%c %s %s", sigrc, (sig->sig_class - 0x10 > 0 && sig->sig_class - 0x10 < 4) ? '0' + sig->sig_class - 0x10 : ' ', sig->flags.exportable ? ' ' : 'L', sig->flags.revocable ? ' ' : 'R', sig->flags.policy_url ? 'P' : ' ', sig->flags.notation ? 'N' : ' ', sig->flags.expired ? 'X' : ' ', (sig->trust_depth > 9) ? 'T' : (sig->trust_depth > 0) ? '0' + sig->trust_depth : ' ', keystr (sig->keyid), datestr_from_sig (sig)); if (opt.list_options & LIST_SHOW_SIG_EXPIRE) es_fprintf (es_stdout, " %s", expirestr_from_sig (sig)); es_fprintf (es_stdout, " "); if (sigrc == '%') es_fprintf (es_stdout, "[%s] ", gpg_strerror (rc)); else if (sigrc == '?') ; else if (!opt.fast_list_mode) { size_t n; char *p = get_user_id (ctrl, sig->keyid, &n); print_utf8_buffer (es_stdout, p, n); xfree (p); } es_putc ('\n', es_stdout); if (sig->flags.policy_url && (opt.list_options & LIST_SHOW_POLICY_URLS)) show_policy_url (sig, 3, 0); if (sig->flags.notation && (opt.list_options & LIST_SHOW_NOTATIONS)) show_notation (sig, 3, 0, ((opt. list_options & LIST_SHOW_STD_NOTATIONS) ? 1 : 0) + ((opt. list_options & LIST_SHOW_USER_NOTATIONS) ? 2 : 0)); if (sig->flags.pref_ks && (opt.list_options & LIST_SHOW_KEYSERVER_URLS)) show_keyserver_url (sig, 3, 0); /* fixme: check or list other sigs here */ } } es_putc ('\n', es_stdout); xfree (serialno); xfree (hexgrip); } void print_revokers (estream_t fp, PKT_public_key * pk) { /* print the revoker record */ if (!pk->revkey && pk->numrevkeys) BUG (); else { int i, j; for (i = 0; i < pk->numrevkeys; i++) { byte *p; es_fprintf (fp, "rvk:::%d::::::", pk->revkey[i].algid); p = pk->revkey[i].fpr; for (j = 0; j < 20; j++, p++) es_fprintf (fp, "%02X", *p); es_fprintf (fp, ":%02x%s:\n", pk->revkey[i].class, (pk->revkey[i].class & 0x40) ? "s" : ""); } } } /* Print the compliance flags to field 18. PK is the public key. * KEYLENGTH is the length of the key in bits and CURVENAME is either * NULL or the name of the curve. The latter two args are here * merely because the caller has already computed them. */ static void print_compliance_flags (PKT_public_key *pk, unsigned int keylength, const char *curvename) { int any = 0; + if (!keylength) + keylength = nbits_from_pk (pk); + if (pk->version == 5) { - es_fputs ("8", es_stdout); + es_fputs (gnupg_status_compliance_flag (CO_GNUPG), es_stdout); any++; } - if (gnupg_pk_is_compliant (CO_DE_VS, pk, keylength, curvename)) + if (gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, pk->pkey, + keylength, curvename)) { - es_fputs (any? " 23":"23", es_stdout); + es_fprintf (es_stdout, any ? " %s" : "%s", + gnupg_status_compliance_flag (CO_DE_VS)); any++; } } /* List a key in colon mode. If SECRET is true this is a secret key record (i.e. requested via --list-secret-key). If HAS_SECRET a secret key is available even if SECRET is not set. */ static void list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock, int secret, int has_secret) { int rc; KBNODE kbctx; KBNODE node; PKT_public_key *pk; u32 keyid[2]; int trustletter = 0; int trustletter_print; int ownertrust_print; int ulti_hack = 0; int i; char *hexgrip_buffer = NULL; const char *hexgrip = NULL; char *serialno = NULL; int stubkey; unsigned int keylength; char *curve = NULL; const char *curvename = NULL; /* Get the keyid from the keyblock. */ node = find_kbnode (keyblock, PKT_PUBLIC_KEY); if (!node) { log_error ("Oops; key lost!\n"); dump_kbnode (keyblock); return; } pk = node->pkt->pkt.public_key; if (secret || has_secret || opt.with_keygrip || opt.with_key_data) { rc = hexkeygrip_from_pk (pk, &hexgrip_buffer); if (rc) log_error ("error computing a keygrip: %s\n", gpg_strerror (rc)); /* In the error case we print an empty string so that we have a * "grp" record for each and subkey - even if it is empty. This * may help to prevent sync problems. */ hexgrip = hexgrip_buffer? hexgrip_buffer : ""; } stubkey = 0; if ((secret || has_secret) && agent_get_keyinfo (NULL, hexgrip, &serialno, NULL)) stubkey = 1; /* Key not found. */ keyid_from_pk (pk, keyid); if (!pk->flags.valid) trustletter_print = 'i'; else if (pk->flags.revoked) trustletter_print = 'r'; else if (pk->has_expired) trustletter_print = 'e'; else if (opt.fast_list_mode || opt.no_expensive_trust_checks) trustletter_print = 0; else { trustletter = get_validity_info (ctrl, keyblock, pk, NULL); if (trustletter == 'u') ulti_hack = 1; trustletter_print = trustletter; } if (!opt.fast_list_mode && !opt.no_expensive_trust_checks) ownertrust_print = get_ownertrust_info (ctrl, pk, 0); else ownertrust_print = 0; keylength = nbits_from_pk (pk); es_fputs (secret? "sec:":"pub:", es_stdout); if (trustletter_print) es_putc (trustletter_print, es_stdout); es_fprintf (es_stdout, ":%u:%d:%08lX%08lX:%s:%s::", keylength, pk->pubkey_algo, (ulong) keyid[0], (ulong) keyid[1], colon_datestr_from_pk (pk), colon_strtime (pk->expiredate)); if (ownertrust_print) es_putc (ownertrust_print, es_stdout); es_putc (':', es_stdout); es_putc (':', es_stdout); es_putc (':', es_stdout); print_capabilities (ctrl, pk, keyblock); es_putc (':', es_stdout); /* End of field 13. */ es_putc (':', es_stdout); /* End of field 14. */ if (secret || has_secret) { if (stubkey) es_putc ('#', es_stdout); else if (serialno) es_fputs (serialno, es_stdout); else if (has_secret) es_putc ('+', es_stdout); } es_putc (':', es_stdout); /* End of field 15. */ es_putc (':', es_stdout); /* End of field 16. */ if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA || pk->pubkey_algo == PUBKEY_ALGO_EDDSA || pk->pubkey_algo == PUBKEY_ALGO_ECDH) { curve = openpgp_oid_to_str (pk->pkey[0]); curvename = openpgp_oid_to_curve (curve, 0); if (!curvename) curvename = curve; es_fputs (curvename, es_stdout); } es_putc (':', es_stdout); /* End of field 17. */ print_compliance_flags (pk, keylength, curvename); es_putc (':', es_stdout); /* End of field 18 (compliance). */ es_putc (':', es_stdout); /* End of field 19 (last_update). */ es_putc (':', es_stdout); /* End of field 20 (origin). */ es_putc ('\n', es_stdout); print_revokers (es_stdout, pk); print_fingerprint (ctrl, NULL, pk, 0); if (hexgrip) es_fprintf (es_stdout, "grp:::::::::%s:\n", hexgrip); if (opt.with_key_data) print_key_data (pk); for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));) { if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; int uid_validity; if (attrib_fp && uid->attrib_data != NULL) dump_attribs (uid, pk); if (uid->flags.revoked) uid_validity = 'r'; else if (uid->flags.expired) uid_validity = 'e'; else if (opt.no_expensive_trust_checks) uid_validity = 0; else if (ulti_hack) uid_validity = 'u'; else uid_validity = get_validity_info (ctrl, keyblock, pk, uid); es_fputs (uid->attrib_data? "uat:":"uid:", es_stdout); if (uid_validity) es_putc (uid_validity, es_stdout); es_fputs ("::::", es_stdout); es_fprintf (es_stdout, "%s:", colon_strtime (uid->created)); es_fprintf (es_stdout, "%s:", colon_strtime (uid->expiredate)); namehash_from_uid (uid); for (i = 0; i < 20; i++) es_fprintf (es_stdout, "%02X", uid->namehash[i]); es_fprintf (es_stdout, "::"); if (uid->attrib_data) es_fprintf (es_stdout, "%u %lu", uid->numattribs, uid->attrib_len); else es_write_sanitized (es_stdout, uid->name, uid->len, ":", NULL); es_fputs (":::::::::", es_stdout); es_putc (':', es_stdout); /* End of field 19 (last_update). */ es_putc (':', es_stdout); /* End of field 20 (origin). */ es_putc ('\n', es_stdout); #ifdef USE_TOFU if (!uid->attrib_data && opt.with_tofu_info && (opt.trust_model == TM_TOFU || opt.trust_model == TM_TOFU_PGP)) { /* Print a "tfs" record. */ tofu_write_tfs_record (ctrl, es_stdout, pk, uid->name); } #endif /*USE_TOFU*/ } else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { u32 keyid2[2]; PKT_public_key *pk2; int need_hexgrip = !!hexgrip; pk2 = node->pkt->pkt.public_key; xfree (hexgrip_buffer); hexgrip_buffer = NULL; hexgrip = NULL; xfree (serialno); serialno = NULL; if (need_hexgrip || secret || has_secret || opt.with_keygrip || opt.with_key_data) { rc = hexkeygrip_from_pk (pk2, &hexgrip_buffer); if (rc) log_error ("error computing a keygrip: %s\n", gpg_strerror (rc)); hexgrip = hexgrip_buffer? hexgrip_buffer : ""; } stubkey = 0; if ((secret||has_secret) && agent_get_keyinfo (NULL, hexgrip, &serialno, NULL)) stubkey = 1; /* Key not found. */ keyid_from_pk (pk2, keyid2); es_fputs (secret? "ssb:":"sub:", es_stdout); if (!pk2->flags.valid) es_putc ('i', es_stdout); else if (pk2->flags.revoked) es_putc ('r', es_stdout); else if (pk2->has_expired) es_putc ('e', es_stdout); else if (opt.fast_list_mode || opt.no_expensive_trust_checks) ; else { /* TRUSTLETTER should always be defined here. */ if (trustletter) es_fprintf (es_stdout, "%c", trustletter); } keylength = nbits_from_pk (pk2); es_fprintf (es_stdout, ":%u:%d:%08lX%08lX:%s:%s:::::", keylength, pk2->pubkey_algo, (ulong) keyid2[0], (ulong) keyid2[1], colon_datestr_from_pk (pk2), colon_strtime (pk2->expiredate)); print_capabilities (ctrl, pk2, NULL); es_putc (':', es_stdout); /* End of field 13. */ es_putc (':', es_stdout); /* End of field 14. */ if (secret || has_secret) { if (stubkey) es_putc ('#', es_stdout); else if (serialno) es_fputs (serialno, es_stdout); else if (has_secret) es_putc ('+', es_stdout); } es_putc (':', es_stdout); /* End of field 15. */ es_putc (':', es_stdout); /* End of field 16. */ if (pk2->pubkey_algo == PUBKEY_ALGO_ECDSA || pk2->pubkey_algo == PUBKEY_ALGO_EDDSA || pk2->pubkey_algo == PUBKEY_ALGO_ECDH) { xfree (curve); curve = openpgp_oid_to_str (pk2->pkey[0]); curvename = openpgp_oid_to_curve (curve, 0); if (!curvename) curvename = curve; es_fputs (curvename, es_stdout); } es_putc (':', es_stdout); /* End of field 17. */ print_compliance_flags (pk2, keylength, curvename); es_putc (':', es_stdout); /* End of field 18. */ es_putc ('\n', es_stdout); print_fingerprint (ctrl, NULL, pk2, 0); if (hexgrip) es_fprintf (es_stdout, "grp:::::::::%s:\n", hexgrip); if (opt.with_key_data) print_key_data (pk2); } else if (opt.list_sigs && node->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig = node->pkt->pkt.signature; int sigrc, fprokay = 0; char *sigstr; size_t fplen; byte fparray[MAX_FINGERPRINT_LEN]; char *siguid; size_t siguidlen; if (sig->sig_class == 0x20 || sig->sig_class == 0x28 || sig->sig_class == 0x30) sigstr = "rev"; else if ((sig->sig_class & ~3) == 0x10) sigstr = "sig"; else if (sig->sig_class == 0x18) sigstr = "sig"; else if (sig->sig_class == 0x1F) sigstr = "sig"; else { es_fprintf (es_stdout, "sig::::::::::%02x%c:\n", sig->sig_class, sig->flags.exportable ? 'x' : 'l'); continue; } if (opt.check_sigs) { PKT_public_key *signer_pk = NULL; es_fflush (es_stdout); if (opt.no_sig_cache) signer_pk = xmalloc_clear (sizeof (PKT_public_key)); rc = check_key_signature2 (ctrl, keyblock, node, NULL, signer_pk, NULL, NULL, NULL); switch (gpg_err_code (rc)) { case 0: sigrc = '!'; break; case GPG_ERR_BAD_SIGNATURE: sigrc = '-'; break; case GPG_ERR_NO_PUBKEY: case GPG_ERR_UNUSABLE_PUBKEY: sigrc = '?'; break; default: sigrc = '%'; break; } if (opt.no_sig_cache) { if (!rc) { fingerprint_from_pk (signer_pk, fparray, &fplen); fprokay = 1; } free_public_key (signer_pk); } } else { rc = 0; sigrc = ' '; } if (sigrc != '%' && sigrc != '?' && !opt.fast_list_mode) siguid = get_user_id (ctrl, sig->keyid, &siguidlen); else { siguid = NULL; siguidlen = 0; } es_fputs (sigstr, es_stdout); es_putc (':', es_stdout); if (sigrc != ' ') es_putc (sigrc, es_stdout); es_fprintf (es_stdout, "::%d:%08lX%08lX:%s:%s:", sig->pubkey_algo, (ulong) sig->keyid[0], (ulong) sig->keyid[1], colon_datestr_from_sig (sig), colon_expirestr_from_sig (sig)); if (sig->trust_depth || sig->trust_value) es_fprintf (es_stdout, "%d %d", sig->trust_depth, sig->trust_value); es_fprintf (es_stdout, ":"); if (sig->trust_regexp) es_write_sanitized (es_stdout, sig->trust_regexp, strlen (sig->trust_regexp), ":", NULL); es_fprintf (es_stdout, ":"); if (sigrc == '%') es_fprintf (es_stdout, "[%s] ", gpg_strerror (rc)); else if (siguid) es_write_sanitized (es_stdout, siguid, siguidlen, ":", NULL); es_fprintf (es_stdout, ":%02x%c::", sig->sig_class, sig->flags.exportable ? 'x' : 'l'); if (opt.no_sig_cache && opt.check_sigs && fprokay) { for (i = 0; i < fplen; i++) es_fprintf (es_stdout, "%02X", fparray[i]); } es_fprintf (es_stdout, ":::%d:\n", sig->digest_algo); if (opt.show_subpackets) print_subpackets_colon (sig); /* fixme: check or list other sigs here */ xfree (siguid); } } xfree (curve); xfree (hexgrip_buffer); xfree (serialno); } /* * Reorder the keyblock so that the primary user ID (and not attribute * packet) comes first. Fixme: Replace this by a generic sort * function. */ static void do_reorder_keyblock (KBNODE keyblock, int attr) { KBNODE primary = NULL, primary0 = NULL, primary2 = NULL; KBNODE last, node; for (node = keyblock; node; primary0 = node, node = node->next) { if (node->pkt->pkttype == PKT_USER_ID && ((attr && node->pkt->pkt.user_id->attrib_data) || (!attr && !node->pkt->pkt.user_id->attrib_data)) && node->pkt->pkt.user_id->flags.primary) { primary = primary2 = node; for (node = node->next; node; primary2 = node, node = node->next) { if (node->pkt->pkttype == PKT_USER_ID || node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) { break; } } break; } } if (!primary) return; /* No primary key flag found (should not happen). */ for (last = NULL, node = keyblock; node; last = node, node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) break; } log_assert (node); log_assert (last); /* The user ID is never the first packet. */ log_assert (primary0); /* Ditto (this is the node before primary). */ if (node == primary) return; /* Already the first one. */ last->next = primary; primary0->next = primary2->next; primary2->next = node; } void reorder_keyblock (KBNODE keyblock) { do_reorder_keyblock (keyblock, 1); do_reorder_keyblock (keyblock, 0); } static void list_keyblock (ctrl_t ctrl, KBNODE keyblock, int secret, int has_secret, int fpr, struct keylist_context *listctx) { reorder_keyblock (keyblock); if (opt.with_colons) list_keyblock_colon (ctrl, keyblock, secret, has_secret); else list_keyblock_print (ctrl, keyblock, secret, fpr, listctx); if (secret) es_fflush (es_stdout); } /* Public function used by keygen to list a keyblock. If NO_VALIDITY * is set the validity of a key is never shown. */ void list_keyblock_direct (ctrl_t ctrl, kbnode_t keyblock, int secret, int has_secret, int fpr, int no_validity) { struct keylist_context listctx; memset (&listctx, 0, sizeof (listctx)); listctx.no_validity = !!no_validity; list_keyblock (ctrl, keyblock, secret, has_secret, fpr, &listctx); keylist_context_release (&listctx); } /* Print an hex digit in ICAO spelling. */ static void print_icao_hexdigit (estream_t fp, int c) { static const char *list[16] = { "Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Niner", "Alfa", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot" }; tty_fprintf (fp, "%s", list[c&15]); } /* * Function to print the finperprint. * mode 0: as used in key listings, opt.with_colons is honored * 1: print using log_info () * 2: direct use of tty * 3: direct use of tty but only primary key. * 4: direct use of tty but only subkey. * 10: Same as 0 but with_colons etc is ignored. * 20: Same as 0 but using a compact format. * * Modes 1 and 2 will try and print both subkey and primary key * fingerprints. A MODE with bit 7 set is used internally. If * OVERRIDE_FP is not NULL that stream will be used in 0 instead * of es_stdout or instead of the TTY in modes 2 and 3. */ void print_fingerprint (ctrl_t ctrl, estream_t override_fp, PKT_public_key *pk, int mode) { char hexfpr[2*MAX_FINGERPRINT_LEN+1]; char *p; size_t i; estream_t fp; const char *text; int primary = 0; int with_colons = opt.with_colons; int with_icao = opt.with_icao_spelling; int compact = 0; if (mode == 10) { mode = 0; with_colons = 0; with_icao = 0; } else if (mode == 20) { mode = 0; with_colons = 0; compact = 1; } if (!opt.fingerprint && !opt.with_fingerprint && opt.with_subkey_fingerprint) compact = 1; if (pk->main_keyid[0] == pk->keyid[0] && pk->main_keyid[1] == pk->keyid[1]) primary = 1; /* Just to be safe */ if ((mode & 0x80) && !primary) { log_error ("primary key is not really primary!\n"); return; } mode &= ~0x80; if (!primary && (mode == 1 || mode == 2)) { PKT_public_key *primary_pk = xmalloc_clear (sizeof (*primary_pk)); get_pubkey (ctrl, primary_pk, pk->main_keyid); print_fingerprint (ctrl, override_fp, primary_pk, (mode | 0x80)); free_public_key (primary_pk); } if (mode == 1) { fp = log_get_stream (); if (primary) text = _("Primary key fingerprint:"); else text = _(" Subkey fingerprint:"); } else if (mode == 2) { fp = override_fp; /* Use tty or given stream. */ if (primary) /* TRANSLATORS: this should fit into 24 bytes so that the * fingerprint data is properly aligned with the user ID */ text = _(" Primary key fingerprint:"); else text = _(" Subkey fingerprint:"); } else if (mode == 3) { fp = override_fp; /* Use tty or given stream. */ text = _(" Key fingerprint ="); } else if (mode == 4) { fp = override_fp; /* Use tty or given stream. */ text = _(" Subkey fingerprint:"); } else { fp = override_fp? override_fp : es_stdout; if (opt.keyid_format == KF_NONE) { text = " "; /* To indent ICAO spelling. */ compact = 1; } else text = _(" Key fingerprint ="); } hexfingerprint (pk, hexfpr, sizeof hexfpr); if (with_colons && !mode) { es_fprintf (fp, "fpr:::::::::%s:", hexfpr); } else if (compact && !opt.fingerprint && !opt.with_fingerprint) { tty_fprintf (fp, "%*s%s", 6, "", hexfpr); } else { char fmtfpr[MAX_FORMATTED_FINGERPRINT_LEN + 1]; format_hexfingerprint (hexfpr, fmtfpr, sizeof fmtfpr); if (compact) tty_fprintf (fp, "%*s%s", 6, "", fmtfpr); else tty_fprintf (fp, "%s %s", text, fmtfpr); } tty_fprintf (fp, "\n"); if (!with_colons && with_icao) { ; tty_fprintf (fp, "%*s\"", (int)strlen(text)+1, ""); for (i = 0, p = hexfpr; *p; i++, p++) { if (!i) ; else if (!(i%8)) tty_fprintf (fp, "\n%*s ", (int)strlen(text)+1, ""); else if (!(i%4)) tty_fprintf (fp, " "); else tty_fprintf (fp, " "); print_icao_hexdigit (fp, xtoi_1 (p)); } tty_fprintf (fp, "\"\n"); } } /* Print the serial number of an OpenPGP card if available. */ static void print_card_serialno (const char *serialno) { if (!serialno) return; if (opt.with_colons) return; /* Handled elsewhere. */ es_fputs (_(" Card serial no. ="), es_stdout); es_putc (' ', es_stdout); if (strlen (serialno) == 32 && !strncmp (serialno, "D27600012401", 12)) { /* This is an OpenPGP card. Print the relevant part. */ /* Example: D2760001240101010001000003470000 */ /* xxxxyyyyyyyy */ es_fprintf (es_stdout, "%.*s %.*s", 4, serialno+16, 8, serialno+20); } else es_fputs (serialno, es_stdout); es_putc ('\n', es_stdout); } /* Print a public or secret (sub)key line. Example: * * pub dsa2048 2007-12-31 [SC] [expires: 2018-12-31] * 80615870F5BAD690333686D0F2AD85AC1E42B367 * * Some global options may result in a different output format. If * SECRET is set, "sec" or "ssb" is used instead of "pub" or "sub" and * depending on the value a flag character is shown: * * 1 := ' ' Regular secret key * 2 := '#' Stub secret key * 3 := '>' Secret key is on a token. */ void print_key_line (ctrl_t ctrl, estream_t fp, PKT_public_key *pk, int secret) { char pkstrbuf[PUBKEY_STRING_SIZE]; tty_fprintf (fp, "%s%c %s", pk->flags.primary? (secret? "sec":"pub") /**/ : (secret? "ssb":"sub"), secret == 2? '#' : secret == 3? '>' : ' ', pubkey_string (pk, pkstrbuf, sizeof pkstrbuf)); if (opt.keyid_format != KF_NONE) tty_fprintf (fp, "/%s", keystr_from_pk (pk)); tty_fprintf (fp, " %s", datestr_from_pk (pk)); if ((opt.list_options & LIST_SHOW_USAGE)) { tty_fprintf (fp, " [%s]", usagestr_from_pk (pk, 0)); } if (pk->flags.revoked) { tty_fprintf (fp, " ["); tty_fprintf (fp, _("revoked: %s"), revokestr_from_pk (pk)); tty_fprintf (fp, "]"); } else if (pk->has_expired) { tty_fprintf (fp, " ["); tty_fprintf (fp, _("expired: %s"), expirestr_from_pk (pk)); tty_fprintf (fp, "]"); } else if (pk->expiredate) { tty_fprintf (fp, " ["); tty_fprintf (fp, _("expires: %s"), expirestr_from_pk (pk)); tty_fprintf (fp, "]"); } #if 0 /* I need to think about this some more. It's easy enough to include, but it looks sort of confusing in the listing... */ if (opt.list_options & LIST_SHOW_VALIDITY) { int validity = get_validity (ctrl, pk, NULL, NULL, 0); tty_fprintf (fp, " [%s]", trust_value_to_string (validity)); } #endif if (pk->pubkey_algo >= 100) tty_fprintf (fp, " [experimental algorithm %d]", pk->pubkey_algo); tty_fprintf (fp, "\n"); /* if the user hasn't explicitly asked for human-readable fingerprints, show compact fpr of primary key: */ if (pk->flags.primary && !opt.fingerprint && !opt.with_fingerprint) print_fingerprint (ctrl, fp, pk, 20); } void set_attrib_fd (int fd) { static int last_fd = -1; if (fd != -1 && last_fd == fd) return; /* Fixme: Do we need to check for the log stream here? */ if (attrib_fp && attrib_fp != log_get_stream ()) es_fclose (attrib_fp); attrib_fp = NULL; if (fd == -1) return; if (! gnupg_fd_valid (fd)) log_fatal ("attribute-fd is invalid: %s\n", strerror (errno)); #ifdef HAVE_DOSISH_SYSTEM setmode (fd, O_BINARY); #endif if (fd == 1) attrib_fp = es_stdout; else if (fd == 2) attrib_fp = es_stderr; else attrib_fp = es_fdopen (fd, "wb"); if (!attrib_fp) { log_fatal ("can't open fd %d for attribute output: %s\n", fd, strerror (errno)); } last_fd = fd; } diff --git a/g10/main.h b/g10/main.h index 129d746ff..c406113df 100644 --- a/g10/main.h +++ b/g10/main.h @@ -1,514 +1,511 @@ /* main.h * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 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 . */ #ifndef G10_MAIN_H #define G10_MAIN_H #include "../common/types.h" #include "../common/iobuf.h" #include "keydb.h" #include "../common/util.h" /* It could be argued that the default cipher should be 3DES rather than AES128, and the default compression should be 0 (i.e. uncompressed) rather than 1 (zip). However, the real world issues of speed and size come into play here. */ #if GPG_USE_AES128 # define DEFAULT_CIPHER_ALGO CIPHER_ALGO_AES #elif GPG_USE_CAST5 # define DEFAULT_CIPHER_ALGO CIPHER_ALGO_CAST5 #else # define DEFAULT_CIPHER_ALGO CIPHER_ALGO_3DES #endif #define DEFAULT_DIGEST_ALGO ((GNUPG)? DIGEST_ALGO_SHA256:DIGEST_ALGO_SHA1) #define DEFAULT_S2K_DIGEST_ALGO DIGEST_ALGO_SHA1 #ifdef HAVE_ZIP # define DEFAULT_COMPRESS_ALGO COMPRESS_ALGO_ZIP #else # define DEFAULT_COMPRESS_ALGO COMPRESS_ALGO_NONE #endif #define S2K_DIGEST_ALGO (opt.s2k_digest_algo?opt.s2k_digest_algo:DEFAULT_S2K_DIGEST_ALGO) /* Various data objects. */ typedef struct { ctrl_t ctrl; int header_okay; PK_LIST pk_list; DEK *symkey_dek; STRING2KEY *symkey_s2k; cipher_filter_context_t cfx; } encrypt_filter_context_t; struct groupitem { char *name; strlist_t values; struct groupitem *next; }; struct weakhash { enum gcry_md_algos algo; int rejection_shown; struct weakhash *next; }; /*-- gpg.c --*/ extern int g10_errors_seen; #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 ) void g10_exit(int rc) __attribute__ ((noreturn)); #else void g10_exit(int rc); #endif void print_pubkey_algo_note (pubkey_algo_t algo); void print_cipher_algo_note (cipher_algo_t algo); void print_digest_algo_note (digest_algo_t algo); void print_digest_rejected_note (enum gcry_md_algos algo); void print_reported_error (gpg_error_t err, gpg_err_code_t skip_if_ec); void print_further_info (const char *format, ...) GPGRT_ATTR_PRINTF(1,2); void additional_weak_digest (const char* digestname); /*-- armor.c --*/ char *make_radix64_string( const byte *data, size_t len ); /*-- misc.c --*/ void trap_unaligned(void); void register_secured_file (const char *fname); void unregister_secured_file (const char *fname); int is_secured_file (int fd); int is_secured_filename (const char *fname); u16 checksum_u16( unsigned n ); u16 checksum( byte *p, unsigned n ); u16 checksum_mpi( gcry_mpi_t a ); u32 buffer_to_u32( const byte *buffer ); const byte *get_session_marker( size_t *rlen ); enum gcry_cipher_algos map_cipher_openpgp_to_gcry (cipher_algo_t algo); #define openpgp_cipher_open(_a,_b,_c,_d) \ gcry_cipher_open((_a),map_cipher_openpgp_to_gcry((_b)),(_c),(_d)) #define openpgp_cipher_get_algo_keylen(_a) \ gcry_cipher_get_algo_keylen(map_cipher_openpgp_to_gcry((_a))) #define openpgp_cipher_get_algo_blklen(_a) \ gcry_cipher_get_algo_blklen(map_cipher_openpgp_to_gcry((_a))) int openpgp_cipher_blocklen (cipher_algo_t algo); int openpgp_cipher_test_algo(cipher_algo_t algo); const char *openpgp_cipher_algo_name (cipher_algo_t algo); pubkey_algo_t map_pk_gcry_to_openpgp (enum gcry_pk_algos algo); int openpgp_pk_test_algo (pubkey_algo_t algo); int openpgp_pk_test_algo2 (pubkey_algo_t algo, unsigned int use); int openpgp_pk_algo_usage ( int algo ); const char *openpgp_pk_algo_name (pubkey_algo_t algo); -int gnupg_pk_is_compliant (int compliance, PKT_public_key *pk, - unsigned int keylength, const char *curvename); - enum gcry_md_algos map_md_openpgp_to_gcry (digest_algo_t algo); int openpgp_md_test_algo (digest_algo_t algo); const char *openpgp_md_algo_name (int algo); struct expando_args { PKT_public_key *pk; PKT_public_key *pksk; byte imagetype; int validity_info; const char *validity_string; const byte *namehash; }; char *pct_expando(const char *string,struct expando_args *args); void deprecated_warning(const char *configname,unsigned int configlineno, const char *option,const char *repl1,const char *repl2); void deprecated_command (const char *name); void obsolete_scdaemon_option (const char *configname, unsigned int configlineno, const char *name); int string_to_cipher_algo (const char *string); int string_to_digest_algo (const char *string); const char *compress_algo_to_string(int algo); int string_to_compress_algo(const char *string); int check_compress_algo(int algo); int default_cipher_algo(void); int default_compress_algo(void); const char *compliance_option_string(void); void compliance_failure(void); struct parse_options { char *name; unsigned int bit; char **value; char *help; }; char *optsep(char **stringp); char *argsplit(char *string); int parse_options(char *str,unsigned int *options, struct parse_options *opts,int noisy); const char *get_libexecdir (void); int path_access(const char *file,int mode); int pubkey_get_npkey (pubkey_algo_t algo); int pubkey_get_nskey (pubkey_algo_t algo); int pubkey_get_nsig (pubkey_algo_t algo); int pubkey_get_nenc (pubkey_algo_t algo); /* Temporary helpers. */ unsigned int pubkey_nbits( int algo, gcry_mpi_t *pkey ); int mpi_print (estream_t stream, gcry_mpi_t a, int mode); unsigned int ecdsa_qbits_from_Q (unsigned int qbits); /*-- cpr.c --*/ void set_status_fd ( int fd ); int is_status_enabled ( void ); void write_status ( int no ); void write_status_error (const char *where, gpg_error_t err); void write_status_errcode (const char *where, int errcode); void write_status_failure (const char *where, gpg_error_t err); void write_status_text ( int no, const char *text ); void write_status_printf (int no, const char *format, ...) GPGRT_ATTR_PRINTF(2,3); void write_status_strings (int no, const char *text, ...) GPGRT_ATTR_SENTINEL(0); void write_status_buffer ( int no, const char *buffer, size_t len, int wrap ); void write_status_text_and_buffer ( int no, const char *text, const char *buffer, size_t len, int wrap ); void write_status_begin_signing (gcry_md_hd_t md); int cpr_enabled(void); char *cpr_get( const char *keyword, const char *prompt ); char *cpr_get_no_help( const char *keyword, const char *prompt ); char *cpr_get_utf8( const char *keyword, const char *prompt ); char *cpr_get_hidden( const char *keyword, const char *prompt ); void cpr_kill_prompt(void); int cpr_get_answer_is_yes_def (const char *keyword, const char *prompt, int def_yes); int cpr_get_answer_is_yes( const char *keyword, const char *prompt ); int cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt ); int cpr_get_answer_okay_cancel (const char *keyword, const char *prompt, int def_answer); /*-- helptext.c --*/ void display_online_help( const char *keyword ); /*-- encode.c --*/ int setup_symkey (STRING2KEY **symkey_s2k,DEK **symkey_dek); void encrypt_seskey (DEK *dek, DEK **seskey, byte *enckey); int use_mdc (pk_list_t pk_list,int algo); int encrypt_symmetric (const char *filename ); int encrypt_store (const char *filename ); int encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, strlist_t remusr, int use_symkey, pk_list_t provided_keys, int outputfd); void encrypt_crypt_files (ctrl_t ctrl, int nfiles, char **files, strlist_t remusr); int encrypt_filter (void *opaque, int control, iobuf_t a, byte *buf, size_t *ret_len); int write_pubkey_enc (ctrl_t ctrl, PKT_public_key *pk, int throw_keyid, DEK *dek, iobuf_t out); /*-- sign.c --*/ int sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, int do_encrypt, strlist_t remusr, const char *outfile ); int clearsign_file (ctrl_t ctrl, const char *fname, strlist_t locusr, const char *outfile); int sign_symencrypt_file (ctrl_t ctrl, const char *fname, strlist_t locusr); /*-- sig-check.c --*/ void sig_check_dump_stats (void); /* SIG is a revocation signature. Check if any of PK's designated revokers generated it. If so, return 0. Note: this function (correctly) doesn't care if the designated revoker is revoked. */ int check_revocation_keys (ctrl_t ctrl, PKT_public_key *pk, PKT_signature *sig); /* Check that the backsig BACKSIG from the subkey SUB_PK to its primary key MAIN_PK is valid. */ int check_backsig(PKT_public_key *main_pk,PKT_public_key *sub_pk, PKT_signature *backsig); /* Check that the signature SIG over a key (e.g., a key binding or a key revocation) is valid. (To check signatures over data, use check_signature.) */ int check_key_signature (ctrl_t ctrl, kbnode_t root, kbnode_t sig, int *is_selfsig ); /* Like check_key_signature, but with the ability to specify some additional parameters and get back additional information. See the documentation for the implementation for details. */ int check_key_signature2 (ctrl_t ctrl, kbnode_t root, kbnode_t node, PKT_public_key *check_pk, PKT_public_key *ret_pk, int *is_selfsig, u32 *r_expiredate, int *r_expired); /* Returns whether SIGNER generated the signature SIG over the packet PACKET, which is a key, subkey or uid, and comes from the key block KB. If SIGNER is NULL, it is looked up based on the information in SIG. If not NULL, sets *IS_SELFSIG to indicate whether the signature is a self-signature and *RET_PK to a copy of the signer's key. */ gpg_error_t check_signature_over_key_or_uid (ctrl_t ctrl, PKT_public_key *signer, PKT_signature *sig, KBNODE kb, PACKET *packet, int *is_selfsig, PKT_public_key *ret_pk); /*-- delkey.c --*/ gpg_error_t delete_keys (ctrl_t ctrl, strlist_t names, int secret, int allow_both); /*-- keyedit.c --*/ void keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, strlist_t commands, int quiet, int seckey_check ); void keyedit_passwd (ctrl_t ctrl, const char *username); void keyedit_quick_adduid (ctrl_t ctrl, const char *username, const char *newuid); void keyedit_quick_addkey (ctrl_t ctrl, const char *fpr, const char *algostr, const char *usagestr, const char *expirestr); void keyedit_quick_revuid (ctrl_t ctrl, const char *username, const char *uidtorev); void keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids, strlist_t locusr, int local); void keyedit_quick_set_expire (ctrl_t ctrl, const char *fpr, const char *expirestr); void keyedit_quick_set_primary (ctrl_t ctrl, const char *username, const char *primaryuid); void show_basic_key_info (ctrl_t ctrl, kbnode_t keyblock); /*-- keygen.c --*/ const char *get_default_pubkey_algo (void); u32 parse_expire_string(const char *string); u32 ask_expire_interval(int object,const char *def_expire); u32 ask_expiredate(void); unsigned int ask_key_flags (int algo, int subkey, unsigned int current); void quick_generate_keypair (ctrl_t ctrl, const char *uid, const char *algostr, const char *usagestr, const char *expirestr); void generate_keypair (ctrl_t ctrl, int full, const char *fname, const char *card_serialno, int card_backup_key); int keygen_set_std_prefs (const char *string,int personal); PKT_user_id *keygen_get_std_prefs (void); int keygen_add_key_expire( PKT_signature *sig, void *opaque ); int keygen_add_key_flags (PKT_signature *sig, void *opaque); int keygen_add_std_prefs( PKT_signature *sig, void *opaque ); int keygen_upd_std_prefs( PKT_signature *sig, void *opaque ); int keygen_add_keyserver_url(PKT_signature *sig, void *opaque); int keygen_add_notations(PKT_signature *sig,void *opaque); int keygen_add_revkey(PKT_signature *sig, void *opaque); gpg_error_t make_backsig (ctrl_t ctrl, PKT_signature *sig, PKT_public_key *pk, PKT_public_key *sub_pk, PKT_public_key *sub_psk, u32 timestamp, const char *cache_nonce); gpg_error_t generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock, const char *algostr, const char *usagestr, const char *expirestr); #ifdef ENABLE_CARD_SUPPORT gpg_error_t generate_card_subkeypair (ctrl_t ctrl, kbnode_t pub_keyblock, int keyno, const char *serialno); #endif /*-- openfile.c --*/ int overwrite_filep( const char *fname ); char *make_outfile_name( const char *iname ); char *ask_outfile_name( const char *name, size_t namelen ); int open_outfile (int inp_fd, const char *iname, int mode, int restrictedperm, iobuf_t *a); char *get_matching_datafile (const char *sigfilename); iobuf_t open_sigfile (const char *sigfilename, progress_filter_context_t *pfx); void try_make_homedir( const char *fname ); char *get_openpgp_revocdir (const char *home); /*-- seskey.c --*/ void make_session_key( DEK *dek ); gcry_mpi_t encode_session_key( int openpgp_pk_algo, DEK *dek, unsigned nbits ); gcry_mpi_t encode_md_value (PKT_public_key *pk, gcry_md_hd_t md, int hash_algo ); /*-- import.c --*/ struct import_stats_s; typedef struct import_stats_s *import_stats_t; struct import_filter_s; typedef struct import_filter_s *import_filter_t; typedef gpg_error_t (*import_screener_t)(kbnode_t keyblock, void *arg); int parse_import_options(char *str,unsigned int *options,int noisy); gpg_error_t parse_and_set_import_filter (const char *string); import_filter_t save_and_clear_import_filter (void); void restore_import_filter (import_filter_t filt); gpg_error_t read_key_from_file (ctrl_t ctrl, const char *fname, kbnode_t *r_keyblock); void import_keys (ctrl_t ctrl, char **fnames, int nnames, import_stats_t stats_hd, unsigned int options); int import_keys_stream (ctrl_t ctrl, iobuf_t inp, import_stats_t stats_hd, unsigned char **fpr, size_t *fpr_len, unsigned int options); int import_keys_es_stream (ctrl_t ctrl, estream_t fp, import_stats_t stats_handle, unsigned char **fpr, size_t *fpr_len, unsigned int options, import_screener_t screener, void *screener_arg); gpg_error_t import_old_secring (ctrl_t ctrl, const char *fname); import_stats_t import_new_stats_handle (void); void import_release_stats_handle (import_stats_t hd); void import_print_stats (import_stats_t hd); /* Communication for impex_filter_getval */ struct impex_filter_parm_s { ctrl_t ctrl; kbnode_t node; }; const char *impex_filter_getval (void *cookie, const char *propname); gpg_error_t transfer_secret_keys (ctrl_t ctrl, struct import_stats_s *stats, kbnode_t sec_keyblock, int batch, int force); int collapse_uids( KBNODE *keyblock ); /*-- export.c --*/ struct export_stats_s; typedef struct export_stats_s *export_stats_t; export_stats_t export_new_stats (void); void export_release_stats (export_stats_t stats); void export_print_stats (export_stats_t stats); int parse_export_options(char *str,unsigned int *options,int noisy); gpg_error_t parse_and_set_export_filter (const char *string); int export_pubkeys (ctrl_t ctrl, strlist_t users, unsigned int options, export_stats_t stats); int export_seckeys (ctrl_t ctrl, strlist_t users, unsigned int options, export_stats_t stats); int export_secsubkeys (ctrl_t ctrl, strlist_t users, unsigned int options, export_stats_t stats); gpg_error_t export_pubkey_buffer (ctrl_t ctrl, const char *keyspec, unsigned int options, export_stats_t stats, kbnode_t *r_keyblock, void **r_data, size_t *r_datalen); gpg_error_t receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd, int cleartext, char **cache_nonce_addr, const char *hexgrip, PKT_public_key *pk); gpg_error_t write_keyblock_to_output (kbnode_t keyblock, int with_armor, unsigned int options); gpg_error_t export_ssh_key (ctrl_t ctrl, const char *userid); /*-- dearmor.c --*/ int dearmor_file( const char *fname ); int enarmor_file( const char *fname ); /*-- revoke.c --*/ struct revocation_reason_info; int gen_standard_revoke (ctrl_t ctrl, PKT_public_key *psk, const char *cache_nonce); int gen_revoke (ctrl_t ctrl, const char *uname); int gen_desig_revoke (ctrl_t ctrl, const char *uname, strlist_t locusr); int revocation_reason_build_cb( PKT_signature *sig, void *opaque ); struct revocation_reason_info * ask_revocation_reason( int key_rev, int cert_rev, int hint ); struct revocation_reason_info * get_default_uid_revocation_reason(void); void release_revocation_reason_info( struct revocation_reason_info *reason ); /*-- keylist.c --*/ void public_key_list (ctrl_t ctrl, strlist_t list, int locate_mode ); void secret_key_list (ctrl_t ctrl, strlist_t list ); void print_subpackets_colon(PKT_signature *sig); void reorder_keyblock (KBNODE keyblock); void list_keyblock_direct (ctrl_t ctrl, kbnode_t keyblock, int secret, int has_secret, int fpr, int no_validity); void print_fingerprint (ctrl_t ctrl, estream_t fp, PKT_public_key *pk, int mode); void print_revokers (estream_t fp, PKT_public_key *pk); void show_policy_url(PKT_signature *sig,int indent,int mode); void show_keyserver_url(PKT_signature *sig,int indent,int mode); void show_notation(PKT_signature *sig,int indent,int mode,int which); void dump_attribs (const PKT_user_id *uid, PKT_public_key *pk); void set_attrib_fd(int fd); char *format_seckey_info (ctrl_t ctrl, PKT_public_key *pk); void print_seckey_info (ctrl_t ctrl, PKT_public_key *pk); void print_pubkey_info (ctrl_t ctrl, estream_t fp, PKT_public_key *pk); void print_card_key_info (estream_t fp, KBNODE keyblock); void print_key_line (ctrl_t ctrl, estream_t fp, PKT_public_key *pk, int secret); /*-- verify.c --*/ void print_file_status( int status, const char *name, int what ); int verify_signatures (ctrl_t ctrl, int nfiles, char **files ); int verify_files (ctrl_t ctrl, int nfiles, char **files ); int gpg_verify (ctrl_t ctrl, int sig_fd, int data_fd, estream_t out_fp); /*-- decrypt.c --*/ int decrypt_message (ctrl_t ctrl, const char *filename ); gpg_error_t decrypt_message_fd (ctrl_t ctrl, int input_fd, int output_fd); void decrypt_messages (ctrl_t ctrl, int nfiles, char *files[]); /*-- plaintext.c --*/ int hash_datafiles( gcry_md_hd_t md, gcry_md_hd_t md2, strlist_t files, const char *sigfilename, int textmode); int hash_datafile_by_fd ( gcry_md_hd_t md, gcry_md_hd_t md2, int data_fd, int textmode ); PKT_plaintext *setup_plaintext_name(const char *filename,IOBUF iobuf); /*-- server.c --*/ int gpg_server (ctrl_t); gpg_error_t gpg_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line); #ifdef ENABLE_CARD_SUPPORT /*-- card-util.c --*/ void change_pin (int no, int allow_admin); void card_status (ctrl_t ctrl, estream_t fp, const char *serialno); void card_edit (ctrl_t ctrl, strlist_t commands); gpg_error_t card_generate_subkey (ctrl_t ctrl, kbnode_t pub_keyblock); int card_store_subkey (KBNODE node, int use); #endif #define S2K_DECODE_COUNT(_val) ((16ul + ((_val) & 15)) << (((_val) >> 4) + 6)) /*-- migrate.c --*/ void migrate_secring (ctrl_t ctrl); #endif /*G10_MAIN_H*/ diff --git a/g10/misc.c b/g10/misc.c index bdd27cf49..d485c9445 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -1,1863 +1,1775 @@ /* misc.c - miscellaneous functions * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, * 2008, 2009, 2010 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 . */ #include #include #include #include #include #include #if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2 #include #include #endif #ifdef HAVE_SETRLIMIT #include #include #include #endif #ifdef ENABLE_SELINUX_HACKS #include #endif #ifdef HAVE_W32_SYSTEM #include #include #ifdef HAVE_WINSOCK2_H # include #endif #include #include #ifndef CSIDL_APPDATA #define CSIDL_APPDATA 0x001a #endif #ifndef CSIDL_LOCAL_APPDATA #define CSIDL_LOCAL_APPDATA 0x001c #endif #ifndef CSIDL_FLAG_CREATE #define CSIDL_FLAG_CREATE 0x8000 #endif #endif /*HAVE_W32_SYSTEM*/ #include "gpg.h" #ifdef HAVE_W32_SYSTEM # include "../common/status.h" #endif /*HAVE_W32_SYSTEM*/ #include "../common/util.h" #include "main.h" #include "photoid.h" #include "options.h" #include "call-agent.h" #include "../common/i18n.h" #include "../common/zb32.h" #ifdef ENABLE_SELINUX_HACKS /* A object and a global variable to keep track of files marked as secured. */ struct secured_file_item { struct secured_file_item *next; ino_t ino; dev_t dev; }; static struct secured_file_item *secured_files; #endif /*ENABLE_SELINUX_HACKS*/ /* For the sake of SELinux we want to restrict access through gpg to certain files we keep under our own control. This function registers such a file and is_secured_file may then be used to check whether a file has ben registered as secured. */ void register_secured_file (const char *fname) { #ifdef ENABLE_SELINUX_HACKS struct stat buf; struct secured_file_item *sf; /* Note that we stop immediately if something goes wrong here. */ if (stat (fname, &buf)) log_fatal (_("fstat of '%s' failed in %s: %s\n"), fname, "register_secured_file", strerror (errno)); /* log_debug ("registering '%s' i=%lu.%lu\n", fname, */ /* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */ for (sf=secured_files; sf; sf = sf->next) { if (sf->ino == buf.st_ino && sf->dev == buf.st_dev) return; /* Already registered. */ } sf = xmalloc (sizeof *sf); sf->ino = buf.st_ino; sf->dev = buf.st_dev; sf->next = secured_files; secured_files = sf; #else /*!ENABLE_SELINUX_HACKS*/ (void)fname; #endif /*!ENABLE_SELINUX_HACKS*/ } /* Remove a file registered as secure. */ void unregister_secured_file (const char *fname) { #ifdef ENABLE_SELINUX_HACKS struct stat buf; struct secured_file_item *sf, *sfprev; if (stat (fname, &buf)) { log_error (_("fstat of '%s' failed in %s: %s\n"), fname, "unregister_secured_file", strerror (errno)); return; } /* log_debug ("unregistering '%s' i=%lu.%lu\n", fname, */ /* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */ for (sfprev=NULL,sf=secured_files; sf; sfprev=sf, sf = sf->next) { if (sf->ino == buf.st_ino && sf->dev == buf.st_dev) { if (sfprev) sfprev->next = sf->next; else secured_files = sf->next; xfree (sf); return; } } #else /*!ENABLE_SELINUX_HACKS*/ (void)fname; #endif /*!ENABLE_SELINUX_HACKS*/ } /* Return true if FD is corresponds to a secured file. Using -1 for FS is allowed and will return false. */ int is_secured_file (int fd) { #ifdef ENABLE_SELINUX_HACKS struct stat buf; struct secured_file_item *sf; if (fd == -1) return 0; /* No file descriptor so it can't be secured either. */ /* Note that we print out a error here and claim that a file is secure if something went wrong. */ if (fstat (fd, &buf)) { log_error (_("fstat(%d) failed in %s: %s\n"), fd, "is_secured_file", strerror (errno)); return 1; } /* log_debug ("is_secured_file (%d) i=%lu.%lu\n", fd, */ /* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */ for (sf=secured_files; sf; sf = sf->next) { if (sf->ino == buf.st_ino && sf->dev == buf.st_dev) return 1; /* Yes. */ } #else /*!ENABLE_SELINUX_HACKS*/ (void)fd; #endif /*!ENABLE_SELINUX_HACKS*/ return 0; /* No. */ } /* Return true if FNAME is corresponds to a secured file. Using NULL, "" or "-" for FS is allowed and will return false. This function is used before creating a file, thus it won't fail if the file does not exist. */ int is_secured_filename (const char *fname) { #ifdef ENABLE_SELINUX_HACKS struct stat buf; struct secured_file_item *sf; if (iobuf_is_pipe_filename (fname) || !*fname) return 0; /* Note that we print out a error here and claim that a file is secure if something went wrong. */ if (stat (fname, &buf)) { if (errno == ENOENT || errno == EPERM || errno == EACCES) return 0; log_error (_("fstat of '%s' failed in %s: %s\n"), fname, "is_secured_filename", strerror (errno)); return 1; } /* log_debug ("is_secured_filename (%s) i=%lu.%lu\n", fname, */ /* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */ for (sf=secured_files; sf; sf = sf->next) { if (sf->ino == buf.st_ino && sf->dev == buf.st_dev) return 1; /* Yes. */ } #else /*!ENABLE_SELINUX_HACKS*/ (void)fname; #endif /*!ENABLE_SELINUX_HACKS*/ return 0; /* No. */ } u16 checksum_u16( unsigned n ) { u16 a; a = (n >> 8) & 0xff; a += n & 0xff; return a; } u16 checksum( byte *p, unsigned n ) { u16 a; for(a=0; n; n-- ) a += *p++; return a; } u16 checksum_mpi (gcry_mpi_t a) { u16 csum; byte *buffer; size_t nbytes; if ( gcry_mpi_print (GCRYMPI_FMT_PGP, NULL, 0, &nbytes, a) ) BUG (); /* Fixme: For numbers not in secure memory we should use a stack * based buffer and only allocate a larger one if mpi_print returns * an error. */ buffer = (gcry_is_secure(a)? gcry_xmalloc_secure (nbytes) : gcry_xmalloc (nbytes)); if ( gcry_mpi_print (GCRYMPI_FMT_PGP, buffer, nbytes, NULL, a) ) BUG (); csum = checksum (buffer, nbytes); xfree (buffer); return csum; } void print_pubkey_algo_note (pubkey_algo_t algo) { if(algo >= 100 && algo <= 110) { static int warn=0; if(!warn) { warn=1; es_fflush (es_stdout); log_info (_("WARNING: using experimental public key algorithm %s\n"), openpgp_pk_algo_name (algo)); } } else if (algo == PUBKEY_ALGO_ELGAMAL) { es_fflush (es_stdout); log_info (_("WARNING: Elgamal sign+encrypt keys are deprecated\n")); } } void print_cipher_algo_note (cipher_algo_t algo) { if(algo >= 100 && algo <= 110) { static int warn=0; if(!warn) { warn=1; es_fflush (es_stdout); log_info (_("WARNING: using experimental cipher algorithm %s\n"), openpgp_cipher_algo_name (algo)); } } } void print_digest_algo_note (digest_algo_t algo) { const enum gcry_md_algos galgo = map_md_openpgp_to_gcry (algo); const struct weakhash *weak; if(algo >= 100 && algo <= 110) { static int warn=0; if(!warn) { warn=1; es_fflush (es_stdout); log_info (_("WARNING: using experimental digest algorithm %s\n"), gcry_md_algo_name (galgo)); } } else for (weak = opt.weak_digests; weak != NULL; weak = weak->next) if (weak->algo == galgo) { es_fflush (es_stdout); log_info (_("WARNING: digest algorithm %s is deprecated\n"), gcry_md_algo_name (galgo)); } } void print_digest_rejected_note (enum gcry_md_algos algo) { struct weakhash* weak; int show = 1; for (weak = opt.weak_digests; weak; weak = weak->next) if (weak->algo == algo) { if (weak->rejection_shown) show = 0; else weak->rejection_shown = 1; break; } if (show) { es_fflush (es_stdout); log_info (_("Note: signatures using the %s algorithm are rejected\n"), gcry_md_algo_name(algo)); } } /* Print a message * "(reported error: %s)\n * in verbose mode to further explain an error. If the error code has * the value IGNORE_EC no message is printed. A message is also not * printed if ERR is 0. */ void print_reported_error (gpg_error_t err, gpg_err_code_t ignore_ec) { if (!opt.verbose) return; if (!gpg_err_code (err)) ; else if (gpg_err_code (err) == ignore_ec) ; else if (gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT) log_info (_("(reported error: %s)\n"), gpg_strerror (err)); else log_info (_("(reported error: %s <%s>)\n"), gpg_strerror (err), gpg_strsource (err)); } /* Print a message * "(further info: %s)\n * in verbose mode to further explain an error. That message is * intended to help debug a problem and should not be translated. */ void print_further_info (const char *format, ...) { va_list arg_ptr; if (!opt.verbose) return; log_info (_("(further info: ")); va_start (arg_ptr, format); log_logv (GPGRT_LOG_CONT, format, arg_ptr); va_end (arg_ptr); log_printf (")\n"); } /* Map OpenPGP algo numbers to those used by Libgcrypt. We need to do this for algorithms we implemented in Libgcrypt after they become part of OpenPGP. */ enum gcry_cipher_algos map_cipher_openpgp_to_gcry (cipher_algo_t algo) { switch (algo) { case CIPHER_ALGO_NONE: return GCRY_CIPHER_NONE; #ifdef GPG_USE_IDEA case CIPHER_ALGO_IDEA: return GCRY_CIPHER_IDEA; #else case CIPHER_ALGO_IDEA: return 0; #endif case CIPHER_ALGO_3DES: return GCRY_CIPHER_3DES; #ifdef GPG_USE_CAST5 case CIPHER_ALGO_CAST5: return GCRY_CIPHER_CAST5; #else case CIPHER_ALGO_CAST5: return 0; #endif #ifdef GPG_USE_BLOWFISH case CIPHER_ALGO_BLOWFISH: return GCRY_CIPHER_BLOWFISH; #else case CIPHER_ALGO_BLOWFISH: return 0; #endif #ifdef GPG_USE_AES128 case CIPHER_ALGO_AES: return GCRY_CIPHER_AES; #else case CIPHER_ALGO_AES: return 0; #endif #ifdef GPG_USE_AES192 case CIPHER_ALGO_AES192: return GCRY_CIPHER_AES192; #else case CIPHER_ALGO_AES192: return 0; #endif #ifdef GPG_USE_AES256 case CIPHER_ALGO_AES256: return GCRY_CIPHER_AES256; #else case CIPHER_ALGO_AES256: return 0; #endif #ifdef GPG_USE_TWOFISH case CIPHER_ALGO_TWOFISH: return GCRY_CIPHER_TWOFISH; #else case CIPHER_ALGO_TWOFISH: return 0; #endif #ifdef GPG_USE_CAMELLIA128 case CIPHER_ALGO_CAMELLIA128: return GCRY_CIPHER_CAMELLIA128; #else case CIPHER_ALGO_CAMELLIA128: return 0; #endif #ifdef GPG_USE_CAMELLIA192 case CIPHER_ALGO_CAMELLIA192: return GCRY_CIPHER_CAMELLIA192; #else case CIPHER_ALGO_CAMELLIA192: return 0; #endif #ifdef GPG_USE_CAMELLIA256 case CIPHER_ALGO_CAMELLIA256: return GCRY_CIPHER_CAMELLIA256; #else case CIPHER_ALGO_CAMELLIA256: return 0; #endif default: return 0; } } /* The inverse function of above. */ static cipher_algo_t map_cipher_gcry_to_openpgp (enum gcry_cipher_algos algo) { switch (algo) { case GCRY_CIPHER_NONE: return CIPHER_ALGO_NONE; case GCRY_CIPHER_IDEA: return CIPHER_ALGO_IDEA; case GCRY_CIPHER_3DES: return CIPHER_ALGO_3DES; case GCRY_CIPHER_CAST5: return CIPHER_ALGO_CAST5; case GCRY_CIPHER_BLOWFISH: return CIPHER_ALGO_BLOWFISH; case GCRY_CIPHER_AES: return CIPHER_ALGO_AES; case GCRY_CIPHER_AES192: return CIPHER_ALGO_AES192; case GCRY_CIPHER_AES256: return CIPHER_ALGO_AES256; case GCRY_CIPHER_TWOFISH: return CIPHER_ALGO_TWOFISH; case GCRY_CIPHER_CAMELLIA128: return CIPHER_ALGO_CAMELLIA128; case GCRY_CIPHER_CAMELLIA192: return CIPHER_ALGO_CAMELLIA192; case GCRY_CIPHER_CAMELLIA256: return CIPHER_ALGO_CAMELLIA256; default: return 0; } } /* Map Gcrypt public key algorithm numbers to those used by OpenPGP. FIXME: This mapping is used at only two places - we should get rid of it. */ pubkey_algo_t map_pk_gcry_to_openpgp (enum gcry_pk_algos algo) { switch (algo) { case GCRY_PK_ECDSA: return PUBKEY_ALGO_ECDSA; case GCRY_PK_ECDH: return PUBKEY_ALGO_ECDH; default: return algo < 110 ? (pubkey_algo_t)algo : 0; } } /* Return the block length of an OpenPGP cipher algorithm. */ int openpgp_cipher_blocklen (cipher_algo_t algo) { /* We use the numbers from OpenPGP to be sure that we get the right block length. This is so that the packet parsing code works even for unknown algorithms (for which we assume 8 due to tradition). NOTE: If you change the returned blocklen above 16, check the callers because they may use a fixed size buffer of that size. */ switch (algo) { case CIPHER_ALGO_AES: case CIPHER_ALGO_AES192: case CIPHER_ALGO_AES256: case CIPHER_ALGO_TWOFISH: case CIPHER_ALGO_CAMELLIA128: case CIPHER_ALGO_CAMELLIA192: case CIPHER_ALGO_CAMELLIA256: return 16; default: return 8; } } /**************** * Wrapper around the libgcrypt function with additional checks on * the OpenPGP contraints for the algo ID. */ int openpgp_cipher_test_algo (cipher_algo_t algo) { enum gcry_cipher_algos ga; ga = map_cipher_openpgp_to_gcry (algo); if (!ga) return gpg_error (GPG_ERR_CIPHER_ALGO); return gcry_cipher_test_algo (ga); } /* Map the OpenPGP cipher algorithm whose ID is contained in ALGORITHM to a string representation of the algorithm name. For unknown algorithm IDs this function returns "?". */ const char * openpgp_cipher_algo_name (cipher_algo_t algo) { switch (algo) { case CIPHER_ALGO_IDEA: return "IDEA"; case CIPHER_ALGO_3DES: return "3DES"; case CIPHER_ALGO_CAST5: return "CAST5"; case CIPHER_ALGO_BLOWFISH: return "BLOWFISH"; case CIPHER_ALGO_AES: return "AES"; case CIPHER_ALGO_AES192: return "AES192"; case CIPHER_ALGO_AES256: return "AES256"; case CIPHER_ALGO_TWOFISH: return "TWOFISH"; case CIPHER_ALGO_CAMELLIA128: return "CAMELLIA128"; case CIPHER_ALGO_CAMELLIA192: return "CAMELLIA192"; case CIPHER_ALGO_CAMELLIA256: return "CAMELLIA256"; case CIPHER_ALGO_NONE: default: return "?"; } } /* Return 0 if ALGO is a supported OpenPGP public key algorithm. */ int openpgp_pk_test_algo (pubkey_algo_t algo) { return openpgp_pk_test_algo2 (algo, 0); } /* Return 0 if ALGO is a supported OpenPGP public key algorithm and allows the usage USE. */ int openpgp_pk_test_algo2 (pubkey_algo_t algo, unsigned int use) { enum gcry_pk_algos ga = 0; size_t use_buf = use; switch (algo) { #ifdef GPG_USE_RSA case PUBKEY_ALGO_RSA: ga = GCRY_PK_RSA; break; case PUBKEY_ALGO_RSA_E: ga = GCRY_PK_RSA_E; break; case PUBKEY_ALGO_RSA_S: ga = GCRY_PK_RSA_S; break; #else case PUBKEY_ALGO_RSA: break; case PUBKEY_ALGO_RSA_E: break; case PUBKEY_ALGO_RSA_S: break; #endif case PUBKEY_ALGO_ELGAMAL_E: ga = GCRY_PK_ELG; break; case PUBKEY_ALGO_DSA: ga = GCRY_PK_DSA; break; #ifdef GPG_USE_ECDH case PUBKEY_ALGO_ECDH: ga = GCRY_PK_ECC; break; #else case PUBKEY_ALGO_ECDH: break; #endif #ifdef GPG_USE_ECDSA case PUBKEY_ALGO_ECDSA: ga = GCRY_PK_ECC; break; #else case PUBKEY_ALGO_ECDSA: break; #endif #ifdef GPG_USE_EDDSA case PUBKEY_ALGO_EDDSA: ga = GCRY_PK_ECC; break; #else case PUBKEY_ALGO_EDDSA: break; #endif case PUBKEY_ALGO_ELGAMAL: /* Dont't allow type 20 keys unless in rfc2440 mode. */ if (RFC2440) ga = GCRY_PK_ELG; break; default: break; } if (!ga) return gpg_error (GPG_ERR_PUBKEY_ALGO); /* Now check whether Libgcrypt has support for the algorithm. */ return gcry_pk_algo_info (ga, GCRYCTL_TEST_ALGO, NULL, &use_buf); } int openpgp_pk_algo_usage ( int algo ) { int use = 0; /* They are hardwired in gpg 1.0. */ switch ( algo ) { case PUBKEY_ALGO_RSA: use = (PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_ENC | PUBKEY_USAGE_AUTH); break; case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_ECDH: use = PUBKEY_USAGE_ENC; break; case PUBKEY_ALGO_RSA_S: use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG; break; case PUBKEY_ALGO_ELGAMAL: if (RFC2440) use = PUBKEY_USAGE_ENC; break; case PUBKEY_ALGO_ELGAMAL_E: use = PUBKEY_USAGE_ENC; break; case PUBKEY_ALGO_DSA: use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH; break; case PUBKEY_ALGO_ECDSA: case PUBKEY_ALGO_EDDSA: use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH; default: break; } return use; } /* Map the OpenPGP pubkey algorithm whose ID is contained in ALGO to a string representation of the algorithm name. For unknown algorithm IDs this function returns "?". */ const char * openpgp_pk_algo_name (pubkey_algo_t algo) { switch (algo) { case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_RSA_S: return "RSA"; case PUBKEY_ALGO_ELGAMAL: case PUBKEY_ALGO_ELGAMAL_E: return "ELG"; case PUBKEY_ALGO_DSA: return "DSA"; case PUBKEY_ALGO_ECDH: return "ECDH"; case PUBKEY_ALGO_ECDSA: return "ECDSA"; case PUBKEY_ALGO_EDDSA: return "EDDSA"; default: return "?"; } } -/* Return true if PK is compliant to the give COMPLIANCE mode. If - * KEYLENGTH and CURVENAME are not 0/NULL the are assumed to be the - * already computed values from PK. */ -int -gnupg_pk_is_compliant (int compliance, PKT_public_key *pk, - unsigned int keylength, const char *curvename) -{ - enum { is_rsa, is_pgp5, is_elg_sign, is_ecc } algotype; - int result; - - switch (pk->pubkey_algo) - { - case PUBKEY_ALGO_RSA: - case PUBKEY_ALGO_RSA_E: - case PUBKEY_ALGO_RSA_S: - algotype = is_rsa; - break; - - case PUBKEY_ALGO_ELGAMAL_E: - case PUBKEY_ALGO_DSA: - algotype = is_pgp5; - break; - - case PUBKEY_ALGO_ECDH: - case PUBKEY_ALGO_ECDSA: - case PUBKEY_ALGO_EDDSA: - algotype = is_ecc; - break; - - case PUBKEY_ALGO_ELGAMAL: - algotype = is_elg_sign; - break; - - default: /* Unknown. */ - return 0; - } - - if (compliance == CO_DE_VS) - { - char *curve = NULL; - - switch (algotype) - { - case is_pgp5: - result = 0; - break; - - case is_rsa: - if (!keylength) - keylength = nbits_from_pk (pk); - result = (keylength >= 2048); - break; - - case is_ecc: - if (!curvename) - { - curve = openpgp_oid_to_str (pk->pkey[0]); - curvename = openpgp_oid_to_curve (curve, 0); - if (!curvename) - curvename = curve; - } - - result = (curvename - && pk->pubkey_algo != PUBKEY_ALGO_EDDSA - && (!strcmp (curvename, "brainpoolP256r1") - || !strcmp (curvename, "brainpoolP384r1") - || !strcmp (curvename, "brainpoolP512r1"))); - break; - - default: - result = 0; - } - xfree (curve); - } - else if (algotype == is_elg_sign) - { - /* An Elgamal signing key is only RFC-2440 compliant. */ - result = (compliance == CO_RFC2440); - } - else - { - result = 1; /* Assume compliance. */ - } - - return result; -} - - /* Explicit mapping of OpenPGP digest algos to Libgcrypt. */ /* FIXME: We do not yes use it everywhere. */ enum gcry_md_algos map_md_openpgp_to_gcry (digest_algo_t algo) { switch (algo) { #ifdef GPG_USE_MD5 case DIGEST_ALGO_MD5: return GCRY_MD_MD5; #else case DIGEST_ALGO_MD5: return 0; #endif case DIGEST_ALGO_SHA1: return GCRY_MD_SHA1; #ifdef GPG_USE_RMD160 case DIGEST_ALGO_RMD160: return GCRY_MD_RMD160; #else case DIGEST_ALGO_RMD160: return 0; #endif #ifdef GPG_USE_SHA224 case DIGEST_ALGO_SHA224: return GCRY_MD_SHA224; #else case DIGEST_ALGO_SHA224: return 0; #endif case DIGEST_ALGO_SHA256: return GCRY_MD_SHA256; #ifdef GPG_USE_SHA384 case DIGEST_ALGO_SHA384: return GCRY_MD_SHA384; #else case DIGEST_ALGO_SHA384: return 0; #endif #ifdef GPG_USE_SHA512 case DIGEST_ALGO_SHA512: return GCRY_MD_SHA512; #else case DIGEST_ALGO_SHA512: return 0; #endif default: return 0; } } /* Return 0 if ALGO is suitable and implemented OpenPGP hash algorithm. */ int openpgp_md_test_algo (digest_algo_t algo) { enum gcry_md_algos ga; ga = map_md_openpgp_to_gcry (algo); if (!ga) return gpg_error (GPG_ERR_DIGEST_ALGO); return gcry_md_test_algo (ga); } /* Map the OpenPGP digest algorithm whose ID is contained in ALGO to a string representation of the algorithm name. For unknown algorithm IDs this function returns "?". */ const char * openpgp_md_algo_name (int algo) { switch (algo) { case DIGEST_ALGO_MD5: return "MD5"; case DIGEST_ALGO_SHA1: return "SHA1"; case DIGEST_ALGO_RMD160: return "RIPEMD160"; case DIGEST_ALGO_SHA256: return "SHA256"; case DIGEST_ALGO_SHA384: return "SHA384"; case DIGEST_ALGO_SHA512: return "SHA512"; case DIGEST_ALGO_SHA224: return "SHA224"; } return "?"; } static unsigned long get_signature_count (PKT_public_key *pk) { #ifdef ENABLE_CARD_SUPPORT struct agent_card_info_s info; (void)pk; if (!agent_scd_getattr ("SIG-COUNTER",&info)) return info.sig_counter; else return 0; #else (void)pk; return 0; #endif } /* Expand %-strings. Returns a string which must be xfreed. Returns NULL if the string cannot be expanded (too large). */ char * pct_expando(const char *string,struct expando_args *args) { const char *ch=string; int idx=0,maxlen=0,done=0; u32 pk_keyid[2]={0,0},sk_keyid[2]={0,0}; char *ret=NULL; if(args->pk) keyid_from_pk(args->pk,pk_keyid); if(args->pksk) keyid_from_pk (args->pksk, sk_keyid); /* This is used so that %k works in photoid command strings in --list-secret-keys (which of course has a sk, but no pk). */ if(!args->pk && args->pksk) keyid_from_pk (args->pksk, pk_keyid); while(*ch!='\0') { if(!done) { /* 8192 is way bigger than we'll need here */ if(maxlen>=8192) goto fail; maxlen+=1024; ret=xrealloc(ret,maxlen); } done=0; if(*ch=='%') { switch(*(ch+1)) { case 's': /* short key id */ if(idx+8namehash) { char *tmp = zb32_encode (args->namehash, 8*20); if (tmp) { if (idx + strlen (tmp) < maxlen) { strcpy (ret+idx, tmp); idx += strlen (tmp); } xfree (tmp); done = 1; } } break; case 'c': /* signature count from card, if any. */ if(idx+10pksk)); idx+=strlen(&ret[idx]); done=1; } break; case 'f': /* Fingerprint of key being signed */ case 'p': /* Fingerprint of the primary key making the signature. */ case 'g': /* Fingerprint of the key making the signature. */ { byte array[MAX_FINGERPRINT_LEN]; size_t len; int i; if ((*(ch+1))=='f' && args->pk) fingerprint_from_pk (args->pk, array, &len); else if ((*(ch+1))=='p' && args->pksk) { if(args->pksk->flags.primary) fingerprint_from_pk (args->pksk, array, &len); else if (args->pksk->main_keyid[0] || args->pksk->main_keyid[1]) { /* Not the primary key: Find the fingerprint of the primary key. */ PKT_public_key *pk= xmalloc_clear(sizeof(PKT_public_key)); if (!get_pubkey_fast (pk,args->pksk->main_keyid)) fingerprint_from_pk (pk, array, &len); else memset (array, 0, (len=MAX_FINGERPRINT_LEN)); free_public_key (pk); } else /* Oops: info about the primary key missing. */ memset(array,0,(len=MAX_FINGERPRINT_LEN)); } else if((*(ch+1))=='g' && args->pksk) fingerprint_from_pk (args->pksk, array, &len); else memset(array,0,(len=MAX_FINGERPRINT_LEN)); if(idx+(len*2)validity_info && idx+1validity_info; ret[idx]='\0'; done=1; } break; /* The text string types */ case 't': case 'T': case 'V': { const char *str=NULL; switch(*(ch+1)) { case 't': /* e.g. "jpg" */ str=image_type_to_string(args->imagetype,0); break; case 'T': /* e.g. "image/jpeg" */ str=image_type_to_string(args->imagetype,2); break; case 'V': /* e.g. "full", "expired", etc. */ str=args->validity_string; break; } if(str && idx+strlen(str)='A' && file[0]<='Z') || (file[0]>='a' && file[0]<='z')) && file[1]==':') #else || file[0]=='/' #endif ) return access(file,mode); else { /* At least as large as, but most often larger than we need. */ char *buffer=xmalloc(strlen(envpath)+1+strlen(file)+1); char *split,*item,*path=xstrdup(envpath); split=path; while((item=strsep(&split,PATHSEP_S))) { strcpy(buffer,item); strcat(buffer,"/"); strcat(buffer,file); ret=access(buffer,mode); if(ret==0) break; } xfree(path); xfree(buffer); } return ret; } /* Return the number of public key parameters as used by OpenPGP. */ int pubkey_get_npkey (pubkey_algo_t algo) { switch (algo) { case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_RSA_S: return 2; case PUBKEY_ALGO_ELGAMAL_E: return 3; case PUBKEY_ALGO_DSA: return 4; case PUBKEY_ALGO_ECDH: return 3; case PUBKEY_ALGO_ECDSA: return 2; case PUBKEY_ALGO_ELGAMAL: return 3; case PUBKEY_ALGO_EDDSA: return 2; default: return 0; } } /* Return the number of secret key parameters as used by OpenPGP. */ int pubkey_get_nskey (pubkey_algo_t algo) { switch (algo) { case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_RSA_S: return 6; case PUBKEY_ALGO_ELGAMAL_E: return 4; case PUBKEY_ALGO_DSA: return 5; case PUBKEY_ALGO_ECDH: return 4; case PUBKEY_ALGO_ECDSA: return 3; case PUBKEY_ALGO_ELGAMAL: return 4; case PUBKEY_ALGO_EDDSA: return 3; default: return 0; } } /* Temporary helper. */ int pubkey_get_nsig (pubkey_algo_t algo) { switch (algo) { case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_RSA_S: return 1; case PUBKEY_ALGO_ELGAMAL_E: return 0; case PUBKEY_ALGO_DSA: return 2; case PUBKEY_ALGO_ECDH: return 0; case PUBKEY_ALGO_ECDSA: return 2; case PUBKEY_ALGO_ELGAMAL: return 2; case PUBKEY_ALGO_EDDSA: return 2; default: return 0; } } /* Temporary helper. */ int pubkey_get_nenc (pubkey_algo_t algo) { switch (algo) { case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_RSA_S: return 1; case PUBKEY_ALGO_ELGAMAL_E: return 2; case PUBKEY_ALGO_DSA: return 0; case PUBKEY_ALGO_ECDH: return 2; case PUBKEY_ALGO_ECDSA: return 0; case PUBKEY_ALGO_ELGAMAL: return 2; case PUBKEY_ALGO_EDDSA: return 0; default: return 0; } } /* Temporary helper. */ unsigned int pubkey_nbits( int algo, gcry_mpi_t *key ) { int rc, nbits; gcry_sexp_t sexp; if (algo == PUBKEY_ALGO_DSA && key[0] && key[1] && key[2] && key[3]) { rc = gcry_sexp_build (&sexp, NULL, "(public-key(dsa(p%m)(q%m)(g%m)(y%m)))", key[0], key[1], key[2], key[3] ); } else if ((algo == PUBKEY_ALGO_ELGAMAL || algo == PUBKEY_ALGO_ELGAMAL_E) && key[0] && key[1] && key[2]) { rc = gcry_sexp_build (&sexp, NULL, "(public-key(elg(p%m)(g%m)(y%m)))", key[0], key[1], key[2] ); } else if (is_RSA (algo) && key[0] && key[1]) { rc = gcry_sexp_build (&sexp, NULL, "(public-key(rsa(n%m)(e%m)))", key[0], key[1] ); } else if ((algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_EDDSA) && key[0] && key[1]) { char *curve = openpgp_oid_to_str (key[0]); if (!curve) rc = gpg_error_from_syserror (); else { rc = gcry_sexp_build (&sexp, NULL, "(public-key(ecc(curve%s)(q%m)))", curve, key[1]); xfree (curve); } } else return 0; if (rc) BUG (); nbits = gcry_pk_get_nbits (sexp); gcry_sexp_release (sexp); return nbits; } int mpi_print (estream_t fp, gcry_mpi_t a, int mode) { int n = 0; size_t nwritten; if (!a) return es_fprintf (fp, "[MPI_NULL]"); if (!mode) { unsigned int n1; n1 = gcry_mpi_get_nbits(a); n += es_fprintf (fp, "[%u bits]", n1); } else if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) { unsigned int nbits; unsigned char *p = gcry_mpi_get_opaque (a, &nbits); if (!p) n += es_fprintf (fp, "[invalid opaque value]"); else { if (!es_write_hexstring (fp, p, (nbits + 7)/8, 0, &nwritten)) n += nwritten; } } else { unsigned char *buffer; size_t buflen; if (gcry_mpi_aprint (GCRYMPI_FMT_USG, &buffer, &buflen, a)) BUG (); if (!es_write_hexstring (fp, buffer, buflen, 0, &nwritten)) n += nwritten; gcry_free (buffer); } return n; } /* pkey[1] or skey[1] is Q for ECDSA, which is an uncompressed point, i.e. 04 */ unsigned int ecdsa_qbits_from_Q (unsigned int qbits) { if ((qbits%8) > 3) { log_error (_("ECDSA public key is expected to be in SEC encoding " "multiple of 8 bits\n")); return 0; } qbits -= qbits%8; qbits /= 2; return qbits; } /* Ignore signatures and certifications made over certain digest * algorithms by default, MD5 is considered weak. This allows users * to deprecate support for other algorithms as well. */ void additional_weak_digest (const char* digestname) { struct weakhash *weak = NULL; const enum gcry_md_algos algo = string_to_digest_algo(digestname); if (algo == GCRY_MD_NONE) { log_error (_("unknown weak digest '%s'\n"), digestname); return; } /* Check to ensure it's not already present. */ for (weak = opt.weak_digests; weak; weak = weak->next) if (algo == weak->algo) return; /* Add it to the head of the list. */ weak = xmalloc(sizeof(*weak)); weak->algo = algo; weak->rejection_shown = 0; weak->next = opt.weak_digests; opt.weak_digests = weak; } diff --git a/g10/options.h b/g10/options.h index c634f0ffd..8d1d93e3d 100644 --- a/g10/options.h +++ b/g10/options.h @@ -1,397 +1,394 @@ /* options.h * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, * 2007, 2010, 2011 Free Software Foundation, Inc. * Copyright (C) 2015 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef G10_OPTIONS_H #define G10_OPTIONS_H #include #include "../common/types.h" #include #include "main.h" #include "packet.h" #include "tofu.h" #include "../common/session-env.h" +#include "../common/compliance.h" #ifndef EXTERN_UNLESS_MAIN_MODULE /* Norcraft can't cope with common symbols */ #if defined (__riscos__) && !defined (INCLUDED_BY_MAIN_MODULE) #define EXTERN_UNLESS_MAIN_MODULE extern #else #define EXTERN_UNLESS_MAIN_MODULE #endif #endif /* Declaration of a keyserver spec type. The definition is found in ../common/keyserver.h. */ struct keyserver_spec; typedef struct keyserver_spec *keyserver_spec_t; /* Global options for GPG. */ EXTERN_UNLESS_MAIN_MODULE struct { int verbose; int quiet; unsigned debug; int armor; char *outfile; estream_t outfp; /* Hack, sometimes used in place of outfile. */ off_t max_output; /* If > 0 a hint with the expected number of input data bytes. This * is not necessary an exact number but intended to be used for * progress info and to decide on how to allocate buffers. */ uint64_t input_size_hint; int dry_run; int autostart; int list_only; int mimemode; int textmode; int expert; const char *def_sig_expire; int ask_sig_expire; const char *def_cert_expire; int ask_cert_expire; int batch; /* run in batch mode */ int answer_yes; /* answer yes on most questions */ int answer_no; /* answer no on most questions */ int check_sigs; /* check key signatures */ int with_colons; int with_key_data; int with_icao_spelling; /* Print ICAO spelling with fingerprints. */ int with_fingerprint; /* Option --with-fingerprint active. */ int with_subkey_fingerprint; /* Option --with-subkey-fingerprint active. */ int with_keygrip; /* Option --with-keygrip active. */ int with_tofu_info; /* Option --with-tofu_info active. */ int with_secret; /* Option --with-secret active. */ int with_wkd_hash; /* Option --with-wkd-hash. */ int fingerprint; /* list fingerprints */ int list_sigs; /* list signatures */ int no_armor; int list_packets; /* Option --list-packets active. */ int def_cipher_algo; int force_mdc; int disable_mdc; int def_digest_algo; int cert_digest_algo; int compress_algo; int compress_level; int bz2_compress_level; int bz2_decompress_lowmem; strlist_t def_secret_key; char *def_recipient; int def_recipient_self; strlist_t secret_keys_to_try; /* A list of mail addresses (addr-spec) provided by the user with * the option --sender. */ strlist_t sender_list; int def_cert_level; int min_cert_level; int ask_cert_level; int emit_version; /* 0 = none, 1 = major only, 2 = major and minor, 3 = full version, 4 = full version plus OS string. */ int marginals_needed; int completes_needed; int max_cert_depth; const char *agent_program; const char *dirmngr_program; const char *def_new_key_algo; /* Options to be passed to the gpg-agent */ session_env_t session_env; char *lc_ctype; char *lc_messages; int skip_verify; int skip_hidden_recipients; /* TM_CLASSIC must be zero to accommodate trustdbsg generated before we started storing the trust model inside the trustdb. */ enum { TM_CLASSIC=0, TM_PGP=1, TM_EXTERNAL=2, TM_ALWAYS, TM_DIRECT, TM_AUTO, TM_TOFU, TM_TOFU_PGP } trust_model; enum tofu_policy tofu_default_policy; int force_ownertrust; - enum - { - CO_GNUPG, CO_RFC4880, CO_RFC2440, - CO_PGP6, CO_PGP7, CO_PGP8, CO_DE_VS - } compliance; + enum gnupg_compliance_mode compliance; enum { KF_DEFAULT, KF_NONE, KF_SHORT, KF_LONG, KF_0xSHORT, KF_0xLONG } keyid_format; const char *set_filename; strlist_t comments; int throw_keyids; const char *photo_viewer; int s2k_mode; int s2k_digest_algo; int s2k_cipher_algo; unsigned char s2k_count; /* This is the encoded form, not the raw count */ int not_dash_escaped; int escape_from; int lock_once; keyserver_spec_t keyserver; /* The list of configured keyservers. */ struct { unsigned int options; unsigned int import_options; unsigned int export_options; char *http_proxy; } keyserver_options; int exec_disable; int exec_path_set; unsigned int import_options; unsigned int export_options; unsigned int list_options; unsigned int verify_options; const char *def_preference_list; const char *def_keyserver_url; prefitem_t *personal_cipher_prefs; prefitem_t *personal_digest_prefs; prefitem_t *personal_compress_prefs; struct weakhash *weak_digests; int no_perm_warn; int no_mdc_warn; char *temp_dir; int no_encrypt_to; int encrypt_to_default_key; int interactive; struct notation *sig_notations; struct notation *cert_notations; strlist_t sig_policy_url; strlist_t cert_policy_url; strlist_t sig_keyserver_url; strlist_t cert_subpackets; strlist_t sig_subpackets; int allow_non_selfsigned_uid; int allow_freeform_uid; int no_literal; ulong set_filesize; int fast_list_mode; int legacy_list_mode; int ignore_time_conflict; int ignore_valid_from; int ignore_crc_error; int ignore_mdc_error; int command_fd; const char *override_session_key; int show_session_key; const char *gpg_agent_info; int try_all_secrets; int no_expensive_trust_checks; int no_sig_cache; int no_auto_check_trustdb; int preserve_permissions; int no_homedir_creation; struct groupitem *grouplist; int mangle_dos_filenames; int enable_progress_filter; unsigned int screen_columns; unsigned int screen_lines; byte *show_subpackets; int rfc2440_text; /* If true, let write failures on the status-fd exit the process. */ int exit_on_status_write_error; /* If > 0, limit the number of card insertion prompts to this value. */ int limit_card_insert_tries; struct { /* If set, require an 0x19 backsig to be present on signatures made by signing subkeys. If not set, a missing backsig is not an error (but an invalid backsig still is). */ unsigned int require_cross_cert:1; unsigned int use_embedded_filename:1; unsigned int utf8_filename:1; unsigned int dsa2:1; unsigned int allow_multiple_messages:1; unsigned int allow_weak_digest_algos:1; unsigned int large_rsa:1; unsigned int disable_signer_uid:1; /* Flag to enbale experimental features from RFC4880bis. */ unsigned int rfc4880bis:1; } flags; /* Linked list of ways to find a key if the key isn't on the local keyring. */ struct akl { enum { AKL_NODEFAULT, AKL_LOCAL, AKL_CERT, AKL_PKA, AKL_DANE, AKL_WKD, AKL_LDAP, AKL_KEYSERVER, AKL_SPEC } type; keyserver_spec_t spec; struct akl *next; } *auto_key_locate; int passphrase_repeat; int pinentry_mode; int unwrap_encryption; int only_sign_text_ids; } opt; /* CTRL is used to keep some global variables we currently can't avoid. Future concurrent versions of gpg will put it into a per request structure CTRL. */ EXTERN_UNLESS_MAIN_MODULE struct { int in_auto_key_retrieve; /* True if we are doing an auto_key_retrieve. */ /* Hack to store the last error. We currently need it because the proc_packet machinery is not able to reliabale return error codes. Thus for the --server purposes we store some of the error codes here. FIXME! */ gpg_error_t lasterr; } glo_ctrl; #define DBG_PACKET_VALUE 1 /* debug packet reading/writing */ #define DBG_MPI_VALUE 2 /* debug mpi details */ #define DBG_CRYPTO_VALUE 4 /* debug crypto handling */ /* (may reveal sensitive data) */ #define DBG_FILTER_VALUE 8 /* debug internal filter handling */ #define DBG_IOBUF_VALUE 16 /* debug iobuf stuff */ #define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */ #define DBG_CACHE_VALUE 64 /* debug the caching */ #define DBG_MEMSTAT_VALUE 128 /* show memory statistics */ #define DBG_TRUST_VALUE 256 /* debug the trustdb */ #define DBG_HASHING_VALUE 512 /* debug hashing operations */ #define DBG_IPC_VALUE 1024 /* debug assuan communication */ #define DBG_CLOCK_VALUE 4096 #define DBG_LOOKUP_VALUE 8192 /* debug the key lookup */ #define DBG_EXTPROG_VALUE 16384 /* debug external program calls */ /* Tests for the debugging flags. */ #define DBG_PACKET (opt.debug & DBG_PACKET_VALUE) #define DBG_MPI (opt.debug & DBG_MPI_VALUE) #define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE) #define DBG_FILTER (opt.debug & DBG_FILTER_VALUE) #define DBG_CACHE (opt.debug & DBG_CACHE_VALUE) #define DBG_TRUST (opt.debug & DBG_TRUST_VALUE) #define DBG_HASHING (opt.debug & DBG_HASHING_VALUE) #define DBG_IPC (opt.debug & DBG_IPC_VALUE) #define DBG_IPC (opt.debug & DBG_IPC_VALUE) #define DBG_CLOCK (opt.debug & DBG_CLOCK_VALUE) #define DBG_LOOKUP (opt.debug & DBG_LOOKUP_VALUE) #define DBG_EXTPROG (opt.debug & DBG_EXTPROG_VALUE) /* FIXME: We need to check why we did not put this into opt. */ #define DBG_MEMORY memory_debug_mode #define DBG_MEMSTAT memory_stat_debug_mode EXTERN_UNLESS_MAIN_MODULE int memory_debug_mode; EXTERN_UNLESS_MAIN_MODULE int memory_stat_debug_mode; /* Compatibility flags. */ #define GNUPG (opt.compliance==CO_GNUPG || opt.compliance==CO_DE_VS) #define RFC2440 (opt.compliance==CO_RFC2440) #define RFC4880 (opt.compliance==CO_RFC4880) #define PGP6 (opt.compliance==CO_PGP6) #define PGP7 (opt.compliance==CO_PGP7) #define PGP8 (opt.compliance==CO_PGP8) #define PGPX (PGP6 || PGP7 || PGP8) /* Various option flags. Note that there should be no common string names between the IMPORT_ and EXPORT_ flags as they can be mixed in the keyserver-options option. */ #define IMPORT_LOCAL_SIGS (1<<0) #define IMPORT_REPAIR_PKS_SUBKEY_BUG (1<<1) #define IMPORT_FAST (1<<2) #define IMPORT_SHOW (1<<3) #define IMPORT_MERGE_ONLY (1<<4) #define IMPORT_MINIMAL (1<<5) #define IMPORT_CLEAN (1<<6) #define IMPORT_NO_SECKEY (1<<7) #define IMPORT_KEEP_OWNERTTRUST (1<<8) #define IMPORT_EXPORT (1<<9) #define IMPORT_RESTORE (1<<10) #define EXPORT_LOCAL_SIGS (1<<0) #define EXPORT_ATTRIBUTES (1<<1) #define EXPORT_SENSITIVE_REVKEYS (1<<2) #define EXPORT_RESET_SUBKEY_PASSWD (1<<3) #define EXPORT_MINIMAL (1<<4) #define EXPORT_CLEAN (1<<5) #define EXPORT_PKA_FORMAT (1<<6) #define EXPORT_DANE_FORMAT (1<<7) #define EXPORT_BACKUP (1<<10) #define LIST_SHOW_PHOTOS (1<<0) #define LIST_SHOW_POLICY_URLS (1<<1) #define LIST_SHOW_STD_NOTATIONS (1<<2) #define LIST_SHOW_USER_NOTATIONS (1<<3) #define LIST_SHOW_NOTATIONS (LIST_SHOW_STD_NOTATIONS|LIST_SHOW_USER_NOTATIONS) #define LIST_SHOW_KEYSERVER_URLS (1<<4) #define LIST_SHOW_UID_VALIDITY (1<<5) #define LIST_SHOW_UNUSABLE_UIDS (1<<6) #define LIST_SHOW_UNUSABLE_SUBKEYS (1<<7) #define LIST_SHOW_KEYRING (1<<8) #define LIST_SHOW_SIG_EXPIRE (1<<9) #define LIST_SHOW_SIG_SUBPACKETS (1<<10) #define LIST_SHOW_USAGE (1<<11) #define VERIFY_SHOW_PHOTOS (1<<0) #define VERIFY_SHOW_POLICY_URLS (1<<1) #define VERIFY_SHOW_STD_NOTATIONS (1<<2) #define VERIFY_SHOW_USER_NOTATIONS (1<<3) #define VERIFY_SHOW_NOTATIONS (VERIFY_SHOW_STD_NOTATIONS|VERIFY_SHOW_USER_NOTATIONS) #define VERIFY_SHOW_KEYSERVER_URLS (1<<4) #define VERIFY_SHOW_UID_VALIDITY (1<<5) #define VERIFY_SHOW_UNUSABLE_UIDS (1<<6) #define VERIFY_PKA_LOOKUPS (1<<7) #define VERIFY_PKA_TRUST_INCREASE (1<<8) #define VERIFY_SHOW_PRIMARY_UID_ONLY (1<<9) #define KEYSERVER_HTTP_PROXY (1<<0) #define KEYSERVER_TIMEOUT (1<<1) #define KEYSERVER_ADD_FAKE_V3 (1<<2) #define KEYSERVER_AUTO_KEY_RETRIEVE (1<<3) #define KEYSERVER_HONOR_KEYSERVER_URL (1<<4) #define KEYSERVER_HONOR_PKA_RECORD (1<<5) #endif /*G10_OPTIONS_H*/ diff --git a/sm/keylist.c b/sm/keylist.c index 13de45d9c..abec049b7 100644 --- a/sm/keylist.c +++ b/sm/keylist.c @@ -1,1598 +1,1599 @@ /* 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" 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 /* 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" }, { 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 } 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); es_putc (':', 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 (int algo, unsigned int nbits, estream_t fp) { - if (algo == GCRY_PK_RSA && nbits >= 2048) - es_fputs ("23", fp); + if (gnupg_pk_is_compliant (CO_DE_VS, algo, NULL, nbits, NULL)) + es_fputs (gnupg_status_compliance_flag (CO_DE_VS), 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; 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_info (cert, &nbits); 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); /* 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. */ es_putc (':', fp); /* End of field 17. */ print_compliance_flags (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; if (opt.with_key_data) { if ( (p = gpgsm_get_keygrip_hexstring (cert))) { es_fprintf (fp, "grp:::::::::%s:\n", p); xfree (p); } 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); } 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); } /* 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); ksba_free (sexp); es_putc ('\n', fp); 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?")":""); { const char *algoname; unsigned int nbits; algoname = gcry_pk_algo_name (gpgsm_get_key_algo_info (cert, &nbits)); es_fprintf (fp, " keyType: %u bit %s\n", nbits, algoname? algoname:"?"); } /* 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 [%d octets]\n", i? "critExtn":" extn", oid, s?" (":"", s?s:"", s?")":"", (int)len); if ((flag & OID_FLAG_UTF8)) { if (!cert_der) cert_der = ksba_cert_get_image (cert, NULL); assert (cert_der); print_utf8_extn_raw (fp, -15, cert_der+off, 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); ksba_free (sexp); es_putc ('\n', fp); 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); { const char *algoname; unsigned int nbits; algoname = gcry_pk_algo_name (gpgsm_get_key_algo_info (cert, &nbits)); es_fprintf (fp, " key type: %u bit %s\n", nbits, algoname? algoname:"?"); } 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); 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 (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)); } } /* 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_FPR20 || desc[i].mode == KEYDB_SEARCH_MODE_FPR16 || 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, 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; }