Page MenuHome GnuPG

No OneTemporary

diff --git a/g10/keylist.c b/g10/keylist.c
index 71f72e233..1ecfce998 100644
--- a/g10/keylist.c
+++ b/g10/keylist.c
@@ -1,1702 +1,1694 @@
/* keylist.c - Print information about OpenPGP keys
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
* 2008, 2010, 2012 Free Software Foundation, Inc.
+ * Copyright (C) 2013, 2014 Werner Koch
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#ifdef HAVE_DOSISH_SYSTEM
#include <fcntl.h> /* for setmode() */
#endif
#include "gpg.h"
#include "options.h"
#include "packet.h"
#include "status.h"
#include "keydb.h"
#include "photoid.h"
#include "util.h"
#include "ttyio.h"
#include "trustdb.h"
#include "main.h"
#include "i18n.h"
#include "status.h"
#include "call-agent.h"
static void list_all (int);
static void list_one (strlist_t names, int secret);
static void locate_one (ctrl_t ctrl, strlist_t names);
static void print_card_serialno (const char *serialno);
struct sig_stats
{
int inv_sigs;
int no_key;
int oth_err;
};
/* The stream used to write attribute packets to. */
static estream_t attrib_fp;
/* List the keys. If list is NULL, all available keys are listed.
With LOCATE_MODE set the locate algorithm is used to find a
key. */
void
public_key_list (ctrl_t ctrl, strlist_t list, int locate_mode)
{
#ifndef NO_TRUST_MODELS
if (opt.with_colons)
{
byte trust_model, marginals, completes, cert_depth, min_cert_level;
ulong created, nextcheck;
read_trust_options (&trust_model, &created, &nextcheck,
&marginals, &completes, &cert_depth, &min_cert_level);
es_fprintf (es_stdout, "tru:");
if (nextcheck && nextcheck <= make_timestamp ())
es_fprintf (es_stdout, "o");
if (trust_model != opt.trust_model)
es_fprintf (es_stdout, "t");
if (opt.trust_model == TM_PGP || opt.trust_model == TM_CLASSIC)
{
if (marginals != opt.marginals_needed)
es_fprintf (es_stdout, "m");
if (completes != opt.completes_needed)
es_fprintf (es_stdout, "c");
if (cert_depth != opt.max_cert_depth)
es_fprintf (es_stdout, "d");
if (min_cert_level != opt.min_cert_level)
es_fprintf (es_stdout, "l");
}
es_fprintf (es_stdout, ":%d:%lu:%lu", trust_model, created, nextcheck);
/* Only show marginals, completes, and cert_depth in the classic
or PGP trust models since they are not meaningful
otherwise. */
if (trust_model == TM_PGP || trust_model == TM_CLASSIC)
es_fprintf (es_stdout, ":%d:%d:%d", marginals, completes, cert_depth);
es_fprintf (es_stdout, "\n");
}
#endif /*!NO_TRUST_MODELS*/
/* We need to do the stale check right here because it might need to
update the keyring while we already have the keyring open. This
is very bad for W32 because of a sharing violation. For real OSes
it might lead to false results if we are later listing a keyring
which is associated with the inode of a deleted file. */
check_trustdb_stale ();
if (locate_mode)
locate_one (ctrl, list);
else if (!list)
list_all (0);
else
list_one (list, 0);
}
void
secret_key_list (ctrl_t ctrl, strlist_t list)
{
(void)ctrl;
check_trustdb_stale ();
if (!list)
list_all (1);
else /* List by user id */
list_one (list, 1);
}
void
print_seckey_info (PKT_public_key *pk)
{
u32 keyid[2];
char *p;
char pkstrbuf[PUBKEY_STRING_SIZE];
keyid_from_pk (pk, keyid);
p = get_user_id_native (keyid);
tty_printf ("\nsec %s/%s %s %s\n",
pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
keystr (keyid), datestr_from_pk (pk), p);
xfree (p);
}
/* Print information about the public key. With FP passed as NULL,
the tty output interface is used, otherwise output is directted to
the given stream. */
void
print_pubkey_info (estream_t fp, PKT_public_key * pk)
{
u32 keyid[2];
char *p;
char pkstrbuf[PUBKEY_STRING_SIZE];
keyid_from_pk (pk, keyid);
/* If the pk was chosen by a particular user ID, that is the one to
print. */
if (pk->user_id)
p = utf8_to_native (pk->user_id->name, pk->user_id->len, 0);
else
p = get_user_id_native (keyid);
if (fp)
tty_printf ("\n");
tty_fprintf (fp, "pub %s/%s %s %s\n",
pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
keystr (keyid), datestr_from_pk (pk), p);
xfree (p);
}
/* Print basic information of a secret key including the card serial
number information. */
#ifdef ENABLE_CARD_SUPPORT
void
print_card_key_info (estream_t fp, kbnode_t keyblock)
{
kbnode_t node;
char *hexgrip;
char *serialno;
int s2k_char;
char pkstrbuf[PUBKEY_STRING_SIZE];
for (node = keyblock; node; node = node->next)
{
if (node->pkt->pkttype == PKT_PUBLIC_KEY
|| node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
{
int rc;
PKT_public_key *pk = node->pkt->pkt.public_key;
serialno = NULL;
rc = hexkeygrip_from_pk (pk, &hexgrip);
if (rc)
{
log_error ("error computing a keygrip: %s\n", gpg_strerror (rc));
s2k_char = '?';
}
else if (!agent_get_keyinfo (NULL, hexgrip, &serialno))
s2k_char = serialno? '>':' ';
else
s2k_char = '#'; /* Key not found. */
tty_fprintf (fp, "%s%c %s/%s ",
node->pkt->pkttype == PKT_PUBLIC_KEY ? "sec" : "ssb",
s2k_char,
pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
keystr_from_pk (pk));
tty_fprintf (fp, _("created: %s"), datestr_from_pk (pk));
tty_fprintf (fp, " ");
tty_fprintf (fp, _("expires: %s"), expirestr_from_pk (pk));
if (serialno)
{
tty_fprintf (fp, "\n ");
tty_fprintf (fp, _("card-no: "));
if (strlen (serialno) == 32
&& !strncmp (serialno, "D27600012401", 12))
{
/* This is an OpenPGP card. Print the relevant part. */
/* Example: D2760001240101010001000003470000 */
/* xxxxyyyyyyyy */
tty_fprintf (fp, "%.*s %.*s", 4, serialno+16, 8, serialno+20);
}
else
tty_fprintf (fp, "%s", serialno);
}
tty_fprintf (fp, "\n");
xfree (hexgrip);
xfree (serialno);
}
}
}
#endif /*ENABLE_CARD_SUPPORT*/
/* Flags = 0x01 hashed 0x02 critical. */
static void
status_one_subpacket (sigsubpkttype_t type, size_t len, int flags,
const byte * buf)
{
char status[40];
/* Don't print these. */
if (len > 256)
return;
snprintf (status, sizeof status,
"%d %u %u ", type, flags, (unsigned int) len);
write_status_text_and_buffer (STATUS_SIG_SUBPACKET, status, buf, len, 0);
}
/* Print a policy URL. Allowed values for MODE are:
* 0 - print to stdout.
* 1 - use log_info and emit status messages.
* 2 - emit only status messages.
*/
void
show_policy_url (PKT_signature * sig, int indent, int mode)
{
const byte *p;
size_t len;
int seq = 0, crit;
estream_t fp = mode ? log_get_stream () : es_stdout;
while ((p =
enum_sig_subpkt (sig->hashed, SIGSUBPKT_POLICY, &len, &seq, &crit)))
{
if (mode != 2)
{
int i;
const char *str;
for (i = 0; i < indent; i++)
es_putc (' ', fp);
if (crit)
str = _("Critical signature policy: ");
else
str = _("Signature policy: ");
if (mode)
log_info ("%s", str);
else
es_fprintf (fp, "%s", str);
print_utf8_buffer (fp, p, len);
es_fprintf (fp, "\n");
}
if (mode)
write_status_buffer (STATUS_POLICY_URL, p, len, 0);
}
}
/*
mode=0 for stdout.
mode=1 for log_info + status messages
mode=2 for status messages only
*/
/* TODO: use this */
void
show_keyserver_url (PKT_signature * sig, int indent, int mode)
{
const byte *p;
size_t len;
int seq = 0, crit;
estream_t fp = mode ? log_get_stream () : es_stdout;
while ((p =
enum_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_KS, &len, &seq,
&crit)))
{
if (mode != 2)
{
int i;
const char *str;
for (i = 0; i < indent; i++)
es_putc (' ', es_stdout);
if (crit)
str = _("Critical preferred keyserver: ");
else
str = _("Preferred keyserver: ");
if (mode)
log_info ("%s", str);
else
es_fprintf (es_stdout, "%s", str);
print_utf8_buffer (fp, p, len);
es_fprintf (fp, "\n");
}
if (mode)
status_one_subpacket (SIGSUBPKT_PREF_KS, len,
(crit ? 0x02 : 0) | 0x01, p);
}
}
/*
mode=0 for stdout.
mode=1 for log_info + status messages
mode=2 for status messages only
Defined bits in WHICH:
1 == standard notations
2 == user notations
*/
void
show_notation (PKT_signature * sig, int indent, int mode, int which)
{
estream_t fp = mode ? log_get_stream () : es_stdout;
struct notation *nd, *notations;
if (which == 0)
which = 3;
notations = sig_to_notation (sig);
/* There may be multiple notations in the same sig. */
for (nd = notations; nd; nd = nd->next)
{
if (mode != 2)
{
int has_at = !!strchr (nd->name, '@');
if ((which & 1 && !has_at) || (which & 2 && has_at))
{
int i;
const char *str;
for (i = 0; i < indent; i++)
es_putc (' ', es_stdout);
if (nd->flags.critical)
str = _("Critical signature notation: ");
else
str = _("Signature notation: ");
if (mode)
log_info ("%s", str);
else
es_fprintf (es_stdout, "%s", str);
/* This is all UTF8 */
print_utf8_buffer (fp, nd->name, strlen (nd->name));
es_fprintf (fp, "=");
print_utf8_buffer (fp, nd->value, strlen (nd->value));
es_fprintf (fp, "\n");
}
}
if (mode)
{
write_status_buffer (STATUS_NOTATION_NAME,
nd->name, strlen (nd->name), 0);
write_status_buffer (STATUS_NOTATION_DATA,
nd->value, strlen (nd->value), 50);
}
}
free_notation (notations);
}
static void
print_signature_stats (struct sig_stats *s)
{
if (s->inv_sigs == 1)
tty_printf (_("1 bad signature\n"));
else if (s->inv_sigs)
tty_printf (_("%d bad signatures\n"), s->inv_sigs);
if (s->no_key == 1)
tty_printf (_("1 signature not checked due to a missing key\n"));
else if (s->no_key)
tty_printf (_("%d signatures not checked due to missing keys\n"),
s->no_key);
if (s->oth_err == 1)
tty_printf (_("1 signature not checked due to an error\n"));
else if (s->oth_err)
tty_printf (_("%d signatures not checked due to errors\n"), s->oth_err);
}
static void
list_all (int secret)
{
KEYDB_HANDLE hd;
KBNODE keyblock = NULL;
int rc = 0;
const char *lastresname, *resname;
struct sig_stats stats;
memset (&stats, 0, sizeof (stats));
hd = keydb_new ();
if (!hd)
rc = gpg_error (GPG_ERR_GENERAL);
else
rc = keydb_search_first (hd);
if (rc)
{
if (gpg_err_code (rc) != GPG_ERR_NOT_FOUND)
log_error ("keydb_search_first failed: %s\n", g10_errstr (rc));
goto leave;
}
lastresname = NULL;
do
{
rc = keydb_get_keyblock (hd, &keyblock);
if (rc)
{
log_error ("keydb_get_keyblock failed: %s\n", g10_errstr (rc));
goto leave;
}
if (secret && agent_probe_any_secret_key (NULL, keyblock))
; /* Secret key listing requested but this isn't one. */
else
{
if (!opt.with_colons)
{
resname = keydb_get_resource_name (hd);
if (lastresname != resname)
{
int i;
es_fprintf (es_stdout, "%s\n", resname);
for (i = strlen (resname); i; i--)
es_putc ('-', es_stdout);
es_putc ('\n', es_stdout);
lastresname = resname;
}
}
merge_keys_and_selfsig (keyblock);
list_keyblock (keyblock, secret, opt.fingerprint,
opt.check_sigs ? &stats : NULL);
}
release_kbnode (keyblock);
keyblock = NULL;
}
while (!(rc = keydb_search_next (hd)));
if (rc && gpg_err_code (rc) != GPG_ERR_NOT_FOUND)
log_error ("keydb_search_next failed: %s\n", g10_errstr (rc));
if (opt.check_sigs && !opt.with_colons)
print_signature_stats (&stats);
leave:
release_kbnode (keyblock);
keydb_release (hd);
}
static void
list_one (strlist_t names, int secret)
{
int rc = 0;
KBNODE keyblock = NULL;
GETKEY_CTX ctx;
const char *resname;
const char *keyring_str = _("Keyring");
int i;
struct sig_stats stats;
memset (&stats, 0, sizeof (stats));
/* fixme: using the bynames function has the disadvantage that we
* don't know wether one of the names given was not found. OTOH,
* this function has the advantage to list the names in the
* sequence as defined by the keyDB and does not duplicate
* outputs. A solution could be do test whether all given have
* been listed (this needs a way to use the keyDB search
* functions) or to have the search function return indicators for
* found names. Yet another way is to use the keydb search
* facilities directly. */
rc = getkey_bynames (&ctx, NULL, names, secret, &keyblock);
if (rc)
{
log_error ("error reading key: %s\n", g10_errstr (rc));
get_pubkey_end (ctx);
return;
}
do
{
if ((opt.list_options & LIST_SHOW_KEYRING) && !opt.with_colons)
{
resname = keydb_get_resource_name (get_ctx_handle (ctx));
es_fprintf (es_stdout, "%s: %s\n", keyring_str, resname);
for (i = strlen (resname) + strlen (keyring_str) + 2; i; i--)
es_putc ('-', es_stdout);
es_putc ('\n', es_stdout);
}
list_keyblock (keyblock, secret, opt.fingerprint,
(!secret && opt.check_sigs)? &stats : NULL);
release_kbnode (keyblock);
}
while (!getkey_next (ctx, NULL, &keyblock));
getkey_end (ctx);
if (opt.check_sigs && !opt.with_colons)
print_signature_stats (&stats);
}
static void
locate_one (ctrl_t ctrl, strlist_t names)
{
int rc = 0;
strlist_t sl;
GETKEY_CTX ctx = NULL;
KBNODE keyblock = NULL;
struct sig_stats stats;
memset (&stats, 0, sizeof (stats));
for (sl = names; sl; sl = sl->next)
{
rc = get_pubkey_byname (ctrl, &ctx, NULL, sl->d, &keyblock, NULL, 1, 0);
if (rc)
{
if (gpg_err_code (rc) != GPG_ERR_NO_PUBKEY)
log_error ("error reading key: %s\n", g10_errstr (rc));
}
else
{
do
{
list_keyblock (keyblock, 0, opt.fingerprint,
opt.check_sigs ? &stats : NULL);
release_kbnode (keyblock);
}
while (ctx && !get_pubkey_next (ctx, NULL, &keyblock));
get_pubkey_end (ctx);
ctx = NULL;
}
}
if (opt.check_sigs && !opt.with_colons)
print_signature_stats (&stats);
}
static void
print_key_data (PKT_public_key * pk)
{
int n = pk ? pubkey_get_npkey (pk->pubkey_algo) : 0;
int i;
for (i = 0; i < n; i++)
{
es_fprintf (es_stdout, "pkd:%d:%u:", i, mpi_get_nbits (pk->pkey[i]));
mpi_print (es_stdout, pk->pkey[i], 1);
es_putc (':', es_stdout);
es_putc ('\n', es_stdout);
}
}
static void
print_capabilities (PKT_public_key *pk, KBNODE keyblock)
{
unsigned int use = pk->pubkey_usage;
int c_printed = 0;
if (use & PUBKEY_USAGE_ENC)
es_putc ('e', es_stdout);
if (use & PUBKEY_USAGE_SIG)
{
es_putc ('s', es_stdout);
if (pk->flags.primary)
{
es_putc ('c', es_stdout);
/* The PUBKEY_USAGE_CERT flag was introduced later and we
used to always print 'c' for a primary key. To avoid any
regression here we better track whether we printed 'c'
already. */
c_printed = 1;
}
}
if ((use & PUBKEY_USAGE_CERT) && !c_printed)
es_putc ('c', es_stdout);
if ((use & PUBKEY_USAGE_AUTH))
es_putc ('a', es_stdout);
if ((use & PUBKEY_USAGE_UNKNOWN))
es_putc ('?', es_stdout);
if (keyblock)
{
/* Figure out the usable capabilities. */
KBNODE k;
int enc = 0, sign = 0, cert = 0, auth = 0, disabled = 0;
for (k = keyblock; k; k = k->next)
{
if (k->pkt->pkttype == PKT_PUBLIC_KEY
|| k->pkt->pkttype == PKT_PUBLIC_SUBKEY)
{
pk = k->pkt->pkt.public_key;
if (pk->flags.primary)
disabled = pk_is_disabled (pk);
if (pk->flags.valid && !pk->flags.revoked && !pk->has_expired)
{
if (pk->pubkey_usage & PUBKEY_USAGE_ENC)
enc = 1;
if (pk->pubkey_usage & PUBKEY_USAGE_SIG)
{
sign = 1;
if (pk->flags.primary)
cert = 1;
}
if (pk->pubkey_usage & PUBKEY_USAGE_CERT)
cert = 1;
if ((pk->pubkey_usage & PUBKEY_USAGE_AUTH))
auth = 1;
}
}
}
if (enc)
es_putc ('E', es_stdout);
if (sign)
es_putc ('S', es_stdout);
if (cert)
es_putc ('C', es_stdout);
if (auth)
es_putc ('A', es_stdout);
if (disabled)
es_putc ('D', es_stdout);
}
es_putc (':', es_stdout);
}
/* FLAGS: 0x01 hashed
0x02 critical */
static void
print_one_subpacket (sigsubpkttype_t type, size_t len, int flags,
const byte * buf)
{
size_t i;
es_fprintf (es_stdout, "spk:%d:%u:%u:", type, flags, (unsigned int) len);
for (i = 0; i < len; i++)
{
/* printable ascii other than : and % */
if (buf[i] >= 32 && buf[i] <= 126 && buf[i] != ':' && buf[i] != '%')
es_fprintf (es_stdout, "%c", buf[i]);
else
es_fprintf (es_stdout, "%%%02X", buf[i]);
}
es_fprintf (es_stdout, "\n");
}
void
print_subpackets_colon (PKT_signature * sig)
{
byte *i;
assert (opt.show_subpackets);
for (i = opt.show_subpackets; *i; i++)
{
const byte *p;
size_t len;
int seq, crit;
seq = 0;
while ((p = enum_sig_subpkt (sig->hashed, *i, &len, &seq, &crit)))
print_one_subpacket (*i, len, 0x01 | (crit ? 0x02 : 0), p);
seq = 0;
while ((p = enum_sig_subpkt (sig->unhashed, *i, &len, &seq, &crit)))
print_one_subpacket (*i, len, 0x00 | (crit ? 0x02 : 0), p);
}
}
void
dump_attribs (const PKT_user_id *uid, PKT_public_key *pk)
{
int i;
if (!attrib_fp)
return;
for (i = 0; i < uid->numattribs; i++)
{
if (is_status_enabled ())
{
byte array[MAX_FINGERPRINT_LEN], *p;
char buf[(MAX_FINGERPRINT_LEN * 2) + 90];
size_t j, n;
if (!pk)
BUG ();
fingerprint_from_pk (pk, array, &n);
p = array;
for (j = 0; j < n; j++, p++)
sprintf (buf + 2 * j, "%02X", *p);
sprintf (buf + strlen (buf), " %lu %u %u %u %lu %lu %u",
(ulong) uid->attribs[i].len, uid->attribs[i].type, i + 1,
uid->numattribs, (ulong) uid->created,
(ulong) uid->expiredate,
((uid->is_primary ? 0x01 : 0) | (uid->
is_revoked ? 0x02 : 0) |
(uid->is_expired ? 0x04 : 0)));
write_status_text (STATUS_ATTRIBUTE, buf);
}
es_fwrite (uid->attribs[i].data, uid->attribs[i].len, 1, attrib_fp);
es_fflush (attrib_fp);
}
}
static void
list_keyblock_print (KBNODE keyblock, int secret, int fpr, void *opaque)
{
int rc;
KBNODE kbctx;
KBNODE node;
PKT_public_key *pk;
struct sig_stats *stats = opaque;
int skip_sigs = 0;
int s2k_char;
char *hexgrip = NULL;
char *serialno = NULL;
char pkstrbuf[PUBKEY_STRING_SIZE];
/* Get the keyid from the keyblock. */
node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
if (!node)
{
log_error ("Oops; key lost!\n");
dump_kbnode (keyblock);
return;
}
pk = node->pkt->pkt.public_key;
if (secret || opt.with_keygrip)
{
rc = hexkeygrip_from_pk (pk, &hexgrip);
if (rc)
log_error ("error computing a keygrip: %s\n", gpg_strerror (rc));
}
if (secret)
{
if (!agent_get_keyinfo (NULL, hexgrip, &serialno))
s2k_char = serialno? '>':' ';
else
s2k_char = '#'; /* Key not found. */
}
else
s2k_char = ' ';
check_trustdb_stale ();
es_fprintf (es_stdout, "%s%c %s/%s %s",
secret? "sec":"pub",
s2k_char,
pubkey_string (pk, pkstrbuf, sizeof pkstrbuf),
keystr_from_pk (pk), datestr_from_pk (pk));
- if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA
- || pk->pubkey_algo == PUBKEY_ALGO_EDDSA
- || pk->pubkey_algo == PUBKEY_ALGO_ECDH)
- {
- char *curve = openpgp_oid_to_str (pk->pkey[0]);
- const char *name = openpgp_oid_to_curve (curve);
- if (!*name || *name == '?')
- name = curve;
- es_fprintf (es_stdout, " %s", name);
- xfree (curve);
- }
-
if (pk->flags.revoked)
{
es_fprintf (es_stdout, " [");
es_fprintf (es_stdout, _("revoked: %s"), revokestr_from_pk (pk));
es_fprintf (es_stdout, "]");
}
else if (pk->has_expired)
{
es_fprintf (es_stdout, " [");
es_fprintf (es_stdout, _("expired: %s"), expirestr_from_pk (pk));
es_fprintf (es_stdout, "]");
}
else if (pk->expiredate)
{
es_fprintf (es_stdout, " [");
es_fprintf (es_stdout, _("expires: %s"), expirestr_from_pk (pk));
es_fprintf (es_stdout, "]");
}
#if 0
/* I need to think about this some more. It's easy enough to
include, but it looks sort of confusing in the listing... */
if (opt.list_options & LIST_SHOW_VALIDITY)
{
int validity = get_validity (pk, NULL);
es_fprintf (es_stdout, " [%s]", trust_value_to_string (validity));
}
#endif
+ if (pk->pubkey_algo >= 100)
+ es_fprintf (es_stdout, " [experimental algorithm %d]", pk->pubkey_algo);
+
es_fprintf (es_stdout, "\n");
if (fpr)
print_fingerprint (NULL, pk, 0);
if (opt.with_keygrip && hexgrip)
es_fprintf (es_stdout, " Keygrip = %s\n", hexgrip);
if (serialno)
print_card_serialno (serialno);
if (opt.with_key_data)
print_key_data (pk);
for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));)
{
if (node->pkt->pkttype == PKT_USER_ID && !opt.fast_list_mode)
{
PKT_user_id *uid = node->pkt->pkt.user_id;
if (pk && (uid->is_expired || uid->is_revoked)
&& !(opt.list_options & LIST_SHOW_UNUSABLE_UIDS))
{
skip_sigs = 1;
continue;
}
else
skip_sigs = 0;
if (attrib_fp && uid->attrib_data != NULL)
dump_attribs (uid, pk);
if ((uid->is_revoked || uid->is_expired)
|| ((opt.list_options & LIST_SHOW_UID_VALIDITY) && pk))
{
const char *validity;
int indent;
validity = uid_trust_string_fixed (pk, uid);
indent =
(keystrlen () + 9) -
atoi (uid_trust_string_fixed (NULL, NULL));
if (indent < 0 || indent > 40)
indent = 0;
es_fprintf (es_stdout, "uid%*s%s ", indent, "", validity);
}
else
es_fprintf (es_stdout, "uid%*s", (int) keystrlen () + 10, "");
print_utf8_buffer (es_stdout, uid->name, uid->len);
es_putc ('\n', es_stdout);
if ((opt.list_options & LIST_SHOW_PHOTOS) && uid->attribs != NULL)
show_photos (uid->attribs, uid->numattribs, pk, uid);
}
else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
{
PKT_public_key *pk2 = node->pkt->pkt.public_key;
if ((pk2->flags.revoked || pk2->has_expired)
&& !(opt.list_options & LIST_SHOW_UNUSABLE_SUBKEYS))
{
skip_sigs = 1;
continue;
}
else
skip_sigs = 0;
xfree (serialno); serialno = NULL;
xfree (hexgrip); hexgrip = NULL;
if (secret || opt.with_keygrip)
{
rc = hexkeygrip_from_pk (pk2, &hexgrip);
if (rc)
log_error ("error computing a keygrip: %s\n",
gpg_strerror (rc));
}
if (secret)
{
if (!agent_get_keyinfo (NULL, hexgrip, &serialno))
s2k_char = serialno? '>':' ';
else
s2k_char = '#'; /* Key not found. */
}
else
s2k_char = ' ';
es_fprintf (es_stdout, "%s%c %s/%s %s",
secret? "ssb":"sub",
s2k_char,
pubkey_string (pk2, pkstrbuf, sizeof pkstrbuf),
keystr_from_pk (pk2), datestr_from_pk (pk2));
if (pk2->pubkey_algo == PUBKEY_ALGO_ECDSA
|| pk2->pubkey_algo == PUBKEY_ALGO_EDDSA
|| pk2->pubkey_algo == PUBKEY_ALGO_ECDH)
{
char *curve = openpgp_oid_to_str (pk2->pkey[0]);
const char *name = openpgp_oid_to_curve (curve);
if (!*name || *name == '?')
name = curve;
es_fprintf (es_stdout, " %s", name);
xfree (curve);
}
if (pk2->flags.revoked)
{
es_fprintf (es_stdout, " [");
es_fprintf (es_stdout, _("revoked: %s"), revokestr_from_pk (pk2));
es_fprintf (es_stdout, "]");
}
else if (pk2->has_expired)
{
es_fprintf (es_stdout, " [");
es_fprintf (es_stdout, _("expired: %s"), expirestr_from_pk (pk2));
es_fprintf (es_stdout, "]");
}
else if (pk2->expiredate)
{
es_fprintf (es_stdout, " [");
es_fprintf (es_stdout, _("expires: %s"), expirestr_from_pk (pk2));
es_fprintf (es_stdout, "]");
}
es_putc ('\n', es_stdout);
if (fpr > 1)
{
print_fingerprint (NULL, pk2, 0);
if (serialno)
print_card_serialno (serialno);
}
if (opt.with_keygrip && hexgrip)
es_fprintf (es_stdout, " Keygrip = %s\n", hexgrip);
if (opt.with_key_data)
print_key_data (pk2);
}
else if (opt.list_sigs
&& node->pkt->pkttype == PKT_SIGNATURE && !skip_sigs)
{
PKT_signature *sig = node->pkt->pkt.signature;
int sigrc;
char *sigstr;
if (stats)
{
rc = check_key_signature (keyblock, node, NULL);
switch (gpg_err_code (rc))
{
case 0:
sigrc = '!';
break;
case GPG_ERR_BAD_SIGNATURE:
stats->inv_sigs++;
sigrc = '-';
break;
case GPG_ERR_NO_PUBKEY:
case GPG_ERR_UNUSABLE_PUBKEY:
stats->no_key++;
continue;
default:
stats->oth_err++;
sigrc = '%';
break;
}
/* TODO: Make sure a cached sig record here still has
the pk that issued it. See also
keyedit.c:print_and_check_one_sig */
}
else
{
rc = 0;
sigrc = ' ';
}
if (sig->sig_class == 0x20 || sig->sig_class == 0x28
|| sig->sig_class == 0x30)
sigstr = "rev";
else if ((sig->sig_class & ~3) == 0x10)
sigstr = "sig";
else if (sig->sig_class == 0x18)
sigstr = "sig";
else if (sig->sig_class == 0x1F)
sigstr = "sig";
else
{
es_fprintf (es_stdout, "sig "
"[unexpected signature class 0x%02x]\n",
sig->sig_class);
continue;
}
es_fputs (sigstr, es_stdout);
es_fprintf (es_stdout, "%c%c %c%c%c%c%c%c %s %s",
sigrc, (sig->sig_class - 0x10 > 0 &&
sig->sig_class - 0x10 <
4) ? '0' + sig->sig_class - 0x10 : ' ',
sig->flags.exportable ? ' ' : 'L',
sig->flags.revocable ? ' ' : 'R',
sig->flags.policy_url ? 'P' : ' ',
sig->flags.notation ? 'N' : ' ',
sig->flags.expired ? 'X' : ' ',
(sig->trust_depth > 9) ? 'T' : (sig->trust_depth >
0) ? '0' +
sig->trust_depth : ' ', keystr (sig->keyid),
datestr_from_sig (sig));
if (opt.list_options & LIST_SHOW_SIG_EXPIRE)
es_fprintf (es_stdout, " %s", expirestr_from_sig (sig));
es_fprintf (es_stdout, " ");
if (sigrc == '%')
es_fprintf (es_stdout, "[%s] ", g10_errstr (rc));
else if (sigrc == '?')
;
else if (!opt.fast_list_mode)
{
size_t n;
char *p = get_user_id (sig->keyid, &n);
print_utf8_buffer (es_stdout, p, n);
xfree (p);
}
es_putc ('\n', es_stdout);
if (sig->flags.policy_url
&& (opt.list_options & LIST_SHOW_POLICY_URLS))
show_policy_url (sig, 3, 0);
if (sig->flags.notation && (opt.list_options & LIST_SHOW_NOTATIONS))
show_notation (sig, 3, 0,
((opt.
list_options & LIST_SHOW_STD_NOTATIONS) ? 1 : 0)
+
((opt.
list_options & LIST_SHOW_USER_NOTATIONS) ? 2 :
0));
if (sig->flags.pref_ks
&& (opt.list_options & LIST_SHOW_KEYSERVER_URLS))
show_keyserver_url (sig, 3, 0);
/* fixme: check or list other sigs here */
}
}
es_putc ('\n', es_stdout);
xfree (serialno);
xfree (hexgrip);
}
void
print_revokers (estream_t fp, PKT_public_key * pk)
{
/* print the revoker record */
if (!pk->revkey && pk->numrevkeys)
BUG ();
else
{
int i, j;
for (i = 0; i < pk->numrevkeys; i++)
{
byte *p;
es_fprintf (fp, "rvk:::%d::::::", pk->revkey[i].algid);
p = pk->revkey[i].fpr;
for (j = 0; j < 20; j++, p++)
es_fprintf (fp, "%02X", *p);
es_fprintf (fp, ":%02x%s:\n",
pk->revkey[i].class,
(pk->revkey[i].class & 0x40) ? "s" : "");
}
}
}
static void
list_keyblock_colon (KBNODE keyblock, int secret, int fpr)
{
int rc;
KBNODE kbctx;
KBNODE node;
PKT_public_key *pk;
u32 keyid[2];
int trustletter = 0;
int ulti_hack = 0;
int i;
char *p;
char *hexgrip = NULL;
char *serialno = NULL;
int stubkey;
/* Get the keyid from the keyblock. */
node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
if (!node)
{
log_error ("Oops; key lost!\n");
dump_kbnode (keyblock);
return;
}
pk = node->pkt->pkt.public_key;
if (secret || opt.with_keygrip || opt.with_key_data)
{
rc = hexkeygrip_from_pk (pk, &hexgrip);
if (rc)
log_error ("error computing a keygrip: %s\n", gpg_strerror (rc));
}
stubkey = 0;
if (secret && agent_get_keyinfo (NULL, hexgrip, &serialno))
stubkey = 1; /* Key not found. */
keyid_from_pk (pk, keyid);
es_fputs (secret? "sec:":"pub:", es_stdout);
if (!pk->flags.valid)
es_putc ('i', es_stdout);
else if (pk->flags.revoked)
es_putc ('r', es_stdout);
else if (pk->has_expired)
es_putc ('e', es_stdout);
else if (opt.fast_list_mode || opt.no_expensive_trust_checks)
;
else
{
trustletter = get_validity_info (pk, NULL);
if (trustletter == 'u')
ulti_hack = 1;
es_putc (trustletter, es_stdout);
}
es_fprintf (es_stdout, ":%u:%d:%08lX%08lX:%s:%s::",
nbits_from_pk (pk),
pk->pubkey_algo,
(ulong) keyid[0], (ulong) keyid[1],
colon_datestr_from_pk (pk), colon_strtime (pk->expiredate));
if (!opt.fast_list_mode && !opt.no_expensive_trust_checks)
es_putc (get_ownertrust_info (pk), es_stdout);
es_putc (':', es_stdout);
es_putc (':', es_stdout);
es_putc (':', es_stdout);
print_capabilities (pk, keyblock);
es_putc (':', es_stdout); /* End of field 13. */
es_putc (':', es_stdout); /* End of field 14. */
if (secret)
{
if (stubkey)
es_putc ('#', es_stdout);
else if (serialno)
es_fputs (serialno, es_stdout);
}
es_putc (':', es_stdout); /* End of field 15. */
es_putc (':', es_stdout); /* End of field 16. */
if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA
|| pk->pubkey_algo == PUBKEY_ALGO_EDDSA
|| pk->pubkey_algo == PUBKEY_ALGO_ECDH)
{
char *curve = openpgp_oid_to_str (pk->pkey[0]);
const char *name = openpgp_oid_to_curve (curve);
if (!*name || *name == '?')
name = curve;
es_fputs (name, es_stdout);
xfree (curve);
}
es_putc (':', es_stdout); /* End of field 17. */
es_putc ('\n', es_stdout);
print_revokers (es_stdout, pk);
if (fpr)
print_fingerprint (NULL, pk, 0);
if (opt.with_key_data || opt.with_keygrip)
{
if (hexgrip)
es_fprintf (es_stdout, "grp:::::::::%s:\n", hexgrip);
if (opt.with_key_data)
print_key_data (pk);
}
for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));)
{
if (node->pkt->pkttype == PKT_USER_ID && !opt.fast_list_mode)
{
char *str;
PKT_user_id *uid = node->pkt->pkt.user_id;
if (attrib_fp && node->pkt->pkt.user_id->attrib_data != NULL)
dump_attribs (node->pkt->pkt.user_id, pk);
/*
* Fixme: We need a valid flag here too
*/
str = uid->attrib_data ? "uat" : "uid";
if (uid->is_revoked)
es_fprintf (es_stdout, "%s:r::::", str);
else if (uid->is_expired)
es_fprintf (es_stdout, "%s:e::::", str);
else if (opt.no_expensive_trust_checks)
es_fprintf (es_stdout, "%s:::::", str);
else
{
int uid_validity;
if (pk && !ulti_hack)
uid_validity = get_validity_info (pk, uid);
else
uid_validity = 'u';
es_fprintf (es_stdout, "%s:%c::::", str, uid_validity);
}
es_fprintf (es_stdout, "%s:", colon_strtime (uid->created));
es_fprintf (es_stdout, "%s:", colon_strtime (uid->expiredate));
namehash_from_uid (uid);
for (i = 0; i < 20; i++)
es_fprintf (es_stdout, "%02X", uid->namehash[i]);
es_fprintf (es_stdout, "::");
if (uid->attrib_data)
es_fprintf (es_stdout, "%u %lu", uid->numattribs, uid->attrib_len);
else
es_write_sanitized (es_stdout, uid->name, uid->len, ":", NULL);
es_putc (':', es_stdout);
es_putc ('\n', es_stdout);
}
else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
{
u32 keyid2[2];
PKT_public_key *pk2;
pk2 = node->pkt->pkt.public_key;
xfree (hexgrip); hexgrip = NULL;
xfree (serialno); serialno = NULL;
if (secret || opt.with_keygrip || opt.with_key_data)
{
rc = hexkeygrip_from_pk (pk2, &hexgrip);
if (rc)
log_error ("error computing a keygrip: %s\n",
gpg_strerror (rc));
}
stubkey = 0;
if (secret && agent_get_keyinfo (NULL, hexgrip, &serialno))
stubkey = 1; /* Key not found. */
keyid_from_pk (pk2, keyid2);
es_fputs (secret? "ssb:":"sub:", es_stdout);
if (!pk2->flags.valid)
es_putc ('i', es_stdout);
else if (pk2->flags.revoked)
es_putc ('r', es_stdout);
else if (pk2->has_expired)
es_putc ('e', es_stdout);
else if (opt.fast_list_mode || opt.no_expensive_trust_checks)
;
else
{
/* TRUSTLETTER should always be defined here. */
if (trustletter)
es_fprintf (es_stdout, "%c", trustletter);
}
es_fprintf (es_stdout, ":%u:%d:%08lX%08lX:%s:%s:::::",
nbits_from_pk (pk2),
pk2->pubkey_algo,
(ulong) keyid2[0], (ulong) keyid2[1],
colon_datestr_from_pk (pk2), colon_strtime (pk2->expiredate)
/* fixme: add LID and ownertrust here */
);
print_capabilities (pk2, NULL);
es_putc (':', es_stdout); /* End of field 13. */
es_putc (':', es_stdout); /* End of field 14. */
if (secret)
{
if (stubkey)
es_putc ('#', es_stdout);
else if (serialno)
es_fputs (serialno, es_stdout);
}
es_putc (':', es_stdout); /* End of field 15. */
es_putc (':', es_stdout); /* End of field 16. */
if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA
|| pk->pubkey_algo == PUBKEY_ALGO_EDDSA
|| pk->pubkey_algo == PUBKEY_ALGO_ECDH)
{
char *curve = openpgp_oid_to_str (pk->pkey[0]);
const char *name = openpgp_oid_to_curve (curve);
if (!*name || *name == '?')
name = curve;
es_fputs (name, es_stdout);
xfree (curve);
}
es_putc (':', es_stdout); /* End of field 17. */
es_putc ('\n', es_stdout);
if (fpr > 1)
print_fingerprint (NULL, pk2, 0);
if (opt.with_key_data || opt.with_keygrip)
{
if (hexgrip)
es_fprintf (es_stdout, "grp:::::::::%s:\n", hexgrip);
if (opt.with_key_data)
print_key_data (pk2);
}
}
else if (opt.list_sigs && node->pkt->pkttype == PKT_SIGNATURE)
{
PKT_signature *sig = node->pkt->pkt.signature;
int sigrc, fprokay = 0;
char *sigstr;
size_t fplen;
byte fparray[MAX_FINGERPRINT_LEN];
if (sig->sig_class == 0x20 || sig->sig_class == 0x28
|| sig->sig_class == 0x30)
sigstr = "rev";
else if ((sig->sig_class & ~3) == 0x10)
sigstr = "sig";
else if (sig->sig_class == 0x18)
sigstr = "sig";
else if (sig->sig_class == 0x1F)
sigstr = "sig";
else
{
es_fprintf (es_stdout, "sig::::::::::%02x%c:\n",
sig->sig_class, sig->flags.exportable ? 'x' : 'l');
continue;
}
if (opt.check_sigs)
{
PKT_public_key *signer_pk = NULL;
fflush (stdout);
if (opt.no_sig_cache)
signer_pk = xmalloc_clear (sizeof (PKT_public_key));
rc = check_key_signature2 (keyblock, node, NULL, signer_pk,
NULL, NULL, NULL);
switch (gpg_err_code (rc))
{
case 0:
sigrc = '!';
break;
case GPG_ERR_BAD_SIGNATURE:
sigrc = '-';
break;
case GPG_ERR_NO_PUBKEY:
case GPG_ERR_UNUSABLE_PUBKEY:
sigrc = '?';
break;
default:
sigrc = '%';
break;
}
if (opt.no_sig_cache)
{
if (!rc)
{
fingerprint_from_pk (signer_pk, fparray, &fplen);
fprokay = 1;
}
free_public_key (signer_pk);
}
}
else
{
rc = 0;
sigrc = ' ';
}
es_fputs (sigstr, es_stdout);
es_putc (':', es_stdout);
if (sigrc != ' ')
es_putc (sigrc, es_stdout);
es_fprintf (es_stdout, "::%d:%08lX%08lX:%s:%s:", sig->pubkey_algo,
(ulong) sig->keyid[0], (ulong) sig->keyid[1],
colon_datestr_from_sig (sig),
colon_expirestr_from_sig (sig));
if (sig->trust_depth || sig->trust_value)
es_fprintf (es_stdout, "%d %d", sig->trust_depth, sig->trust_value);
es_fprintf (es_stdout, ":");
if (sig->trust_regexp)
es_write_sanitized (es_stdout, sig->trust_regexp,
strlen (sig->trust_regexp), ":", NULL);
es_fprintf (es_stdout, ":");
if (sigrc == '%')
es_fprintf (es_stdout, "[%s] ", g10_errstr (rc));
else if (sigrc == '?')
;
else if (!opt.fast_list_mode)
{
size_t n;
p = get_user_id (sig->keyid, &n);
es_write_sanitized (es_stdout, p, n, ":", NULL);
xfree (p);
}
es_fprintf (es_stdout, ":%02x%c::", sig->sig_class,
sig->flags.exportable ? 'x' : 'l');
if (opt.no_sig_cache && opt.check_sigs && fprokay)
{
for (i = 0; i < fplen; i++)
es_fprintf (es_stdout, "%02X", fparray[i]);
}
es_fprintf (es_stdout, ":::%d:\n", sig->digest_algo);
if (opt.show_subpackets)
print_subpackets_colon (sig);
/* fixme: check or list other sigs here */
}
}
xfree (hexgrip);
xfree (serialno);
}
/*
* Reorder the keyblock so that the primary user ID (and not attribute
* packet) comes first. Fixme: Replace this by a generic sort
* function. */
static void
do_reorder_keyblock (KBNODE keyblock, int attr)
{
KBNODE primary = NULL, primary0 = NULL, primary2 = NULL;
KBNODE last, node;
for (node = keyblock; node; primary0 = node, node = node->next)
{
if (node->pkt->pkttype == PKT_USER_ID &&
((attr && node->pkt->pkt.user_id->attrib_data) ||
(!attr && !node->pkt->pkt.user_id->attrib_data)) &&
node->pkt->pkt.user_id->is_primary)
{
primary = primary2 = node;
for (node = node->next; node; primary2 = node, node = node->next)
{
if (node->pkt->pkttype == PKT_USER_ID
|| node->pkt->pkttype == PKT_PUBLIC_SUBKEY
|| node->pkt->pkttype == PKT_SECRET_SUBKEY)
{
break;
}
}
break;
}
}
if (!primary)
return; /* No primary key flag found (should not happen). */
for (last = NULL, node = keyblock; node; last = node, node = node->next)
{
if (node->pkt->pkttype == PKT_USER_ID)
break;
}
assert (node);
assert (last); /* The user ID is never the first packet. */
assert (primary0); /* Ditto (this is the node before primary). */
if (node == primary)
return; /* Already the first one. */
last->next = primary;
primary0->next = primary2->next;
primary2->next = node;
}
void
reorder_keyblock (KBNODE keyblock)
{
do_reorder_keyblock (keyblock, 1);
do_reorder_keyblock (keyblock, 0);
}
void
list_keyblock (KBNODE keyblock, int secret, int fpr, void *opaque)
{
reorder_keyblock (keyblock);
if (opt.with_colons)
list_keyblock_colon (keyblock, secret, fpr);
else
list_keyblock_print (keyblock, secret, fpr, opaque);
}
/*
* Function to print the finperprint.
* mode 0: as used in key listings, opt.with_colons is honored
* 1: print using log_info ()
* 2: direct use of tty
* 3: direct use of tty but only primary key.
*
* Modes 1 and 2 will try and print both subkey and primary key
* fingerprints. A MODE with bit 7 set is used internally. If
* OVERRIDE_FP is not NULL that stream will be used in 0 instead
* of es_stdout or instead of the TTY in modes 2 and 3.
*/
void
print_fingerprint (estream_t override_fp, PKT_public_key *pk, int mode)
{
byte array[MAX_FINGERPRINT_LEN], *p;
size_t i, n;
estream_t fp;
const char *text;
int primary = 0;
if (pk->main_keyid[0] == pk->keyid[0]
&& pk->main_keyid[1] == pk->keyid[1])
primary = 1;
/* Just to be safe */
if ((mode & 0x80) && !primary)
{
log_error ("primary key is not really primary!\n");
return;
}
mode &= ~0x80;
if (!primary && (mode == 1 || mode == 2))
{
PKT_public_key *primary_pk = xmalloc_clear (sizeof (*primary_pk));
get_pubkey (primary_pk, pk->main_keyid);
print_fingerprint (override_fp, primary_pk, (mode | 0x80));
free_public_key (primary_pk);
}
if (mode == 1)
{
fp = log_get_stream ();
if (primary)
text = _("Primary key fingerprint:");
else
text = _(" Subkey fingerprint:");
}
else if (mode == 2)
{
fp = override_fp; /* Use tty or given stream. */
if (primary)
/* TRANSLATORS: this should fit into 24 bytes to that the
* fingerprint data is properly aligned with the user ID */
text = _(" Primary key fingerprint:");
else
text = _(" Subkey fingerprint:");
}
else if (mode == 3)
{
fp = override_fp; /* Use tty or given stream. */
text = _(" Key fingerprint =");
}
else
{
fp = override_fp? override_fp : es_stdout;
text = _(" Key fingerprint =");
}
fingerprint_from_pk (pk, array, &n);
p = array;
if (opt.with_colons && !mode)
{
es_fprintf (fp, "fpr:::::::::");
for (i = 0; i < n; i++, p++)
es_fprintf (fp, "%02X", *p);
es_putc (':', fp);
}
else
{
tty_fprintf (fp, "%s", text);
if (n == 20)
{
for (i = 0; i < n; i++, i++, p += 2)
tty_fprintf (fp, "%s %02X%02X", i==10? " ":"", *p, p[1]);
}
else
{
for (i = 0; i < n; i++, p++)
tty_fprintf (fp, "%s %02X", (i && !(i % 8))? " ":"", *p);
}
}
tty_fprintf (fp, "\n");
}
/* Print the serial number of an OpenPGP card if available. */
static void
print_card_serialno (const char *serialno)
{
if (!serialno)
return;
if (opt.with_colons)
return; /* Handled elsewhere. */
es_fputs (_(" Card serial no. ="), es_stdout);
es_putc (' ', es_stdout);
if (strlen (serialno) == 32 && !strncmp (serialno, "D27600012401", 12))
{
/* This is an OpenPGP card. Print the relevant part. */
/* Example: D2760001240101010001000003470000 */
/* xxxxyyyyyyyy */
es_fprintf (es_stdout, "%.*s %.*s", 4, serialno+16, 8, serialno+20);
}
else
es_fputs (serialno, es_stdout);
es_putc ('\n', es_stdout);
}
void
set_attrib_fd (int fd)
{
static int last_fd = -1;
if (fd != -1 && last_fd == fd)
return;
/* Fixme: Do we need to check for the log stream here? */
if (attrib_fp && attrib_fp != log_get_stream ())
es_fclose (attrib_fp);
attrib_fp = NULL;
if (fd == -1)
return;
#ifdef HAVE_DOSISH_SYSTEM
setmode (fd, O_BINARY);
#endif
if (fd == 1)
attrib_fp = es_stdout;
else if (fd == 2)
attrib_fp = es_stderr;
else
attrib_fp = es_fdopen (fd, "wb");
if (!attrib_fp)
{
log_fatal ("can't open fd %d for attribute output: %s\n",
fd, strerror (errno));
}
last_fd = fd;
}
diff --git a/g10/misc.c b/g10/misc.c
index 77928aa6c..54ddad2d2 100644
--- a/g10/misc.c
+++ b/g10/misc.c
@@ -1,1729 +1,1729 @@
/* misc.c - miscellaneous functions
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
* 2008, 2009, 2010 Free Software Foundation, Inc.
* Copyright (C) 2014 Werner Koch
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2
#include <asm/sysinfo.h>
#include <asm/unistd.h>
#endif
#ifdef HAVE_SETRLIMIT
#include <time.h>
#include <sys/time.h>
#include <sys/resource.h>
#endif
#ifdef ENABLE_SELINUX_HACKS
#include <sys/stat.h>
#endif
#ifdef HAVE_W32_SYSTEM
#include <time.h>
#include <process.h>
#ifdef HAVE_WINSOCK2_H
# include <winsock2.h>
#endif
#include <windows.h>
#include <shlobj.h>
#ifndef CSIDL_APPDATA
#define CSIDL_APPDATA 0x001a
#endif
#ifndef CSIDL_LOCAL_APPDATA
#define CSIDL_LOCAL_APPDATA 0x001c
#endif
#ifndef CSIDL_FLAG_CREATE
#define CSIDL_FLAG_CREATE 0x8000
#endif
#endif /*HAVE_W32_SYSTEM*/
#include "gpg.h"
#ifdef HAVE_W32_SYSTEM
# include "status.h"
#endif /*HAVE_W32_SYSTEM*/
#include "util.h"
#include "main.h"
#include "photoid.h"
#include "options.h"
#include "call-agent.h"
#include "i18n.h"
#include <assert.h>
static int
string_count_chr (const char *string, int c)
{
int count;
for (count=0; *string; string++ )
if ( *string == c )
count++;
return count;
}
#ifdef ENABLE_SELINUX_HACKS
/* A object and a global variable to keep track of files marked as
secured. */
struct secured_file_item
{
struct secured_file_item *next;
ino_t ino;
dev_t dev;
};
static struct secured_file_item *secured_files;
#endif /*ENABLE_SELINUX_HACKS*/
/* For the sake of SELinux we want to restrict access through gpg to
certain files we keep under our own control. This function
registers such a file and is_secured_file may then be used to
check whether a file has ben registered as secured. */
void
register_secured_file (const char *fname)
{
#ifdef ENABLE_SELINUX_HACKS
struct stat buf;
struct secured_file_item *sf;
/* Note that we stop immediatley if something goes wrong here. */
if (stat (fname, &buf))
log_fatal (_("fstat of '%s' failed in %s: %s\n"), fname,
"register_secured_file", strerror (errno));
/* log_debug ("registering '%s' i=%lu.%lu\n", fname, */
/* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */
for (sf=secured_files; sf; sf = sf->next)
{
if (sf->ino == buf.st_ino && sf->dev == buf.st_dev)
return; /* Already registered. */
}
sf = xmalloc (sizeof *sf);
sf->ino = buf.st_ino;
sf->dev = buf.st_dev;
sf->next = secured_files;
secured_files = sf;
#else /*!ENABLE_SELINUX_HACKS*/
(void)fname;
#endif /*!ENABLE_SELINUX_HACKS*/
}
/* Remove a file registered as secure. */
void
unregister_secured_file (const char *fname)
{
#ifdef ENABLE_SELINUX_HACKS
struct stat buf;
struct secured_file_item *sf, *sfprev;
if (stat (fname, &buf))
{
log_error (_("fstat of '%s' failed in %s: %s\n"), fname,
"unregister_secured_file", strerror (errno));
return;
}
/* log_debug ("unregistering '%s' i=%lu.%lu\n", fname, */
/* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */
for (sfprev=NULL,sf=secured_files; sf; sfprev=sf, sf = sf->next)
{
if (sf->ino == buf.st_ino && sf->dev == buf.st_dev)
{
if (sfprev)
sfprev->next = sf->next;
else
secured_files = sf->next;
xfree (sf);
return;
}
}
#else /*!ENABLE_SELINUX_HACKS*/
(void)fname;
#endif /*!ENABLE_SELINUX_HACKS*/
}
/* Return true if FD is corresponds to a secured file. Using -1 for
FS is allowed and will return false. */
int
is_secured_file (int fd)
{
#ifdef ENABLE_SELINUX_HACKS
struct stat buf;
struct secured_file_item *sf;
if (fd == -1)
return 0; /* No file descriptor so it can't be secured either. */
/* Note that we print out a error here and claim that a file is
secure if something went wrong. */
if (fstat (fd, &buf))
{
log_error (_("fstat(%d) failed in %s: %s\n"), fd,
"is_secured_file", strerror (errno));
return 1;
}
/* log_debug ("is_secured_file (%d) i=%lu.%lu\n", fd, */
/* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */
for (sf=secured_files; sf; sf = sf->next)
{
if (sf->ino == buf.st_ino && sf->dev == buf.st_dev)
return 1; /* Yes. */
}
#else /*!ENABLE_SELINUX_HACKS*/
(void)fd;
#endif /*!ENABLE_SELINUX_HACKS*/
return 0; /* No. */
}
/* Return true if FNAME is corresponds to a secured file. Using NULL,
"" or "-" for FS is allowed and will return false. This function is
used before creating a file, thus it won't fail if the file does
not exist. */
int
is_secured_filename (const char *fname)
{
#ifdef ENABLE_SELINUX_HACKS
struct stat buf;
struct secured_file_item *sf;
if (iobuf_is_pipe_filename (fname) || !*fname)
return 0;
/* Note that we print out a error here and claim that a file is
secure if something went wrong. */
if (stat (fname, &buf))
{
if (errno == ENOENT || errno == EPERM || errno == EACCES)
return 0;
log_error (_("fstat of '%s' failed in %s: %s\n"), fname,
"is_secured_filename", strerror (errno));
return 1;
}
/* log_debug ("is_secured_filename (%s) i=%lu.%lu\n", fname, */
/* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */
for (sf=secured_files; sf; sf = sf->next)
{
if (sf->ino == buf.st_ino && sf->dev == buf.st_dev)
return 1; /* Yes. */
}
#else /*!ENABLE_SELINUX_HACKS*/
(void)fname;
#endif /*!ENABLE_SELINUX_HACKS*/
return 0; /* No. */
}
u16
checksum_u16( unsigned n )
{
u16 a;
a = (n >> 8) & 0xff;
a += n & 0xff;
return a;
}
u16
checksum( byte *p, unsigned n )
{
u16 a;
for(a=0; n; n-- )
a += *p++;
return a;
}
u16
checksum_mpi (gcry_mpi_t a)
{
u16 csum;
byte *buffer;
size_t nbytes;
if ( gcry_mpi_print (GCRYMPI_FMT_PGP, NULL, 0, &nbytes, a) )
BUG ();
/* Fixme: For numbers not in secure memory we should use a stack
* based buffer and only allocate a larger one if mpi_print returns
* an error. */
buffer = (gcry_is_secure(a)?
gcry_xmalloc_secure (nbytes) : gcry_xmalloc (nbytes));
if ( gcry_mpi_print (GCRYMPI_FMT_PGP, buffer, nbytes, NULL, a) )
BUG ();
csum = checksum (buffer, nbytes);
xfree (buffer);
return csum;
}
u32
buffer_to_u32( const byte *buffer )
{
unsigned long a;
a = *buffer << 24;
a |= buffer[1] << 16;
a |= buffer[2] << 8;
a |= buffer[3];
return a;
}
void
print_pubkey_algo_note (pubkey_algo_t algo)
{
if(algo >= 100 && algo <= 110)
{
static int warn=0;
if(!warn)
{
warn=1;
log_info (_("WARNING: using experimental public key algorithm %s\n"),
- openpgp_cipher_algo_name (algo));
+ openpgp_pk_algo_name (algo));
}
}
- else if (algo == 20)
+ else if (algo == PUBKEY_ALGO_ELGAMAL)
{
log_info (_("WARNING: Elgamal sign+encrypt keys are deprecated\n"));
}
}
void
print_cipher_algo_note (cipher_algo_t algo)
{
if(algo >= 100 && algo <= 110)
{
static int warn=0;
if(!warn)
{
warn=1;
log_info (_("WARNING: using experimental cipher algorithm %s\n"),
openpgp_cipher_algo_name (algo));
}
}
}
void
print_digest_algo_note (digest_algo_t algo)
{
if(algo >= 100 && algo <= 110)
{
static int warn=0;
if(!warn)
{
warn=1;
log_info (_("WARNING: using experimental digest algorithm %s\n"),
gcry_md_algo_name (algo));
}
}
else if(algo==DIGEST_ALGO_MD5)
log_info (_("WARNING: digest algorithm %s is deprecated\n"),
gcry_md_algo_name (algo));
}
/* Map OpenPGP algo numbers to those used by Libgcrypt. We need to do
this for algorithms we implemented in Libgcrypt after they become
part of OpenPGP. */
enum gcry_cipher_algos
map_cipher_openpgp_to_gcry (cipher_algo_t algo)
{
switch (algo)
{
case CIPHER_ALGO_NONE: return GCRY_CIPHER_NONE;
#ifdef GPG_USE_IDEA
case CIPHER_ALGO_IDEA: return GCRY_CIPHER_IDEA;
#else
case CIPHER_ALGO_IDEA: return 0;
#endif
case CIPHER_ALGO_3DES: return GCRY_CIPHER_3DES;
#ifdef GPG_USE_CAST5
case CIPHER_ALGO_CAST5: return GCRY_CIPHER_CAST5;
#else
case CIPHER_ALGO_CAST5: return 0;
#endif
#ifdef GPG_USE_BLOWFISH
case CIPHER_ALGO_BLOWFISH: return GCRY_CIPHER_BLOWFISH;
#else
case CIPHER_ALGO_BLOWFISH: return 0;
#endif
#ifdef GPG_USE_AES128
case CIPHER_ALGO_AES: return GCRY_CIPHER_AES;
#else
case CIPHER_ALGO_AES: return 0;
#endif
#ifdef GPG_USE_AES192
case CIPHER_ALGO_AES192: return GCRY_CIPHER_AES192;
#else
case CIPHER_ALGO_AES192: return 0;
#endif
#ifdef GPG_USE_AES256
case CIPHER_ALGO_AES256: return GCRY_CIPHER_AES256;
#else
case CIPHER_ALGO_AES256: return 0;
#endif
#ifdef GPG_USE_TWOFISH
case CIPHER_ALGO_TWOFISH: return GCRY_CIPHER_TWOFISH;
#else
case CIPHER_ALGO_TWOFISH: return 0;
#endif
#ifdef GPG_USE_CAMELLIA128
case CIPHER_ALGO_CAMELLIA128: return GCRY_CIPHER_CAMELLIA128;
#else
case CIPHER_ALGO_CAMELLIA128: return 0;
#endif
#ifdef GPG_USE_CAMELLIA192
case CIPHER_ALGO_CAMELLIA192: return GCRY_CIPHER_CAMELLIA192;
#else
case CIPHER_ALGO_CAMELLIA192: return 0;
#endif
#ifdef GPG_USE_CAMELLIA256
case CIPHER_ALGO_CAMELLIA256: return GCRY_CIPHER_CAMELLIA256;
#else
case CIPHER_ALGO_CAMELLIA256: return 0;
#endif
}
return 0;
}
/* The inverse function of above. */
static cipher_algo_t
map_cipher_gcry_to_openpgp (enum gcry_cipher_algos algo)
{
switch (algo)
{
case GCRY_CIPHER_NONE: return CIPHER_ALGO_NONE;
case GCRY_CIPHER_IDEA: return CIPHER_ALGO_IDEA;
case GCRY_CIPHER_3DES: return CIPHER_ALGO_3DES;
case GCRY_CIPHER_CAST5: return CIPHER_ALGO_CAST5;
case GCRY_CIPHER_BLOWFISH: return CIPHER_ALGO_BLOWFISH;
case GCRY_CIPHER_AES: return CIPHER_ALGO_AES;
case GCRY_CIPHER_AES192: return CIPHER_ALGO_AES192;
case GCRY_CIPHER_AES256: return CIPHER_ALGO_AES256;
case GCRY_CIPHER_TWOFISH: return CIPHER_ALGO_TWOFISH;
case GCRY_CIPHER_CAMELLIA128: return CIPHER_ALGO_CAMELLIA128;
case GCRY_CIPHER_CAMELLIA192: return CIPHER_ALGO_CAMELLIA192;
case GCRY_CIPHER_CAMELLIA256: return CIPHER_ALGO_CAMELLIA256;
default: return 0;
}
}
/* Map Gcrypt public key algorithm numbers to those used by OpenPGP.
FIXME: This mapping is used at only two places - we should get rid
of it. */
pubkey_algo_t
map_pk_gcry_to_openpgp (enum gcry_pk_algos algo)
{
switch (algo)
{
case GCRY_PK_ECDSA: return PUBKEY_ALGO_ECDSA;
case GCRY_PK_ECDH: return PUBKEY_ALGO_ECDH;
default: return algo < 110 ? algo : 0;
}
}
/* Return the block length of an OpenPGP cipher algorithm. */
int
openpgp_cipher_blocklen (cipher_algo_t algo)
{
/* We use the numbers from OpenPGP to be sure that we get the right
block length. This is so that the packet parsing code works even
for unknown algorithms (for which we assume 8 due to tradition).
NOTE: If you change the the returned blocklen above 16, check
the callers because they may use a fixed size buffer of that
size. */
switch (algo)
{
case CIPHER_ALGO_AES:
case CIPHER_ALGO_AES192:
case CIPHER_ALGO_AES256:
case CIPHER_ALGO_TWOFISH:
case CIPHER_ALGO_CAMELLIA128:
case CIPHER_ALGO_CAMELLIA192:
case CIPHER_ALGO_CAMELLIA256:
return 16;
default:
return 8;
}
}
/****************
* Wrapper around the libgcrypt function with additonal checks on
* the OpenPGP contraints for the algo ID.
*/
int
openpgp_cipher_test_algo (cipher_algo_t algo)
{
enum gcry_cipher_algos ga;
ga = map_cipher_openpgp_to_gcry (algo);
if (!ga)
return gpg_error (GPG_ERR_CIPHER_ALGO);
return gcry_cipher_test_algo (ga);
}
/* Map the OpenPGP cipher algorithm whose ID is contained in ALGORITHM to a
string representation of the algorithm name. For unknown algorithm
IDs this function returns "?". */
const char *
openpgp_cipher_algo_name (cipher_algo_t algo)
{
switch (algo)
{
case CIPHER_ALGO_NONE: break;
case CIPHER_ALGO_IDEA: return "IDEA";
case CIPHER_ALGO_3DES: return "3DES";
case CIPHER_ALGO_CAST5: return "CAST5";
case CIPHER_ALGO_BLOWFISH: return "BLOWFISH";
case CIPHER_ALGO_AES: return "AES";
case CIPHER_ALGO_AES192: return "AES192";
case CIPHER_ALGO_AES256: return "AES256";
case CIPHER_ALGO_TWOFISH: return "TWOFISH";
case CIPHER_ALGO_CAMELLIA128: return "CAMELLIA128";
case CIPHER_ALGO_CAMELLIA192: return "CAMELLIA192";
case CIPHER_ALGO_CAMELLIA256: return "CAMELLIA256";
}
return "?";
}
/* Return 0 if ALGO is a supported OpenPGP public key algorithm. */
int
openpgp_pk_test_algo (pubkey_algo_t algo)
{
return openpgp_pk_test_algo2 (algo, 0);
}
/* Return 0 if ALGO is a supported OpenPGP public key algorithm and
allows the usage USE. */
int
openpgp_pk_test_algo2 (pubkey_algo_t algo, unsigned int use)
{
enum gcry_pk_algos ga = 0;
size_t use_buf = use;
switch (algo)
{
#ifdef GPG_USE_RSA
case PUBKEY_ALGO_RSA: ga = GCRY_PK_RSA; break;
case PUBKEY_ALGO_RSA_E: ga = GCRY_PK_RSA_E; break;
case PUBKEY_ALGO_RSA_S: ga = GCRY_PK_RSA_S; break;
#else
case PUBKEY_ALGO_RSA: break;
case PUBKEY_ALGO_RSA_E: break;
case PUBKEY_ALGO_RSA_S: break;
#endif
case PUBKEY_ALGO_ELGAMAL_E: ga = GCRY_PK_ELG; break;
case PUBKEY_ALGO_DSA: ga = GCRY_PK_DSA; break;
#ifdef GPG_USE_ECDH
case PUBKEY_ALGO_ECDH: ga = GCRY_PK_ECC; break;
#else
case PUBKEY_ALGO_ECDH: break;
#endif
#ifdef GPG_USE_ECDSA
case PUBKEY_ALGO_ECDSA: ga = GCRY_PK_ECC; break;
#else
case PUBKEY_ALGO_ECDSA: break;
#endif
#ifdef GPG_USE_EDDSA
case PUBKEY_ALGO_EDDSA: ga = GCRY_PK_ECC; break;
#else
case PUBKEY_ALGO_EDDSA: break;
#endif
case PUBKEY_ALGO_ELGAMAL:
/* Dont't allow type 20 keys unless in rfc2440 mode. */
if (RFC2440)
ga = GCRY_PK_ELG;
break;
}
if (!ga)
return gpg_error (GPG_ERR_PUBKEY_ALGO);
/* No check whether Libgcrypt has support for the algorithm. */
return gcry_pk_algo_info (ga, GCRYCTL_TEST_ALGO, NULL, &use_buf);
}
int
openpgp_pk_algo_usage ( int algo )
{
int use = 0;
/* They are hardwired in gpg 1.0. */
switch ( algo ) {
case PUBKEY_ALGO_RSA:
use = (PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG
| PUBKEY_USAGE_ENC | PUBKEY_USAGE_AUTH);
break;
case PUBKEY_ALGO_RSA_E:
case PUBKEY_ALGO_ECDH:
use = PUBKEY_USAGE_ENC;
break;
case PUBKEY_ALGO_RSA_S:
use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG;
break;
case PUBKEY_ALGO_ELGAMAL:
if (RFC2440)
use = PUBKEY_USAGE_ENC;
break;
case PUBKEY_ALGO_ELGAMAL_E:
use = PUBKEY_USAGE_ENC;
break;
case PUBKEY_ALGO_DSA:
use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH;
break;
case PUBKEY_ALGO_ECDSA:
case PUBKEY_ALGO_EDDSA:
use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH;
default:
break;
}
return use;
}
/* Map the OpenPGP pubkey algorithm whose ID is contained in ALGO to a
string representation of the algorithm name. For unknown algorithm
IDs this function returns "?". */
const char *
openpgp_pk_algo_name (pubkey_algo_t algo)
{
switch (algo)
{
case PUBKEY_ALGO_RSA:
case PUBKEY_ALGO_RSA_E:
case PUBKEY_ALGO_RSA_S: return "RSA";
case PUBKEY_ALGO_ELGAMAL:
case PUBKEY_ALGO_ELGAMAL_E: return "ELG";
case PUBKEY_ALGO_DSA: return "DSA";
case PUBKEY_ALGO_ECDH: return "ECDH";
case PUBKEY_ALGO_ECDSA: return "ECDSA";
case PUBKEY_ALGO_EDDSA: return "EDDSA";
}
return "?";
}
/* Explicit mapping of OpenPGP digest algos to Libgcrypt. */
/* FIXME: We do not yes use it everywhere. */
enum gcry_md_algos
map_md_openpgp_to_gcry (digest_algo_t algo)
{
switch (algo)
{
#ifdef GPG_USE_MD5
case DIGEST_ALGO_MD5: return GCRY_MD_MD5;
#else
case DIGEST_ALGO_MD5: return 0;
#endif
case DIGEST_ALGO_SHA1: return GCRY_MD_SHA1;
#ifdef GPG_USE_RMD160
case DIGEST_ALGO_RMD160: return GCRY_MD_RMD160;
#else
case DIGEST_ALGO_RMD160: return 0;
#endif
#ifdef GPG_USE_SHA224
case DIGEST_ALGO_SHA224: return GCRY_MD_SHA224;
#else
case DIGEST_ALGO_SHA224: return 0;
#endif
#ifdef GPG_USE_SHA256
case DIGEST_ALGO_SHA256: return GCRY_MD_SHA256;
#else
case DIGEST_ALGO_SHA256: return 0;
#endif
#ifdef GPG_USE_SHA384
case DIGEST_ALGO_SHA384: return GCRY_MD_SHA384;
#else
case DIGEST_ALGO_SHA384: return 0;
#endif
#ifdef GPG_USE_SHA512
case DIGEST_ALGO_SHA512: return GCRY_MD_SHA512;
#else
case DIGEST_ALGO_SHA512: return 0;
#endif
}
return 0;
}
/* Return 0 if ALGO is suitable and implemented OpenPGP hash
algorithm. */
int
openpgp_md_test_algo (digest_algo_t algo)
{
enum gcry_md_algos ga;
ga = map_md_openpgp_to_gcry (algo);
if (!ga)
return gpg_error (GPG_ERR_DIGEST_ALGO);
return gcry_md_test_algo (ga);
}
/* Map the OpenPGP digest algorithm whose ID is contained in ALGO to a
string representation of the algorithm name. For unknown algorithm
IDs this function returns "?". */
const char *
openpgp_md_algo_name (int algo)
{
switch (algo)
{
case DIGEST_ALGO_MD5: return "MD5";
case DIGEST_ALGO_SHA1: return "SHA1";
case DIGEST_ALGO_RMD160: return "RIPEMD160";
case DIGEST_ALGO_SHA256: return "SHA256";
case DIGEST_ALGO_SHA384: return "SHA384";
case DIGEST_ALGO_SHA512: return "SHA512";
case DIGEST_ALGO_SHA224: return "SHA224";
}
return "?";
}
static unsigned long
get_signature_count (PKT_public_key *pk)
{
#ifdef ENABLE_CARD_SUPPORT
struct agent_card_info_s info;
(void)pk;
if (!agent_scd_getattr ("SIG-COUNTER",&info))
return info.sig_counter;
else
return 0;
#else
(void)pk;
return 0;
#endif
}
/* Expand %-strings. Returns a string which must be xfreed. Returns
NULL if the string cannot be expanded (too large). */
char *
pct_expando(const char *string,struct expando_args *args)
{
const char *ch=string;
int idx=0,maxlen=0,done=0;
u32 pk_keyid[2]={0,0},sk_keyid[2]={0,0};
char *ret=NULL;
if(args->pk)
keyid_from_pk(args->pk,pk_keyid);
if(args->pksk)
keyid_from_pk (args->pksk, sk_keyid);
/* This is used so that %k works in photoid command strings in
--list-secret-keys (which of course has a sk, but no pk). */
if(!args->pk && args->pksk)
keyid_from_pk (args->pksk, pk_keyid);
while(*ch!='\0')
{
if(!done)
{
/* 8192 is way bigger than we'll need here */
if(maxlen>=8192)
goto fail;
maxlen+=1024;
ret=xrealloc(ret,maxlen);
}
done=0;
if(*ch=='%')
{
switch(*(ch+1))
{
case 's': /* short key id */
if(idx+8<maxlen)
{
sprintf(&ret[idx],"%08lX",(ulong)sk_keyid[1]);
idx+=8;
done=1;
}
break;
case 'S': /* long key id */
if(idx+16<maxlen)
{
sprintf(&ret[idx],"%08lX%08lX",
(ulong)sk_keyid[0],(ulong)sk_keyid[1]);
idx+=16;
done=1;
}
break;
case 'k': /* short key id */
if(idx+8<maxlen)
{
sprintf(&ret[idx],"%08lX",(ulong)pk_keyid[1]);
idx+=8;
done=1;
}
break;
case 'K': /* long key id */
if(idx+16<maxlen)
{
sprintf(&ret[idx],"%08lX%08lX",
(ulong)pk_keyid[0],(ulong)pk_keyid[1]);
idx+=16;
done=1;
}
break;
case 'U': /* z-base-32 encoded user id hash. */
if (args->namehash)
{
char *tmp = zb32_encode (args->namehash, 8*20);
if (tmp)
{
if (idx + strlen (tmp) < maxlen)
{
strcpy (ret+idx, tmp);
idx += strlen (tmp);
}
xfree (tmp);
done = 1;
}
}
break;
case 'c': /* signature count from card, if any. */
if(idx+10<maxlen)
{
sprintf (&ret[idx],"%lu", get_signature_count (args->pksk));
idx+=strlen(&ret[idx]);
done=1;
}
break;
case 'p': /* primary pk fingerprint of a sk */
case 'f': /* pk fingerprint */
case 'g': /* sk fingerprint */
{
byte array[MAX_FINGERPRINT_LEN];
size_t len;
int i;
if((*(ch+1))=='p' && args->pksk)
{
if(args->pksk->flags.primary)
fingerprint_from_pk (args->pksk, array, &len);
else if (args->pksk->main_keyid[0]
|| args->pksk->main_keyid[1])
{
/* FIXME: Document teh code and check whether
it is still needed. */
PKT_public_key *pk=
xmalloc_clear(sizeof(PKT_public_key));
if (!get_pubkey_fast (pk,args->pksk->main_keyid))
fingerprint_from_pk (pk, array, &len);
else
memset (array, 0, (len=MAX_FINGERPRINT_LEN));
free_public_key (pk);
}
else
memset(array,0,(len=MAX_FINGERPRINT_LEN));
}
else if((*(ch+1))=='f' && args->pk)
fingerprint_from_pk (args->pk, array, &len);
else if((*(ch+1))=='g' && args->pksk)
fingerprint_from_pk (args->pksk, array, &len);
else
memset(array,0,(len=MAX_FINGERPRINT_LEN));
if(idx+(len*2)<maxlen)
{
for(i=0;i<len;i++)
{
sprintf(&ret[idx],"%02X",array[i]);
idx+=2;
}
done=1;
}
}
break;
case 'v': /* validity letters */
if(args->validity_info && idx+1<maxlen)
{
ret[idx++]=args->validity_info;
ret[idx]='\0';
done=1;
}
break;
/* The text string types */
case 't':
case 'T':
case 'V':
{
const char *str=NULL;
switch(*(ch+1))
{
case 't': /* e.g. "jpg" */
str=image_type_to_string(args->imagetype,0);
break;
case 'T': /* e.g. "image/jpeg" */
str=image_type_to_string(args->imagetype,2);
break;
case 'V': /* e.g. "full", "expired", etc. */
str=args->validity_string;
break;
}
if(str && idx+strlen(str)<maxlen)
{
strcpy(&ret[idx],str);
idx+=strlen(str);
done=1;
}
}
break;
case '%':
if(idx+1<maxlen)
{
ret[idx++]='%';
ret[idx]='\0';
done=1;
}
break;
/* Any unknown %-keys (like %i, %o, %I, and %O) are
passed through for later expansion. Note this also
handles the case where the last character in the
string is a '%' - the terminating \0 will end up here
and properly terminate the string. */
default:
if(idx+2<maxlen)
{
ret[idx++]='%';
ret[idx++]=*(ch+1);
ret[idx]='\0';
done=1;
}
break;
}
if(done)
ch++;
}
else
{
if(idx+1<maxlen)
{
ret[idx++]=*ch;
ret[idx]='\0';
done=1;
}
}
if(done)
ch++;
}
return ret;
fail:
xfree(ret);
return NULL;
}
void
deprecated_warning(const char *configname,unsigned int configlineno,
const char *option,const char *repl1,const char *repl2)
{
if(configname)
{
if(strncmp("--",option,2)==0)
option+=2;
if(strncmp("--",repl1,2)==0)
repl1+=2;
log_info(_("%s:%d: deprecated option \"%s\"\n"),
configname,configlineno,option);
}
else
log_info(_("WARNING: \"%s\" is a deprecated option\n"),option);
log_info(_("please use \"%s%s\" instead\n"),repl1,repl2);
}
void
deprecated_command (const char *name)
{
log_info(_("WARNING: \"%s\" is a deprecated command - do not use it\n"),
name);
}
void
obsolete_option (const char *configname, unsigned int configlineno,
const char *name)
{
if(configname)
log_info (_("%s:%u: obsolete option \"%s\" - it has no effect\n"),
configname, configlineno, name);
else
log_info (_("WARNING: \"%s\" is an obsolete option - it has no effect\n"),
name);
}
/*
* Wrapper around gcry_cipher_map_name to provide a fallback using the
* "Sn" syntax as used by the preference strings.
*/
int
string_to_cipher_algo (const char *string)
{
int val;
val = map_cipher_gcry_to_openpgp (gcry_cipher_map_name (string));
if (!val && string && (string[0]=='S' || string[0]=='s'))
{
char *endptr;
string++;
val = strtol (string, &endptr, 10);
if (!*string || *endptr || openpgp_cipher_test_algo (val))
val = 0;
}
return val;
}
/*
* Wrapper around gcry_md_map_name to provide a fallback using the
* "Hn" syntax as used by the preference strings.
*/
int
string_to_digest_algo (const char *string)
{
int val;
/* FIXME: We should make use of our wrapper fucntion and not assume
that there is a 1 to 1 mapping between OpenPGP and Libgcrypt. */
val = gcry_md_map_name (string);
if (!val && string && (string[0]=='H' || string[0]=='h'))
{
char *endptr;
string++;
val = strtol (string, &endptr, 10);
if (!*string || *endptr || openpgp_md_test_algo (val))
val = 0;
}
return val;
}
const char *
compress_algo_to_string(int algo)
{
const char *s=NULL;
switch(algo)
{
case COMPRESS_ALGO_NONE:
s=_("Uncompressed");
break;
case COMPRESS_ALGO_ZIP:
s="ZIP";
break;
case COMPRESS_ALGO_ZLIB:
s="ZLIB";
break;
#ifdef HAVE_BZIP2
case COMPRESS_ALGO_BZIP2:
s="BZIP2";
break;
#endif
}
return s;
}
int
string_to_compress_algo(const char *string)
{
/* TRANSLATORS: See doc/TRANSLATE about this string. */
if(match_multistr(_("uncompressed|none"),string))
return 0;
else if(ascii_strcasecmp(string,"uncompressed")==0)
return 0;
else if(ascii_strcasecmp(string,"none")==0)
return 0;
else if(ascii_strcasecmp(string,"zip")==0)
return 1;
else if(ascii_strcasecmp(string,"zlib")==0)
return 2;
#ifdef HAVE_BZIP2
else if(ascii_strcasecmp(string,"bzip2")==0)
return 3;
#endif
else if(ascii_strcasecmp(string,"z0")==0)
return 0;
else if(ascii_strcasecmp(string,"z1")==0)
return 1;
else if(ascii_strcasecmp(string,"z2")==0)
return 2;
#ifdef HAVE_BZIP2
else if(ascii_strcasecmp(string,"z3")==0)
return 3;
#endif
else
return -1;
}
int
check_compress_algo(int algo)
{
switch (algo)
{
case 0: return 0;
#ifdef HAVE_ZIP
case 1:
case 2: return 0;
#endif
#ifdef HAVE_BZIP2
case 3: return 0;
#endif
default: return G10ERR_COMPR_ALGO;
}
}
int
default_cipher_algo(void)
{
if(opt.def_cipher_algo)
return opt.def_cipher_algo;
else if(opt.personal_cipher_prefs)
return opt.personal_cipher_prefs[0].value;
else
return opt.s2k_cipher_algo;
}
/* There is no default_digest_algo function, but see
sign.c:hash_for() */
int
default_compress_algo(void)
{
if(opt.compress_algo!=-1)
return opt.compress_algo;
else if(opt.personal_compress_prefs)
return opt.personal_compress_prefs[0].value;
else
return DEFAULT_COMPRESS_ALGO;
}
const char *
compliance_option_string(void)
{
char *ver="???";
switch(opt.compliance)
{
case CO_GNUPG: return "--gnupg";
case CO_RFC4880: return "--openpgp";
case CO_RFC2440: return "--rfc2440";
case CO_RFC1991: return "--rfc1991";
case CO_PGP2: return "--pgp2";
case CO_PGP6: return "--pgp6";
case CO_PGP7: return "--pgp7";
case CO_PGP8: return "--pgp8";
}
return ver;
}
void
compliance_failure(void)
{
char *ver="???";
switch(opt.compliance)
{
case CO_GNUPG:
ver="GnuPG";
break;
case CO_RFC4880:
ver="OpenPGP";
break;
case CO_RFC2440:
ver="OpenPGP (older)";
break;
case CO_RFC1991:
ver="old PGP";
break;
case CO_PGP2:
ver="PGP 2.x";
break;
case CO_PGP6:
ver="PGP 6.x";
break;
case CO_PGP7:
ver="PGP 7.x";
break;
case CO_PGP8:
ver="PGP 8.x";
break;
}
log_info(_("this message may not be usable by %s\n"),ver);
opt.compliance=CO_GNUPG;
}
/* Break a string into successive option pieces. Accepts single word
options and key=value argument options. */
char *
optsep(char **stringp)
{
char *tok,*end;
tok=*stringp;
if(tok)
{
end=strpbrk(tok," ,=");
if(end)
{
int sawequals=0;
char *ptr=end;
/* what we need to do now is scan along starting with *end,
If the next character we see (ignoring spaces) is an =
sign, then there is an argument. */
while(*ptr)
{
if(*ptr=='=')
sawequals=1;
else if(*ptr!=' ')
break;
ptr++;
}
/* There is an argument, so grab that too. At this point,
ptr points to the first character of the argument. */
if(sawequals)
{
/* Is it a quoted argument? */
if(*ptr=='"')
{
ptr++;
end=strchr(ptr,'"');
if(end)
end++;
}
else
end=strpbrk(ptr," ,");
}
if(end && *end)
{
*end='\0';
*stringp=end+1;
}
else
*stringp=NULL;
}
else
*stringp=NULL;
}
return tok;
}
/* Breaks an option value into key and value. Returns NULL if there
is no value. Note that "string" is modified to remove the =value
part. */
char *
argsplit(char *string)
{
char *equals,*arg=NULL;
equals=strchr(string,'=');
if(equals)
{
char *quote,*space;
*equals='\0';
arg=equals+1;
/* Quoted arg? */
quote=strchr(arg,'"');
if(quote)
{
arg=quote+1;
quote=strchr(arg,'"');
if(quote)
*quote='\0';
}
else
{
size_t spaces;
/* Trim leading spaces off of the arg */
spaces=strspn(arg," ");
arg+=spaces;
}
/* Trim tailing spaces off of the tag */
space=strchr(string,' ');
if(space)
*space='\0';
}
return arg;
}
/* Return the length of the initial token, leaving off any
argument. */
static size_t
optlen(const char *s)
{
char *end=strpbrk(s," =");
if(end)
return end-s;
else
return strlen(s);
}
int
parse_options(char *str,unsigned int *options,
struct parse_options *opts,int noisy)
{
char *tok;
if (str && !strcmp (str, "help"))
{
int i,maxlen=0;
/* Figure out the longest option name so we can line these up
neatly. */
for(i=0;opts[i].name;i++)
if(opts[i].help && maxlen<strlen(opts[i].name))
maxlen=strlen(opts[i].name);
for(i=0;opts[i].name;i++)
if(opts[i].help)
printf("%s%*s%s\n",opts[i].name,
maxlen+2-(int)strlen(opts[i].name),"",_(opts[i].help));
g10_exit(0);
}
while((tok=optsep(&str)))
{
int i,rev=0;
char *otok=tok;
if(tok[0]=='\0')
continue;
if(ascii_strncasecmp("no-",tok,3)==0)
{
rev=1;
tok+=3;
}
for(i=0;opts[i].name;i++)
{
size_t toklen=optlen(tok);
if(ascii_strncasecmp(opts[i].name,tok,toklen)==0)
{
/* We have a match, but it might be incomplete */
if(toklen!=strlen(opts[i].name))
{
int j;
for(j=i+1;opts[j].name;j++)
{
if(ascii_strncasecmp(opts[j].name,tok,toklen)==0)
{
if(noisy)
log_info(_("ambiguous option '%s'\n"),otok);
return 0;
}
}
}
if(rev)
{
*options&=~opts[i].bit;
if(opts[i].value)
*opts[i].value=NULL;
}
else
{
*options|=opts[i].bit;
if(opts[i].value)
*opts[i].value=argsplit(tok);
}
break;
}
}
if(!opts[i].name)
{
if(noisy)
log_info(_("unknown option '%s'\n"),otok);
return 0;
}
}
return 1;
}
/* Check whether the string has characters not valid in an RFC-822
address. To cope with OpenPGP we ignore non-ascii characters
so that for example umlauts are legal in an email address. An
OpenPGP user ID must be utf-8 encoded but there is no strict
requirement for RFC-822. Thus to avoid IDNA encoding we put the
address verbatim as utf-8 into the user ID under the assumption
that mail programs handle IDNA at a lower level and take OpenPGP
user IDs as utf-8. Note that we can't do an utf-8 encoding
checking here because in keygen.c this function is called with the
native encoding and native to utf-8 encoding is only done later. */
int
has_invalid_email_chars (const char *s)
{
int at_seen=0;
const char *valid_chars=
"01234567890_-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
for ( ; *s; s++ )
{
if ( (*s & 0x80) )
continue; /* We only care about ASCII. */
if ( *s == '@' )
at_seen=1;
else if ( !at_seen && !(strchr (valid_chars, *s)
|| strchr ("!#$%&'*+/=?^`{|}~", *s)))
return 1;
else if ( at_seen && !strchr( valid_chars, *s ) )
return 1;
}
return 0;
}
/* Check whether NAME represents a valid mailbox according to
RFC822. Returns true if so. */
int
is_valid_mailbox (const char *name)
{
return !( !name
|| !*name
|| has_invalid_email_chars (name)
|| string_count_chr (name,'@') != 1
|| *name == '@'
|| name[strlen(name)-1] == '@'
|| name[strlen(name)-1] == '.'
|| strstr (name, "..") );
}
/* Similar to access(2), but uses PATH to find the file. */
int
path_access(const char *file,int mode)
{
char *envpath;
int ret=-1;
envpath=getenv("PATH");
if(!envpath
#ifdef HAVE_DRIVE_LETTERS
|| (((file[0]>='A' && file[0]<='Z')
|| (file[0]>='a' && file[0]<='z'))
&& file[1]==':')
#else
|| file[0]=='/'
#endif
)
return access(file,mode);
else
{
/* At least as large as, but most often larger than we need. */
char *buffer=xmalloc(strlen(envpath)+1+strlen(file)+1);
char *split,*item,*path=xstrdup(envpath);
split=path;
while((item=strsep(&split,PATHSEP_S)))
{
strcpy(buffer,item);
strcat(buffer,"/");
strcat(buffer,file);
ret=access(buffer,mode);
if(ret==0)
break;
}
xfree(path);
xfree(buffer);
}
return ret;
}
/* Return the number of public key parameters as used by OpenPGP. */
int
pubkey_get_npkey (pubkey_algo_t algo)
{
switch (algo)
{
case PUBKEY_ALGO_RSA:
case PUBKEY_ALGO_RSA_E:
case PUBKEY_ALGO_RSA_S: return 2;
case PUBKEY_ALGO_ELGAMAL_E: return 3;
case PUBKEY_ALGO_DSA: return 4;
case PUBKEY_ALGO_ECDH: return 3;
case PUBKEY_ALGO_ECDSA: return 2;
case PUBKEY_ALGO_ELGAMAL: return 3;
case PUBKEY_ALGO_EDDSA: return 2;
}
return 0;
}
/* Return the number of secret key parameters as used by OpenPGP. */
int
pubkey_get_nskey (pubkey_algo_t algo)
{
switch (algo)
{
case PUBKEY_ALGO_RSA:
case PUBKEY_ALGO_RSA_E:
case PUBKEY_ALGO_RSA_S: return 6;
case PUBKEY_ALGO_ELGAMAL_E: return 4;
case PUBKEY_ALGO_DSA: return 5;
case PUBKEY_ALGO_ECDH: return 4;
case PUBKEY_ALGO_ECDSA: return 3;
case PUBKEY_ALGO_ELGAMAL: return 4;
case PUBKEY_ALGO_EDDSA: return 3;
}
return 0;
}
/* Temporary helper. */
int
pubkey_get_nsig (pubkey_algo_t algo)
{
switch (algo)
{
case PUBKEY_ALGO_RSA:
case PUBKEY_ALGO_RSA_E:
case PUBKEY_ALGO_RSA_S: return 1;
case PUBKEY_ALGO_ELGAMAL_E: return 0;
case PUBKEY_ALGO_DSA: return 2;
case PUBKEY_ALGO_ECDH: return 0;
case PUBKEY_ALGO_ECDSA: return 2;
case PUBKEY_ALGO_ELGAMAL: return 2;
case PUBKEY_ALGO_EDDSA: return 2;
}
return 0;
}
/* Temporary helper. */
int
pubkey_get_nenc (pubkey_algo_t algo)
{
switch (algo)
{
case PUBKEY_ALGO_RSA:
case PUBKEY_ALGO_RSA_E:
case PUBKEY_ALGO_RSA_S: return 1;
case PUBKEY_ALGO_ELGAMAL_E: return 2;
case PUBKEY_ALGO_DSA: return 0;
case PUBKEY_ALGO_ECDH: return 2;
case PUBKEY_ALGO_ECDSA: return 0;
case PUBKEY_ALGO_ELGAMAL: return 2;
case PUBKEY_ALGO_EDDSA: return 0;
}
return 0;
}
/* Temporary helper. */
unsigned int
pubkey_nbits( int algo, gcry_mpi_t *key )
{
int rc, nbits;
gcry_sexp_t sexp;
if( algo == PUBKEY_ALGO_DSA ) {
rc = gcry_sexp_build ( &sexp, NULL,
"(public-key(dsa(p%m)(q%m)(g%m)(y%m)))",
key[0], key[1], key[2], key[3] );
}
else if( algo == PUBKEY_ALGO_ELGAMAL || algo == PUBKEY_ALGO_ELGAMAL_E ) {
rc = gcry_sexp_build ( &sexp, NULL,
"(public-key(elg(p%m)(g%m)(y%m)))",
key[0], key[1], key[2] );
}
else if( is_RSA (algo) ) {
rc = gcry_sexp_build ( &sexp, NULL,
"(public-key(rsa(n%m)(e%m)))",
key[0], key[1] );
}
else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH
|| algo == PUBKEY_ALGO_EDDSA) {
char *curve = openpgp_oid_to_str (key[0]);
if (!curve)
rc = gpg_error_from_syserror ();
else
{
rc = gcry_sexp_build (&sexp, NULL,
"(public-key(ecc(curve%s)(q%m)))",
curve, key[1]);
xfree (curve);
}
}
else
return 0;
if ( rc )
BUG ();
nbits = gcry_pk_get_nbits( sexp );
gcry_sexp_release( sexp );
return nbits;
}
int
mpi_print (estream_t fp, gcry_mpi_t a, int mode)
{
int n=0;
if (!a)
return es_fprintf (fp, "[MPI_NULL]");
if (!mode)
{
unsigned int n1;
n1 = gcry_mpi_get_nbits(a);
n += es_fprintf (fp, "[%u bits]", n1);
}
else if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE))
{
unsigned int nbits;
unsigned char *p = gcry_mpi_get_opaque (a, &nbits);
if (!p)
n += es_fprintf (fp, "[invalid opaque value]");
else
{
nbits = (nbits + 7)/8;
for (; nbits; nbits--, p++)
n += es_fprintf (fp, "%02X", *p);
}
}
else
{
unsigned char *buffer;
if (gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buffer, NULL, a))
BUG ();
es_fputs (buffer, fp);
n += strlen (buffer);
gcry_free (buffer);
}
return n;
}
/* pkey[1] or skey[1] is Q for ECDSA, which is an uncompressed point,
i.e. 04 <x> <y> */
unsigned int
ecdsa_qbits_from_Q (unsigned int qbits)
{
if ((qbits%8) > 3)
{
log_error (_("ECDSA public key is expected to be in SEC encoding "
"multiple of 8 bits\n"));
return 0;
}
qbits -= qbits%8;
qbits /= 2;
return qbits;
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jan 27, 8:44 PM (1 d, 17 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
ba/aa/019140d8b21bbd5b040c03e7fcbf

Event Timeline