diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:14:06 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:14:06 +0000 |
commit | eee068778cb28ecf3c14e1bf843a95547d72c42d (patch) | |
tree | 0e07b30ddc5ea579d682d5dbe57998200d1c9ab7 /sm/keylist.c | |
parent | Initial commit. (diff) | |
download | gnupg2-eee068778cb28ecf3c14e1bf843a95547d72c42d.tar.xz gnupg2-eee068778cb28ecf3c14e1bf843a95547d72c42d.zip |
Adding upstream version 2.2.40.upstream/2.2.40upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | sm/keylist.c | 1686 |
1 files changed, 1686 insertions, 0 deletions
diff --git a/sm/keylist.c b/sm/keylist.c new file mode 100644 index 0000000..2d51aa7 --- /dev/null +++ b/sm/keylist.c @@ -0,0 +1,1686 @@ +/* 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 <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> +#include <assert.h> + +#include "gpgsm.h" + +#include <gcrypt.h> +#include <ksba.h> + +#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 +/* The extension can be trnted as a hex string. */ +#define OID_FLAG_HEX 4 + +/* A table mapping OIDs to a descriptive string. */ +static struct +{ + char *oid; + char *name; + unsigned int flag; /* A flag as described above. */ +} oidtranstbl[] = { + + /* Algorithms. */ + { "1.2.840.10040.4.1", "dsa" }, + { "1.2.840.10040.4.3", "dsaWithSha1" }, + + { "1.2.840.113549.1.1.1", "rsaEncryption" }, + { "1.2.840.113549.1.1.2", "md2WithRSAEncryption" }, + { "1.2.840.113549.1.1.3", "md4WithRSAEncryption" }, + { "1.2.840.113549.1.1.4", "md5WithRSAEncryption" }, + { "1.2.840.113549.1.1.5", "sha1WithRSAEncryption" }, + { "1.2.840.113549.1.1.7", "rsaOAEP" }, + { "1.2.840.113549.1.1.8", "rsaOAEP-MGF" }, + { "1.2.840.113549.1.1.9", "rsaOAEP-pSpecified" }, + { "1.2.840.113549.1.1.10", "rsaPSS" }, + { "1.2.840.113549.1.1.11", "sha256WithRSAEncryption" }, + { "1.2.840.113549.1.1.12", "sha384WithRSAEncryption" }, + { "1.2.840.113549.1.1.13", "sha512WithRSAEncryption" }, + + { "1.3.14.3.2.26", "sha1" }, + { "1.3.14.3.2.29", "sha-1WithRSAEncryption" }, + { "1.3.36.3.3.1.2", "rsaSignatureWithripemd160" }, + + + /* Telesec extensions. */ + { "0.2.262.1.10.12.0", "certExtensionLiabilityLimitationExt" }, + { "0.2.262.1.10.12.1", "telesecCertIdExt" }, + { "0.2.262.1.10.12.2", "telesecPolicyIdentifier" }, + { "0.2.262.1.10.12.3", "telesecPolicyQualifierID" }, + { "0.2.262.1.10.12.4", "telesecCRLFilteredExt" }, + { "0.2.262.1.10.12.5", "telesecCRLFilterExt"}, + { "0.2.262.1.10.12.6", "telesecNamingAuthorityExt" }, +#define OIDSTR_restriction \ + "1.3.36.8.3.8" + { OIDSTR_restriction, "restriction", OID_FLAG_UTF8 }, + + + /* PKIX private extensions. */ + { "1.3.6.1.5.5.7.1.1", "authorityInfoAccess" }, + { "1.3.6.1.5.5.7.1.2", "biometricInfo" }, + { "1.3.6.1.5.5.7.1.3", "qcStatements" }, + { "1.3.6.1.5.5.7.1.4", "acAuditIdentity" }, + { "1.3.6.1.5.5.7.1.5", "acTargeting" }, + { "1.3.6.1.5.5.7.1.6", "acAaControls" }, + { "1.3.6.1.5.5.7.1.7", "sbgp-ipAddrBlock" }, + { "1.3.6.1.5.5.7.1.8", "sbgp-autonomousSysNum" }, + { "1.3.6.1.5.5.7.1.9", "sbgp-routerIdentifier" }, + { "1.3.6.1.5.5.7.1.10", "acProxying" }, + { "1.3.6.1.5.5.7.1.11", "subjectInfoAccess" }, + + { "1.3.6.1.5.5.7.48.1", "ocsp" }, + { "1.3.6.1.5.5.7.48.2", "caIssuers" }, + { "1.3.6.1.5.5.7.48.3", "timeStamping" }, + { "1.3.6.1.5.5.7.48.5", "caRepository" }, + + /* X.509 id-ce */ + { "2.5.29.14", "subjectKeyIdentifier", OID_FLAG_SKIP}, + { "2.5.29.15", "keyUsage", OID_FLAG_SKIP}, + { "2.5.29.16", "privateKeyUsagePeriod" }, + { "2.5.29.17", "subjectAltName", OID_FLAG_SKIP}, + { "2.5.29.18", "issuerAltName", OID_FLAG_SKIP}, + { "2.5.29.19", "basicConstraints", OID_FLAG_SKIP}, + { "2.5.29.20", "cRLNumber" }, + { "2.5.29.21", "cRLReason" }, + { "2.5.29.22", "expirationDate" }, + { "2.5.29.23", "instructionCode" }, + { "2.5.29.24", "invalidityDate" }, + { "2.5.29.27", "deltaCRLIndicator" }, + { "2.5.29.28", "issuingDistributionPoint" }, + { "2.5.29.29", "certificateIssuer" }, + { "2.5.29.30", "nameConstraints" }, + { "2.5.29.31", "cRLDistributionPoints", OID_FLAG_SKIP}, + { "2.5.29.32", "certificatePolicies", OID_FLAG_SKIP}, + { "2.5.29.32.0", "anyPolicy" }, + { "2.5.29.33", "policyMappings" }, + { "2.5.29.35", "authorityKeyIdentifier", OID_FLAG_SKIP}, + { "2.5.29.36", "policyConstraints" }, + { "2.5.29.37", "extKeyUsage", OID_FLAG_SKIP}, + { "2.5.29.46", "freshestCRL" }, + { "2.5.29.54", "inhibitAnyPolicy" }, + + /* Netscape certificate extensions. */ + { "2.16.840.1.113730.1.1", "netscape-cert-type" }, + { "2.16.840.1.113730.1.2", "netscape-base-url" }, + { "2.16.840.1.113730.1.3", "netscape-revocation-url" }, + { "2.16.840.1.113730.1.4", "netscape-ca-revocation-url" }, + { "2.16.840.1.113730.1.7", "netscape-cert-renewal-url" }, + { "2.16.840.1.113730.1.8", "netscape-ca-policy-url" }, + { "2.16.840.1.113730.1.9", "netscape-homePage-url" }, + { "2.16.840.1.113730.1.10", "netscape-entitylogo" }, + { "2.16.840.1.113730.1.11", "netscape-userPicture" }, + { "2.16.840.1.113730.1.12", "netscape-ssl-server-name" }, + { "2.16.840.1.113730.1.13", "netscape-comment" }, + + /* GnuPG extensions */ + { "1.3.6.1.4.1.11591.2.1.1", "pkaAddress" }, + { "1.3.6.1.4.1.11591.2.2.1", "standaloneCertificate" }, + { "1.3.6.1.4.1.11591.2.2.2", "wellKnownPrivateKey" }, + + /* Extensions used by the Bundesnetzagentur. */ + { "1.3.6.1.4.1.8301.3.5", "validityModel" }, + + /* Yubikey extensions for attestation certificates. */ + { "1.3.6.1.4.1.41482.3.3", "yubikey-firmware-version", OID_FLAG_HEX }, + { "1.3.6.1.4.1.41482.3.7", "yubikey-serial-number", OID_FLAG_HEX }, + { "1.3.6.1.4.1.41482.3.8", "yubikey-pin-touch-policy", OID_FLAG_HEX }, + { "1.3.6.1.4.1.41482.3.9", "yubikey-formfactor", OID_FLAG_HEX }, + + { NULL } +}; + + +/* Return the description for OID; if no description is available + NULL is returned. */ +static const char * +get_oid_desc (const char *oid, unsigned int *flag) +{ + int i; + + if (oid) + for (i=0; oidtranstbl[i].oid; i++) + if (!strcmp (oidtranstbl[i].oid, oid)) + { + if (flag) + *flag = oidtranstbl[i].flag; + return oidtranstbl[i].name; + } + if (flag) + *flag = 0; + return NULL; +} + + +static void +print_key_data (ksba_cert_t cert, estream_t fp) +{ +#if 0 + int n = pk ? pubkey_get_npkey( pk->pubkey_algo ) : 0; + int i; + + for(i=0; i < n; i++ ) + { + es_fprintf (fp, "pkd:%d:%u:", i, mpi_get_nbits( pk->pkey[i] ) ); + mpi_print(stdout, pk->pkey[i], 1 ); + putchar(':'); + putchar('\n'); + } +#else + (void)cert; + (void)fp; +#endif +} + +static void +print_capabilities (ksba_cert_t cert, estream_t fp) +{ + gpg_error_t err; + unsigned int use; + unsigned int is_encr, is_sign, is_cert; + size_t buflen; + char buffer[1]; + + + err = ksba_cert_get_user_data (cert, "is_qualified", + &buffer, sizeof (buffer), &buflen); + if (!err && buflen) + { + 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; + } + + is_encr = is_sign = is_cert = 0; + + if ((use & (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT))) + is_encr = 1; + if ((use & (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION))) + is_sign = 1; + if ((use & KSBA_KEYUSAGE_KEY_CERT_SIGN)) + is_cert = 1; + + /* We need to returned the faked key usage to frontends so that they + * can select the right key. Note that we don't do this for the + * human readable keyUsage. */ + if ((opt.compat_flags & COMPAT_ALLOW_KA_TO_ENCR) + && (use & KSBA_KEYUSAGE_KEY_AGREEMENT)) + is_encr = 1; + + if (is_encr) + es_putc ('e', fp); + if (is_sign) + es_putc ('s', fp); + if (is_cert) + es_putc ('c', fp); + if (is_encr) + es_putc ('E', fp); + if (is_sign) + es_putc ('S', fp); + if (is_cert) + es_putc ('C', fp); +} + + +static void +print_time (gnupg_isotime_t t, estream_t fp) +{ + if (!t || !*t) + ; + else + es_fputs (t, fp); +} + + +/* Return an allocated string with the email address extracted from a + DN. Note hat we use this code also in ../kbx/keybox-blob.c. */ +static char * +email_kludge (const char *name) +{ + const char *p, *string; + unsigned char *buf; + int n; + + string = name; + for (;;) + { + p = strstr (string, "1.2.840.113549.1.9.1=#"); + if (!p) + return NULL; + if (p == name || (p > string+1 && p[-1] == ',' && p[-2] != '\\')) + { + name = p + 22; + break; + } + string = p + 22; + } + + + /* This looks pretty much like an email address in the subject's DN + we use this to add an additional user ID entry. This way, + OpenSSL generated keys get a nicer and usable listing. */ + for (n=0, p=name; hexdigitp (p) && hexdigitp (p+1); p +=2, n++) + ; + if (!n) + return NULL; + buf = xtrymalloc (n+3); + if (!buf) + return NULL; /* oops, out of core */ + *buf = '<'; + for (n=1, p=name; hexdigitp (p); p +=2, n++) + buf[n] = xtoi_2 (p); + buf[n++] = '>'; + buf[n] = 0; + return (char*)buf; +} + + +/* Print the compliance flags to field 18. ALGO is the gcrypt algo + * number. NBITS is the length of the key in bits. */ +static void +print_compliance_flags (ksba_cert_t cert, int algo, unsigned int nbits, + estream_t fp) +{ + int hashalgo; + + /* Note that we do not need to test for PK_ALGO_FLAG_RSAPSS because + * that is not a property of the key but one of the created + * signature. */ + if (gnupg_pk_is_compliant (CO_DE_VS, algo, 0, NULL, nbits, NULL)) + { + hashalgo = gcry_md_map_name (ksba_cert_get_digest_algo (cert)); + if (gnupg_digest_is_compliant (CO_DE_VS, hashalgo)) + { + es_fputs (gnupg_status_compliance_flag (CO_DE_VS), fp); + } + } +} + + +/* 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); + es_putc (':', fp); + /* Field 13, not used: */ + es_putc (':', fp); + /* Field 14, not used: */ + es_putc (':', fp); + if (have_secret || ctrl->with_secret) + { + char *cardsn; + + p = gpgsm_get_keygrip_hexstring (cert); + if (!gpgsm_agent_keyinfo (ctrl, p, &cardsn) + && (cardsn || ctrl->with_secret)) + { + /* Field 15: Token serial number or secret key indicator. */ + if (cardsn) + es_fputs (cardsn, fp); + else if (ctrl->with_secret) + es_putc ('+', fp); + } + xfree (cardsn); + xfree (p); + } + es_putc (':', fp); /* End of field 15. */ + es_putc (':', fp); /* End of field 16. */ + es_putc (':', fp); /* End of field 17. */ + print_compliance_flags (cert, algo, nbits, fp); + es_putc (':', fp); /* End of field 18. */ + es_putc ('\n', fp); + + /* FPR record */ + es_fprintf (fp, "fpr:::::::::%s:::", fpr); + /* Print chaining ID (field 13)*/ + if (chain_id) + es_fputs (chain_id, fp); + es_putc (':', fp); + es_putc ('\n', fp); + xfree (fpr); fpr = NULL; chain_id = NULL; + xfree (chain_id_buffer); chain_id_buffer = NULL; + /* SHA256 FPR record */ + fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA256); + es_fprintf (fp, "fp2:::::::::%s::::\n", fpr); + xfree (fpr); fpr = NULL; + + /* Always print the keygrip. */ + if ( (p = gpgsm_get_keygrip_hexstring (cert))) + { + es_fprintf (fp, "grp:::::::::%s:\n", p); + xfree (p); + } + + if (opt.with_key_data) + print_key_data (cert, fp); + + kludge_uid = NULL; + for (idx=0; (p = ksba_cert_get_subject (cert,idx)); idx++) + { + /* In the case that the same email address is in the subject DN + as well as in an alternate subject name we avoid printing it + a second time. */ + if (kludge_uid && !strcmp (kludge_uid, p)) + continue; + + es_fprintf (fp, "uid:%s::::::::", truststring); + es_write_sanitized (fp, p, strlen (p), ":", NULL); + es_putc (':', fp); + es_putc (':', fp); + es_putc ('\n', fp); + if (!idx) + { + /* It would be better to get the faked email address from + the keydb. But as long as we don't have a way to pass + the meta data back, we just check it the same way as the + code used to create the keybox meta data does */ + kludge_uid = email_kludge (p); + if (kludge_uid) + { + es_fprintf (fp, "uid:%s::::::::", truststring); + es_write_sanitized (fp, kludge_uid, strlen (kludge_uid), + ":", NULL); + es_putc (':', fp); + es_putc (':', fp); + es_putc ('\n', fp); + } + } + xfree (p); + } + xfree (kludge_uid); +} + + +static void +print_name_raw (estream_t fp, const char *string) +{ + if (!string) + es_fputs ("[error]", fp); + else + es_write_sanitized (fp, string, strlen (string), NULL, NULL); +} + +static void +print_names_raw (estream_t fp, int indent, ksba_name_t name) +{ + int idx; + const char *s; + int indent_all; + + if ((indent_all = (indent < 0))) + indent = - indent; + + if (!name) + { + es_fputs ("none\n", fp); + return; + } + + for (idx=0; (s = ksba_name_enum (name, idx)); idx++) + { + char *p = ksba_name_get_uri (name, idx); + es_fprintf (fp, "%*s", idx||indent_all?indent:0, ""); + es_write_sanitized (fp, p?p:s, strlen (p?p:s), NULL, NULL); + es_putc ('\n', fp); + xfree (p); + } +} + + +static void +print_utf8_extn_raw (estream_t fp, int indent, + const unsigned char *der, size_t derlen) +{ + gpg_error_t err; + int class, tag, constructed, ndef; + size_t objlen, hdrlen; + + if (indent < 0) + indent = - indent; + + err = parse_ber_header (&der, &derlen, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > derlen || tag != TAG_UTF8_STRING)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + { + es_fprintf (fp, "%*s[%s]\n", indent, "", gpg_strerror (err)); + return; + } + es_fprintf (fp, "%*s(%.*s)\n", indent, "", (int)objlen, der); +} + + +static void +print_utf8_extn (estream_t fp, int indent, + const unsigned char *der, size_t derlen) +{ + gpg_error_t err; + int class, tag, constructed, ndef; + size_t objlen, hdrlen; + int indent_all; + + if ((indent_all = (indent < 0))) + indent = - indent; + + err = parse_ber_header (&der, &derlen, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (!err && (objlen > derlen || tag != TAG_UTF8_STRING)) + err = gpg_error (GPG_ERR_INV_OBJ); + if (err) + { + es_fprintf (fp, "%*s[%s%s]\n", + indent_all? indent:0, "", _("Error - "), gpg_strerror (err)); + return; + } + es_fprintf (fp, "%*s\"", indent_all? indent:0, ""); + /* Fixme: we should implement word wrapping */ + es_write_sanitized (fp, der, objlen, "\"", NULL); + es_fputs ("\"\n", fp); +} + + +/* Print the extension described by (DER,DERLEN) in hex. */ +static void +print_hex_extn (estream_t fp, int indent, + const unsigned char *der, size_t derlen) +{ + if (indent < 0) + indent = - indent; + + es_fprintf (fp, "%*s(", indent, ""); + for (; derlen; der++, derlen--) + es_fprintf (fp, "%02X%s", *der, derlen > 1? " ":""); + es_fprintf (fp, ")\n"); +} + + +/* List one certificate in raw mode useful to have a closer look at + the certificate. This one does no beautification and only minimal + output sanitation. It is mainly useful for debugging. */ +static void +list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd, + ksba_cert_t cert, estream_t fp, int have_secret, + int with_validation) +{ + gpg_error_t err; + size_t off, len; + ksba_sexp_t sexp, keyid; + char *dn; + ksba_isotime_t t; + int idx, i; + int is_ca, chainlen; + unsigned int kusage; + char *string, *p, *pend; + const char *oid, *s; + ksba_name_t name, name2; + unsigned int reason; + const unsigned char *cert_der = NULL; + + (void)have_secret; + + es_fprintf (fp, " ID: 0x%08lX\n", + gpgsm_get_short_fingerprint (cert, NULL)); + + sexp = ksba_cert_get_serial (cert); + es_fputs (" S/N: ", fp); + gpgsm_print_serial (fp, sexp); + es_putc ('\n', fp); + es_fputs (" (dec): ", fp); + gpgsm_print_serial_decimal (fp, sexp); + es_putc ('\n', fp); + ksba_free (sexp); + + dn = ksba_cert_get_issuer (cert, 0); + es_fputs (" Issuer: ", fp); + print_name_raw (fp, dn); + ksba_free (dn); + es_putc ('\n', fp); + for (idx=1; (dn = ksba_cert_get_issuer (cert, idx)); idx++) + { + es_fputs (" aka: ", fp); + print_name_raw (fp, dn); + ksba_free (dn); + es_putc ('\n', fp); + } + + dn = ksba_cert_get_subject (cert, 0); + es_fputs (" Subject: ", fp); + print_name_raw (fp, dn); + ksba_free (dn); + es_putc ('\n', fp); + for (idx=1; (dn = ksba_cert_get_subject (cert, idx)); idx++) + { + es_fputs (" aka: ", fp); + print_name_raw (fp, dn); + ksba_free (dn); + es_putc ('\n', fp); + } + + dn = gpgsm_get_fingerprint_string (cert, GCRY_MD_SHA256); + es_fprintf (fp, " sha2_fpr: %s\n", dn?dn:"error"); + xfree (dn); + + 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", + i? "critExtn":" extn", + oid, s?" (":"", s?s:"", s?")":""); + if ((flag & OID_FLAG_UTF8)) + { + if (!cert_der) + cert_der = ksba_cert_get_image (cert, NULL); + log_assert (cert_der); + es_fprintf (fp, "\n"); + print_utf8_extn_raw (fp, -15, cert_der+off, len); + } + else if ((flag & OID_FLAG_HEX)) + { + if (!cert_der) + cert_der = ksba_cert_get_image (cert, NULL); + log_assert (cert_der); + es_fprintf (fp, "\n"); + print_hex_extn (fp, -15, cert_der+off, len); + } + else + es_fprintf (fp, " [%d octets]\n", (int)len); + } + + + if (with_validation) + { + err = gpgsm_validate_chain (ctrl, cert, "", NULL, 1, fp, 0, NULL); + if (!err) + es_fprintf (fp, " [certificate is good]\n"); + else + es_fprintf (fp, " [certificate is bad: %s]\n", gpg_strerror (err)); + } + + if (hd) + { + unsigned int blobflags; + + err = keydb_get_flags (hd, KEYBOX_FLAG_BLOB, 0, &blobflags); + if (err) + es_fprintf (fp, " [error getting keyflags: %s]\n",gpg_strerror (err)); + else if ((blobflags & KEYBOX_FLAG_BLOB_EPHEMERAL)) + es_fprintf (fp, " [stored as ephemeral]\n"); + } + +} + + + + +/* List one certificate in standard mode */ +static void +list_cert_std (ctrl_t ctrl, ksba_cert_t cert, estream_t fp, int have_secret, + int with_validation) +{ + gpg_error_t err; + ksba_sexp_t sexp; + char *dn; + ksba_isotime_t t; + int idx, i; + int is_ca, chainlen; + unsigned int kusage; + char *string, *p, *pend; + size_t off, len; + const char *oid; + const unsigned char *cert_der = NULL; + + + es_fprintf (fp, " ID: 0x%08lX\n", + gpgsm_get_short_fingerprint (cert, NULL)); + + sexp = ksba_cert_get_serial (cert); + es_fputs (" S/N: ", fp); + gpgsm_print_serial (fp, sexp); + es_putc ('\n', fp); + es_fputs (" (dec): ", fp); + gpgsm_print_serial_decimal (fp, sexp); + es_putc ('\n', fp); + ksba_free (sexp); + + dn = ksba_cert_get_issuer (cert, 0); + es_fputs (" Issuer: ", fp); + gpgsm_es_print_name (fp, dn); + ksba_free (dn); + es_putc ('\n', fp); + for (idx=1; (dn = ksba_cert_get_issuer (cert, idx)); idx++) + { + es_fputs (" aka: ", fp); + gpgsm_es_print_name (fp, dn); + ksba_free (dn); + es_putc ('\n', fp); + } + + dn = ksba_cert_get_subject (cert, 0); + es_fputs (" Subject: ", fp); + gpgsm_es_print_name (fp, dn); + ksba_free (dn); + es_putc ('\n', fp); + for (idx=1; (dn = ksba_cert_get_subject (cert, idx)); idx++) + { + es_fputs (" aka: ", fp); + gpgsm_es_print_name (fp, dn); + ksba_free (dn); + es_putc ('\n', fp); + } + + ksba_cert_get_validity (cert, 0, t); + es_fputs (" validity: ", fp); + gpgsm_print_time (fp, t); + es_fputs (" through ", fp); + ksba_cert_get_validity (cert, 1, t); + gpgsm_print_time (fp, t); + es_putc ('\n', fp); + + + { + 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); + + dn = gpgsm_get_fingerprint_string (cert, GCRY_MD_SHA256); + es_fprintf (fp, " sha2 fpr: %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; + int depth = 0; + + 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)) + { + es_fputs ("Certified by\n", fp); + if (++depth > 50) + { + es_fputs (_("certificate chain too long\n"), fp); + break; + } + ksba_cert_release (cert); + 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, NULL, 0, list_external_cb, &parm); + if (gpg_err_code (rc) == GPG_ERR_EOF || rc == -1 + || gpg_err_code (rc) == GPG_ERR_NOT_FOUND) + rc = 0; /* "Not found" is not an error here. */ + if (rc) + log_error ("listing external keys failed: %s\n", gpg_strerror (rc)); + return rc; +} + +/* List all keys or just the key given as NAMES. + MODE controls the operation mode: + Bit 0-2: + 0 = list all public keys but don't flag secret ones + 1 = list only public keys + 2 = list only secret keys + 3 = list secret and public keys + Bit 6: list internal keys + Bit 7: list external keys + Bit 8: Do a raw format dump. + */ +gpg_error_t +gpgsm_list_keys (ctrl_t ctrl, strlist_t names, estream_t fp, + unsigned int mode) +{ + gpg_error_t err = 0; + + if ((mode & (1<<6))) + err = list_internal_keys (ctrl, names, fp, (mode & 3), (mode&256)); + if (!err && (mode & (1<<7))) + err = list_external_keys (ctrl, names, fp, (mode&256)); + return err; +} |