Page MenuHome GnuPG

No OneTemporary

diff --git a/g10/card-util.c b/g10/card-util.c
index 37fa9a672..3d5c43c3e 100644
--- a/g10/card-util.c
+++ b/g10/card-util.c
@@ -1,1970 +1,1970 @@
/* card-util.c - Utility functions for the OpenPGP card.
* Copyright (C) 2003-2005, 2009 Free Software Foundation, Inc.
* Copyright (C) 2003-2005, 2009 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_LIBREADLINE
# define GNUPG_LIBREADLINE_H_INCLUDED
# include <readline/readline.h>
#endif /*HAVE_LIBREADLINE*/
#if GNUPG_MAJOR_VERSION != 1
# include "gpg.h"
#endif /*GNUPG_MAJOR_VERSION != 1*/
#include "util.h"
#include "i18n.h"
#include "ttyio.h"
#include "status.h"
#include "options.h"
#include "main.h"
#include "keyserver-internal.h"
#if GNUPG_MAJOR_VERSION == 1
# include "cardglue.h"
#else /*GNUPG_MAJOR_VERSION!=1*/
# include "call-agent.h"
#endif /*GNUPG_MAJOR_VERSION!=1*/
#define CONTROL_D ('D' - 'A' + 1)
static void
write_sc_op_status (gpg_error_t err)
{
switch (gpg_err_code (err))
{
case 0:
write_status (STATUS_SC_OP_SUCCESS);
break;
#if GNUPG_MAJOR_VERSION != 1
case GPG_ERR_CANCELED:
case GPG_ERR_FULLY_CANCELED:
write_status_text (STATUS_SC_OP_FAILURE, "1");
break;
case GPG_ERR_BAD_PIN:
write_status_text (STATUS_SC_OP_FAILURE, "2");
break;
default:
write_status (STATUS_SC_OP_FAILURE);
break;
#endif /* GNUPG_MAJOR_VERSION != 1 */
}
}
/* Change the PIN of a an OpenPGP card. This is an interactive
function. */
void
change_pin (int unblock_v2, int allow_admin)
{
struct agent_card_info_s info;
int rc;
rc = agent_scd_learn (&info);
if (rc)
{
log_error (_("OpenPGP card not available: %s\n"),
gpg_strerror (rc));
return;
}
log_info (_("OpenPGP card no. %s detected\n"),
info.serialno? info.serialno : "[none]");
agent_clear_pin_cache (info.serialno);
if (opt.batch)
{
agent_release_card_info (&info);
log_error (_("can't do this in batch mode\n"));
return;
}
if (unblock_v2)
{
if (!info.is_v2)
log_error (_("This command is only available for version 2 cards\n"));
else if (!info.chvretry[1])
log_error (_("Reset Code not or not anymore available\n"));
else
{
rc = agent_scd_change_pin (2, info.serialno);
write_sc_op_status (rc);
if (rc)
tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
else
tty_printf ("PIN changed.\n");
}
}
else if (!allow_admin)
{
rc = agent_scd_change_pin (1, info.serialno);
write_sc_op_status (rc);
if (rc)
tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
else
tty_printf ("PIN changed.\n");
}
else
for (;;)
{
char *answer;
tty_printf ("\n");
tty_printf ("1 - change PIN\n"
"2 - unblock PIN\n"
"3 - change Admin PIN\n"
"4 - set the Reset Code\n"
"Q - quit\n");
tty_printf ("\n");
answer = cpr_get("cardutil.change_pin.menu",_("Your selection? "));
cpr_kill_prompt();
if (strlen (answer) != 1)
continue;
rc = 0;
if (*answer == '1')
{
/* Change PIN. */
rc = agent_scd_change_pin (1, info.serialno);
write_sc_op_status (rc);
if (rc)
tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
else
tty_printf ("PIN changed.\n");
}
else if (*answer == '2')
{
/* Unblock PIN. */
rc = agent_scd_change_pin (101, info.serialno);
write_sc_op_status (rc);
if (rc)
tty_printf ("Error unblocking the PIN: %s\n", gpg_strerror (rc));
else
tty_printf ("PIN unblocked and new PIN set.\n");
}
else if (*answer == '3')
{
/* Change Admin PIN. */
rc = agent_scd_change_pin (3, info.serialno);
write_sc_op_status (rc);
if (rc)
tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
else
tty_printf ("PIN changed.\n");
}
else if (*answer == '4')
{
/* Set a new Reset Code. */
rc = agent_scd_change_pin (102, info.serialno);
write_sc_op_status (rc);
if (rc)
tty_printf ("Error setting the Reset Code: %s\n",
gpg_strerror (rc));
else
tty_printf ("Reset Code set.\n");
}
else if (*answer == 'q' || *answer == 'Q')
{
break;
}
}
agent_release_card_info (&info);
}
static const char *
get_manufacturer (unsigned int no)
{
/* Note: Make sure that there is no colon or linefeed in the string. */
switch (no)
{
case 0x0001: return "PPC Card Systems";
case 0x0002: return "Prism";
case 0x0003: return "OpenFortress";
case 0x0004: return "Wewid";
case 0x0005: return "ZeitControl";
case 0x0006: return "Yubico";
case 0x0007: return "OpenKMS";
case 0x0008: return "LogoEmail";
case 0x002A: return "Magrathea";
case 0xF517: return "FSIJ";
- /* 0x00000 and 0xFFFF are defined as test cards per spec,
- 0xFFF00 to 0xFFFE are assigned for use with randomly created
+ /* 0x0000 and 0xFFFF are defined as test cards per spec,
+ 0xFF00 to 0xFFFE are assigned for use with randomly created
serial numbers. */
case 0x0000:
case 0xffff: return "test card";
default: return (no & 0xff00) == 0xff00? "unmanaged S/N range":"unknown";
}
}
static void
print_sha1_fpr (estream_t fp, const unsigned char *fpr)
{
int i;
if (fpr)
{
for (i=0; i < 20 ; i+=2, fpr += 2 )
{
if (i == 10 )
tty_fprintf (fp, " ");
tty_fprintf (fp, " %02X%02X", *fpr, fpr[1]);
}
}
else
tty_fprintf (fp, " [none]");
tty_fprintf (fp, "\n");
}
static void
print_sha1_fpr_colon (estream_t fp, const unsigned char *fpr)
{
int i;
if (fpr)
{
for (i=0; i < 20 ; i++, fpr++)
es_fprintf (fp, "%02X", *fpr);
}
es_putc (':', fp);
}
static void
print_name (estream_t fp, const char *text, const char *name)
{
tty_fprintf (fp, "%s", text);
/* FIXME: tty_printf_utf8_string2 eats everything after and
including an @ - e.g. when printing an url. */
if (name && *name)
{
if (fp)
print_utf8_buffer2 (fp, name, strlen (name), '\n');
else
tty_print_utf8_string2 (NULL, name, strlen (name), 0);
}
else
tty_fprintf (fp, _("[not set]"));
tty_fprintf (fp, "\n");
}
static void
print_isoname (estream_t fp, const char *text,
const char *tag, const char *name)
{
if (opt.with_colons)
es_fprintf (fp, "%s:", tag);
else
tty_fprintf (fp, "%s", text);
if (name && *name)
{
char *p, *given, *buf = xstrdup (name);
given = strstr (buf, "<<");
for (p=buf; *p; p++)
if (*p == '<')
*p = ' ';
if (given && given[2])
{
*given = 0;
given += 2;
if (opt.with_colons)
es_write_sanitized (fp, given, strlen (given), ":", NULL);
else if (fp)
print_utf8_buffer2 (fp, given, strlen (given), '\n');
else
tty_print_utf8_string2 (NULL, given, strlen (given), 0);
if (opt.with_colons)
es_putc (':', fp);
else if (*buf)
tty_fprintf (fp, " ");
}
if (opt.with_colons)
es_write_sanitized (fp, buf, strlen (buf), ":", NULL);
else if (fp)
print_utf8_buffer2 (fp, buf, strlen (buf), '\n');
else
tty_print_utf8_string2 (NULL, buf, strlen (buf), 0);
xfree (buf);
}
else
{
if (opt.with_colons)
es_putc (':', fp);
else
tty_fprintf (fp, _("[not set]"));
}
if (opt.with_colons)
es_fputs (":\n", fp);
else
tty_fprintf (fp, "\n");
}
/* Return true if the SHA1 fingerprint FPR consists only of zeroes. */
static int
fpr_is_zero (const char *fpr)
{
int i;
for (i=0; i < 20 && !fpr[i]; i++)
;
return (i == 20);
}
/* Return true if the SHA1 fingerprint FPR consists only of 0xFF. */
static int
fpr_is_ff (const char *fpr)
{
int i;
for (i=0; i < 20 && fpr[i] == '\xff'; i++)
;
return (i == 20);
}
/* Print all available information about the current card. */
void
card_status (estream_t fp, char *serialno, size_t serialnobuflen)
{
struct agent_card_info_s info;
PKT_public_key *pk = xcalloc (1, sizeof *pk);
int rc;
unsigned int uval;
const unsigned char *thefpr;
int i;
if (serialno && serialnobuflen)
*serialno = 0;
rc = agent_scd_learn (&info);
if (rc)
{
if (opt.with_colons)
es_fputs ("AID:::\n", fp);
log_error (_("OpenPGP card not available: %s\n"), gpg_strerror (rc));
xfree (pk);
return;
}
if (opt.with_colons)
es_fprintf (fp, "AID:%s:", info.serialno? info.serialno : "");
else
tty_fprintf (fp, "Application ID ...: %s\n",
info.serialno? info.serialno : "[none]");
if (!info.serialno || strncmp (info.serialno, "D27600012401", 12)
|| strlen (info.serialno) != 32 )
{
if (info.apptype && !strcmp (info.apptype, "NKS"))
{
if (opt.with_colons)
es_fputs ("netkey-card:\n", fp);
log_info ("this is a NetKey card\n");
}
else if (info.apptype && !strcmp (info.apptype, "DINSIG"))
{
if (opt.with_colons)
es_fputs ("dinsig-card:\n", fp);
log_info ("this is a DINSIG compliant card\n");
}
else if (info.apptype && !strcmp (info.apptype, "P15"))
{
if (opt.with_colons)
es_fputs ("pkcs15-card:\n", fp);
log_info ("this is a PKCS#15 compliant card\n");
}
else if (info.apptype && !strcmp (info.apptype, "GELDKARTE"))
{
if (opt.with_colons)
es_fputs ("geldkarte-card:\n", fp);
log_info ("this is a Geldkarte compliant card\n");
}
else
{
if (opt.with_colons)
es_fputs ("unknown:\n", fp);
}
log_info ("not an OpenPGP card\n");
agent_release_card_info (&info);
xfree (pk);
return;
}
if (!serialno)
;
else if (strlen (serialno)+1 > serialnobuflen)
log_error ("serial number longer than expected\n");
else
strcpy (serialno, info.serialno);
if (opt.with_colons)
es_fputs ("openpgp-card:\n", fp);
if (opt.with_colons)
{
es_fprintf (fp, "version:%.4s:\n", info.serialno+12);
uval = xtoi_2(info.serialno+16)*256 + xtoi_2 (info.serialno+18);
es_fprintf (fp, "vendor:%04x:%s:\n", uval, get_manufacturer (uval));
es_fprintf (fp, "serial:%.8s:\n", info.serialno+20);
print_isoname (fp, "Name of cardholder: ", "name", info.disp_name);
es_fputs ("lang:", fp);
if (info.disp_lang)
es_write_sanitized (fp, info.disp_lang, strlen (info.disp_lang),
":", NULL);
es_fputs (":\n", fp);
es_fprintf (fp, "sex:%c:\n", (info.disp_sex == 1? 'm':
info.disp_sex == 2? 'f' : 'u'));
es_fputs ("url:", fp);
if (info.pubkey_url)
es_write_sanitized (fp, info.pubkey_url, strlen (info.pubkey_url),
":", NULL);
es_fputs (":\n", fp);
es_fputs ("login:", fp);
if (info.login_data)
es_write_sanitized (fp, info.login_data, strlen (info.login_data),
":", NULL);
es_fputs (":\n", fp);
es_fprintf (fp, "forcepin:%d:::\n", !info.chv1_cached);
for (i=0; i < DIM (info.key_attr); i++)
if (info.key_attr[0].algo)
es_fprintf (fp, "keyattr:%d:%d:%u:\n", i+1,
info.key_attr[i].algo, info.key_attr[i].nbits);
es_fprintf (fp, "maxpinlen:%d:%d:%d:\n",
info.chvmaxlen[0], info.chvmaxlen[1], info.chvmaxlen[2]);
es_fprintf (fp, "pinretry:%d:%d:%d:\n",
info.chvretry[0], info.chvretry[1], info.chvretry[2]);
es_fprintf (fp, "sigcount:%lu:::\n", info.sig_counter);
for (i=0; i < 4; i++)
{
if (info.private_do[i])
{
es_fprintf (fp, "private_do:%d:", i+1);
es_write_sanitized (fp, info.private_do[i],
strlen (info.private_do[i]), ":", NULL);
es_fputs (":\n", fp);
}
}
es_fputs ("cafpr:", fp);
print_sha1_fpr_colon (fp, info.cafpr1valid? info.cafpr1:NULL);
print_sha1_fpr_colon (fp, info.cafpr2valid? info.cafpr2:NULL);
print_sha1_fpr_colon (fp, info.cafpr3valid? info.cafpr3:NULL);
es_putc ('\n', fp);
es_fputs ("fpr:", fp);
print_sha1_fpr_colon (fp, info.fpr1valid? info.fpr1:NULL);
print_sha1_fpr_colon (fp, info.fpr2valid? info.fpr2:NULL);
print_sha1_fpr_colon (fp, info.fpr3valid? info.fpr3:NULL);
es_putc ('\n', fp);
es_fprintf (fp, "fprtime:%lu:%lu:%lu:\n",
(unsigned long)info.fpr1time, (unsigned long)info.fpr2time,
(unsigned long)info.fpr3time);
}
else
{
tty_fprintf (fp, "Version ..........: %.1s%c.%.1s%c\n",
info.serialno[12] == '0'?"":info.serialno+12,
info.serialno[13],
info.serialno[14] == '0'?"":info.serialno+14,
info.serialno[15]);
tty_fprintf (fp, "Manufacturer .....: %s\n",
get_manufacturer (xtoi_2(info.serialno+16)*256
+ xtoi_2 (info.serialno+18)));
tty_fprintf (fp, "Serial number ....: %.8s\n", info.serialno+20);
print_isoname (fp, "Name of cardholder: ", "name", info.disp_name);
print_name (fp, "Language prefs ...: ", info.disp_lang);
tty_fprintf (fp, "Sex ..............: %s\n",
info.disp_sex == 1? _("male"):
info.disp_sex == 2? _("female") : _("unspecified"));
print_name (fp, "URL of public key : ", info.pubkey_url);
print_name (fp, "Login data .......: ", info.login_data);
if (info.private_do[0])
print_name (fp, "Private DO 1 .....: ", info.private_do[0]);
if (info.private_do[1])
print_name (fp, "Private DO 2 .....: ", info.private_do[1]);
if (info.private_do[2])
print_name (fp, "Private DO 3 .....: ", info.private_do[2]);
if (info.private_do[3])
print_name (fp, "Private DO 4 .....: ", info.private_do[3]);
if (info.cafpr1valid)
{
tty_fprintf (fp, "CA fingerprint %d .:", 1);
print_sha1_fpr (fp, info.cafpr1);
}
if (info.cafpr2valid)
{
tty_fprintf (fp, "CA fingerprint %d .:", 2);
print_sha1_fpr (fp, info.cafpr2);
}
if (info.cafpr3valid)
{
tty_fprintf (fp, "CA fingerprint %d .:", 3);
print_sha1_fpr (fp, info.cafpr3);
}
tty_fprintf (fp, "Signature PIN ....: %s\n",
info.chv1_cached? _("not forced"): _("forced"));
if (info.key_attr[0].algo)
{
tty_fprintf (fp, "Key attributes ...:");
for (i=0; i < DIM (info.key_attr); i++)
tty_fprintf (fp, " %u%c",
info.key_attr[i].nbits,
info.key_attr[i].algo == 1? 'R':
info.key_attr[i].algo == 17? 'D':
info.key_attr[i].algo == 18? 'e':
info.key_attr[i].algo == 19? 'E': '?');
tty_fprintf (fp, "\n");
}
tty_fprintf (fp, "Max. PIN lengths .: %d %d %d\n",
info.chvmaxlen[0], info.chvmaxlen[1], info.chvmaxlen[2]);
tty_fprintf (fp, "PIN retry counter : %d %d %d\n",
info.chvretry[0], info.chvretry[1], info.chvretry[2]);
tty_fprintf (fp, "Signature counter : %lu\n", info.sig_counter);
tty_fprintf (fp, "Signature key ....:");
print_sha1_fpr (fp, info.fpr1valid? info.fpr1:NULL);
if (info.fpr1valid && info.fpr1time)
tty_fprintf (fp, " created ....: %s\n",
isotimestamp (info.fpr1time));
tty_fprintf (fp, "Encryption key....:");
print_sha1_fpr (fp, info.fpr2valid? info.fpr2:NULL);
if (info.fpr2valid && info.fpr2time)
tty_fprintf (fp, " created ....: %s\n",
isotimestamp (info.fpr2time));
tty_fprintf (fp, "Authentication key:");
print_sha1_fpr (fp, info.fpr3valid? info.fpr3:NULL);
if (info.fpr3valid && info.fpr3time)
tty_fprintf (fp, " created ....: %s\n",
isotimestamp (info.fpr3time));
tty_fprintf (fp, "General key info..: ");
thefpr = (info.fpr1valid? info.fpr1 : info.fpr2valid? info.fpr2 :
info.fpr3valid? info.fpr3 : NULL);
/* If the fingerprint is all 0xff, the key has no asssociated
OpenPGP certificate. */
if ( thefpr && !fpr_is_ff (thefpr)
&& !get_pubkey_byfprint (pk, thefpr, 20))
{
kbnode_t keyblock = NULL;
print_pubkey_info (fp, pk);
#if GNUPG_MAJOR_VERSION == 1
if ( !get_seckeyblock_byfprint (&keyblock, thefpr, 20) )
print_card_key_info (fp, keyblock);
else if ( !get_keyblock_byfprint (&keyblock, thefpr, 20) )
{
release_kbnode (keyblock);
keyblock = NULL;
if (!auto_create_card_key_stub (info.serialno,
info.fpr1valid? info.fpr1:NULL,
info.fpr2valid? info.fpr2:NULL,
info.fpr3valid? info.fpr3:NULL))
{
if ( !get_seckeyblock_byfprint (&keyblock, thefpr, 20) )
print_card_key_info (fp, keyblock);
}
}
#else /* GNUPG_MAJOR_VERSION != 1 */
if (!get_keyblock_byfprint (&keyblock, thefpr, 20))
print_card_key_info (fp, keyblock);
#endif /* GNUPG_MAJOR_VERSION != 1 */
release_kbnode (keyblock);
}
else
tty_fprintf (fp, "[none]\n");
}
free_public_key (pk);
agent_release_card_info (&info);
}
static char *
get_one_name (const char *prompt1, const char *prompt2)
{
char *name;
int i;
for (;;)
{
name = cpr_get (prompt1, prompt2);
if (!name)
return NULL;
trim_spaces (name);
cpr_kill_prompt ();
for (i=0; name[i] && name[i] >= ' ' && name[i] <= 126; i++)
;
/* The name must be in Latin-1 and not UTF-8 - lacking the code
to ensure this we restrict it to ASCII. */
if (name[i])
tty_printf (_("Error: Only plain ASCII is currently allowed.\n"));
else if (strchr (name, '<'))
tty_printf (_("Error: The \"<\" character may not be used.\n"));
else if (strstr (name, " "))
tty_printf (_("Error: Double spaces are not allowed.\n"));
else
return name;
xfree (name);
}
}
static int
change_name (void)
{
char *surname = NULL, *givenname = NULL;
char *isoname, *p;
int rc;
surname = get_one_name ("keygen.smartcard.surname",
_("Cardholder's surname: "));
givenname = get_one_name ("keygen.smartcard.givenname",
_("Cardholder's given name: "));
if (!surname || !givenname || (!*surname && !*givenname))
{
xfree (surname);
xfree (givenname);
return -1; /*canceled*/
}
isoname = xmalloc ( strlen (surname) + 2 + strlen (givenname) + 1);
strcpy (stpcpy (stpcpy (isoname, surname), "<<"), givenname);
xfree (surname);
xfree (givenname);
for (p=isoname; *p; p++)
if (*p == ' ')
*p = '<';
if (strlen (isoname) > 39 )
{
tty_printf (_("Error: Combined name too long "
"(limit is %d characters).\n"), 39);
xfree (isoname);
return -1;
}
rc = agent_scd_setattr ("DISP-NAME", isoname, strlen (isoname), NULL );
if (rc)
log_error ("error setting Name: %s\n", gpg_strerror (rc));
xfree (isoname);
return rc;
}
static int
change_url (void)
{
char *url;
int rc;
url = cpr_get ("cardedit.change_url", _("URL to retrieve public key: "));
if (!url)
return -1;
trim_spaces (url);
cpr_kill_prompt ();
if (strlen (url) > 254 )
{
tty_printf (_("Error: URL too long "
"(limit is %d characters).\n"), 254);
xfree (url);
return -1;
}
rc = agent_scd_setattr ("PUBKEY-URL", url, strlen (url), NULL );
if (rc)
log_error ("error setting URL: %s\n", gpg_strerror (rc));
xfree (url);
write_sc_op_status (rc);
return rc;
}
/* Fetch the key from the URL given on the card or try to get it from
the default keyserver. */
static int
fetch_url (ctrl_t ctrl)
{
int rc;
struct agent_card_info_s info;
memset(&info,0,sizeof(info));
rc=agent_scd_getattr("PUBKEY-URL",&info);
if(rc)
log_error("error retrieving URL from card: %s\n",gpg_strerror(rc));
else
{
struct keyserver_spec *spec=NULL;
rc=agent_scd_getattr("KEY-FPR",&info);
if(rc)
log_error("error retrieving key fingerprint from card: %s\n",
gpg_strerror(rc));
else if (info.pubkey_url && *info.pubkey_url)
{
spec=parse_keyserver_uri(info.pubkey_url,1,NULL,0);
if(spec && info.fpr1valid)
{
/* This is not perfectly right. Currently, all card
fingerprints are 20 digits, but what about
fingerprints for a future v5 key? We should get the
length from somewhere lower in the code. In any
event, the fpr/keyid is not meaningful for straight
HTTP fetches, but using it allows the card to point
to HKP and LDAP servers as well. */
rc = keyserver_import_fprint (ctrl, info.fpr1, 20, spec);
free_keyserver_spec(spec);
}
}
else if (info.fpr1valid)
{
rc = keyserver_import_fprint (ctrl, info.fpr1, 20, opt.keyserver);
}
}
return rc;
}
/* Read data from file FNAME up to MAXLEN characters. On error return
-1 and store NULL at R_BUFFER; on success return the number of
bytes read and store the address of a newly allocated buffer at
R_BUFFER. */
static int
get_data_from_file (const char *fname, size_t maxlen, char **r_buffer)
{
estream_t fp;
char *data;
int n;
*r_buffer = NULL;
fp = es_fopen (fname, "rb");
#if GNUPG_MAJOR_VERSION == 1
if (fp && is_secured_file (fileno (fp)))
{
fclose (fp);
fp = NULL;
errno = EPERM;
}
#endif
if (!fp)
{
tty_printf (_("can't open '%s': %s\n"), fname, strerror (errno));
return -1;
}
data = xtrymalloc (maxlen? maxlen:1);
if (!data)
{
tty_printf (_("error allocating enough memory: %s\n"), strerror (errno));
es_fclose (fp);
return -1;
}
if (maxlen)
n = es_fread (data, 1, maxlen, fp);
else
n = 0;
es_fclose (fp);
if (n < 0)
{
tty_printf (_("error reading '%s': %s\n"), fname, strerror (errno));
xfree (data);
return -1;
}
*r_buffer = data;
return n;
}
/* Write LENGTH bytes from BUFFER to file FNAME. Return 0 on
success. */
static int
put_data_to_file (const char *fname, const void *buffer, size_t length)
{
estream_t fp;
fp = es_fopen (fname, "wb");
#if GNUPG_MAJOR_VERSION == 1
if (fp && is_secured_file (fileno (fp)))
{
fclose (fp);
fp = NULL;
errno = EPERM;
}
#endif
if (!fp)
{
tty_printf (_("can't create '%s': %s\n"), fname, strerror (errno));
return -1;
}
if (length && es_fwrite (buffer, length, 1, fp) != 1)
{
tty_printf (_("error writing '%s': %s\n"), fname, strerror (errno));
es_fclose (fp);
return -1;
}
es_fclose (fp);
return 0;
}
static int
change_login (const char *args)
{
char *data;
int n;
int rc;
if (args && *args == '<') /* Read it from a file */
{
for (args++; spacep (args); args++)
;
n = get_data_from_file (args, 254, &data);
if (n < 0)
return -1;
}
else
{
data = cpr_get ("cardedit.change_login",
_("Login data (account name): "));
if (!data)
return -1;
trim_spaces (data);
cpr_kill_prompt ();
n = strlen (data);
}
if (n > 254 )
{
tty_printf (_("Error: Login data too long "
"(limit is %d characters).\n"), 254);
xfree (data);
return -1;
}
rc = agent_scd_setattr ("LOGIN-DATA", data, n, NULL );
if (rc)
log_error ("error setting login data: %s\n", gpg_strerror (rc));
xfree (data);
write_sc_op_status (rc);
return rc;
}
static int
change_private_do (const char *args, int nr)
{
char do_name[] = "PRIVATE-DO-X";
char *data;
int n;
int rc;
assert (nr >= 1 && nr <= 4);
do_name[11] = '0' + nr;
if (args && (args = strchr (args, '<'))) /* Read it from a file */
{
for (args++; spacep (args); args++)
;
n = get_data_from_file (args, 254, &data);
if (n < 0)
return -1;
}
else
{
data = cpr_get ("cardedit.change_private_do",
_("Private DO data: "));
if (!data)
return -1;
trim_spaces (data);
cpr_kill_prompt ();
n = strlen (data);
}
if (n > 254 )
{
tty_printf (_("Error: Private DO too long "
"(limit is %d characters).\n"), 254);
xfree (data);
return -1;
}
rc = agent_scd_setattr (do_name, data, n, NULL );
if (rc)
log_error ("error setting private DO: %s\n", gpg_strerror (rc));
xfree (data);
write_sc_op_status (rc);
return rc;
}
static int
change_cert (const char *args)
{
char *data;
int n;
int rc;
if (args && *args == '<') /* Read it from a file */
{
for (args++; spacep (args); args++)
;
n = get_data_from_file (args, 16384, &data);
if (n < 0)
return -1;
}
else
{
tty_printf ("usage error: redirection to file required\n");
return -1;
}
rc = agent_scd_writecert ("OPENPGP.3", data, n);
if (rc)
log_error ("error writing certificate to card: %s\n", gpg_strerror (rc));
xfree (data);
write_sc_op_status (rc);
return rc;
}
static int
read_cert (const char *args)
{
const char *fname;
void *buffer;
size_t length;
int rc;
if (args && *args == '>') /* Write it to a file */
{
for (args++; spacep (args); args++)
;
fname = args;
}
else
{
tty_printf ("usage error: redirection to file required\n");
return -1;
}
rc = agent_scd_readcert ("OPENPGP.3", &buffer, &length);
if (rc)
log_error ("error reading certificate from card: %s\n", gpg_strerror (rc));
else
rc = put_data_to_file (fname, buffer, length);
xfree (buffer);
write_sc_op_status (rc);
return rc;
}
static int
change_lang (void)
{
char *data, *p;
int rc;
data = cpr_get ("cardedit.change_lang",
_("Language preferences: "));
if (!data)
return -1;
trim_spaces (data);
cpr_kill_prompt ();
if (strlen (data) > 8 || (strlen (data) & 1))
{
tty_printf (_("Error: invalid length of preference string.\n"));
xfree (data);
return -1;
}
for (p=data; *p && *p >= 'a' && *p <= 'z'; p++)
;
if (*p)
{
tty_printf (_("Error: invalid characters in preference string.\n"));
xfree (data);
return -1;
}
rc = agent_scd_setattr ("DISP-LANG", data, strlen (data), NULL );
if (rc)
log_error ("error setting lang: %s\n", gpg_strerror (rc));
xfree (data);
write_sc_op_status (rc);
return rc;
}
static int
change_sex (void)
{
char *data;
const char *str;
int rc;
data = cpr_get ("cardedit.change_sex",
_("Sex ((M)ale, (F)emale or space): "));
if (!data)
return -1;
trim_spaces (data);
cpr_kill_prompt ();
if (!*data)
str = "9";
else if ((*data == 'M' || *data == 'm') && !data[1])
str = "1";
else if ((*data == 'F' || *data == 'f') && !data[1])
str = "2";
else
{
tty_printf (_("Error: invalid response.\n"));
xfree (data);
return -1;
}
rc = agent_scd_setattr ("DISP-SEX", str, 1, NULL );
if (rc)
log_error ("error setting sex: %s\n", gpg_strerror (rc));
xfree (data);
write_sc_op_status (rc);
return rc;
}
static int
change_cafpr (int fprno)
{
char *data;
const char *s;
int i, c, rc;
unsigned char fpr[20];
data = cpr_get ("cardedit.change_cafpr", _("CA fingerprint: "));
if (!data)
return -1;
trim_spaces (data);
cpr_kill_prompt ();
for (i=0, s=data; i < 20 && *s; )
{
while (spacep(s))
s++;
if (*s == ':')
s++;
while (spacep(s))
s++;
c = hextobyte (s);
if (c == -1)
break;
fpr[i++] = c;
s += 2;
}
xfree (data);
if (i != 20 || *s)
{
tty_printf (_("Error: invalid formatted fingerprint.\n"));
return -1;
}
rc = agent_scd_setattr (fprno==1?"CA-FPR-1":
fprno==2?"CA-FPR-2":
fprno==3?"CA-FPR-3":"x", fpr, 20, NULL );
if (rc)
log_error ("error setting cafpr: %s\n", gpg_strerror (rc));
write_sc_op_status (rc);
return rc;
}
static void
toggle_forcesig (void)
{
struct agent_card_info_s info;
int rc;
int newstate;
memset (&info, 0, sizeof info);
rc = agent_scd_getattr ("CHV-STATUS", &info);
if (rc)
{
log_error ("error getting current status: %s\n", gpg_strerror (rc));
return;
}
newstate = !info.chv1_cached;
agent_release_card_info (&info);
rc = agent_scd_setattr ("CHV-STATUS-1", newstate? "\x01":"", 1, NULL);
if (rc)
log_error ("error toggling signature PIN flag: %s\n", gpg_strerror (rc));
write_sc_op_status (rc);
}
/* Helper for the key generation/edit functions. */
static int
get_info_for_key_operation (struct agent_card_info_s *info)
{
int rc;
memset (info, 0, sizeof *info);
rc = agent_scd_getattr ("SERIALNO", info);
if (rc || !info->serialno || strncmp (info->serialno, "D27600012401", 12)
|| strlen (info->serialno) != 32 )
{
log_error (_("key operation not possible: %s\n"),
rc ? gpg_strerror (rc) : _("not an OpenPGP card"));
return rc? rc: -1;
}
rc = agent_scd_getattr ("KEY-FPR", info);
if (!rc)
rc = agent_scd_getattr ("CHV-STATUS", info);
if (!rc)
rc = agent_scd_getattr ("DISP-NAME", info);
if (!rc)
rc = agent_scd_getattr ("EXTCAP", info);
if (!rc)
rc = agent_scd_getattr ("KEY-ATTR", info);
if (rc)
log_error (_("error getting current key info: %s\n"), gpg_strerror (rc));
return rc;
}
/* Helper for the key generation/edit functions. */
static int
check_pin_for_key_operation (struct agent_card_info_s *info, int *forced_chv1)
{
int rc = 0;
agent_clear_pin_cache (info->serialno);
*forced_chv1 = !info->chv1_cached;
if (*forced_chv1)
{ /* Switch off the forced mode so that during key generation we
don't get bothered with PIN queries for each
self-signature. */
rc = agent_scd_setattr ("CHV-STATUS-1", "\x01", 1, info->serialno);
if (rc)
{
log_error ("error clearing forced signature PIN flag: %s\n",
gpg_strerror (rc));
*forced_chv1 = 0;
}
}
if (!rc)
{
/* Check the PIN now, so that we won't get asked later for each
binding signature. */
rc = agent_scd_checkpin (info->serialno);
if (rc)
{
log_error ("error checking the PIN: %s\n", gpg_strerror (rc));
write_sc_op_status (rc);
}
}
return rc;
}
/* Helper for the key generation/edit functions. */
static void
restore_forced_chv1 (int *forced_chv1)
{
int rc;
if (*forced_chv1)
{ /* Switch back to forced state. */
rc = agent_scd_setattr ("CHV-STATUS-1", "", 1, NULL);
if (rc)
{
log_error ("error setting forced signature PIN flag: %s\n",
gpg_strerror (rc));
}
}
}
/* Helper for the key generation/edit functions. */
static void
show_card_key_info (struct agent_card_info_s *info)
{
tty_fprintf (NULL, "Signature key ....:");
print_sha1_fpr (NULL, info->fpr1valid? info->fpr1:NULL);
tty_fprintf (NULL, "Encryption key....:");
print_sha1_fpr (NULL, info->fpr2valid? info->fpr2:NULL);
tty_fprintf (NULL, "Authentication key:");
print_sha1_fpr (NULL, info->fpr3valid? info->fpr3:NULL);
tty_printf ("\n");
}
/* Helper for the key generation/edit functions. */
static int
replace_existing_key_p (struct agent_card_info_s *info, int keyno)
{
assert (keyno >= 0 && keyno <= 3);
if ((keyno == 1 && info->fpr1valid)
|| (keyno == 2 && info->fpr2valid)
|| (keyno == 3 && info->fpr3valid))
{
tty_printf ("\n");
log_info ("WARNING: such a key has already been stored on the card!\n");
tty_printf ("\n");
if ( !cpr_get_answer_is_yes( "cardedit.genkeys.replace_key",
_("Replace existing key? (y/N) ")))
return -1;
return 1;
}
return 0;
}
static void
show_keysize_warning (void)
{
static int shown;
if (shown)
return;
shown = 1;
tty_printf
(_("Note: There is no guarantee that the card "
"supports the requested size.\n"
" If the key generation does not succeed, "
"please check the\n"
" documentation of your card to see what "
"sizes are allowed.\n"));
}
/* Ask for the size of a card key. NBITS is the current size
configured for the card. KEYNO is the number of the key used to
select the prompt. Returns 0 to use the default size (i.e. NBITS)
or the selected size. */
static unsigned int
ask_card_keysize (int keyno, unsigned int nbits)
{
unsigned int min_nbits = 1024;
unsigned int max_nbits = 4096;
char *prompt, *answer;
unsigned int req_nbits;
for (;;)
{
prompt = xasprintf
(keyno == 0?
_("What keysize do you want for the Signature key? (%u) "):
keyno == 1?
_("What keysize do you want for the Encryption key? (%u) "):
_("What keysize do you want for the Authentication key? (%u) "),
nbits);
answer = cpr_get ("cardedit.genkeys.size", prompt);
cpr_kill_prompt ();
req_nbits = *answer? atoi (answer): nbits;
xfree (prompt);
xfree (answer);
if (req_nbits != nbits && (req_nbits % 32) )
{
req_nbits = ((req_nbits + 31) / 32) * 32;
tty_printf (_("rounded up to %u bits\n"), req_nbits);
}
if (req_nbits == nbits)
return 0; /* Use default. */
if (req_nbits < min_nbits || req_nbits > max_nbits)
{
tty_printf (_("%s keysizes must be in the range %u-%u\n"),
"RSA", min_nbits, max_nbits);
}
else
{
tty_printf (_("The card will now be re-configured "
"to generate a key of %u bits\n"), req_nbits);
show_keysize_warning ();
return req_nbits;
}
}
}
/* Change the size of key KEYNO (0..2) to NBITS and show an error
message if that fails. */
static gpg_error_t
do_change_keysize (int keyno, unsigned int nbits)
{
gpg_error_t err;
char args[100];
snprintf (args, sizeof args, "--force %d 1 %u", keyno+1, nbits);
err = agent_scd_setattr ("KEY-ATTR", args, strlen (args), NULL);
if (err)
log_error (_("error changing size of key %d to %u bits: %s\n"),
keyno+1, nbits, gpg_strerror (err));
return err;
}
static void
generate_card_keys (ctrl_t ctrl)
{
struct agent_card_info_s info;
int forced_chv1;
int want_backup;
int keyno;
if (get_info_for_key_operation (&info))
return;
if (info.extcap.ki)
{
char *answer;
/* FIXME: Should be something like cpr_get_bool so that a status
GET_BOOL will be emitted. */
answer = cpr_get ("cardedit.genkeys.backup_enc",
_("Make off-card backup of encryption key? (Y/n) "));
want_backup = answer_is_yes_no_default (answer, 1/*(default to Yes)*/);
cpr_kill_prompt ();
xfree (answer);
}
else
want_backup = 0;
if ( (info.fpr1valid && !fpr_is_zero (info.fpr1))
|| (info.fpr2valid && !fpr_is_zero (info.fpr2))
|| (info.fpr3valid && !fpr_is_zero (info.fpr3)))
{
tty_printf ("\n");
log_info (_("Note: keys are already stored on the card!\n"));
tty_printf ("\n");
if ( !cpr_get_answer_is_yes ("cardedit.genkeys.replace_keys",
_("Replace existing keys? (y/N) ")))
{
agent_release_card_info (&info);
return;
}
}
/* If no displayed name has been set, we assume that this is a fresh
card and print a hint about the default PINs. */
if (!info.disp_name || !*info.disp_name)
{
tty_printf ("\n");
tty_printf (_("Please note that the factory settings of the PINs are\n"
" PIN = '%s' Admin PIN = '%s'\n"
"You should change them using the command --change-pin\n"),
"123456", "12345678");
tty_printf ("\n");
}
if (check_pin_for_key_operation (&info, &forced_chv1))
goto leave;
/* If the cards features changeable key attributes, we ask for the
key size. */
if (info.is_v2 && info.extcap.aac)
{
unsigned int nbits;
for (keyno = 0; keyno < DIM (info.key_attr); keyno++)
{
nbits = ask_card_keysize (keyno, info.key_attr[keyno].nbits);
if (nbits && do_change_keysize (keyno, nbits))
{
/* Error: Better read the default key size again. */
agent_release_card_info (&info);
if (get_info_for_key_operation (&info))
goto leave;
/* Ask again for this key size. */
keyno--;
}
}
/* Note that INFO has not be synced. However we will only use
the serialnumber and thus it won't harm. */
}
generate_keypair (ctrl, 1, NULL, info.serialno, want_backup);
leave:
agent_release_card_info (&info);
restore_forced_chv1 (&forced_chv1);
}
/* This function is used by the key edit menu to generate an arbitrary
subkey. */
gpg_error_t
card_generate_subkey (KBNODE pub_keyblock)
{
gpg_error_t err;
struct agent_card_info_s info;
int forced_chv1 = 0;
int keyno;
err = get_info_for_key_operation (&info);
if (err)
return err;
show_card_key_info (&info);
tty_printf (_("Please select the type of key to generate:\n"));
tty_printf (_(" (1) Signature key\n"));
tty_printf (_(" (2) Encryption key\n"));
tty_printf (_(" (3) Authentication key\n"));
for (;;)
{
char *answer = cpr_get ("cardedit.genkeys.subkeytype",
_("Your selection? "));
cpr_kill_prompt();
if (*answer == CONTROL_D)
{
xfree (answer);
err = gpg_error (GPG_ERR_CANCELED);
goto leave;
}
keyno = *answer? atoi(answer): 0;
xfree(answer);
if (keyno >= 1 && keyno <= 3)
break; /* Okay. */
tty_printf(_("Invalid selection.\n"));
}
if (replace_existing_key_p (&info, keyno) < 0)
{
err = gpg_error (GPG_ERR_CANCELED);
goto leave;
}
err = check_pin_for_key_operation (&info, &forced_chv1);
if (err)
goto leave;
/* If the cards features changeable key attributes, we ask for the
key size. */
if (info.is_v2 && info.extcap.aac)
{
unsigned int nbits;
ask_again:
nbits = ask_card_keysize (keyno-1, info.key_attr[keyno-1].nbits);
if (nbits && do_change_keysize (keyno-1, nbits))
{
/* Error: Better read the default key size again. */
agent_release_card_info (&info);
err = get_info_for_key_operation (&info);
if (err)
goto leave;
goto ask_again;
}
/* Note that INFO has not be synced. However we will only use
the serialnumber and thus it won't harm. */
}
err = generate_card_subkeypair (pub_keyblock, keyno, info.serialno);
leave:
agent_release_card_info (&info);
restore_forced_chv1 (&forced_chv1);
return err;
}
/* Store the key at NODE into the smartcard and modify NODE to
carry the serialno stuff instead of the actual secret key
parameters. USE is the usage for that key; 0 means any
usage. */
int
card_store_subkey (KBNODE node, int use)
{
struct agent_card_info_s info;
int okay = 0;
unsigned int nbits;
int allow_keyno[3];
int keyno;
PKT_public_key *pk;
gpg_error_t err;
char *hexgrip;
int rc;
gnupg_isotime_t timebuf;
assert (node->pkt->pkttype == PKT_PUBLIC_KEY
|| node->pkt->pkttype == PKT_PUBLIC_SUBKEY);
pk = node->pkt->pkt.public_key;
if (get_info_for_key_operation (&info))
return 0;
if (!info.extcap.ki)
{
tty_printf ("The card does not support the import of keys\n");
tty_printf ("\n");
goto leave;
}
nbits = nbits_from_pk (pk);
if (!info.is_v2 && nbits != 1024)
{
tty_printf ("You may only store a 1024 bit RSA key on the card\n");
tty_printf ("\n");
goto leave;
}
allow_keyno[0] = (!use || (use & (PUBKEY_USAGE_SIG|PUBKEY_USAGE_CERT)));
allow_keyno[1] = (!use || (use & (PUBKEY_USAGE_ENC)));
allow_keyno[2] = (!use || (use & (PUBKEY_USAGE_SIG|PUBKEY_USAGE_AUTH)));
tty_printf (_("Please select where to store the key:\n"));
if (allow_keyno[0])
tty_printf (_(" (1) Signature key\n"));
if (allow_keyno[1])
tty_printf (_(" (2) Encryption key\n"));
if (allow_keyno[2])
tty_printf (_(" (3) Authentication key\n"));
for (;;)
{
char *answer = cpr_get ("cardedit.genkeys.storekeytype",
_("Your selection? "));
cpr_kill_prompt();
if (*answer == CONTROL_D || !*answer)
{
xfree (answer);
goto leave;
}
keyno = *answer? atoi(answer): 0;
xfree(answer);
if (keyno >= 1 && keyno <= 3 && allow_keyno[keyno-1])
{
if (info.is_v2 && !info.extcap.aac
&& info.key_attr[keyno-1].nbits != nbits)
{
tty_printf ("Key does not match the card's capability.\n");
}
else
break; /* Okay. */
}
else
tty_printf(_("Invalid selection.\n"));
}
if ((rc = replace_existing_key_p (&info, keyno)) < 0)
goto leave;
err = hexkeygrip_from_pk (pk, &hexgrip);
if (err)
goto leave;
epoch2isotime (timebuf, (time_t)pk->timestamp);
agent_keytocard (hexgrip, keyno, rc, info.serialno, timebuf);
if (rc)
log_error (_("KEYTOCARD failed: %s\n"), gpg_strerror (rc));
else
okay = 1;
xfree (hexgrip);
leave:
agent_release_card_info (&info);
return okay;
}
/* Data used by the command parser. This needs to be outside of the
function scope to allow readline based command completion. */
enum cmdids
{
cmdNOP = 0,
cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG, cmdVERIFY,
cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSEX, cmdCAFPR,
cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT,
cmdREADCERT, cmdUNBLOCK,
cmdINVCMD
};
static struct
{
const char *name;
enum cmdids id;
int admin_only;
const char *desc;
} cmds[] =
{
{ "quit" , cmdQUIT , 0, N_("quit this menu")},
{ "q" , cmdQUIT , 0, NULL },
{ "admin" , cmdADMIN , 0, N_("show admin commands")},
{ "help" , cmdHELP , 0, N_("show this help")},
{ "?" , cmdHELP , 0, NULL },
{ "list" , cmdLIST , 0, N_("list all available data")},
{ "l" , cmdLIST , 0, NULL },
{ "debug" , cmdDEBUG , 0, NULL },
{ "name" , cmdNAME , 1, N_("change card holder's name")},
{ "url" , cmdURL , 1, N_("change URL to retrieve key")},
{ "fetch" , cmdFETCH , 0, N_("fetch the key specified in the card URL")},
{ "login" , cmdLOGIN , 1, N_("change the login name")},
{ "lang" , cmdLANG , 1, N_("change the language preferences")},
{ "sex" , cmdSEX , 1, N_("change card holder's sex")},
{ "cafpr" , cmdCAFPR , 1, N_("change a CA fingerprint")},
{ "forcesig", cmdFORCESIG, 1, N_("toggle the signature force PIN flag")},
{ "generate", cmdGENERATE, 1, N_("generate new keys")},
{ "passwd" , cmdPASSWD, 0, N_("menu to change or unblock the PIN")},
{ "verify" , cmdVERIFY, 0, N_("verify the PIN and list all data")},
{ "unblock" , cmdUNBLOCK,0, N_("unblock the PIN using a Reset Code") },
/* Note, that we do not announce these command yet. */
{ "privatedo", cmdPRIVATEDO, 0, NULL },
{ "readcert", cmdREADCERT, 0, NULL },
{ "writecert", cmdWRITECERT, 1, NULL },
{ NULL, cmdINVCMD, 0, NULL }
};
#ifdef HAVE_LIBREADLINE
/* These two functions are used by readline for command completion. */
static char *
command_generator(const char *text,int state)
{
static int list_index,len;
const char *name;
/* If this is a new word to complete, initialize now. This includes
saving the length of TEXT for efficiency, and initializing the
index variable to 0. */
if(!state)
{
list_index=0;
len=strlen(text);
}
/* Return the next partial match */
while((name=cmds[list_index].name))
{
/* Only complete commands that have help text */
if(cmds[list_index++].desc && strncmp(name,text,len)==0)
return strdup(name);
}
return NULL;
}
static char **
card_edit_completion(const char *text, int start, int end)
{
(void)end;
/* If we are at the start of a line, we try and command-complete.
If not, just do nothing for now. */
if(start==0)
return rl_completion_matches(text,command_generator);
rl_attempted_completion_over=1;
return NULL;
}
#endif /*HAVE_LIBREADLINE*/
/* Menu to edit all user changeable values on an OpenPGP card. Only
Key creation is not handled here. */
void
card_edit (ctrl_t ctrl, strlist_t commands)
{
enum cmdids cmd = cmdNOP;
int have_commands = !!commands;
int redisplay = 1;
char *answer = NULL;
int allow_admin=0;
char serialnobuf[50];
if (opt.command_fd != -1)
;
else if (opt.batch && !have_commands)
{
log_error(_("can't do this in batch mode\n"));
goto leave;
}
for (;;)
{
int arg_number;
const char *arg_string = "";
const char *arg_rest = "";
char *p;
int i;
int cmd_admin_only;
tty_printf("\n");
if (redisplay )
{
if (opt.with_colons)
{
card_status (es_stdout, serialnobuf, DIM (serialnobuf));
fflush (stdout);
}
else
{
card_status (NULL, serialnobuf, DIM (serialnobuf));
tty_printf("\n");
}
redisplay = 0;
}
do
{
xfree (answer);
if (have_commands)
{
if (commands)
{
answer = xstrdup (commands->d);
commands = commands->next;
}
else if (opt.batch)
{
answer = xstrdup ("quit");
}
else
have_commands = 0;
}
if (!have_commands)
{
tty_enable_completion (card_edit_completion);
answer = cpr_get_no_help("cardedit.prompt", _("gpg/card> "));
cpr_kill_prompt();
tty_disable_completion ();
}
trim_spaces(answer);
}
while ( *answer == '#' );
arg_number = 0; /* Yes, here is the init which egcc complains about */
cmd_admin_only = 0;
if (!*answer)
cmd = cmdLIST; /* Default to the list command */
else if (*answer == CONTROL_D)
cmd = cmdQUIT;
else
{
if ((p=strchr (answer,' ')))
{
*p++ = 0;
trim_spaces (answer);
trim_spaces (p);
arg_number = atoi(p);
arg_string = p;
arg_rest = p;
while (digitp (arg_rest))
arg_rest++;
while (spacep (arg_rest))
arg_rest++;
}
for (i=0; cmds[i].name; i++ )
if (!ascii_strcasecmp (answer, cmds[i].name ))
break;
cmd = cmds[i].id;
cmd_admin_only = cmds[i].admin_only;
}
if (!allow_admin && cmd_admin_only)
{
tty_printf ("\n");
tty_printf (_("Admin-only command\n"));
continue;
}
switch (cmd)
{
case cmdHELP:
for (i=0; cmds[i].name; i++ )
if(cmds[i].desc
&& (!cmds[i].admin_only || (cmds[i].admin_only && allow_admin)))
tty_printf("%-10s %s\n", cmds[i].name, _(cmds[i].desc) );
break;
case cmdADMIN:
if ( !strcmp (arg_string, "on") )
allow_admin = 1;
else if ( !strcmp (arg_string, "off") )
allow_admin = 0;
else if ( !strcmp (arg_string, "verify") )
{
/* Force verification of the Admin Command. However,
this is only done if the retry counter is at initial
state. */
char *tmp = xmalloc (strlen (serialnobuf) + 6 + 1);
strcpy (stpcpy (tmp, serialnobuf), "[CHV3]");
allow_admin = !agent_scd_checkpin (tmp);
xfree (tmp);
}
else /* Toggle. */
allow_admin=!allow_admin;
if(allow_admin)
tty_printf(_("Admin commands are allowed\n"));
else
tty_printf(_("Admin commands are not allowed\n"));
break;
case cmdVERIFY:
agent_scd_checkpin (serialnobuf);
redisplay = 1;
break;
case cmdLIST:
redisplay = 1;
break;
case cmdNAME:
change_name ();
break;
case cmdURL:
change_url ();
break;
case cmdFETCH:
fetch_url (ctrl);
break;
case cmdLOGIN:
change_login (arg_string);
break;
case cmdLANG:
change_lang ();
break;
case cmdSEX:
change_sex ();
break;
case cmdCAFPR:
if ( arg_number < 1 || arg_number > 3 )
tty_printf ("usage: cafpr N\n"
" 1 <= N <= 3\n");
else
change_cafpr (arg_number);
break;
case cmdPRIVATEDO:
if ( arg_number < 1 || arg_number > 4 )
tty_printf ("usage: privatedo N\n"
" 1 <= N <= 4\n");
else
change_private_do (arg_string, arg_number);
break;
case cmdWRITECERT:
if ( arg_number != 3 )
tty_printf ("usage: writecert 3 < FILE\n");
else
change_cert (arg_rest);
break;
case cmdREADCERT:
if ( arg_number != 3 )
tty_printf ("usage: readcert 3 > FILE\n");
else
read_cert (arg_rest);
break;
case cmdFORCESIG:
toggle_forcesig ();
break;
case cmdGENERATE:
generate_card_keys (ctrl);
break;
case cmdPASSWD:
change_pin (0, allow_admin);
break;
case cmdUNBLOCK:
change_pin (1, allow_admin);
break;
case cmdQUIT:
goto leave;
case cmdNOP:
break;
case cmdINVCMD:
default:
tty_printf ("\n");
tty_printf (_("Invalid command (try \"help\")\n"));
break;
} /* End command switch. */
} /* End of main menu loop. */
leave:
xfree (answer);
}
diff --git a/g10/passphrase.c b/g10/passphrase.c
index 4d0fda634..08984eff9 100644
--- a/g10/passphrase.c
+++ b/g10/passphrase.c
@@ -1,709 +1,709 @@
/* passphrase.c - Get a passphrase
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
* 2005, 2006, 2007, 2009, 2011 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif
#ifdef HAVE_LANGINFO_CODESET
#include <langinfo.h>
#endif
#include "gpg.h"
#include "util.h"
#include "options.h"
#include "ttyio.h"
#include "keydb.h"
#include "main.h"
#include "i18n.h"
#include "status.h"
#include "call-agent.h"
#include "../common/shareddefs.h"
static char *fd_passwd = NULL;
static char *next_pw = NULL;
static char *last_pw = NULL;
/* Pack an s2k iteration count into the form specified in 2440. If
we're in between valid values, round up. With value 0 return the
old default. */
unsigned char
encode_s2k_iterations (int iterations)
{
gpg_error_t err;
unsigned char c=0;
unsigned char result;
unsigned int count;
if (!iterations)
{
unsigned long mycnt;
/* Ask the gpg-agent for a useful iteration count. */
err = agent_get_s2k_count (&mycnt);
if (err || mycnt < 65536)
{
/* Don't print an error if an older agent is used. */
if (err && gpg_err_code (err) != GPG_ERR_ASS_PARAMETER)
log_error (_("problem with the agent: %s\n"), gpg_strerror (err));
/* Default to 65536 which we used up to 2.0.13. */
return 96;
}
else if (mycnt >= 65011712)
return 255; /* Largest possible value. */
else
return encode_s2k_iterations ((int)mycnt);
}
if (iterations <= 1024)
return 0; /* Command line arg compatibility. */
if (iterations >= 65011712)
return 255;
/* Need count to be in the range 16-31 */
for (count=iterations>>6; count>=32; count>>=1)
c++;
result = (c<<4)|(count-16);
if (S2K_DECODE_COUNT(result) < iterations)
result++;
return result;
}
int
have_static_passphrase()
{
return (!!fd_passwd
&& (opt.batch || opt.pinentry_mode == PINENTRY_MODE_LOOPBACK));
}
/* Return a static passphrase. The returned value is only valid as
long as no other passphrase related function is called. NULL may
be returned if no passphrase has been set; better use
have_static_passphrase first. */
const char *
get_static_passphrase (void)
{
return fd_passwd;
}
/****************
* Set the passphrase to be used for the next query and only for the next
* one.
*/
void
set_next_passphrase( const char *s )
{
xfree(next_pw);
next_pw = NULL;
if ( s )
{
next_pw = xmalloc_secure( strlen(s)+1 );
strcpy (next_pw, s );
}
}
/****************
* Get the last passphrase used in passphrase_to_dek.
* Note: This removes the passphrase from this modules and
* the caller must free the result. May return NULL:
*/
char *
get_last_passphrase()
{
char *p = last_pw;
last_pw = NULL;
return p;
}
/* Here's an interesting question: since this passphrase was passed in
on the command line, is there really any point in using secure
memory for it? I'm going with 'yes', since it doesn't hurt, and
might help in some small way (swapping). */
void
set_passphrase_from_string(const char *pass)
{
xfree (fd_passwd);
fd_passwd = xmalloc_secure(strlen(pass)+1);
strcpy (fd_passwd, pass);
}
void
read_passphrase_from_fd( int fd )
{
int i, len;
char *pw;
if ( !opt.batch && opt.pinentry_mode != PINENTRY_MODE_LOOPBACK)
{ /* Not used but we have to do a dummy read, so that it won't end
up at the begin of the message if the quite usual trick to
prepend the passphtrase to the message is used. */
char buf[1];
while (!(read (fd, buf, 1) != 1 || *buf == '\n' ))
;
*buf = 0;
return;
}
for (pw = NULL, i = len = 100; ; i++ )
{
if (i >= len-1 )
{
char *pw2 = pw;
len += 100;
pw = xmalloc_secure( len );
if( pw2 )
{
memcpy(pw, pw2, i );
xfree (pw2);
}
else
i=0;
}
if (read( fd, pw+i, 1) != 1 || pw[i] == '\n' )
break;
}
pw[i] = 0;
if (!opt.batch && opt.pinentry_mode != PINENTRY_MODE_LOOPBACK)
tty_printf("\b\b\b \n" );
xfree ( fd_passwd );
fd_passwd = pw;
}
/*
* Ask the GPG Agent for the passphrase.
* Mode 0: Allow cached passphrase
* 1: No cached passphrase; that is we are asking for a new passphrase
* FIXME: Only partially implemented
*
* Note that TRYAGAIN_TEXT must not be translated. If CANCELED is not
* NULL, the function does set it to 1 if the user canceled the
* operation. If CACHEID is not NULL, it will be used as the cacheID
* for the gpg-agent; if is NULL and a key fingerprint can be
* computed, this will be used as the cacheid.
*/
static char *
passphrase_get ( u32 *keyid, int mode, const char *cacheid, int repeat,
const char *tryagain_text,
const char *custom_description,
const char *custom_prompt, int *canceled)
{
int rc;
char *atext = NULL;
char *pw = NULL;
PKT_public_key *pk = xmalloc_clear( sizeof *pk );
byte fpr[MAX_FINGERPRINT_LEN];
int have_fpr = 0;
char *orig_codeset;
char *my_prompt;
char hexfprbuf[20*2+1];
const char *my_cacheid;
int check = (mode == 1);
if (canceled)
*canceled = 0;
#if MAX_FINGERPRINT_LEN < 20
#error agent needs a 20 byte fingerprint
#endif
memset (fpr, 0, MAX_FINGERPRINT_LEN );
if( keyid && get_pubkey( pk, keyid ) )
{
free_public_key (pk);
pk = NULL; /* oops: no key for some reason */
}
orig_codeset = i18n_switchto_utf8 ();
if (custom_description)
atext = native_to_utf8 (custom_description);
else if ( !mode && pk && keyid )
{
char *uid;
size_t uidlen;
const char *algo_name = openpgp_pk_algo_name ( pk->pubkey_algo );
const char *timestr;
char *maink;
if ( !algo_name )
algo_name = "?";
if (keyid[2] && keyid[3]
&& keyid[0] != keyid[2]
&& keyid[1] != keyid[3] )
maink = xasprintf (_(" (main key ID %s)"), keystr (&keyid[2]));
else
maink = xstrdup ("");
uid = get_user_id ( keyid, &uidlen );
timestr = strtimestamp (pk->timestamp);
atext = xasprintf (_("Please enter the passphrase to unlock the"
" secret key for the OpenPGP certificate:\n"
"\"%.*s\"\n"
"%u-bit %s key, ID %s,\n"
"created %s%s.\n"),
(int)uidlen, uid,
nbits_from_pk (pk), algo_name, keystr(&keyid[0]),
timestr, maink);
xfree (uid);
xfree (maink);
{
size_t dummy;
fingerprint_from_pk( pk, fpr, &dummy );
have_fpr = 1;
}
}
else
atext = xstrdup ( _("Enter passphrase\n") );
if (!mode && cacheid)
my_cacheid = cacheid;
else if (!mode && have_fpr)
my_cacheid = bin2hex (fpr, 20, hexfprbuf);
else
my_cacheid = NULL;
if (tryagain_text)
tryagain_text = _(tryagain_text);
my_prompt = custom_prompt ? native_to_utf8 (custom_prompt): NULL;
rc = agent_get_passphrase (my_cacheid, tryagain_text, my_prompt, atext,
repeat, check, &pw);
xfree (my_prompt);
xfree (atext); atext = NULL;
i18n_switchback (orig_codeset);
if (!rc)
;
else if (gpg_err_code (rc) == GPG_ERR_CANCELED
|| gpg_err_code (rc) == GPG_ERR_FULLY_CANCELED)
{
log_info (_("cancelled by user\n") );
if (canceled)
*canceled = 1;
}
else
{
log_error (_("problem with the agent: %s\n"), gpg_strerror (rc));
/* Due to limitations in the API of the upper layers they
consider an error as no passphrase entered. This works in
most cases but not during key creation where this should
definitely not happen and let it continue without requiring a
passphrase. Given that now all the upper layers handle a
cancel correctly, we simply set the cancel flag now for all
errors from the agent. */
if (canceled)
*canceled = 1;
write_status_errcode ("get_passphrase", rc);
}
free_public_key (pk);
if (rc)
{
xfree (pw);
return NULL;
}
return pw;
}
/*
* Clear the cached passphrase. If CACHEID is not NULL, it will be
* used instead of a cache ID derived from KEYID.
*/
void
passphrase_clear_cache ( u32 *keyid, const char *cacheid, int algo )
{
int rc;
(void)algo;
if (!cacheid)
{
PKT_public_key *pk;
# if MAX_FINGERPRINT_LEN < 20
# error agent needs a 20 byte fingerprint
# endif
byte fpr[MAX_FINGERPRINT_LEN];
char hexfprbuf[2*20+1];
size_t dummy;
pk = xcalloc (1, sizeof *pk);
if ( !keyid || get_pubkey( pk, keyid ) )
{
log_error ("key not found in passphrase_clear_cache\n");
free_public_key (pk);
return;
}
memset (fpr, 0, MAX_FINGERPRINT_LEN );
fingerprint_from_pk ( pk, fpr, &dummy );
bin2hex (fpr, 20, hexfprbuf);
rc = agent_clear_passphrase (hexfprbuf);
free_public_key ( pk );
}
else
rc = agent_clear_passphrase (cacheid);
if (rc)
log_error (_("problem with the agent: %s\n"), gpg_strerror (rc));
}
-/* Return a new DEK object Using the string-to-key sepcifier S2K. Use
+/* Return a new DEK object using the string-to-key specifier S2K. Use
KEYID and PUBKEY_ALGO to prompt the user. Returns NULL is the user
selected to cancel the passphrase entry and if CANCELED is not
NULL, sets it to true.
MODE 0: Allow cached passphrase
1: Ignore cached passphrase
2: Ditto, but create a new key
3: Allow cached passphrase; use the S2K salt as the cache ID
4: Ditto, but create a new key
*/
DEK *
passphrase_to_dek_ext (u32 *keyid, int pubkey_algo,
int cipher_algo, STRING2KEY *s2k, int mode,
const char *tryagain_text,
const char *custdesc, const char *custprompt,
int *canceled)
{
char *pw = NULL;
DEK *dek;
STRING2KEY help_s2k;
int dummy_canceled;
char s2k_cacheidbuf[1+16+1], *s2k_cacheid = NULL;
if (!canceled)
canceled = &dummy_canceled;
*canceled = 0;
if ( !s2k )
{
assert (mode != 3 && mode != 4);
/* This is used for the old rfc1991 mode
* Note: This must match the code in encode.c with opt.rfc1991 set */
s2k = &help_s2k;
s2k->mode = 0;
s2k->hash_algo = S2K_DIGEST_ALGO;
}
/* Create a new salt or what else to be filled into the s2k for a
new key. */
if ((mode == 2 || mode == 4) && (s2k->mode == 1 || s2k->mode == 3))
{
gcry_randomize (s2k->salt, 8, GCRY_STRONG_RANDOM);
if ( s2k->mode == 3 )
{
/* We delay the encoding until it is really needed. This is
if we are going to dynamically calibrate it, we need to
call out to gpg-agent and that should not be done during
option processing in main(). */
if (!opt.s2k_count)
opt.s2k_count = encode_s2k_iterations (0);
s2k->count = opt.s2k_count;
}
}
/* If we do not have a passphrase available in NEXT_PW and status
information are request, we print them now. */
if ( !next_pw && is_status_enabled() )
{
char buf[50];
if ( keyid )
{
emit_status_need_passphrase (keyid,
keyid[2] && keyid[3]? keyid+2:NULL,
pubkey_algo);
}
else
{
snprintf (buf, sizeof buf -1, "%d %d %d",
cipher_algo, s2k->mode, s2k->hash_algo );
write_status_text ( STATUS_NEED_PASSPHRASE_SYM, buf );
}
}
/* If we do have a keyID, we do not have a passphrase available in
NEXT_PW, we are not running in batch mode and we do not want to
ignore the passphrase cache (mode!=1), print a prompt with
information on that key. */
if ( keyid && !opt.batch && !next_pw && mode!=1 )
{
PKT_public_key *pk = xmalloc_clear( sizeof *pk );
char *p;
p = get_user_id_native(keyid);
tty_printf ("\n");
tty_printf (_("You need a passphrase to unlock the secret key for\n"
"user: \"%s\"\n"),p);
xfree(p);
if ( !get_pubkey( pk, keyid ) )
{
const char *s = openpgp_pk_algo_name ( pk->pubkey_algo );
tty_printf (_("%u-bit %s key, ID %s, created %s"),
nbits_from_pk( pk ), s?s:"?", keystr(keyid),
strtimestamp(pk->timestamp) );
if ( keyid[2] && keyid[3]
&& keyid[0] != keyid[2] && keyid[1] != keyid[3] )
{
if ( keystrlen () > 10 )
{
tty_printf ("\n");
tty_printf (_(" (subkey on main key ID %s)"),
keystr(&keyid[2]) );
}
else
tty_printf ( _(" (main key ID %s)"), keystr(&keyid[2]) );
}
tty_printf("\n");
}
tty_printf("\n");
free_public_key (pk);
}
if ( next_pw )
{
/* Simply return the passphrase we already have in NEXT_PW. */
pw = next_pw;
next_pw = NULL;
}
else if ( have_static_passphrase () )
{
/* Return the passphrase we have stored in FD_PASSWD. */
pw = xmalloc_secure ( strlen(fd_passwd)+1 );
strcpy ( pw, fd_passwd );
}
else
{
if ((mode == 3 || mode == 4) && (s2k->mode == 1 || s2k->mode == 3))
{
memset (s2k_cacheidbuf, 0, sizeof s2k_cacheidbuf);
*s2k_cacheidbuf = 'S';
bin2hex (s2k->salt, 8, s2k_cacheidbuf + 1);
s2k_cacheid = s2k_cacheidbuf;
}
/* Divert to the gpg-agent. */
pw = passphrase_get (keyid, mode == 2, s2k_cacheid,
(mode == 2 || mode == 4)? opt.passphrase_repeat : 0,
tryagain_text, custdesc, custprompt, canceled);
if (*canceled)
{
xfree (pw);
write_status( STATUS_MISSING_PASSPHRASE );
return NULL;
}
}
if ( !pw || !*pw )
write_status( STATUS_MISSING_PASSPHRASE );
/* Hash the passphrase and store it in a newly allocated DEK object.
Keep a copy of the passphrase in LAST_PW for use by
get_last_passphrase(). */
dek = xmalloc_secure_clear ( sizeof *dek );
dek->algo = cipher_algo;
if ( (!pw || !*pw) && (mode == 2 || mode == 4))
dek->keylen = 0;
else
{
gpg_error_t err;
dek->keylen = openpgp_cipher_get_algo_keylen (dek->algo);
if (!(dek->keylen > 0 && dek->keylen <= DIM(dek->key)))
BUG ();
err = gcry_kdf_derive (pw, strlen (pw),
s2k->mode == 3? GCRY_KDF_ITERSALTED_S2K :
s2k->mode == 1? GCRY_KDF_SALTED_S2K :
/* */ GCRY_KDF_SIMPLE_S2K,
s2k->hash_algo, s2k->salt, 8,
S2K_DECODE_COUNT(s2k->count),
dek->keylen, dek->key);
if (err)
{
log_error ("gcry_kdf_derive failed: %s", gpg_strerror (err));
xfree (pw);
xfree (dek);
write_status( STATUS_MISSING_PASSPHRASE );
return NULL;
}
}
if (s2k_cacheid)
memcpy (dek->s2k_cacheid, s2k_cacheid, sizeof dek->s2k_cacheid);
xfree(last_pw);
last_pw = pw;
return dek;
}
DEK *
passphrase_to_dek (u32 *keyid, int pubkey_algo,
int cipher_algo, STRING2KEY *s2k, int mode,
const char *tryagain_text, int *canceled)
{
return passphrase_to_dek_ext (keyid, pubkey_algo, cipher_algo,
s2k, mode, tryagain_text, NULL, NULL,
canceled);
}
/* Emit the USERID_HINT and the NEED_PASSPHRASE status messages.
MAINKEYID may be NULL. */
void
emit_status_need_passphrase (u32 *keyid, u32 *mainkeyid, int pubkey_algo)
{
char buf[50];
char *us;
us = get_long_user_id_string (keyid);
write_status_text (STATUS_USERID_HINT, us);
xfree (us);
snprintf (buf, sizeof buf -1, "%08lX%08lX %08lX%08lX %d 0",
(ulong)keyid[0],
(ulong)keyid[1],
(ulong)(mainkeyid? mainkeyid[0]:keyid[0]),
(ulong)(mainkeyid? mainkeyid[1]:keyid[1]),
pubkey_algo);
write_status_text (STATUS_NEED_PASSPHRASE, buf);
}
/* Return an allocated utf-8 string describing the key PK. If ESCAPED
is true spaces and control characters are percent or plus escaped.
MODE describes the use of the key description; use one of the
FORMAT_KEYDESC_ macros. */
char *
gpg_format_keydesc (PKT_public_key *pk, int mode, int escaped)
{
char *uid;
size_t uidlen;
const char *algo_name;
const char *timestr;
char *orig_codeset;
char *maink;
char *desc;
const char *prompt;
const char *trailer = "";
int is_subkey;
is_subkey = (pk->main_keyid[0] && pk->main_keyid[1]
&& pk->keyid[0] != pk->main_keyid[0]
&& pk->keyid[1] != pk->main_keyid[1]);
algo_name = openpgp_pk_algo_name (pk->pubkey_algo);
timestr = strtimestamp (pk->timestamp);
uid = get_user_id (is_subkey? pk->main_keyid:pk->keyid, &uidlen);
orig_codeset = i18n_switchto_utf8 ();
if (is_subkey)
maink = xtryasprintf (_(" (main key ID %s)"), keystr (pk->main_keyid));
else
maink = NULL;
switch (mode)
{
case FORMAT_KEYDESC_NORMAL:
prompt = _("Please enter the passphrase to unlock the"
" OpenPGP secret key:");
break;
case FORMAT_KEYDESC_IMPORT:
prompt = _("Please enter the passphrase to import the"
" OpenPGP secret key:");
break;
case FORMAT_KEYDESC_EXPORT:
if (is_subkey)
prompt = _("Please enter the passphrase to export the"
" OpenPGP secret subkey:");
else
prompt = _("Please enter the passphrase to export the"
" OpenPGP secret key:");
break;
case FORMAT_KEYDESC_DELKEY:
if (is_subkey)
prompt = _("Do you really want to permanently delete the"
" OpenPGP secret subkey key:");
else
prompt = _("Do you really want to permanently delete the"
" OpenPGP secret key:");
trailer = "?";
break;
default:
prompt = "?";
break;
}
desc = xtryasprintf (_("%s\n"
"\"%.*s\"\n"
"%u-bit %s key, ID %s,\n"
"created %s%s.\n%s"),
prompt,
(int)uidlen, uid,
nbits_from_pk (pk), algo_name,
keystr (pk->keyid), timestr,
maink?maink:"", trailer);
xfree (maink);
xfree (uid);
i18n_switchback (orig_codeset);
if (escaped)
{
char *tmp = percent_plus_escape (desc);
xfree (desc);
desc = tmp;
}
return desc;
}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Jan 3, 11:42 PM (14 h, 38 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
de/6a/f1f71b408bc66cc36337d1fd4d39

Event Timeline