/* keylist.c * Copyright (C) 1998, 1999, 2000, 2001, 2003, * 2004 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 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include #include #include #include #include #include #include #include #include "gpgsm.h" #include #include #include "keydb.h" #include "../kbx/keybox.h" /* for KEYBOX_FLAG_* */ #include "i18n.h" struct list_external_parm_s { ctrl_t ctrl; FILE *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" }, { NULL, NULL } }; static void print_key_data (ksba_cert_t cert, FILE *fp) { #if 0 int n = pk ? pubkey_get_npkey( pk->pubkey_algo ) : 0; int i; for(i=0; i < n; i++ ) { fprintf (fp, "pkd:%d:%u:", i, mpi_get_nbits( pk->pkey[i] ) ); mpi_print(stdout, pk->pkey[i], 1 ); putchar(':'); putchar('\n'); } #endif } static void print_capabilities (ksba_cert_t cert, FILE *fp) { gpg_error_t err; unsigned int use; err = ksba_cert_get_key_usage (cert, &use); if (gpg_err_code (err) == GPG_ERR_NO_DATA) { putc ('e', fp); putc ('s', fp); putc ('c', fp); putc ('E', fp); putc ('S', fp); 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))) putc ('e', fp); if ((use & (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION))) putc ('s', fp); if ((use & KSBA_KEYUSAGE_KEY_CERT_SIGN)) putc ('c', fp); if ((use & (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT))) putc ('E', fp); if ((use & (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION))) putc ('S', fp); if ((use & KSBA_KEYUSAGE_KEY_CERT_SIGN)) putc ('C', fp); } static void print_time (gnupg_isotime_t t, FILE *fp) { if (!t || !*t) ; else fputs (t, fp); } /* return an allocated string with the email address extracted from a DN */ static char * email_kludge (const char *name) { const unsigned char *p; unsigned char *buf; int n; if (strncmp (name, "1.2.840.113549.1.9.1=#", 22)) return NULL; /* 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 */ name += 22; for (n=0, p=name; hexdigitp (p) && hexdigitp (p+1); p +=2, n++) ; if (*p != '#' || !n) return NULL; buf = xtrymalloc (n+3); if (!buf) return NULL; /* oops, out of core */ *buf = '<'; for (n=1, p=name; *p != '#'; p +=2, n++) buf[n] = xtoi_2 (p); buf[n++] = '>'; buf[n] = 0; return buf; } /* List one certificate in colon mode */ static void list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity, FILE *fp, int have_secret) { int idx; char truststring[2]; char *p; ksba_sexp_t sexp; char *fpr; ksba_isotime_t t; gpg_error_t valerr; if (ctrl->with_validation) valerr = gpgsm_validate_chain (ctrl, cert, NULL, 1, NULL, 0); else valerr = 0; fputs (have_secret? "crs:":"crt:", fp); 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 if (valerr) *truststring = 'i'; 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'; } if (*truststring) fputs (truststring, fp); fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); fprintf (fp, ":%u:%d:%s:", /*keylen_of_cert (cert)*/1024, /* pubkey_algo_of_cert (cert)*/1, fpr+24); /* We assume --fixed-list-mode for gpgsm */ ksba_cert_get_validity (cert, 0, t); print_time (t, fp); putc (':', fp); ksba_cert_get_validity (cert, 1, t); print_time ( t, fp); 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++) fprintf (fp,"%02X", *s); } xfree (sexp); } putc (':', fp); /* Field 9, ownertrust - not used here */ putc (':', fp); /* field 10, old user ID - we use it here for the issuer DN */ if ((p = ksba_cert_get_issuer (cert,0))) { print_sanitized_string (fp, p, ':'); xfree (p); } putc (':', fp); /* Field 11, signature class - not used */ putc (':', fp); /* Field 12, capabilities: */ print_capabilities (cert, fp); putc (':', fp); putc ('\n', fp); /* FPR record */ fprintf (fp, "fpr:::::::::%s:::", fpr); /* Print chaining ID (field 13)*/ { ksba_cert_t next; int rc; rc = gpgsm_walk_cert_chain (cert, &next); if (!rc) /* We known the issuer's certificate. */ { p = gpgsm_get_fingerprint_hexstring (next, GCRY_MD_SHA1); fputs (p, fp); xfree (p); ksba_cert_release (next); } else if (rc == -1) /* We reached the root certificate. */ { fputs (fpr, fp); } } putc (':', fp); putc ('\n', fp); xfree (fpr); fpr = NULL; if (opt.with_key_data) { if ( (p = gpgsm_get_keygrip_hexstring (cert))) { fprintf (fp, "grp:::::::::%s:\n", p); xfree (p); } print_key_data (cert, fp); } for (idx=0; (p = ksba_cert_get_subject (cert,idx)); idx++) { fprintf (fp, "uid:%s::::::::", truststring); print_sanitized_string (fp, p, ':'); putc (':', fp); putc (':', fp); 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 */ char *pp = email_kludge (p); if (pp) { fprintf (fp, "uid:%s::::::::", truststring); print_sanitized_string (fp, pp, ':'); putc (':', fp); putc (':', fp); putc ('\n', fp); xfree (pp); } } xfree (p); } } /* List one certificate in raw mode useful to have a closer look at the certificate. This one does not beautification and only minimal output sanitation. It is mainly useful for debugging. */ static void list_cert_raw (ctrl_t ctrl, ksba_cert_t cert, FILE *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; sexp = ksba_cert_get_serial (cert); fputs ("Serial number: ", fp); gpgsm_print_serial (fp, sexp); ksba_free (sexp); putc ('\n', fp); dn = ksba_cert_get_issuer (cert, 0); fputs (" Issuer: ", fp); gpgsm_print_name (fp, dn); ksba_free (dn); putc ('\n', fp); for (idx=1; (dn = ksba_cert_get_issuer (cert, idx)); idx++) { fputs (" aka: ", fp); gpgsm_print_name (fp, dn); ksba_free (dn); putc ('\n', fp); } dn = ksba_cert_get_subject (cert, 0); fputs (" Subject: ", fp); gpgsm_print_name (fp, dn); ksba_free (dn); putc ('\n', fp); for (idx=1; (dn = ksba_cert_get_subject (cert, idx)); idx++) { fputs (" aka: ", fp); gpgsm_print_name (fp, dn); ksba_free (dn); putc ('\n', fp); } ksba_cert_get_validity (cert, 0, t); fputs (" validity: ", fp); gpgsm_print_time (fp, t); fputs (" through ", fp); ksba_cert_get_validity (cert, 1, t); gpgsm_print_time (fp, t); putc ('\n', fp); err = ksba_cert_get_key_usage (cert, &kusage); if (gpg_err_code (err) != GPG_ERR_NO_DATA) { fputs (" key usage:", fp); if (err) fprintf (fp, " [error: %s]", gpg_strerror (err)); else { if ( (kusage & KSBA_KEYUSAGE_DIGITAL_SIGNATURE)) fputs (" digitalSignature", fp); if ( (kusage & KSBA_KEYUSAGE_NON_REPUDIATION)) fputs (" nonRepudiation", fp); if ( (kusage & KSBA_KEYUSAGE_KEY_ENCIPHERMENT)) fputs (" keyEncipherment", fp); if ( (kusage & KSBA_KEYUSAGE_DATA_ENCIPHERMENT)) fputs (" dataEncipherment", fp); if ( (kusage & KSBA_KEYUSAGE_KEY_AGREEMENT)) fputs (" keyAgreement", fp); if ( (kusage & KSBA_KEYUSAGE_KEY_CERT_SIGN)) fputs (" certSign", fp); if ( (kusage & KSBA_KEYUSAGE_CRL_SIGN)) fputs (" crlSign", fp); if ( (kusage & KSBA_KEYUSAGE_ENCIPHER_ONLY)) fputs (" encipherOnly", fp); if ( (kusage & KSBA_KEYUSAGE_DECIPHER_ONLY)) fputs (" decipherOnly", fp); } putc ('\n', fp); } err = ksba_cert_get_ext_key_usages (cert, &string); if (gpg_err_code (err) != GPG_ERR_NO_DATA) { fputs ("ext key usage: ", fp); if (err) 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; fputs (key_purpose_map[i].oid?key_purpose_map[i].name:p, fp); p = pend; if (*p != 'C') fputs (" (suggested)", fp); if ((p = strchr (p, '\n'))) { p++; fputs (", ", fp); } } xfree (string); } putc ('\n', fp); } err = ksba_cert_get_cert_policies (cert, &string); if (gpg_err_code (err) != GPG_ERR_NO_DATA) { fputs (" policies: ", fp); if (err) fprintf (fp, "[error: %s]", gpg_strerror (err)); else { for (p=string; *p; p++) { if (*p == '\n') *p = ','; } print_sanitized_string (fp, string, 0); xfree (string); } putc ('\n', fp); } err = ksba_cert_is_ca (cert, &is_ca, &chainlen); if (err || is_ca) { fputs (" chain length: ", fp); if (err) fprintf (fp, "[error: %s]", gpg_strerror (err)); else if (chainlen == -1) fputs ("unlimited", fp); else fprintf (fp, "%d", chainlen); putc ('\n', fp); } dn = gpgsm_get_fingerprint_string (cert, GCRY_MD_MD5); fprintf (fp, " md5 fpr: %s\n", dn?dn:"error"); xfree (dn); dn = gpgsm_get_fingerprint_string (cert, 0); fprintf (fp, " fingerprint: %s\n", dn?dn:"error"); xfree (dn); if (with_validation) { err = gpgsm_validate_chain (ctrl, cert, NULL, 1, fp, 0); if (!err) fprintf (fp, " [certificate is good]\n"); else fprintf (fp, " [certificate is bad: %s]\n", gpg_strerror (err)); } } /* List one certificate in standard mode */ static void list_cert_std (ctrl_t ctrl, ksba_cert_t cert, FILE *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; sexp = ksba_cert_get_serial (cert); fputs ("Serial number: ", fp); gpgsm_print_serial (fp, sexp); ksba_free (sexp); putc ('\n', fp); dn = ksba_cert_get_issuer (cert, 0); fputs (" Issuer: ", fp); gpgsm_print_name (fp, dn); ksba_free (dn); putc ('\n', fp); for (idx=1; (dn = ksba_cert_get_issuer (cert, idx)); idx++) { fputs (" aka: ", fp); gpgsm_print_name (fp, dn); ksba_free (dn); putc ('\n', fp); } dn = ksba_cert_get_subject (cert, 0); fputs (" Subject: ", fp); gpgsm_print_name (fp, dn); ksba_free (dn); putc ('\n', fp); for (idx=1; (dn = ksba_cert_get_subject (cert, idx)); idx++) { fputs (" aka: ", fp); gpgsm_print_name (fp, dn); ksba_free (dn); putc ('\n', fp); } ksba_cert_get_validity (cert, 0, t); fputs (" validity: ", fp); gpgsm_print_time (fp, t); fputs (" through ", fp); ksba_cert_get_validity (cert, 1, t); gpgsm_print_time (fp, t); putc ('\n', fp); err = ksba_cert_get_key_usage (cert, &kusage); if (gpg_err_code (err) != GPG_ERR_NO_DATA) { fputs (" key usage:", fp); if (err) fprintf (fp, " [error: %s]", gpg_strerror (err)); else { if ( (kusage & KSBA_KEYUSAGE_DIGITAL_SIGNATURE)) fputs (" digitalSignature", fp); if ( (kusage & KSBA_KEYUSAGE_NON_REPUDIATION)) fputs (" nonRepudiation", fp); if ( (kusage & KSBA_KEYUSAGE_KEY_ENCIPHERMENT)) fputs (" keyEncipherment", fp); if ( (kusage & KSBA_KEYUSAGE_DATA_ENCIPHERMENT)) fputs (" dataEncipherment", fp); if ( (kusage & KSBA_KEYUSAGE_KEY_AGREEMENT)) fputs (" keyAgreement", fp); if ( (kusage & KSBA_KEYUSAGE_KEY_CERT_SIGN)) fputs (" certSign", fp); if ( (kusage & KSBA_KEYUSAGE_CRL_SIGN)) fputs (" crlSign", fp); if ( (kusage & KSBA_KEYUSAGE_ENCIPHER_ONLY)) fputs (" encipherOnly", fp); if ( (kusage & KSBA_KEYUSAGE_DECIPHER_ONLY)) fputs (" decipherOnly", fp); } putc ('\n', fp); } err = ksba_cert_get_ext_key_usages (cert, &string); if (gpg_err_code (err) != GPG_ERR_NO_DATA) { fputs ("ext key usage: ", fp); if (err) 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; fputs (key_purpose_map[i].oid?key_purpose_map[i].name:p, fp); p = pend; if (*p != 'C') fputs (" (suggested)", fp); if ((p = strchr (p, '\n'))) { p++; fputs (", ", fp); } } xfree (string); } putc ('\n', fp); } err = ksba_cert_get_cert_policies (cert, &string); if (gpg_err_code (err) != GPG_ERR_NO_DATA) { fputs (" policies: ", fp); if (err) fprintf (fp, "[error: %s]", gpg_strerror (err)); else { for (p=string; *p; p++) { if (*p == '\n') *p = ','; } print_sanitized_string (fp, string, 0); xfree (string); } putc ('\n', fp); } err = ksba_cert_is_ca (cert, &is_ca, &chainlen); if (err || is_ca) { fputs (" chain length: ", fp); if (err) fprintf (fp, "[error: %s]", gpg_strerror (err)); else if (chainlen == -1) fputs ("unlimited", fp); else fprintf (fp, "%d", chainlen); putc ('\n', fp); } if (opt.with_md5_fingerprint) { dn = gpgsm_get_fingerprint_string (cert, GCRY_MD_MD5); fprintf (fp, " md5 fpr: %s\n", dn?dn:"error"); xfree (dn); } dn = gpgsm_get_fingerprint_string (cert, 0); fprintf (fp, " fingerprint: %s\n", dn?dn:"error"); xfree (dn); if (with_validation) { err = gpgsm_validate_chain (ctrl, cert, NULL, 1, fp, 0); if (!err) fprintf (fp, " [certificate is good]\n"); else 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, ksba_cert_t cert, int raw_mode, FILE *fp, int with_validation) { ksba_cert_t next = NULL; if (raw_mode) list_cert_raw (ctrl, cert, fp, 0, with_validation); else list_cert_std (ctrl, cert, fp, 0, with_validation); ksba_cert_ref (cert); while (!gpgsm_walk_cert_chain (cert, &next)) { ksba_cert_release (cert); fputs ("Certified by\n", fp); if (raw_mode) list_cert_raw (ctrl, next, fp, 0, with_validation); else list_cert_std (ctrl, next, fp, 0, with_validation); cert = next; } ksba_cert_release (cert); 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 intead of the standard beautified one. */ static gpg_error_t list_internal_keys (CTRL ctrl, STRLIST names, FILE *fp, unsigned int mode, int raw_mode) { KEYDB_HANDLE hd; KEYDB_SEARCH_DESC *desc = NULL; STRLIST sl; int ndesc; ksba_cert_t cert = NULL; gpg_error_t rc = 0; const char *lastresname, *resname; int have_secret; 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_errno (errno); 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 = keydb_classify_name (sl->d, desc+ndesc); if (rc) { log_error ("key `%s' not found: %s\n", sl->d, gpg_strerror (rc)); rc = 0; } else ndesc++; } } /* 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 */ 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; } resname = keydb_get_resource_name (hd); if (lastresname != resname ) { int i; if (ctrl->no_server) { fprintf (fp, "%s\n", resname ); for (i=strlen(resname); i; i-- ) putchar('-'); putc ('\n', fp); lastresname = resname; } } have_secret = 0; if (mode) { char *p = gpgsm_get_keygrip_hexstring (cert); if (p) { rc = gpgsm_agent_havekey (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, cert, raw_mode, fp, ctrl->with_validation); else { if (raw_mode) list_cert_raw (ctrl, cert, fp, have_secret, ctrl->with_validation); else list_cert_std (ctrl, cert, fp, have_secret, ctrl->with_validation); putc ('\n', fp); } } ksba_cert_release (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); 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; fprintf (parm->fp, "%s\n", resname ); for (i=strlen(resname); i; i-- ) putchar('-'); 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, cert, parm->raw_mode, parm->fp, 0); else { if (parm->raw_mode) list_cert_raw (parm->ctrl, cert, parm->fp, 0, 0); else list_cert_std (parm->ctrl, cert, parm->fp, 0, 0); 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 ctrl, STRLIST names, FILE *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, list_external_cb, &parm); 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 ctrl, STRLIST names, FILE *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; }