mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
096e7457ec
The asymmetric quotes used by GNU in the past (`...') don't render nicely on modern systems. We now use two \x27 characters ('...'). The proper solution would be to use the correct Unicode symmetric quotes here. However this has the disadvantage that the system requires Unicode support. We don't want that today. If Unicode is available a generated po file can be used to output proper quotes. A simple sed script like the one used for en@quote is sufficient to change them. The changes have been done by applying sed -i "s/\`\([^'\`]*\)'/'\1'/g" to most files and fixing obvious problems by hand. The msgid strings in the po files were fixed with a similar command.
1585 lines
45 KiB
C
1585 lines
45 KiB
C
/* 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 <http://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 "i18n.h"
|
||
#include "tlv.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 fucntions. */
|
||
#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;
|
||
}
|
||
|
||
|
||
|
||
|
||
/* 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);
|
||
|
||
/* We assume --fixed-list-mode for gpgsm */
|
||
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);
|
||
if (have_secret)
|
||
{
|
||
char *cardsn;
|
||
|
||
p = gpgsm_get_keygrip_hexstring (cert);
|
||
if (!gpgsm_agent_keyinfo (ctrl, p, &cardsn) && cardsn)
|
||
{
|
||
/* Field 14, not used: */
|
||
es_putc (':', fp);
|
||
/* Field 15: Token serial number. */
|
||
es_fputs (cardsn, fp);
|
||
es_putc (':', fp);
|
||
}
|
||
xfree (cardsn);
|
||
xfree (p);
|
||
}
|
||
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 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 (0);
|
||
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 (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 (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;
|
||
}
|