1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-12-22 10:19:57 +01:00

First set of changes to backport the new card code from 2.0.

For compatibility reasons a few new files had to be added.
Also added estream-printf as this is now used in app-openpgp.c and provides
a better and generic asprintf implementation than the hack we used for the
W32 code in ttyio.c.  Card code is not yet finished.
This commit is contained in:
Werner Koch 2009-07-21 14:30:13 +00:00
parent b478389753
commit 3459c6b015
37 changed files with 7385 additions and 1694 deletions

View File

@ -1,6 +1,6 @@
Program: GnuPG Program: GnuPG
Maintainer: Werner Koch <wk@gnupg.org> Maintainer: Werner Koch <wk@gnupg.org>
Bug reports: <bug-gnupg@gnu.org> Bug reports: http://bugs.gnupg.org
Security related bug reports: <security@gnupg.org> Security related bug reports: <security@gnupg.org>
License: GPLv3+ License: GPLv3+

View File

@ -1,3 +1,8 @@
2009-07-21 Werner Koch <wk@g10code.com>
* configure.ac (AH_BOTTOM): Add macros for estream-printf.
(estream_PRINTF_INIT): Add it.
2009-06-05 David Shaw <dshaw@jabberwocky.com> 2009-06-05 David Shaw <dshaw@jabberwocky.com>
* configure.ac: Remove Camellia restriction. * configure.ac: Remove Camellia restriction.

4
NEWS
View File

@ -8,6 +8,10 @@ Noteworthy changes in version 1.4.10 (unreleased)
* Fixed a memory leak which made imports of many keys very slow. * Fixed a memory leak which made imports of many keys very slow.
* Support v2 OpenPGP cards.
* FIXME: Anything else?
Noteworthy changes in version 1.4.9 (2008-03-26) Noteworthy changes in version 1.4.9 (2008-03-26)
------------------------------------------------ ------------------------------------------------

View File

@ -499,6 +499,11 @@ is intended for making fat binary builds on OS X. */
#define SAFE_VERSION_DOT '.' #define SAFE_VERSION_DOT '.'
#define SAFE_VERSION_DASH '-' #define SAFE_VERSION_DASH '-'
/* We want to use our memory allocator for estream-printf. */
#define _ESTREAM_PRINTF_MALLOC xtrymalloc
#define _ESTREAM_PRINTF_FREE xfree
#define _ESTREAM_PRINTF_EXTRA_INCLUDE "memory.h"
#endif /*GNUPG_CONFIG_H_INCLUDED*/ #endif /*GNUPG_CONFIG_H_INCLUDED*/
]) ])
@ -1049,6 +1054,12 @@ fi
GNUPG_CHECK_MLOCK GNUPG_CHECK_MLOCK
GNUPG_FUNC_MKDIR_TAKES_ONE_ARG GNUPG_FUNC_MKDIR_TAKES_ONE_ARG
#
# Prepare building of estream-printf
#
estream_PRINTF_INIT
dnl dnl
dnl Check whether we can use Linux capabilities as requested dnl Check whether we can use Linux capabilities as requested
dnl dnl

View File

@ -1,3 +1,31 @@
2009-07-21 Werner Koch <wk@g10code.com>
* app-common.h, app-openpgp.c, iso7816.c, iso7816.h, apdu.c,
* apdu.h, ccid-driver.c, ccid-driver.h, card-util.c: Update from
GnuPG 2.0 SVN revision 5084.
* cardglue.h (GCRY_MD_SHA256): Add more GCRY_MD constants.
(gcry_handler_progress_t): Add definition.
(struct agent_card_info_s): Add fields apptype, is_v2, key_attr.
* cardglue.c (learn_status_cb): Set them.
(agent_release_card_info): Release APPTYPE.
(unescape_status_string, send_status_direct): New.
(gcry_mpi_release, gcry_mpi_set_opaque): New.
(gcry_md_algo_name): New.
(open_card): s/initialized/ref_count/.
(agent_learn): Pass new new flag arg to learn_status.
(agent_scd_genkey): Add new arg createtime.
* keygen.c (gen_card_key, gen_card_key_with_backup): Add new arg
TIMESTAMP.
(write_direct_sig, write_selfsigs, write_keybinding)
(make_backsig): Ditto.
(do_generate_keypair): Pass timestamp to all signing functions.
(generate_card_subkeypair): Ditto.
* keyedit.c (menu_backsign): Pass a new timestamp to all backsisg.
* gpg.c (main): Disable keypad support.
* options.h (struct): Add field disable_keypad.
2009-07-17 Werner Koch <wk@g10code.com> 2009-07-17 Werner Koch <wk@g10code.com>
* keyring.c (keyring_rebuild_cache): Replace the assert by a * keyring.c (keyring_rebuild_cache): Replace the assert by a

1892
g10/apdu.c

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/* apdu.h - ISO 7816 APDU functions and low level I/O /* apdu.h - ISO 7816 APDU functions and low level I/O
* Copyright (C) 2003 Free Software Foundation, Inc. * Copyright (C) 2003, 2008 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -29,8 +29,11 @@ enum {
SW_MORE_DATA = 0x6100, /* Note: that the low byte must be SW_MORE_DATA = 0x6100, /* Note: that the low byte must be
masked of.*/ masked of.*/
SW_EOF_REACHED = 0x6282, SW_EOF_REACHED = 0x6282,
SW_TERM_STATE = 0x6285, /* Selected file is in termination state. */
SW_EEPROM_FAILURE = 0x6581, SW_EEPROM_FAILURE = 0x6581,
SW_WRONG_LENGTH = 0x6700, SW_WRONG_LENGTH = 0x6700,
SW_SM_NOT_SUP = 0x6882, /* Secure Messaging is not supported. */
SW_CC_NOT_SUP = 0x6884, /* Command Chaining is not supported. */
SW_CHV_WRONG = 0x6982, SW_CHV_WRONG = 0x6982,
SW_CHV_BLOCKED = 0x6983, SW_CHV_BLOCKED = 0x6983,
SW_USE_CONDITIONS = 0x6985, SW_USE_CONDITIONS = 0x6985,
@ -38,6 +41,7 @@ enum {
SW_NOT_SUPPORTED = 0x6a81, SW_NOT_SUPPORTED = 0x6a81,
SW_FILE_NOT_FOUND = 0x6a82, SW_FILE_NOT_FOUND = 0x6a82,
SW_RECORD_NOT_FOUND = 0x6a83, SW_RECORD_NOT_FOUND = 0x6a83,
SW_BAD_LC = 0x6a87, /* Lc does not match command or p1/p2. */
SW_REF_NOT_FOUND = 0x6a88, SW_REF_NOT_FOUND = 0x6a88,
SW_BAD_P0_P1 = 0x6b00, SW_BAD_P0_P1 = 0x6b00,
SW_EXACT_LENGTH = 0x6c00, SW_EXACT_LENGTH = 0x6c00,
@ -62,13 +66,20 @@ enum {
SW_HOST_GENERAL_ERROR = 0x1000b, SW_HOST_GENERAL_ERROR = 0x1000b,
SW_HOST_NO_READER = 0x1000c, SW_HOST_NO_READER = 0x1000c,
SW_HOST_ABORTED = 0x1000d, SW_HOST_ABORTED = 0x1000d,
SW_HOST_NO_KEYPAD = 0x1000e SW_HOST_NO_KEYPAD = 0x1000e,
SW_HOST_ALREADY_CONNECTED = 0x1000f
}; };
#define SW_EXACT_LENGTH_P(a) (((a)&~0xff) == SW_EXACT_LENGTH) #define SW_EXACT_LENGTH_P(a) (((a)&~0xff) == SW_EXACT_LENGTH)
/* Bit flags for the card status. */
#define APDU_CARD_USABLE (1) /* Card is present and ready for use. */
#define APDU_CARD_PRESENT (2) /* Card is just present. */
#define APDU_CARD_ACTIVE (4) /* Card is active. */
/* Note , that apdu_open_reader returns no status word but -1 on error. */ /* Note , that apdu_open_reader returns no status word but -1 on error. */
int apdu_open_reader (const char *portstr); int apdu_open_reader (const char *portstr);
int apdu_open_remote_reader (const char *portstr, int apdu_open_remote_reader (const char *portstr,
@ -83,13 +94,19 @@ int apdu_open_remote_reader (const char *portstr,
void *closefnc_value); void *closefnc_value);
int apdu_shutdown_reader (int slot); int apdu_shutdown_reader (int slot);
int apdu_close_reader (int slot); int apdu_close_reader (int slot);
void apdu_prepare_exit (void);
int apdu_enum_reader (int slot, int *used); int apdu_enum_reader (int slot, int *used);
unsigned char *apdu_get_atr (int slot, size_t *atrlen); unsigned char *apdu_get_atr (int slot, size_t *atrlen);
const char *apdu_strerror (int rc); const char *apdu_strerror (int rc);
/* These apdu functions do return status words. */ /* These APDU functions return status words. */
int apdu_connect (int slot);
int apdu_disconnect (int slot);
int apdu_set_progress_cb (int slot, gcry_handler_progress_t cb, void *cb_arg);
int apdu_activate (int slot); int apdu_activate (int slot);
int apdu_reset (int slot); int apdu_reset (int slot);
@ -97,19 +114,21 @@ int apdu_get_status (int slot, int hang,
unsigned int *status, unsigned int *changed); unsigned int *status, unsigned int *changed);
int apdu_check_keypad (int slot, int command, int pin_mode, int apdu_check_keypad (int slot, int command, int pin_mode,
int pinlen_min, int pinlen_max, int pin_padlen); int pinlen_min, int pinlen_max, int pin_padlen);
int apdu_send_simple (int slot, int class, int ins, int p0, int p1, int apdu_send_simple (int slot, int extended_mode,
int class, int ins, int p0, int p1,
int lc, const char *data); int lc, const char *data);
int apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1, int apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1,
int lc, const char *data, int lc, const char *data,
int pin_mode, int pin_mode,
int pinlen_min, int pinlen_max, int pin_padlen); int pinlen_min, int pinlen_max, int pin_padlen);
int apdu_send (int slot, int class, int ins, int p0, int p1, int apdu_send (int slot, int extended_mode,
int lc, const char *data, int class, int ins, int p0, int p1, int lc, const char *data,
unsigned char **retbuf, size_t *retbuflen); unsigned char **retbuf, size_t *retbuflen);
int apdu_send_le (int slot, int class, int ins, int p0, int p1, int apdu_send_le (int slot, int extended_mode,
int class, int ins, int p0, int p1,
int lc, const char *data, int le, int lc, const char *data, int le,
unsigned char **retbuf, size_t *retbuflen); unsigned char **retbuf, size_t *retbuflen);
int apdu_send_direct (int slot, int apdu_send_direct (int slot, size_t extended_length,
const unsigned char *apdudata, size_t apdudatalen, const unsigned char *apdudata, size_t apdudatalen,
int handle_more, int handle_more,
unsigned char **retbuf, size_t *retbuflen); unsigned char **retbuf, size_t *retbuflen);

View File

@ -1,5 +1,5 @@
/* app-common.h - Common declarations for all card applications /* app-common.h - Common declarations for all card applications
* Copyright (C) 2003, 2005 Free Software Foundation, Inc. * Copyright (C) 2003, 2005, 2008 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -31,14 +31,26 @@
#endif #endif
#define APP_CHANGE_FLAG_RESET 1
#define APP_CHANGE_FLAG_NULLPIN 2
struct app_local_s; /* Defined by all app-*.c. */ struct app_local_s; /* Defined by all app-*.c. */
struct app_ctx_s { struct app_ctx_s {
int initialized; /* The application has been initialied and the /* Number of connections currently using this application context.
function pointers may be used. Note that for If this is not 0 the application has been initialized and the
unsupported operations the particular function pointers may be used. Note that for unsupported
function pointer is set to NULL */ operations the particular function pointer is set to NULL */
int slot; /* Used reader. */ unsigned int ref_count;
/* Flag indicating that a reset has been done for that application
and that this context is merely lingering and just should not be
reused. */
int no_reuse;
/* Used reader slot. */
int slot;
/* If this is used by GnuPG 1.4 we need to know the assuan context /* If this is used by GnuPG 1.4 we need to know the assuan context
in case we need to divert the operation to an already running in case we need to divert the operation to an already running
@ -59,7 +71,7 @@ struct app_ctx_s {
struct app_local_s *app_local; /* Local to the application. */ struct app_local_s *app_local; /* Local to the application. */
struct { struct {
void (*deinit) (app_t app); void (*deinit) (app_t app);
gpg_error_t (*learn_status) (app_t app, ctrl_t ctrl); gpg_error_t (*learn_status) (app_t app, ctrl_t ctrl, unsigned int flags);
gpg_error_t (*readcert) (app_t app, const char *certid, gpg_error_t (*readcert) (app_t app, const char *certid,
unsigned char **cert, size_t *certlen); unsigned char **cert, size_t *certlen);
gpg_error_t (*readkey) (app_t app, const char *certid, gpg_error_t (*readkey) (app_t app, const char *certid,
@ -85,17 +97,23 @@ struct app_ctx_s {
void *pincb_arg, void *pincb_arg,
const void *indata, size_t indatalen, const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen); unsigned char **outdata, size_t *outdatalen);
gpg_error_t (*writecert) (app_t app, ctrl_t ctrl,
const char *certid,
gpg_error_t (*pincb)(void*,const char *,char **),
void *pincb_arg,
const unsigned char *data, size_t datalen);
gpg_error_t (*writekey) (app_t app, ctrl_t ctrl, gpg_error_t (*writekey) (app_t app, ctrl_t ctrl,
const char *certid, unsigned int flags, const char *keyid, unsigned int flags,
gpg_error_t (*pincb)(void*,const char *,char **), gpg_error_t (*pincb)(void*,const char *,char **),
void *pincb_arg, void *pincb_arg,
const unsigned char *pk, size_t pklen); const unsigned char *pk, size_t pklen);
gpg_error_t (*genkey) (app_t app, ctrl_t ctrl, gpg_error_t (*genkey) (app_t app, ctrl_t ctrl,
const char *keynostr, unsigned int flags, const char *keynostr, unsigned int flags,
time_t createtime,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg); void *pincb_arg);
gpg_error_t (*change_pin) (app_t app, ctrl_t ctrl, gpg_error_t (*change_pin) (app_t app, ctrl_t ctrl,
const char *chvnostr, int reset_mode, const char *chvnostr, unsigned int flags,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg); void *pincb_arg);
gpg_error_t (*check_pin) (app_t app, const char *keyidstr, gpg_error_t (*check_pin) (app_t app, const char *keyidstr,
@ -117,17 +135,23 @@ gpg_error_t app_openpgp_storekey (app_t app, int keyno,
void *pincb_arg); void *pincb_arg);
#else #else
/*-- app-help.c --*/ /*-- app-help.c --*/
unsigned int app_help_count_bits (const unsigned char *a, size_t len);
gpg_error_t app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip); gpg_error_t app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip);
size_t app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff); size_t app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff);
/*-- app.c --*/ /*-- app.c --*/
void app_dump_state (void);
void application_notify_card_reset (int slot);
gpg_error_t check_application_conflict (ctrl_t ctrl, const char *name);
gpg_error_t select_application (ctrl_t ctrl, int slot, const char *name, gpg_error_t select_application (ctrl_t ctrl, int slot, const char *name,
app_t *r_app); app_t *r_app);
char *get_supported_applications (void);
void release_application (app_t app); void release_application (app_t app);
gpg_error_t app_munge_serialno (app_t app); gpg_error_t app_munge_serialno (app_t app);
gpg_error_t app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp); gpg_error_t app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp);
gpg_error_t app_write_learn_status (app_t app, ctrl_t ctrl); gpg_error_t app_write_learn_status (app_t app, ctrl_t ctrl,
unsigned int flags);
gpg_error_t app_readcert (app_t app, const char *certid, gpg_error_t app_readcert (app_t app, const char *certid,
unsigned char **cert, size_t *certlen); unsigned char **cert, size_t *certlen);
gpg_error_t app_readkey (app_t app, const char *keyid, gpg_error_t app_readkey (app_t app, const char *keyid,
@ -152,6 +176,11 @@ gpg_error_t app_decipher (app_t app, const char *keyidstr,
void *pincb_arg, void *pincb_arg,
const void *indata, size_t indatalen, const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen ); unsigned char **outdata, size_t *outdatalen );
gpg_error_t app_writecert (app_t app, ctrl_t ctrl,
const char *certidstr,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
const unsigned char *keydata, size_t keydatalen);
gpg_error_t app_writekey (app_t app, ctrl_t ctrl, gpg_error_t app_writekey (app_t app, ctrl_t ctrl,
const char *keyidstr, unsigned int flags, const char *keyidstr, unsigned int flags,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
@ -159,6 +188,7 @@ gpg_error_t app_writekey (app_t app, ctrl_t ctrl,
const unsigned char *keydata, size_t keydatalen); const unsigned char *keydata, size_t keydatalen);
gpg_error_t app_genkey (app_t app, ctrl_t ctrl, gpg_error_t app_genkey (app_t app, ctrl_t ctrl,
const char *keynostr, unsigned int flags, const char *keynostr, unsigned int flags,
time_t createtime,
gpg_error_t (*pincb)(void*, const char *, char **), gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg); void *pincb_arg);
gpg_error_t app_get_challenge (app_t app, size_t nbytes, gpg_error_t app_get_challenge (app_t app, size_t nbytes,
@ -184,6 +214,9 @@ gpg_error_t app_select_dinsig (app_t app);
/*-- app-p15.c --*/ /*-- app-p15.c --*/
gpg_error_t app_select_p15 (app_t app); gpg_error_t app_select_p15 (app_t app);
/*-- app-geldkarte.c --*/
gpg_error_t app_select_geldkarte (app_t app);
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/* card-util.c - Utility functions for the OpenPGP card. /* card-util.c - Utility functions for the OpenPGP card.
* Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. * Copyright (C) 2003, 2004, 2005, 2009 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -36,6 +36,7 @@
#include "keyserver-internal.h" #include "keyserver-internal.h"
#if GNUPG_MAJOR_VERSION == 1 #if GNUPG_MAJOR_VERSION == 1
# ifdef HAVE_LIBREADLINE # ifdef HAVE_LIBREADLINE
# define GNUPG_LIBREADLINE_H_INCLUDED
# include <stdio.h> # include <stdio.h>
# include <readline/readline.h> # include <readline/readline.h>
# endif /*HAVE_LIBREADLINE*/ # endif /*HAVE_LIBREADLINE*/
@ -47,10 +48,33 @@
#define CONTROL_D ('D' - 'A' + 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:
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 /* Change the PIN of a an OpenPGP card. This is an interactive
function. */ function. */
void void
change_pin (int chvno, int allow_admin) change_pin (int unblock_v2, int allow_admin)
{ {
struct agent_card_info_s info; struct agent_card_info_s info;
int rc; int rc;
@ -75,17 +99,32 @@ change_pin (int chvno, int allow_admin)
return; return;
} }
if(!allow_admin)
if (unblock_v2)
{ {
rc = agent_scd_change_pin (1, info.serialno); 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) if (rc)
tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc)); tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
else else
{
write_status (STATUS_SC_OP_SUCCESS);
tty_printf ("PIN changed.\n"); 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 else
for (;;) for (;;)
{ {
@ -95,6 +134,7 @@ change_pin (int chvno, int allow_admin)
tty_printf ("1 - change PIN\n" tty_printf ("1 - change PIN\n"
"2 - unblock PIN\n" "2 - unblock PIN\n"
"3 - change Admin PIN\n" "3 - change Admin PIN\n"
"4 - set the Reset Code\n"
"Q - quit\n"); "Q - quit\n");
tty_printf ("\n"); tty_printf ("\n");
@ -106,36 +146,44 @@ change_pin (int chvno, int allow_admin)
rc = 0; rc = 0;
if (*answer == '1') if (*answer == '1')
{ {
/* Change PIN. */
rc = agent_scd_change_pin (1, info.serialno); rc = agent_scd_change_pin (1, info.serialno);
write_sc_op_status (rc);
if (rc) if (rc)
tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc)); tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
else else
{
write_status (STATUS_SC_OP_SUCCESS);
tty_printf ("PIN changed.\n"); tty_printf ("PIN changed.\n");
} }
}
else if (*answer == '2') else if (*answer == '2')
{ {
/* Unblock PIN. */
rc = agent_scd_change_pin (101, info.serialno); rc = agent_scd_change_pin (101, info.serialno);
write_sc_op_status (rc);
if (rc) if (rc)
tty_printf ("Error unblocking the PIN: %s\n", gpg_strerror (rc)); tty_printf ("Error unblocking the PIN: %s\n", gpg_strerror (rc));
else else
{
write_status (STATUS_SC_OP_SUCCESS);
tty_printf ("PIN unblocked and new PIN set.\n"); tty_printf ("PIN unblocked and new PIN set.\n");
} }
}
else if (*answer == '3') else if (*answer == '3')
{ {
/* Change Admin PIN. */
rc = agent_scd_change_pin (3, info.serialno); rc = agent_scd_change_pin (3, info.serialno);
write_sc_op_status (rc);
if (rc) if (rc)
tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc)); tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
else else
{
write_status (STATUS_SC_OP_SUCCESS);
tty_printf ("PIN changed.\n"); 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') else if (*answer == 'q' || *answer == 'Q')
{ {
@ -156,11 +204,13 @@ get_manufacturer (unsigned int no)
case 0x0002: return "Prism"; case 0x0002: return "Prism";
case 0x0003: return "OpenFortress"; case 0x0003: return "OpenFortress";
case 0x0004: return "Wewid AB"; case 0x0004: return "Wewid AB";
case 0x0005: return "ZeitControl";
case 0x002A: return "Magrathea";
/* 0x00000 and 0xFFFF are defined as test cards per spec, /* 0x00000 and 0xFFFF are defined as test cards per spec,
0xFFF00 to 0xFFFE are assigned for use with randomly created 0xFFF00 to 0xFFFE are assigned for use with randomly created
serial numbers. */ serial numbers. */
case 0: case 0x0000:
case 0xffff: return "test card"; case 0xffff: return "test card";
default: return (no & 0xff00) == 0xff00? "unmanaged S/N range":"unknown"; default: return (no & 0xff00) == 0xff00? "unmanaged S/N range":"unknown";
} }
@ -287,6 +337,18 @@ fpr_is_zero (const char *fpr)
} }
/* 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. */ /* Print all available information about the current card. */
void void
card_status (FILE *fp, char *serialno, size_t serialnobuflen) card_status (FILE *fp, char *serialno, size_t serialnobuflen)
@ -319,9 +381,36 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen)
info.serialno? info.serialno : "[none]"); info.serialno? info.serialno : "[none]");
if (!info.serialno || strncmp (info.serialno, "D27600012401", 12) if (!info.serialno || strncmp (info.serialno, "D27600012401", 12)
|| strlen (info.serialno) != 32 ) || strlen (info.serialno) != 32 )
{
if (info.apptype && !strcmp (info.apptype, "NKS"))
{
if (opt.with_colons)
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)
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)
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)
fputs ("geldkarte-card:\n", fp);
log_info ("this is a Geldkarte compliant card\n");
}
else
{ {
if (opt.with_colons) if (opt.with_colons)
fputs ("unknown:\n", fp); fputs ("unknown:\n", fp);
}
log_info ("not an OpenPGP card\n"); log_info ("not an OpenPGP card\n");
agent_release_card_info (&info); agent_release_card_info (&info);
xfree (pk); xfree (pk);
@ -367,6 +456,10 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen)
fputs (":\n", fp); fputs (":\n", fp);
fprintf (fp, "forcepin:%d:::\n", !info.chv1_cached); fprintf (fp, "forcepin:%d:::\n", !info.chv1_cached);
for (i=0; i < DIM (info.key_attr); i++)
if (info.key_attr[0].algo)
fprintf (fp, "keyattr:%d:%d:%u:\n", i+1,
info.key_attr[i].algo, info.key_attr[i].nbits);
fprintf (fp, "maxpinlen:%d:%d:%d:\n", fprintf (fp, "maxpinlen:%d:%d:%d:\n",
info.chvmaxlen[0], info.chvmaxlen[1], info.chvmaxlen[2]); info.chvmaxlen[0], info.chvmaxlen[1], info.chvmaxlen[2]);
fprintf (fp, "pinretry:%d:%d:%d:\n", fprintf (fp, "pinretry:%d:%d:%d:\n",
@ -442,6 +535,16 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen)
} }
tty_fprintf (fp, "Signature PIN ....: %s\n", tty_fprintf (fp, "Signature PIN ....: %s\n",
info.chv1_cached? _("not forced"): _("forced")); 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': '?');
tty_fprintf (fp, "\n");
}
tty_fprintf (fp, "Max. PIN lengths .: %d %d %d\n", tty_fprintf (fp, "Max. PIN lengths .: %d %d %d\n",
info.chvmaxlen[0], info.chvmaxlen[1], info.chvmaxlen[2]); info.chvmaxlen[0], info.chvmaxlen[1], info.chvmaxlen[2]);
tty_fprintf (fp, "PIN retry counter : %d %d %d\n", tty_fprintf (fp, "PIN retry counter : %d %d %d\n",
@ -466,7 +569,10 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen)
thefpr = (info.fpr1valid? info.fpr1 : info.fpr2valid? info.fpr2 : thefpr = (info.fpr1valid? info.fpr1 : info.fpr2valid? info.fpr2 :
info.fpr3valid? info.fpr3 : NULL); info.fpr3valid? info.fpr3 : NULL);
if ( thefpr && !get_pubkey_byfprint (pk, thefpr, 20)) /* 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 keyblock = NULL; KBNODE keyblock = NULL;
@ -599,6 +705,7 @@ change_url (void)
if (rc) if (rc)
log_error ("error setting URL: %s\n", gpg_strerror (rc)); log_error ("error setting URL: %s\n", gpg_strerror (rc));
xfree (url); xfree (url);
write_sc_op_status (rc);
return rc; return rc;
} }
@ -608,7 +715,6 @@ change_url (void)
static int static int
fetch_url(void) fetch_url(void)
{ {
#if GNUPG_MAJOR_VERSION == 1
int rc; int rc;
struct agent_card_info_s info; struct agent_card_info_s info;
@ -648,9 +754,91 @@ fetch_url(void)
} }
return rc; return rc;
#else }
return 0;
/* 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)
{
FILE *fp;
char *data;
int n;
*r_buffer = NULL;
fp = fopen (fname, "rb");
#if GNUPG_MAJOR_VERSION == 1
if (fp && is_secured_file (fileno (fp)))
{
fclose (fp);
fp = NULL;
errno = EPERM;
}
#endif #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));
fclose (fp);
return -1;
}
if (maxlen)
n = fread (data, 1, maxlen, fp);
else
n = 0;
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)
{
FILE *fp;
fp = 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 && fwrite (buffer, length, 1, fp) != 1)
{
tty_printf (_("error writing `%s': %s\n"), fname, strerror (errno));
fclose (fp);
return -1;
}
fclose (fp);
return 0;
} }
@ -663,35 +851,12 @@ change_login (const char *args)
if (args && *args == '<') /* Read it from a file */ if (args && *args == '<') /* Read it from a file */
{ {
FILE *fp;
for (args++; spacep (args); args++) for (args++; spacep (args); args++)
; ;
fp = fopen (args, "rb"); n = get_data_from_file (args, 254, &data);
#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"), args, strerror (errno));
return -1;
}
data = xmalloc (254);
n = fread (data, 1, 254, fp);
fclose (fp);
if (n < 0) if (n < 0)
{
tty_printf (_("error reading `%s': %s\n"), args, strerror (errno));
xfree (data);
return -1; return -1;
} }
}
else else
{ {
data = cpr_get ("cardedit.change_login", data = cpr_get ("cardedit.change_login",
@ -715,6 +880,7 @@ change_login (const char *args)
if (rc) if (rc)
log_error ("error setting login data: %s\n", gpg_strerror (rc)); log_error ("error setting login data: %s\n", gpg_strerror (rc));
xfree (data); xfree (data);
write_sc_op_status (rc);
return rc; return rc;
} }
@ -731,36 +897,12 @@ change_private_do (const char *args, int nr)
if (args && (args = strchr (args, '<'))) /* Read it from a file */ if (args && (args = strchr (args, '<'))) /* Read it from a file */
{ {
FILE *fp;
/* Fixme: Factor this duplicated code out. */
for (args++; spacep (args); args++) for (args++; spacep (args); args++)
; ;
fp = fopen (args, "rb"); n = get_data_from_file (args, 254, &data);
#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"), args, strerror (errno));
return -1;
}
data = xmalloc (254);
n = fread (data, 1, 254, fp);
fclose (fp);
if (n < 0) if (n < 0)
{
tty_printf (_("error reading `%s': %s\n"), args, strerror (errno));
xfree (data);
return -1; return -1;
} }
}
else else
{ {
data = cpr_get ("cardedit.change_private_do", data = cpr_get ("cardedit.change_private_do",
@ -784,9 +926,74 @@ change_private_do (const char *args, int nr)
if (rc) if (rc)
log_error ("error setting private DO: %s\n", gpg_strerror (rc)); log_error ("error setting private DO: %s\n", gpg_strerror (rc));
xfree (data); xfree (data);
write_sc_op_status (rc);
return 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: redirectrion to file required\n");
return -1;
}
#warning need to implement this fucntion
rc = -1; /*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: redirectrion to file required\n");
return -1;
}
#warning need to implement this fucntion
rc = -1; /*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 static int
change_lang (void) change_lang (void)
{ {
@ -820,6 +1027,7 @@ change_lang (void)
if (rc) if (rc)
log_error ("error setting lang: %s\n", gpg_strerror (rc)); log_error ("error setting lang: %s\n", gpg_strerror (rc));
xfree (data); xfree (data);
write_sc_op_status (rc);
return rc; return rc;
} }
@ -855,6 +1063,7 @@ change_sex (void)
if (rc) if (rc)
log_error ("error setting sex: %s\n", gpg_strerror (rc)); log_error ("error setting sex: %s\n", gpg_strerror (rc));
xfree (data); xfree (data);
write_sc_op_status (rc);
return rc; return rc;
} }
@ -899,6 +1108,7 @@ change_cafpr (int fprno)
fprno==3?"CA-FPR-3":"x", fpr, 20, NULL ); fprno==3?"CA-FPR-3":"x", fpr, 20, NULL );
if (rc) if (rc)
log_error ("error setting cafpr: %s\n", gpg_strerror (rc)); log_error ("error setting cafpr: %s\n", gpg_strerror (rc));
write_sc_op_status (rc);
return rc; return rc;
} }
@ -924,6 +1134,7 @@ toggle_forcesig (void)
rc = agent_scd_setattr ("CHV-STATUS-1", newstate? "\x01":"", 1, NULL); rc = agent_scd_setattr ("CHV-STATUS-1", newstate? "\x01":"", 1, NULL);
if (rc) if (rc)
log_error ("error toggling signature PIN flag: %s\n", gpg_strerror (rc)); log_error ("error toggling signature PIN flag: %s\n", gpg_strerror (rc));
write_sc_op_status (rc);
} }
@ -963,7 +1174,7 @@ check_pin_for_key_operation (struct agent_card_info_s *info, int *forced_chv1)
*forced_chv1 = !info->chv1_cached; *forced_chv1 = !info->chv1_cached;
if (*forced_chv1) if (*forced_chv1)
{ /* Switch of the forced mode so that during key generation we { /* Switch off the forced mode so that during key generation we
don't get bothered with PIN queries for each don't get bothered with PIN queries for each
self-signature. */ self-signature. */
rc = agent_scd_setattr ("CHV-STATUS-1", "\x01", 1, info->serialno); rc = agent_scd_setattr ("CHV-STATUS-1", "\x01", 1, info->serialno);
@ -981,7 +1192,10 @@ check_pin_for_key_operation (struct agent_card_info_s *info, int *forced_chv1)
binding signature. */ binding signature. */
rc = agent_scd_checkpin (info->serialno); rc = agent_scd_checkpin (info->serialno);
if (rc) if (rc)
{
log_error ("error checking the PIN: %s\n", gpg_strerror (rc)); log_error ("error checking the PIN: %s\n", gpg_strerror (rc));
write_sc_op_status (rc);
}
} }
return rc; return rc;
} }
@ -1003,7 +1217,7 @@ restore_forced_chv1 (int *forced_chv1)
} }
} }
#if GNUPG_MAJOR_VERSION == 1
/* Helper for the key generation/edit functions. */ /* Helper for the key generation/edit functions. */
static void static void
show_card_key_info (struct agent_card_info_s *info) show_card_key_info (struct agent_card_info_s *info)
@ -1016,9 +1230,8 @@ show_card_key_info (struct agent_card_info_s *info)
print_sha1_fpr (NULL, info->fpr3valid? info->fpr3:NULL); print_sha1_fpr (NULL, info->fpr3valid? info->fpr3:NULL);
tty_printf ("\n"); tty_printf ("\n");
} }
#endif
#if GNUPG_MAJOR_VERSION == 1
/* Helper for the key generation/edit functions. */ /* Helper for the key generation/edit functions. */
static int static int
replace_existing_key_p (struct agent_card_info_s *info, int keyno) replace_existing_key_p (struct agent_card_info_s *info, int keyno)
@ -1038,11 +1251,10 @@ replace_existing_key_p (struct agent_card_info_s *info, int keyno)
} }
return 0; return 0;
} }
#endif
static void static void
generate_card_keys (const char *serialno) generate_card_keys (void)
{ {
struct agent_card_info_s info; struct agent_card_info_s info;
int forced_chv1; int forced_chv1;
@ -1094,12 +1306,8 @@ generate_card_keys (const char *serialno)
if (check_pin_for_key_operation (&info, &forced_chv1)) if (check_pin_for_key_operation (&info, &forced_chv1))
goto leave; goto leave;
#if GNUPG_MAJOR_VERSION == 1
generate_keypair (NULL, info.serialno, generate_keypair (NULL, info.serialno,
want_backup? opt.homedir:NULL); want_backup? opt.homedir:NULL);
#else
generate_keypair (NULL, info.serialno);
#endif
leave: leave:
agent_release_card_info (&info); agent_release_card_info (&info);
@ -1112,7 +1320,6 @@ generate_card_keys (const char *serialno)
int int
card_generate_subkey (KBNODE pub_keyblock, KBNODE sec_keyblock) card_generate_subkey (KBNODE pub_keyblock, KBNODE sec_keyblock)
{ {
#if GNUPG_MAJOR_VERSION == 1
struct agent_card_info_s info; struct agent_card_info_s info;
int okay = 0; int okay = 0;
int forced_chv1 = 0; int forced_chv1 = 0;
@ -1159,9 +1366,6 @@ card_generate_subkey (KBNODE pub_keyblock, KBNODE sec_keyblock)
agent_release_card_info (&info); agent_release_card_info (&info);
restore_forced_chv1 (&forced_chv1); restore_forced_chv1 (&forced_chv1);
return okay; return okay;
#else
return 0;
#endif
} }
@ -1172,7 +1376,6 @@ card_generate_subkey (KBNODE pub_keyblock, KBNODE sec_keyblock)
int int
card_store_subkey (KBNODE node, int use) card_store_subkey (KBNODE node, int use)
{ {
#if GNUPG_MAJOR_VERSION == 1
struct agent_card_info_s info; struct agent_card_info_s info;
int okay = 0; int okay = 0;
int rc; int rc;
@ -1192,7 +1395,8 @@ card_store_subkey (KBNODE node, int use)
show_card_key_info (&info); show_card_key_info (&info);
if (!is_RSA (sk->pubkey_algo) || nbits_from_sk (sk) != 1024 ) if (!is_RSA (sk->pubkey_algo)
|| (!info.is_v2 && nbits_from_sk (sk) != 1024) )
{ {
tty_printf ("You may only store a 1024 bit RSA key on the card\n"); tty_printf ("You may only store a 1024 bit RSA key on the card\n");
tty_printf ("\n"); tty_printf ("\n");
@ -1260,7 +1464,10 @@ card_store_subkey (KBNODE node, int use)
rc = save_unprotected_key_to_card (sk, keyno); rc = save_unprotected_key_to_card (sk, keyno);
if (rc) if (rc)
{
log_error (_("error writing key to card: %s\n"), gpg_strerror (rc));
goto leave; goto leave;
}
/* Get back to the maybe protected original secret key. */ /* Get back to the maybe protected original secret key. */
if (copied_sk) if (copied_sk)
@ -1274,11 +1481,11 @@ card_store_subkey (KBNODE node, int use)
n = pubkey_get_nskey (sk->pubkey_algo); n = pubkey_get_nskey (sk->pubkey_algo);
for (i=pubkey_get_npkey (sk->pubkey_algo); i < n; i++) for (i=pubkey_get_npkey (sk->pubkey_algo); i < n; i++)
{ {
mpi_free (sk->skey[i]); gcry_mpi_release (sk->skey[i]);
sk->skey[i] = NULL; sk->skey[i] = NULL;
} }
i = pubkey_get_npkey (sk->pubkey_algo); i = pubkey_get_npkey (sk->pubkey_algo);
sk->skey[i] = mpi_set_opaque (NULL, xstrdup ("dummydata"), 10); sk->skey[i] = gcry_mpi_set_opaque (NULL, xstrdup ("dummydata"), 10*8);
sk->is_protected = 1; sk->is_protected = 1;
sk->protect.s2k.mode = 1002; sk->protect.s2k.mode = 1002;
s = info.serialno; s = info.serialno;
@ -1293,9 +1500,6 @@ card_store_subkey (KBNODE node, int use)
free_secret_key (copied_sk); free_secret_key (copied_sk);
agent_release_card_info (&info); agent_release_card_info (&info);
return okay; return okay;
#else
return 0;
#endif
} }
@ -1307,7 +1511,8 @@ enum cmdids
cmdNOP = 0, cmdNOP = 0,
cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG, cmdVERIFY, cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG, cmdVERIFY,
cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSEX, cmdCAFPR, cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSEX, cmdCAFPR,
cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT,
cmdREADCERT, cmdUNBLOCK,
cmdINVCMD cmdINVCMD
}; };
@ -1338,8 +1543,11 @@ static struct
{ "generate", cmdGENERATE, 1, N_("generate new keys")}, { "generate", cmdGENERATE, 1, N_("generate new keys")},
{ "passwd" , cmdPASSWD, 0, N_("menu to change or unblock the PIN")}, { "passwd" , cmdPASSWD, 0, N_("menu to change or unblock the PIN")},
{ "verify" , cmdVERIFY, 0, N_("verify the PIN and list all data")}, { "verify" , cmdVERIFY, 0, N_("verify the PIN and list all data")},
/* Note, that we do not announce this command yet. */ { "unblock" , cmdUNBLOCK,0, N_("unblock the PIN using a Reset Code") },
/* Note, that we do not announce these command yet. */
{ "privatedo", cmdPRIVATEDO, 0, NULL }, { "privatedo", cmdPRIVATEDO, 0, NULL },
{ "readcert", cmdREADCERT, 0, NULL },
{ "writecert", cmdWRITECERT, 1, NULL },
{ NULL, cmdINVCMD, 0, NULL } { NULL, cmdINVCMD, 0, NULL }
}; };
@ -1392,13 +1600,13 @@ card_edit_completion(const char *text, int start, int end)
/* Menu to edit all user changeable values on an OpenPGP card. Only /* Menu to edit all user changeable values on an OpenPGP card. Only
Key creation is not handled here. */ Key creation is not handled here. */
void void
card_edit (STRLIST commands) card_edit (strlist_t commands)
{ {
enum cmdids cmd = cmdNOP; enum cmdids cmd = cmdNOP;
int have_commands = !!commands; int have_commands = !!commands;
int redisplay = 1; int redisplay = 1;
char *answer = NULL; char *answer = NULL;
int did_checkpin = 0, allow_admin=0; int allow_admin=0;
char serialnobuf[50]; char serialnobuf[50];
@ -1414,6 +1622,7 @@ card_edit (STRLIST commands)
{ {
int arg_number; int arg_number;
const char *arg_string = ""; const char *arg_string = "";
const char *arg_rest = "";
char *p; char *p;
int i; int i;
int cmd_admin_only; int cmd_admin_only;
@ -1482,6 +1691,11 @@ card_edit (STRLIST commands)
trim_spaces (p); trim_spaces (p);
arg_number = atoi(p); arg_number = atoi(p);
arg_string = 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++ ) for (i=0; cmds[i].name; i++ )
@ -1580,17 +1794,34 @@ card_edit (STRLIST commands)
change_private_do (arg_string, arg_number); change_private_do (arg_string, arg_number);
break; 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: case cmdFORCESIG:
toggle_forcesig (); toggle_forcesig ();
break; break;
case cmdGENERATE: case cmdGENERATE:
generate_card_keys (serialnobuf); generate_card_keys ();
break; break;
case cmdPASSWD: case cmdPASSWD:
change_pin (0, allow_admin); change_pin (0, allow_admin);
did_checkpin = 0; /* Need to reset it of course. */ break;
case cmdUNBLOCK:
change_pin (1, allow_admin);
break; break;
case cmdQUIT: case cmdQUIT:

View File

@ -190,6 +190,37 @@ send_status_info (ctrl_t ctrl, const char *keyword, ...)
va_end (arg_ptr); va_end (arg_ptr);
} }
/* Send a ready formatted status line via assuan. */
void
send_status_direct (ctrl_t ctrl, const char *keyword, const char *args)
{
char buf[950];
if (strchr (args, '\n'))
log_error ("error: LF detected in status line - not sending\n");
else
{
snprintf (buf, sizeof buf, "%s%s%s",
keyword, args? " ":"", args? args:"");
if (ctrl && ctrl->status_cb)
ctrl->status_cb (ctrl->status_cb_arg, buf);
}
}
void
gcry_mpi_release (MPI a)
{
mpi_free (a);
}
MPI
gcry_mpi_set_opaque (MPI a, void *p, unsigned int len)
{
return mpi_set_opaque (a, p, len);
}
/* Replacement function of the Libgcrypt onewhich is used in gnupg /* Replacement function of the Libgcrypt onewhich is used in gnupg
1.9. Thus function computes the digest of ALGO from the data in 1.9. Thus function computes the digest of ALGO from the data in
@ -208,6 +239,17 @@ gcry_md_hash_buffer (int algo, void *digest,
} }
/* This function simply returns the name of the algorithm or some
constant string when there is no algo. It will never return
NULL. */
const char *
gcry_md_algo_name (int algorithm)
{
const char *s = digest_algo_to_string (algorithm);
return s ? s : "?";
}
/* This is a limited version of the one in 1.9 but it should be /* This is a limited version of the one in 1.9 but it should be
sufficient here. */ sufficient here. */
void void
@ -297,6 +339,7 @@ agent_release_card_info (struct agent_card_info_s *info)
return; return;
xfree (info->serialno); info->serialno = NULL; xfree (info->serialno); info->serialno = NULL;
xfree (info->apptype); info->apptype = NULL;
xfree (info->disp_name); info->disp_name = NULL; xfree (info->disp_name); info->disp_name = NULL;
xfree (info->disp_lang); info->disp_lang = NULL; xfree (info->disp_lang); info->disp_lang = NULL;
xfree (info->pubkey_url); info->pubkey_url = NULL; xfree (info->pubkey_url); info->pubkey_url = NULL;
@ -448,7 +491,7 @@ open_card (void)
} }
ready: ready:
app->initialized = 1; app->ref_count = 1;
current_app = app; current_app = app;
if (is_status_enabled () ) if (is_status_enabled () )
{ {
@ -629,6 +672,15 @@ store_serialno (const char *line)
return p; return p;
} }
/* Return a new malloced string by unescaping the string S. Escaping
is percent escaping and '+'/space mapping. A binary nul will
silently be replaced by a 0xFF. Function returns NULL to indicate
an out of memory status. */
static char *
unescape_status_string (const unsigned char *s)
{
return unescape_percent_string (s);
}
static assuan_error_t static assuan_error_t
@ -649,6 +701,13 @@ learn_status_cb (void *opaque, const char *line)
{ {
xfree (parm->serialno); xfree (parm->serialno);
parm->serialno = store_serialno (line); parm->serialno = store_serialno (line);
parm->is_v2 = (strlen (parm->serialno) >= 16
&& xtoi_2 (parm->serialno+12) >= 2 );
}
else if (keywordlen == 7 && !memcmp (keyword, "APPTYPE", keywordlen))
{
xfree (parm->apptype);
parm->apptype = unescape_status_string (line);
} }
else if (keywordlen == 9 && !memcmp (keyword, "DISP-NAME", keywordlen)) else if (keywordlen == 9 && !memcmp (keyword, "DISP-NAME", keywordlen))
{ {
@ -761,6 +820,18 @@ learn_status_cb (void *opaque, const char *line)
xfree (parm->private_do[no]); xfree (parm->private_do[no]);
parm->private_do[no] = unescape_percent_string (line); parm->private_do[no] = unescape_percent_string (line);
} }
else if (keywordlen == 8 && !memcmp (keyword, "KEY-ATTR", keywordlen))
{
int keyno, algo, nbits;
sscanf (line, "%d %d %d", &keyno, &algo, &nbits);
keyno--;
if (keyno >= 0 && keyno < DIM (parm->key_attr))
{
parm->key_attr[keyno].algo = algo;
parm->key_attr[keyno].nbits = nbits;
}
}
return 0; return 0;
} }
@ -801,7 +872,7 @@ agent_learn (struct agent_card_info_s *info)
send_status_info (&ctrl, "SERIALNO", send_status_info (&ctrl, "SERIALNO",
serial, strlen(serial), NULL, 0); serial, strlen(serial), NULL, 0);
xfree (serial); xfree (serial);
rc = app->fnc.learn_status (app, &ctrl); rc = app->fnc.learn_status (app, &ctrl, 0);
} }
} }
@ -1132,7 +1203,7 @@ genkey_status_cb (void *opaque, const char *line)
/* Send a GENKEY command to the SCdaemon. */ /* Send a GENKEY command to the SCdaemon. */
int int
agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force, agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force,
const char *serialno) const char *serialno, u32 *createtime)
{ {
app_t app; app_t app;
char line[ASSUAN_LINELENGTH]; char line[ASSUAN_LINELENGTH];
@ -1166,6 +1237,7 @@ agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force,
ctrl.status_cb_arg = info; ctrl.status_cb_arg = info;
rc = app->fnc.genkey (app, &ctrl, line, rc = app->fnc.genkey (app, &ctrl, line,
force? 1:0, force? 1:0,
*createtime,
pin_cb, &parm); pin_cb, &parm);
} }

View File

@ -21,15 +21,21 @@
#ifdef ENABLE_CARD_SUPPORT #ifdef ENABLE_CARD_SUPPORT
/* /*
Note, that most card related code has been taken from 1.9.x branch Note, that most card related code has been taken from 2.x branch
and is maintained over there if at all possible. Thus, if you make and is maintained over there if at all possible. Thus, if you make
changes here, please check that a similar change has been commited changes here, please check that a similar change has been commited
to the 1.9.x branch. to the 2.x branch.
*/ */
/* We don't use libgcrypt but the shared codes uses a function type
from libgcrypt. Thus we have to provide this type here. */
typedef void (*gcry_handler_progress_t) (void *, const char *, int, int, int);
/* Object to hold all info about the card. */
struct agent_card_info_s { struct agent_card_info_s {
int error; /* private. */ int error; /* private. */
char *apptype; /* Malloced application type string. */
char *serialno; /* malloced hex string. */ char *serialno; /* malloced hex string. */
char *disp_name; /* malloced. */ char *disp_name; /* malloced. */
char *disp_lang; /* malloced. */ char *disp_lang; /* malloced. */
@ -56,8 +62,13 @@ struct agent_card_info_s {
int chv1_cached; /* True if a PIN is not required for each int chv1_cached; /* True if a PIN is not required for each
signing. Note that the gpg-agent might cache signing. Note that the gpg-agent might cache
it anyway. */ it anyway. */
int is_v2; /* True if this is a v2 card. */
int chvmaxlen[3]; /* Maximum allowed length of a CHV. */ int chvmaxlen[3]; /* Maximum allowed length of a CHV. */
int chvretry[3]; /* Allowed retries for the CHV; 0 = blocked. */ int chvretry[3]; /* Allowed retries for the CHV; 0 = blocked. */
struct { /* Array with key attributes. */
int algo; /* Algorithm identifier. */
unsigned int nbits; /* Supported keysize. */
} key_attr[3];
}; };
struct agent_card_genkey_s { struct agent_card_genkey_s {
@ -147,14 +158,24 @@ void card_set_reader_port (const char *portstr);
char *serialno_and_fpr_from_sk (const unsigned char *sn, size_t snlen, char *serialno_and_fpr_from_sk (const unsigned char *sn, size_t snlen,
PKT_secret_key *sk); PKT_secret_key *sk);
void send_status_info (ctrl_t ctrl, const char *keyword, ...); void send_status_info (ctrl_t ctrl, const char *keyword, ...);
void send_status_direct (ctrl_t ctrl, const char *keyword, const char *args);
void gcry_md_hash_buffer (int algo, void *digest, void gcry_md_hash_buffer (int algo, void *digest,
const void *buffer, size_t length); const void *buffer, size_t length);
const char *gcry_md_algo_name (int algorithm);
void log_printf (const char *fmt, ...); void log_printf (const char *fmt, ...);
void log_printhex (const char *text, const void *buffer, size_t length); void log_printhex (const char *text, const void *buffer, size_t length);
#define GCRY_MD_SHA1 DIGEST_ALGO_SHA1 #define GCRY_MD_SHA1 DIGEST_ALGO_SHA1
#define GCRY_MD_RMD160 DIGEST_ALGO_RMD160 #define GCRY_MD_RMD160 DIGEST_ALGO_RMD160
#define GCRY_MD_SHA256 DIGEST_ALGO_SHA256
#define GCRY_MD_SHA384 DIGEST_ALGO_SHA384
#define GCRY_MD_SHA512 DIGEST_ALGO_SHA512
#define GCRY_MD_SHA224 DIGEST_ALGO_SHA224
void gcry_mpi_release (MPI a);
MPI gcry_mpi_set_opaque (MPI a, void *p, unsigned int len);
void card_close (void); void card_close (void);
@ -183,7 +204,7 @@ int agent_scd_writekey (int keyno, const char *serialno,
/* Send a GENKEY command to the SCdaemon. */ /* Send a GENKEY command to the SCdaemon. */
int agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force, int agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force,
const char *serialno); const char *serialno, u32 *createtime);
/* Send a PKSIGN command to the SCdaemon. */ /* Send a PKSIGN command to the SCdaemon. */
int agent_scd_pksign (const char *keyid, int hashalgo, int agent_scd_pksign (const char *keyid, int hashalgo,

File diff suppressed because it is too large Load Diff

View File

@ -80,6 +80,9 @@ typedef struct ccid_driver_s *ccid_driver_t;
int ccid_set_debug_level (int level); int ccid_set_debug_level (int level);
char *ccid_get_reader_list (void); char *ccid_get_reader_list (void);
int ccid_open_reader (ccid_driver_t *handle, const char *readerid); int ccid_open_reader (ccid_driver_t *handle, const char *readerid);
int ccid_set_progress_cb (ccid_driver_t handle,
void (*cb)(void *, const char *, int, int, int),
void *cb_arg);
int ccid_shutdown_reader (ccid_driver_t handle); int ccid_shutdown_reader (ccid_driver_t handle);
int ccid_close_reader (ccid_driver_t handle); int ccid_close_reader (ccid_driver_t handle);
int ccid_get_atr (ccid_driver_t handle, int ccid_get_atr (ccid_driver_t handle,

View File

@ -1932,6 +1932,7 @@ main (int argc, char **argv )
#else #else
opt.pcsc_driver = "libpcsclite.so"; opt.pcsc_driver = "libpcsclite.so";
#endif #endif
opt.disable_keypad = 1; /* No keypad support; use gpg2 instead. */
#endif /*ENABLE_CARD_SUPPORT*/ #endif /*ENABLE_CARD_SUPPORT*/
/* check whether we have a config file on the commandline */ /* check whether we have a config file on the commandline */

View File

@ -1,5 +1,5 @@
/* iso7816.c - ISO 7816 commands /* iso7816.c - ISO 7816 commands
* Copyright (C) 2003, 2004 Free Software Foundation, Inc. * Copyright (C) 2003, 2004, 2008, 2009 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -45,9 +45,9 @@
#define CMD_SELECT_FILE 0xA4 #define CMD_SELECT_FILE 0xA4
#define CMD_VERIFY 0x20 #define CMD_VERIFY ISO7816_VERIFY
#define CMD_CHANGE_REFERENCE_DATA 0x24 #define CMD_CHANGE_REFERENCE_DATA ISO7816_CHANGE_REFERENCE_DATA
#define CMD_RESET_RETRY_COUNTER 0x2C #define CMD_RESET_RETRY_COUNTER ISO7816_RESET_RETRY_COUNTER
#define CMD_GET_DATA 0xCA #define CMD_GET_DATA 0xCA
#define CMD_PUT_DATA 0xDA #define CMD_PUT_DATA 0xDA
#define CMD_MSE 0x22 #define CMD_MSE 0x22
@ -66,7 +66,10 @@ map_sw (int sw)
switch (sw) switch (sw)
{ {
case SW_EEPROM_FAILURE: ec = GPG_ERR_HARDWARE; break; case SW_EEPROM_FAILURE: ec = GPG_ERR_HARDWARE; break;
case SW_TERM_STATE: ec = GPG_ERR_CARD; break;
case SW_WRONG_LENGTH: ec = GPG_ERR_INV_VALUE; break; case SW_WRONG_LENGTH: ec = GPG_ERR_INV_VALUE; break;
case SW_SM_NOT_SUP: ec = GPG_ERR_NOT_SUPPORTED; break;
case SW_CC_NOT_SUP: ec = GPG_ERR_NOT_SUPPORTED; break;
case SW_CHV_WRONG: ec = GPG_ERR_BAD_PIN; break; case SW_CHV_WRONG: ec = GPG_ERR_BAD_PIN; break;
case SW_CHV_BLOCKED: ec = GPG_ERR_PIN_BLOCKED; break; case SW_CHV_BLOCKED: ec = GPG_ERR_PIN_BLOCKED; break;
case SW_USE_CONDITIONS: ec = GPG_ERR_USE_CONDITIONS; break; case SW_USE_CONDITIONS: ec = GPG_ERR_USE_CONDITIONS; break;
@ -93,6 +96,7 @@ map_sw (int sw)
case SW_HOST_GENERAL_ERROR: ec = GPG_ERR_GENERAL; break; case SW_HOST_GENERAL_ERROR: ec = GPG_ERR_GENERAL; break;
case SW_HOST_NO_READER: ec = GPG_ERR_ENODEV; break; case SW_HOST_NO_READER: ec = GPG_ERR_ENODEV; break;
case SW_HOST_ABORTED: ec = GPG_ERR_CANCELED; break; case SW_HOST_ABORTED: ec = GPG_ERR_CANCELED; break;
case SW_HOST_NO_KEYPAD: ec = GPG_ERR_NOT_SUPPORTED; break;
default: default:
if ((sw & 0x010000)) if ((sw & 0x010000))
@ -122,12 +126,15 @@ iso7816_map_sw (int sw)
requested application ID. The function can't be used to enumerate requested application ID. The function can't be used to enumerate
AIDs and won't return the AID on success. The return value is 0 AIDs and won't return the AID on success. The return value is 0
for okay or a GPG error code. Note that ISO error codes are for okay or a GPG error code. Note that ISO error codes are
internally mapped. */ internally mapped. Bit 0 of FLAGS should be set if the card does
not understand P2=0xC0. */
gpg_error_t gpg_error_t
iso7816_select_application (int slot, const char *aid, size_t aidlen) iso7816_select_application (int slot, const char *aid, size_t aidlen,
unsigned int flags)
{ {
int sw; int sw;
sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, 0, aidlen, aid); sw = apdu_send_simple (slot, 0, 0x00, CMD_SELECT_FILE, 4,
(flags&1)? 0 :0x0c, aidlen, aid);
return map_sw (sw); return map_sw (sw);
} }
@ -152,7 +159,7 @@ iso7816_select_file (int slot, int tag, int is_dir,
{ {
p0 = (tag == 0x3F00)? 0: is_dir? 1:2; p0 = (tag == 0x3F00)? 0: is_dir? 1:2;
p1 = 0x0c; /* No FC return. */ p1 = 0x0c; /* No FC return. */
sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, sw = apdu_send_simple (slot, 0, 0x00, CMD_SELECT_FILE,
p0, p1, 2, (char*)tagbuf ); p0, p1, 2, (char*)tagbuf );
return map_sw (sw); return map_sw (sw);
} }
@ -188,7 +195,7 @@ iso7816_select_path (int slot, const unsigned short *path, size_t pathlen,
p0 = 0x08; p0 = 0x08;
p1 = 0x0c; /* No FC return. */ p1 = 0x0c; /* No FC return. */
sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, sw = apdu_send_simple (slot, 0, 0x00, CMD_SELECT_FILE,
p0, p1, buflen, (char*)buffer ); p0, p1, buflen, (char*)buffer );
return map_sw (sw); return map_sw (sw);
} }
@ -206,7 +213,7 @@ iso7816_list_directory (int slot, int list_dirs,
*result = NULL; *result = NULL;
*resultlen = 0; *resultlen = 0;
sw = apdu_send (slot, 0x80, 0xAA, list_dirs? 1:2, 0, -1, NULL, sw = apdu_send (slot, 0, 0x80, 0xAA, list_dirs? 1:2, 0, -1, NULL,
result, resultlen); result, resultlen);
if (sw != SW_SUCCESS) if (sw != SW_SUCCESS)
{ {
@ -219,27 +226,101 @@ iso7816_list_directory (int slot, int list_dirs,
} }
/* This funcion sends an already formatted APDU to the card. With
HANDLE_MORE set to true a MORE DATA status will be handled
internally. The return value is a gpg error code (i.e. a mapped
status word). This is basically the same as apdu_send_direct but
it maps the status word and does not return it in the result
buffer. */
gpg_error_t
iso7816_apdu_direct (int slot, const void *apdudata, size_t apdudatalen,
int handle_more,
unsigned char **result, size_t *resultlen)
{
int sw;
if (!result || !resultlen)
return gpg_error (GPG_ERR_INV_VALUE);
*result = NULL;
*resultlen = 0;
sw = apdu_send_direct (slot, 0, apdudata, apdudatalen, handle_more,
result, resultlen);
if (!sw)
{
if (*resultlen < 2)
sw = SW_HOST_GENERAL_ERROR;
else
{
sw = ((*result)[*resultlen-2] << 8) | (*result)[*resultlen-1];
(*resultlen)--;
(*resultlen)--;
}
}
if (sw != SW_SUCCESS)
{
/* Make sure that pending buffers are released. */
xfree (*result);
*result = NULL;
*resultlen = 0;
}
return map_sw (sw);
}
/* Check whether the reader supports the ISO command code COMMAND on
the keypad. Returns 0 on success. */
gpg_error_t
iso7816_check_keypad (int slot, int command, iso7816_pininfo_t *pininfo)
{
int sw;
sw = apdu_check_keypad (slot, command,
pininfo->mode, pininfo->minlen, pininfo->maxlen,
pininfo->padlen);
return iso7816_map_sw (sw);
}
/* Perform a VERIFY command on SLOT using the card holder verification
vector CHVNO with a CHV of lenght CHVLEN. With PININFO non-NULL
the keypad of the reader will be used. Returns 0 on success. */
gpg_error_t
iso7816_verify_kp (int slot, int chvno, const char *chv, size_t chvlen,
iso7816_pininfo_t *pininfo)
{
int sw;
if (pininfo && pininfo->mode)
sw = apdu_send_simple_kp (slot, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv,
pininfo->mode,
pininfo->minlen,
pininfo->maxlen,
pininfo->padlen);
else
sw = apdu_send_simple (slot, 0, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv);
return map_sw (sw);
}
/* Perform a VERIFY command on SLOT using the card holder verification /* Perform a VERIFY command on SLOT using the card holder verification
vector CHVNO with a CHV of lenght CHVLEN. Returns 0 on success. */ vector CHVNO with a CHV of lenght CHVLEN. Returns 0 on success. */
gpg_error_t gpg_error_t
iso7816_verify (int slot, int chvno, const char *chv, size_t chvlen) iso7816_verify (int slot, int chvno, const char *chv, size_t chvlen)
{ {
int sw; return iso7816_verify_kp (slot, chvno, chv, chvlen, NULL);
sw = apdu_send_simple (slot, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv);
return map_sw (sw);
} }
/* Perform a CHANGE_REFERENCE_DATA command on SLOT for the card holder /* Perform a CHANGE_REFERENCE_DATA command on SLOT for the card holder
verification vector CHVNO. If the OLDCHV is NULL (and OLDCHVLEN verification vector CHVNO. If the OLDCHV is NULL (and OLDCHVLEN
0), a "change reference data" is done, otherwise an "exchange 0), a "change reference data" is done, otherwise an "exchange
reference data". The new reference data is expected in NEWCHV of reference data". The new reference data is expected in NEWCHV of
length NEWCHVLEN. */ length NEWCHVLEN. With PININFO non-NULL the keypad of the reader
will be used. */
gpg_error_t gpg_error_t
iso7816_change_reference_data (int slot, int chvno, iso7816_change_reference_data_kp (int slot, int chvno,
const char *oldchv, size_t oldchvlen, const char *oldchv, size_t oldchvlen,
const char *newchv, size_t newchvlen) const char *newchv, size_t newchvlen,
iso7816_pininfo_t *pininfo)
{ {
int sw; int sw;
char *buf; char *buf;
@ -256,44 +337,109 @@ iso7816_change_reference_data (int slot, int chvno,
memcpy (buf, oldchv, oldchvlen); memcpy (buf, oldchv, oldchvlen);
memcpy (buf+oldchvlen, newchv, newchvlen); memcpy (buf+oldchvlen, newchv, newchvlen);
sw = apdu_send_simple (slot, 0x00, CMD_CHANGE_REFERENCE_DATA, if (pininfo && pininfo->mode)
sw = apdu_send_simple_kp (slot, 0x00, CMD_CHANGE_REFERENCE_DATA,
oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf,
pininfo->mode,
pininfo->minlen,
pininfo->maxlen,
pininfo->padlen);
else
sw = apdu_send_simple (slot, 0, 0x00, CMD_CHANGE_REFERENCE_DATA,
oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf); oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf);
xfree (buf); xfree (buf);
return map_sw (sw); return map_sw (sw);
} }
/* Perform a CHANGE_REFERENCE_DATA command on SLOT for the card holder
verification vector CHVNO. If the OLDCHV is NULL (and OLDCHVLEN
0), a "change reference data" is done, otherwise an "exchange
reference data". The new reference data is expected in NEWCHV of
length NEWCHVLEN. */
gpg_error_t gpg_error_t
iso7816_reset_retry_counter (int slot, int chvno, iso7816_change_reference_data (int slot, int chvno,
const char *oldchv, size_t oldchvlen,
const char *newchv, size_t newchvlen) const char *newchv, size_t newchvlen)
{
return iso7816_change_reference_data_kp (slot, chvno, oldchv, oldchvlen,
newchv, newchvlen, NULL);
}
gpg_error_t
iso7816_reset_retry_counter_kp (int slot, int chvno,
const char *newchv, size_t newchvlen,
iso7816_pininfo_t *pininfo)
{ {
int sw; int sw;
if (!newchv || !newchvlen ) if (!newchv || !newchvlen )
return gpg_error (GPG_ERR_INV_VALUE); return gpg_error (GPG_ERR_INV_VALUE);
sw = apdu_send_simple (slot, 0x00, CMD_RESET_RETRY_COUNTER, /* FIXME: The keypad mode has not yet been tested. */
if (pininfo && pininfo->mode)
sw = apdu_send_simple_kp (slot, 0x00, CMD_RESET_RETRY_COUNTER,
2, chvno, newchvlen, newchv,
pininfo->mode,
pininfo->minlen,
pininfo->maxlen,
pininfo->padlen);
else
sw = apdu_send_simple (slot, 0, 0x00, CMD_RESET_RETRY_COUNTER,
2, chvno, newchvlen, newchv); 2, chvno, newchvlen, newchv);
return map_sw (sw); return map_sw (sw);
} }
gpg_error_t
iso7816_reset_retry_counter_with_rc (int slot, int chvno,
const char *data, size_t datalen)
{
int sw;
if (!data || !datalen )
return gpg_error (GPG_ERR_INV_VALUE);
sw = apdu_send_simple (slot, 0, 0x00, CMD_RESET_RETRY_COUNTER,
0, chvno, datalen, data);
return map_sw (sw);
}
gpg_error_t
iso7816_reset_retry_counter (int slot, int chvno,
const char *newchv, size_t newchvlen)
{
return iso7816_reset_retry_counter_kp (slot, chvno, newchv, newchvlen, NULL);
}
/* Perform a GET DATA command requesting TAG and storing the result in /* Perform a GET DATA command requesting TAG and storing the result in
a newly allocated buffer at the address passed by RESULT. Return a newly allocated buffer at the address passed by RESULT. Return
the length of this data at the address of RESULTLEN. */ the length of this data at the address of RESULTLEN. */
gpg_error_t gpg_error_t
iso7816_get_data (int slot, int tag, iso7816_get_data (int slot, int extended_mode, int tag,
unsigned char **result, size_t *resultlen) unsigned char **result, size_t *resultlen)
{ {
int sw; int sw;
int le;
if (!result || !resultlen) if (!result || !resultlen)
return gpg_error (GPG_ERR_INV_VALUE); return gpg_error (GPG_ERR_INV_VALUE);
*result = NULL; *result = NULL;
*resultlen = 0; *resultlen = 0;
sw = apdu_send (slot, 0x00, CMD_GET_DATA, if (extended_mode > 0 && extended_mode < 256)
((tag >> 8) & 0xff), (tag & 0xff), -1, NULL, le = 65534; /* Not 65535 in case it is used as some special flag. */
else if (extended_mode > 0)
le = extended_mode;
else
le = 256;
sw = apdu_send_le (slot, extended_mode, 0x00, CMD_GET_DATA,
((tag >> 8) & 0xff), (tag & 0xff), -1, NULL, le,
result, resultlen); result, resultlen);
if (sw != SW_SUCCESS) if (sw != SW_SUCCESS)
{ {
@ -309,14 +455,29 @@ iso7816_get_data (int slot, int tag,
/* Perform a PUT DATA command on card in SLOT. Write DATA of length /* Perform a PUT DATA command on card in SLOT. Write DATA of length
DATALEN to TAG. */ DATALEN to TAG. EXTENDED_MODE controls whether extended length
headers or command chaining is used instead of single length
bytes. */
gpg_error_t gpg_error_t
iso7816_put_data (int slot, int tag, iso7816_put_data (int slot, int extended_mode, int tag,
const unsigned char *data, size_t datalen) const unsigned char *data, size_t datalen)
{ {
int sw; int sw;
sw = apdu_send_simple (slot, 0x00, CMD_PUT_DATA, sw = apdu_send_simple (slot, extended_mode, 0x00, CMD_PUT_DATA,
((tag >> 8) & 0xff), (tag & 0xff),
datalen, (const char*)data);
return map_sw (sw);
}
/* Same as iso7816_put_data but uses an odd instruction byte. */
gpg_error_t
iso7816_put_data_odd (int slot, int extended_mode, int tag,
const unsigned char *data, size_t datalen)
{
int sw;
sw = apdu_send_simple (slot, extended_mode, 0x00, CMD_PUT_DATA+1,
((tag >> 8) & 0xff), (tag & 0xff), ((tag >> 8) & 0xff), (tag & 0xff),
datalen, (const char*)data); datalen, (const char*)data);
return map_sw (sw); return map_sw (sw);
@ -335,7 +496,7 @@ iso7816_manage_security_env (int slot, int p1, int p2,
if (p1 < 0 || p1 > 255 || p2 < 0 || p2 > 255 ) if (p1 < 0 || p1 > 255 || p2 < 0 || p2 > 255 )
return gpg_error (GPG_ERR_INV_VALUE); return gpg_error (GPG_ERR_INV_VALUE);
sw = apdu_send_simple (slot, 0x00, CMD_MSE, p1, p2, sw = apdu_send_simple (slot, 0, 0x00, CMD_MSE, p1, p2,
data? datalen : -1, (const char*)data); data? datalen : -1, (const char*)data);
return map_sw (sw); return map_sw (sw);
} }
@ -344,9 +505,10 @@ iso7816_manage_security_env (int slot, int p1, int p2,
/* Perform the security operation COMPUTE DIGITAL SIGANTURE. On /* Perform the security operation COMPUTE DIGITAL SIGANTURE. On
success 0 is returned and the data is availavle in a newly success 0 is returned and the data is availavle in a newly
allocated buffer stored at RESULT with its length stored at allocated buffer stored at RESULT with its length stored at
RESULTLEN. */ RESULTLEN. For LE see do_generate_keypair. */
gpg_error_t gpg_error_t
iso7816_compute_ds (int slot, const unsigned char *data, size_t datalen, iso7816_compute_ds (int slot, int extended_mode,
const unsigned char *data, size_t datalen, int le,
unsigned char **result, size_t *resultlen) unsigned char **result, size_t *resultlen)
{ {
int sw; int sw;
@ -356,7 +518,15 @@ iso7816_compute_ds (int slot, const unsigned char *data, size_t datalen,
*result = NULL; *result = NULL;
*resultlen = 0; *resultlen = 0;
sw = apdu_send (slot, 0x00, CMD_PSO, 0x9E, 0x9A, datalen, (const char*)data, if (!extended_mode)
le = 256; /* Ignore provided Le and use what apdu_send uses. */
else if (le >= 0 && le < 256)
le = 256;
sw = apdu_send_le (slot, extended_mode,
0x00, CMD_PSO, 0x9E, 0x9A,
datalen, (const char*)data,
le,
result, resultlen); result, resultlen);
if (sw != SW_SUCCESS) if (sw != SW_SUCCESS)
{ {
@ -377,7 +547,8 @@ iso7816_compute_ds (int slot, const unsigned char *data, size_t datalen,
and the plaintext is available in a newly allocated buffer stored and the plaintext is available in a newly allocated buffer stored
at RESULT with its length stored at RESULTLEN. */ at RESULT with its length stored at RESULTLEN. */
gpg_error_t gpg_error_t
iso7816_decipher (int slot, const unsigned char *data, size_t datalen, iso7816_decipher (int slot, int extended_mode,
const unsigned char *data, size_t datalen,
int padind, unsigned char **result, size_t *resultlen) int padind, unsigned char **result, size_t *resultlen)
{ {
int sw; int sw;
@ -397,14 +568,16 @@ iso7816_decipher (int slot, const unsigned char *data, size_t datalen,
*buf = padind; /* Padding indicator. */ *buf = padind; /* Padding indicator. */
memcpy (buf+1, data, datalen); memcpy (buf+1, data, datalen);
sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86, sw = apdu_send (slot, extended_mode,
0x00, CMD_PSO, 0x80, 0x86,
datalen+1, (char*)buf, datalen+1, (char*)buf,
result, resultlen); result, resultlen);
xfree (buf); xfree (buf);
} }
else else
{ {
sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86, sw = apdu_send (slot, extended_mode,
0x00, CMD_PSO, 0x80, 0x86,
datalen, (const char *)data, datalen, (const char *)data,
result, resultlen); result, resultlen);
} }
@ -421,9 +594,11 @@ iso7816_decipher (int slot, const unsigned char *data, size_t datalen,
} }
/* For LE see do_generate_keypair. */
gpg_error_t gpg_error_t
iso7816_internal_authenticate (int slot, iso7816_internal_authenticate (int slot, int extended_mode,
const unsigned char *data, size_t datalen, const unsigned char *data, size_t datalen,
int le,
unsigned char **result, size_t *resultlen) unsigned char **result, size_t *resultlen)
{ {
int sw; int sw;
@ -433,8 +608,16 @@ iso7816_internal_authenticate (int slot,
*result = NULL; *result = NULL;
*resultlen = 0; *resultlen = 0;
sw = apdu_send (slot, 0x00, CMD_INTERNAL_AUTHENTICATE, 0, 0, if (!extended_mode)
datalen, (const char*)data, result, resultlen); le = 256; /* Ignore provided Le and use what apdu_send uses. */
else if (le >= 0 && le < 256)
le = 256;
sw = apdu_send_le (slot, extended_mode,
0x00, CMD_INTERNAL_AUTHENTICATE, 0, 0,
datalen, (const char*)data,
le,
result, resultlen);
if (sw != SW_SUCCESS) if (sw != SW_SUCCESS)
{ {
/* Make sure that pending buffers are released. */ /* Make sure that pending buffers are released. */
@ -448,9 +631,14 @@ iso7816_internal_authenticate (int slot,
} }
/* LE is the expected return length. This is usually 0 except if
extended length mode is used and more than 256 byte will be
returned. In that case a value of -1 uses a large default
(e.g. 4096 bytes), a value larger 256 used that value. */
static gpg_error_t static gpg_error_t
do_generate_keypair (int slot, int readonly, do_generate_keypair (int slot, int extended_mode, int readonly,
const unsigned char *data, size_t datalen, const unsigned char *data, size_t datalen,
int le,
unsigned char **result, size_t *resultlen) unsigned char **result, size_t *resultlen)
{ {
int sw; int sw;
@ -460,8 +648,11 @@ do_generate_keypair (int slot, int readonly,
*result = NULL; *result = NULL;
*resultlen = 0; *resultlen = 0;
sw = apdu_send (slot, 0x00, CMD_GENERATE_KEYPAIR, readonly? 0x81:0x80, 0, sw = apdu_send_le (slot, extended_mode,
datalen, (const char*)data, result, resultlen); 0x00, CMD_GENERATE_KEYPAIR, readonly? 0x81:0x80, 0,
datalen, (const char*)data,
le >= 0 && le < 256? 256:le,
result, resultlen);
if (sw != SW_SUCCESS) if (sw != SW_SUCCESS)
{ {
/* Make sure that pending buffers are released. */ /* Make sure that pending buffers are released. */
@ -476,20 +667,24 @@ do_generate_keypair (int slot, int readonly,
gpg_error_t gpg_error_t
iso7816_generate_keypair (int slot, iso7816_generate_keypair (int slot, int extended_mode,
const unsigned char *data, size_t datalen, const unsigned char *data, size_t datalen,
int le,
unsigned char **result, size_t *resultlen) unsigned char **result, size_t *resultlen)
{ {
return do_generate_keypair (slot, 0, data, datalen, result, resultlen); return do_generate_keypair (slot, extended_mode, 0,
data, datalen, le, result, resultlen);
} }
gpg_error_t gpg_error_t
iso7816_read_public_key (int slot, iso7816_read_public_key (int slot, int extended_mode,
const unsigned char *data, size_t datalen, const unsigned char *data, size_t datalen,
int le,
unsigned char **result, size_t *resultlen) unsigned char **result, size_t *resultlen)
{ {
return do_generate_keypair (slot, 1, data, datalen, result, resultlen); return do_generate_keypair (slot, extended_mode, 1,
data, datalen, le, result, resultlen);
} }
@ -508,8 +703,8 @@ iso7816_get_challenge (int slot, int length, unsigned char *buffer)
{ {
result = NULL; result = NULL;
n = length > 254? 254 : length; n = length > 254? 254 : length;
sw = apdu_send_le (slot, 0x00, CMD_GET_CHALLENGE, 0, 0, -1, NULL, sw = apdu_send_le (slot, 0,
n, 0x00, CMD_GET_CHALLENGE, 0, 0, -1, NULL, n,
&result, &resultlen); &result, &resultlen);
if (sw != SW_SUCCESS) if (sw != SW_SUCCESS)
{ {
@ -557,21 +752,14 @@ iso7816_read_binary (int slot, size_t offset, size_t nmax,
{ {
buffer = NULL; buffer = NULL;
bufferlen = 0; bufferlen = 0;
/* Note, that we to set N to 254 due to problems either with the n = read_all? 0 : nmax;
ccid driver or some TCOS cards. It actually should be 0 sw = apdu_send_le (slot, 0, 0x00, CMD_READ_BINARY,
which is the official ISO value to read a variable length
object. */
if (read_all || nmax > 254)
n = 254;
else
n = nmax;
sw = apdu_send_le (slot, 0x00, CMD_READ_BINARY,
((offset>>8) & 0xff), (offset & 0xff) , -1, NULL, ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL,
n, &buffer, &bufferlen); n, &buffer, &bufferlen);
if ( SW_EXACT_LENGTH_P(sw) ) if ( SW_EXACT_LENGTH_P(sw) )
{ {
n = (sw & 0x00ff); n = (sw & 0x00ff);
sw = apdu_send_le (slot, 0x00, CMD_READ_BINARY, sw = apdu_send_le (slot, 0, 0x00, CMD_READ_BINARY,
((offset>>8) & 0xff), (offset & 0xff) , -1, NULL, ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL,
n, &buffer, &bufferlen); n, &buffer, &bufferlen);
} }
@ -598,7 +786,7 @@ iso7816_read_binary (int slot, size_t offset, size_t nmax,
unsigned char *p = xtryrealloc (*result, *resultlen + bufferlen); unsigned char *p = xtryrealloc (*result, *resultlen + bufferlen);
if (!p) if (!p)
{ {
gpg_error_t err = gpg_error_from_errno (errno); gpg_error_t err = gpg_error_from_syserror ();
xfree (buffer); xfree (buffer);
xfree (*result); xfree (*result);
*result = NULL; *result = NULL;
@ -658,13 +846,11 @@ iso7816_read_record (int slot, int recno, int reccount, int short_ef,
buffer = NULL; buffer = NULL;
bufferlen = 0; bufferlen = 0;
/* Fixme: Either the ccid driver or the TCOS cards have problems sw = apdu_send_le (slot, 0, 0x00, CMD_READ_RECORD,
with an Le of 0. */
sw = apdu_send_le (slot, 0x00, CMD_READ_RECORD,
recno, recno,
short_ef? short_ef : 0x04, short_ef? short_ef : 0x04,
-1, NULL, -1, NULL,
254, &buffer, &bufferlen); 0, &buffer, &bufferlen);
if (sw != SW_SUCCESS && sw != SW_EOF_REACHED) if (sw != SW_SUCCESS && sw != SW_EOF_REACHED)
{ {

View File

@ -26,10 +26,30 @@
#include "cardglue.h" #include "cardglue.h"
#endif #endif
/* Command codes used by iso7816_check_keypad. */
#define ISO7816_VERIFY 0x20
#define ISO7816_CHANGE_REFERENCE_DATA 0x24
#define ISO7816_RESET_RETRY_COUNTER 0x2C
/* Information to be passed to keypad equipped readers. See
ccid-driver.c for details. */
struct iso7816_pininfo_s
{
int mode; /* A mode of 0 means: Do not use the keypad. */
int minlen;
int maxlen;
int padlen;
int padchar;
};
typedef struct iso7816_pininfo_s iso7816_pininfo_t;
gpg_error_t iso7816_map_sw (int sw); gpg_error_t iso7816_map_sw (int sw);
gpg_error_t iso7816_select_application (int slot, gpg_error_t iso7816_select_application (int slot,
const char *aid, size_t aidlen); const char *aid, size_t aidlen,
unsigned int flags);
gpg_error_t iso7816_select_file (int slot, int tag, int is_dir, gpg_error_t iso7816_select_file (int slot, int tag, int is_dir,
unsigned char **result, size_t *resultlen); unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_select_path (int slot, gpg_error_t iso7816_select_path (int slot,
@ -37,35 +57,61 @@ gpg_error_t iso7816_select_path (int slot,
unsigned char **result, size_t *resultlen); unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_list_directory (int slot, int list_dirs, gpg_error_t iso7816_list_directory (int slot, int list_dirs,
unsigned char **result, size_t *resultlen); unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_apdu_direct (int slot,
const void *apdudata, size_t apdudatalen,
int handle_more,
unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_check_keypad (int slot, int command,
iso7816_pininfo_t *pininfo);
gpg_error_t iso7816_verify (int slot, gpg_error_t iso7816_verify (int slot,
int chvno, const char *chv, size_t chvlen); int chvno, const char *chv, size_t chvlen);
gpg_error_t iso7816_verify_kp (int slot,
int chvno, const char *chv, size_t chvlen,
iso7816_pininfo_t *pininfo);
gpg_error_t iso7816_change_reference_data (int slot, int chvno, gpg_error_t iso7816_change_reference_data (int slot, int chvno,
const char *oldchv, size_t oldchvlen, const char *oldchv, size_t oldchvlen,
const char *newchv, size_t newchvlen); const char *newchv, size_t newchvlen);
gpg_error_t iso7816_change_reference_data_kp (int slot, int chvno,
const char *oldchv, size_t oldchvlen,
const char *newchv, size_t newchvlen,
iso7816_pininfo_t *pininfo);
gpg_error_t iso7816_reset_retry_counter (int slot, int chvno, gpg_error_t iso7816_reset_retry_counter (int slot, int chvno,
const char *newchv, size_t newchvlen); const char *newchv, size_t newchvlen);
gpg_error_t iso7816_get_data (int slot, int tag, gpg_error_t iso7816_reset_retry_counter_kp (int slot, int chvno,
const char *newchv,
size_t newchvlen,
iso7816_pininfo_t *pininfo);
gpg_error_t iso7816_reset_retry_counter_with_rc (int slot, int chvno,
const char *data,
size_t datalen);
gpg_error_t iso7816_get_data (int slot, int extended_mode, int tag,
unsigned char **result, size_t *resultlen); unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_put_data (int slot, int tag, gpg_error_t iso7816_put_data (int slot, int extended_mode, int tag,
const unsigned char *data, size_t datalen);
gpg_error_t iso7816_put_data_odd (int slot, int extended_mode, int tag,
const unsigned char *data, size_t datalen); const unsigned char *data, size_t datalen);
gpg_error_t iso7816_manage_security_env (int slot, int p1, int p2, gpg_error_t iso7816_manage_security_env (int slot, int p1, int p2,
const unsigned char *data, const unsigned char *data,
size_t datalen); size_t datalen);
gpg_error_t iso7816_compute_ds (int slot, gpg_error_t iso7816_compute_ds (int slot, int extended_mode,
const unsigned char *data, size_t datalen, const unsigned char *data, size_t datalen,
int le,
unsigned char **result, size_t *resultlen); unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_decipher (int slot, gpg_error_t iso7816_decipher (int slot, int extended_mode,
const unsigned char *data, size_t datalen, const unsigned char *data, size_t datalen,
int padind, int padind,
unsigned char **result, size_t *resultlen); unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_internal_authenticate (int slot, gpg_error_t iso7816_internal_authenticate (int slot, int extended_mode,
const unsigned char *data, size_t datalen, const unsigned char *data, size_t datalen,
int le,
unsigned char **result, size_t *resultlen); unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_generate_keypair (int slot, gpg_error_t iso7816_generate_keypair (int slot, int extended_mode,
const unsigned char *data, size_t datalen, const unsigned char *data, size_t datalen,
int le,
unsigned char **result, size_t *resultlen); unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_read_public_key (int slot, gpg_error_t iso7816_read_public_key (int slot, int extended_mode,
const unsigned char *data, size_t datalen, const unsigned char *data, size_t datalen,
int le,
unsigned char **result, size_t *resultlen); unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_get_challenge (int slot, gpg_error_t iso7816_get_challenge (int slot,
int length, unsigned char *buffer); int length, unsigned char *buffer);

View File

@ -3652,6 +3652,7 @@ menu_backsign(KBNODE pub_keyblock,KBNODE sec_keyblock)
PKT_public_key *main_pk; PKT_public_key *main_pk;
PKT_secret_key *main_sk,*sub_sk=NULL; PKT_secret_key *main_sk,*sub_sk=NULL;
KBNODE node; KBNODE node;
u32 timestamp;
assert(pub_keyblock->pkt->pkttype==PKT_PUBLIC_KEY); assert(pub_keyblock->pkt->pkttype==PKT_PUBLIC_KEY);
assert(sec_keyblock->pkt->pkttype==PKT_SECRET_KEY); assert(sec_keyblock->pkt->pkttype==PKT_SECRET_KEY);
@ -3661,6 +3662,10 @@ menu_backsign(KBNODE pub_keyblock,KBNODE sec_keyblock)
main_sk=copy_secret_key(NULL,sec_keyblock->pkt->pkt.secret_key); main_sk=copy_secret_key(NULL,sec_keyblock->pkt->pkt.secret_key);
keyid_from_pk(main_pk,NULL); keyid_from_pk(main_pk,NULL);
/* We use the same timestamp for all backsigs so that we don't
reveal information about the used machine. */
timestamp = make_timestamp ();
for(node=pub_keyblock;node;node=node->next) for(node=pub_keyblock;node;node=node->next)
{ {
PKT_public_key *sub_pk=NULL; PKT_public_key *sub_pk=NULL;
@ -3748,7 +3753,8 @@ menu_backsign(KBNODE pub_keyblock,KBNODE sec_keyblock)
set_next_passphrase(passphrase); set_next_passphrase(passphrase);
xfree(passphrase); xfree(passphrase);
rc=make_backsig(sig_pk->pkt->pkt.signature,main_pk,sub_pk,sub_sk); rc = make_backsig (sig_pk->pkt->pkt.signature, main_pk, sub_pk, sub_sk,
timestamp);
if(rc==0) if(rc==0)
{ {
PKT_signature *newsig; PKT_signature *newsig;

View File

@ -1,6 +1,6 @@
/* keygen.c - generate a key pair /* keygen.c - generate a key pair
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
* 2007 Free Software Foundation, Inc. * 2007, 2009 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -127,9 +127,11 @@ static int write_keyblock( IOBUF out, KBNODE node );
static int gen_card_key (int algo, int keyno, int is_primary, static int gen_card_key (int algo, int keyno, int is_primary,
KBNODE pub_root, KBNODE sec_root, KBNODE pub_root, KBNODE sec_root,
PKT_secret_key **ret_sk, PKT_secret_key **ret_sk,
u32 *timestamp,
u32 expireval, struct para_data_s *para); u32 expireval, struct para_data_s *para);
static int gen_card_key_with_backup (int algo, int keyno, int is_primary, static int gen_card_key_with_backup (int algo, int keyno, int is_primary,
KBNODE pub_root, KBNODE sec_root, KBNODE pub_root, KBNODE sec_root,
u32 timestamp,
u32 expireval, struct para_data_s *para, u32 expireval, struct para_data_s *para,
const char *backup_dir); const char *backup_dir);
@ -770,17 +772,20 @@ keygen_add_revkey(PKT_signature *sig, void *opaque)
return 0; return 0;
} }
/* Create a back-signature. If TIMESTAMP is not NULL, use it for the
signature creation time. */
int int
make_backsig (PKT_signature *sig, PKT_public_key *pk, make_backsig (PKT_signature *sig, PKT_public_key *pk,
PKT_public_key *sub_pk,PKT_secret_key *sub_sk) PKT_public_key *sub_pk, PKT_secret_key *sub_sk,
u32 timestamp)
{ {
PKT_signature *backsig; PKT_signature *backsig;
int rc; int rc;
cache_public_key(sub_pk); cache_public_key(sub_pk);
rc=make_keysig_packet(&backsig,pk,NULL,sub_pk,sub_sk,0x19,0,0, rc= make_keysig_packet (&backsig, pk, NULL, sub_pk, sub_sk, 0x19,
sub_pk->timestamp,0,NULL,NULL); 0, 0, timestamp, 0, NULL, NULL);
if(rc) if(rc)
log_error("make_keysig_packet failed for backsig: %s\n",g10_errstr(rc)); log_error("make_keysig_packet failed for backsig: %s\n",g10_errstr(rc));
else else
@ -863,7 +868,7 @@ make_backsig(PKT_signature *sig,PKT_public_key *pk,
static int static int
write_direct_sig( KBNODE root, KBNODE pub_root, PKT_secret_key *sk, write_direct_sig( KBNODE root, KBNODE pub_root, PKT_secret_key *sk,
struct revocation_key *revkey ) struct revocation_key *revkey, u32 timestamp)
{ {
PACKET *pkt; PACKET *pkt;
PKT_signature *sig; PKT_signature *sig;
@ -885,7 +890,8 @@ write_direct_sig( KBNODE root, KBNODE pub_root, PKT_secret_key *sk,
cache_public_key (pk); cache_public_key (pk);
/* and make the signature */ /* and make the signature */
rc = make_keysig_packet(&sig,pk,NULL,NULL,sk,0x1F,0,0,pk->timestamp,0, rc = make_keysig_packet (&sig, pk, NULL, NULL, sk, 0x1F,
0, 0, timestamp, 0,
keygen_add_revkey, revkey); keygen_add_revkey, revkey);
if( rc ) { if( rc ) {
log_error("make_keysig_packet failed: %s\n", g10_errstr(rc) ); log_error("make_keysig_packet failed: %s\n", g10_errstr(rc) );
@ -901,7 +907,7 @@ write_direct_sig( KBNODE root, KBNODE pub_root, PKT_secret_key *sk,
static int static int
write_selfsigs (KBNODE sec_root, KBNODE pub_root, PKT_secret_key *sk, write_selfsigs (KBNODE sec_root, KBNODE pub_root, PKT_secret_key *sk,
unsigned int use ) unsigned int use, u32 timestamp)
{ {
PACKET *pkt; PACKET *pkt;
PKT_signature *sig; PKT_signature *sig;
@ -929,8 +935,9 @@ write_selfsigs( KBNODE sec_root, KBNODE pub_root, PKT_secret_key *sk,
cache_public_key (pk); cache_public_key (pk);
/* and make the signature */ /* and make the signature */
rc = make_keysig_packet( &sig, pk, uid, NULL, sk, 0x13, 0, 0, rc = make_keysig_packet (&sig, pk, uid, NULL, sk, 0x13,
pk->timestamp, 0, keygen_add_std_prefs, pk ); 0, 0, timestamp, 0,
keygen_add_std_prefs, pk);
if( rc ) { if( rc ) {
log_error("make_keysig_packet failed: %s\n", g10_errstr(rc) ); log_error("make_keysig_packet failed: %s\n", g10_errstr(rc) );
return rc; return rc;
@ -951,7 +958,7 @@ write_selfsigs( KBNODE sec_root, KBNODE pub_root, PKT_secret_key *sk,
static int static int
write_keybinding (KBNODE root, KBNODE pub_root, write_keybinding (KBNODE root, KBNODE pub_root,
PKT_secret_key *pri_sk, PKT_secret_key *sub_sk, PKT_secret_key *pri_sk, PKT_secret_key *sub_sk,
unsigned int use ) unsigned int use, u32 timestamp)
{ {
PACKET *pkt; PACKET *pkt;
PKT_signature *sig; PKT_signature *sig;
@ -984,8 +991,8 @@ write_keybinding( KBNODE root, KBNODE pub_root,
/* and make the signature */ /* and make the signature */
oduap.usage = use; oduap.usage = use;
oduap.pk = sub_pk; oduap.pk = sub_pk;
rc=make_keysig_packet(&sig, pri_pk, NULL, sub_pk, pri_sk, 0x18, 0, 0, rc=make_keysig_packet(&sig, pri_pk, NULL, sub_pk, pri_sk, 0x18,
sub_pk->timestamp, 0, 0, 0, timestamp, 0,
keygen_add_key_flags_and_expire, &oduap ); keygen_add_key_flags_and_expire, &oduap );
if( rc ) { if( rc ) {
log_error("make_keysig_packet failed: %s\n", g10_errstr(rc) ); log_error("make_keysig_packet failed: %s\n", g10_errstr(rc) );
@ -995,7 +1002,7 @@ write_keybinding( KBNODE root, KBNODE pub_root,
/* make a backsig */ /* make a backsig */
if(use&PUBKEY_USAGE_SIG) if(use&PUBKEY_USAGE_SIG)
{ {
rc=make_backsig(sig,pri_pk,sub_pk,sub_sk); rc = make_backsig (sig, pri_pk, sub_pk, sub_sk, timestamp);
if(rc) if(rc)
return rc; return rc;
} }
@ -3041,6 +3048,15 @@ do_generate_keypair (struct para_data_s *para,struct output_control_s *outctrl,
timestamp = get_parameter_u32 (para, pKEYCREATIONDATE); timestamp = get_parameter_u32 (para, pKEYCREATIONDATE);
/* Note that, depending on the backend (i.e. the used scdaemon
version or the internal code), the card key generation may
update TIMESTAMP for each key. Thus we need to pass TIMESTAMP
to all signing function to make sure that the binding signature
is done using the timestamp of the corresponding (sub)key and
not that of the primary key. An alternative implementation
could tell the signing function the node of the subkey but that
is more work than just to pass the current timestamp. */
if (!card) if (!card)
{ {
rc = do_create( get_parameter_algo( para, pKEYTYPE ), rc = do_create( get_parameter_algo( para, pKEYTYPE ),
@ -3055,6 +3071,7 @@ do_generate_keypair (struct para_data_s *para,struct output_control_s *outctrl,
else else
{ {
rc = gen_card_key (PUBKEY_ALGO_RSA, 1, 1, pub_root, sec_root, NULL, rc = gen_card_key (PUBKEY_ALGO_RSA, 1, 1, pub_root, sec_root, NULL,
&timestamp,
get_parameter_u32 (para, pKEYEXPIRE), para); get_parameter_u32 (para, pKEYEXPIRE), para);
if (!rc) if (!rc)
{ {
@ -3065,9 +3082,9 @@ do_generate_keypair (struct para_data_s *para,struct output_control_s *outctrl,
if(!rc && (revkey=get_parameter_revkey(para,pREVOKER))) if(!rc && (revkey=get_parameter_revkey(para,pREVOKER)))
{ {
rc=write_direct_sig(pub_root,pub_root,pri_sk,revkey); rc = write_direct_sig (pub_root, pub_root, pri_sk, revkey, timestamp);
if (!rc) if (!rc)
write_direct_sig(sec_root,pub_root,pri_sk,revkey); write_direct_sig (sec_root, pub_root, pri_sk, revkey, timestamp);
} }
if( !rc && (s=get_parameter_value(para, pUSERID)) ) if( !rc && (s=get_parameter_value(para, pUSERID)) )
@ -3078,7 +3095,8 @@ do_generate_keypair (struct para_data_s *para,struct output_control_s *outctrl,
if (!rc) if (!rc)
rc = write_selfsigs (sec_root, pub_root, pri_sk, rc = write_selfsigs (sec_root, pub_root, pri_sk,
get_parameter_uint (para, pKEYUSAGE)); get_parameter_uint (para, pKEYUSAGE),
timestamp);
} }
/* Write the auth key to the card before the encryption key. This /* Write the auth key to the card before the encryption key. This
@ -3091,12 +3109,15 @@ do_generate_keypair (struct para_data_s *para,struct output_control_s *outctrl,
if (!rc && card && get_parameter (para, pAUTHKEYTYPE)) if (!rc && card && get_parameter (para, pAUTHKEYTYPE))
{ {
rc = gen_card_key (PUBKEY_ALGO_RSA, 3, 0, pub_root, sec_root, NULL, rc = gen_card_key (PUBKEY_ALGO_RSA, 3, 0, pub_root, sec_root, NULL,
&timestamp,
get_parameter_u32 (para, pKEYEXPIRE), para); get_parameter_u32 (para, pKEYEXPIRE), para);
if (!rc) if (!rc)
rc = write_keybinding (pub_root, pub_root, pri_sk, sub_sk, PUBKEY_USAGE_AUTH); rc = write_keybinding (pub_root, pub_root, pri_sk, sub_sk,
PUBKEY_USAGE_AUTH, timestamp);
if (!rc) if (!rc)
rc = write_keybinding (sec_root, pub_root, pri_sk, sub_sk, PUBKEY_USAGE_AUTH); rc = write_keybinding (sec_root, pub_root, pri_sk, sub_sk,
PUBKEY_USAGE_AUTH, timestamp);
} }
if( !rc && get_parameter( para, pSUBKEYTYPE ) ) if( !rc && get_parameter( para, pSUBKEYTYPE ) )
@ -3121,6 +3142,7 @@ do_generate_keypair (struct para_data_s *para,struct output_control_s *outctrl,
the card. Write a backup file. */ the card. Write a backup file. */
rc = gen_card_key_with_backup (PUBKEY_ALGO_RSA, 2, 0, rc = gen_card_key_with_backup (PUBKEY_ALGO_RSA, 2, 0,
pub_root, sec_root, pub_root, sec_root,
timestamp,
get_parameter_u32 (para, get_parameter_u32 (para,
pKEYEXPIRE), pKEYEXPIRE),
para, s); para, s);
@ -3128,15 +3150,18 @@ do_generate_keypair (struct para_data_s *para,struct output_control_s *outctrl,
else else
rc = gen_card_key (PUBKEY_ALGO_RSA, 2, 0, pub_root, sec_root, rc = gen_card_key (PUBKEY_ALGO_RSA, 2, 0, pub_root, sec_root,
NULL, NULL,
&timestamp,
get_parameter_u32 (para, pKEYEXPIRE), para); get_parameter_u32 (para, pKEYEXPIRE), para);
} }
if( !rc ) if( !rc )
rc = write_keybinding (pub_root, pub_root, pri_sk, sub_sk, rc = write_keybinding (pub_root, pub_root, pri_sk, sub_sk,
get_parameter_uint (para, pSUBKEYUSAGE)); get_parameter_uint (para, pSUBKEYUSAGE),
timestamp );
if( !rc ) if( !rc )
rc = write_keybinding (sec_root, pub_root, pri_sk, sub_sk, rc = write_keybinding (sec_root, pub_root, pri_sk, sub_sk,
get_parameter_uint (para, pSUBKEYUSAGE)); get_parameter_uint (para, pSUBKEYUSAGE),
timestamp);
did_sub = 1; did_sub = 1;
} }
@ -3354,10 +3379,13 @@ generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock )
rc = do_create (algo, nbits, pub_keyblock, sec_keyblock, rc = do_create (algo, nbits, pub_keyblock, sec_keyblock,
dek, s2k, &sub_sk, timestamp, expire, 1 ); dek, s2k, &sub_sk, timestamp, expire, 1 );
if (!rc) if (!rc)
rc = write_keybinding(pub_keyblock, pub_keyblock, pri_sk, sub_sk, use); rc = write_keybinding (pub_keyblock, pub_keyblock, pri_sk, sub_sk,
use, timestamp);
if (!rc) if (!rc)
rc = write_keybinding(sec_keyblock, pub_keyblock, pri_sk, sub_sk, use); rc = write_keybinding (sec_keyblock, pub_keyblock, pri_sk, sub_sk,
if( !rc ) { use, timestamp);
if (!rc)
{
okay = 1; okay = 1;
write_status_text (STATUS_KEY_CREATED, "S"); write_status_text (STATUS_KEY_CREATED, "S");
} }
@ -3466,11 +3494,13 @@ generate_card_subkeypair (KBNODE pub_keyblock, KBNODE sec_keyblock,
if (passphrase) if (passphrase)
set_next_passphrase (passphrase); set_next_passphrase (passphrase);
rc = gen_card_key (algo, keyno, 0, pub_keyblock, sec_keyblock, rc = gen_card_key (algo, keyno, 0, pub_keyblock, sec_keyblock,
&sub_sk, expire, para); &sub_sk, &timestamp, expire, para);
if (!rc) if (!rc)
rc = write_keybinding (pub_keyblock, pub_keyblock, pri_sk, sub_sk, use); rc = write_keybinding (pub_keyblock, pub_keyblock, pri_sk, sub_sk, use,
timestamp);
if (!rc) if (!rc)
rc = write_keybinding (sec_keyblock, pub_keyblock, pri_sk, sub_sk, use); rc = write_keybinding (sec_keyblock, pub_keyblock, pri_sk, sub_sk, use,
timestamp);
if (!rc) if (!rc)
{ {
okay = 1; okay = 1;
@ -3515,10 +3545,11 @@ write_keyblock( IOBUF out, KBNODE node )
} }
/* Note that TIMESTAMP is an in/out arg. */
static int static int
gen_card_key (int algo, int keyno, int is_primary, gen_card_key (int algo, int keyno, int is_primary,
KBNODE pub_root, KBNODE sec_root, PKT_secret_key **ret_sk, KBNODE pub_root, KBNODE sec_root, PKT_secret_key **ret_sk,
u32 expireval, struct para_data_s *para) u32 *timestamp, u32 expireval, struct para_data_s *para)
{ {
#ifdef ENABLE_CARD_SUPPORT #ifdef ENABLE_CARD_SUPPORT
int rc; int rc;
@ -3531,7 +3562,7 @@ gen_card_key (int algo, int keyno, int is_primary,
assert (algo == PUBKEY_ALGO_RSA); assert (algo == PUBKEY_ALGO_RSA);
/* Fixme: We don't have the serialnumber available, thus passing NULL. */ /* Fixme: We don't have the serialnumber available, thus passing NULL. */
rc = agent_scd_genkey (&info, keyno, 1, NULL); rc = agent_scd_genkey (&info, keyno, 1, NULL, timestamp);
/* if (gpg_err_code (rc) == GPG_ERR_EEXIST) */ /* if (gpg_err_code (rc) == GPG_ERR_EEXIST) */
/* { */ /* { */
/* tty_printf ("\n"); */ /* tty_printf ("\n"); */
@ -3555,6 +3586,9 @@ gen_card_key (int algo, int keyno, int is_primary,
return gpg_error (GPG_ERR_GENERAL); return gpg_error (GPG_ERR_GENERAL);
} }
if (*timestamp != info.created_at)
log_info ("Note that the key does not use the suggested creation date\n");
*timestamp = info.created_at;
pk = xcalloc (1, sizeof *pk ); pk = xcalloc (1, sizeof *pk );
sk = xcalloc (1, sizeof *sk ); sk = xcalloc (1, sizeof *sk );
@ -3602,6 +3636,7 @@ gen_card_key (int algo, int keyno, int is_primary,
static int static int
gen_card_key_with_backup (int algo, int keyno, int is_primary, gen_card_key_with_backup (int algo, int keyno, int is_primary,
KBNODE pub_root, KBNODE sec_root, KBNODE pub_root, KBNODE sec_root,
u32 timestamp,
u32 expireval, struct para_data_s *para, u32 expireval, struct para_data_s *para,
const char *backup_dir) const char *backup_dir)
{ {
@ -3616,7 +3651,7 @@ gen_card_key_with_backup (int algo, int keyno, int is_primary,
sk_unprotected = NULL; sk_unprotected = NULL;
sk_protected = NULL; sk_protected = NULL;
rc = generate_raw_key (algo, 1024, make_timestamp (), rc = generate_raw_key (algo, 1024, timestamp,
&sk_unprotected, &sk_protected); &sk_unprotected, &sk_protected);
if (rc) if (rc)
return rc; return rc;

View File

@ -183,7 +183,8 @@ int keygen_add_keyserver_url(PKT_signature *sig, void *opaque);
int keygen_add_notations(PKT_signature *sig,void *opaque); int keygen_add_notations(PKT_signature *sig,void *opaque);
int keygen_add_revkey(PKT_signature *sig, void *opaque); int keygen_add_revkey(PKT_signature *sig, void *opaque);
int make_backsig(PKT_signature *sig,PKT_public_key *pk, int make_backsig(PKT_signature *sig,PKT_public_key *pk,
PKT_public_key *sub_pk,PKT_secret_key *sub_sk); PKT_public_key *sub_pk,PKT_secret_key *sub_sk,
u32 timestamp);
int generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock ); int generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock );
#ifdef ENABLE_CARD_SUPPORT #ifdef ENABLE_CARD_SUPPORT
int generate_card_subkeypair (KBNODE pub_keyblock, KBNODE sec_keyblock, int generate_card_subkeypair (KBNODE pub_keyblock, KBNODE sec_keyblock,

View File

@ -214,6 +214,7 @@ struct
const char *ctapi_driver; /* Library to access the ctAPI. */ const char *ctapi_driver; /* Library to access the ctAPI. */
const char *pcsc_driver; /* Library to access the PC/SC system. */ const char *pcsc_driver; /* Library to access the PC/SC system. */
int disable_ccid; /* Disable the use of the internal CCID driver. */ int disable_ccid; /* Disable the use of the internal CCID driver. */
int disable_keypad; /* Do not allow the use of a keypad. */
#endif /*ENABLE_CARD_SUPPORT*/ #endif /*ENABLE_CARD_SUPPORT*/
struct struct

View File

@ -1,3 +1,15 @@
2009-07-21 Werner Koch <wk@g10code.com>
* estream-printf.h: New. Taken from libestream.x
2009-07-20 Werner Koch <wk@g10code.com>
* types.h (strlist_t): Add new alias for STRLIST.
* memory.h (xtrymalloc,xtrystrdup): New.
* util.h: Add prototypes for util/convert.c.
2009-05-26 David Shaw <dshaw@jabberwocky.com> 2009-05-26 David Shaw <dshaw@jabberwocky.com>
* http.h: Pass in a STRLIST for additional headers on http_open * http.h: Pass in a STRLIST for additional headers on http_open

View File

@ -15,5 +15,6 @@ dynload.h
assuan.h assuan.h
compat.h compat.h
srv.h srv.h
estream-printf.h
ChangeLog ChangeLog

110
include/estream-printf.h Normal file
View File

@ -0,0 +1,110 @@
/* estream-printf.h - Versatile C-99 compliant printf formatting.
* Copyright (C) 2007 g10 Code GmbH
*
* This file is part of Libestream.
*
* Libestream is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* Libestream 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 Libestream; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ESTREAM_PRINTF_H
#define ESTREAM_PRINTF_H
#include <stdarg.h>
#include <stdio.h>
/* To use this file with libraries the following macro is useful:
#define _ESTREAM_EXT_SYM_PREFIX _foo_
This prefixes all external symbols with "_foo_".
For the implementation of the code (estream-printf.c) the following
macros may be used to tune the implementation for certain systems:
#define _ESTREAM_PRINTF_MALLOC foo_malloc
#define _ESTREAM_PRINTF_FREE foo_free
Make estream_asprintf and estream_vasprintf use foo_malloc and
foo_free instead of the standard malloc and free functions to
allocate the memory returned to the caller.
#define _ESTREAM_PRINTF_EXTRA_INCLUDE "foo.h"
This includes the file "foo.h" which may provide prototypes for
the custom memory allocation functions.
*/
#ifdef _ESTREAM_EXT_SYM_PREFIX
#ifndef _ESTREAM_PREFIX
#define _ESTREAM_PREFIX1(x,y) x ## y
#define _ESTREAM_PREFIX2(x,y) _ESTREAM_PREFIX1(x,y)
#define _ESTREAM_PREFIX(x) _ESTREAM_PREFIX2(_ESTREAM_EXT_SYM_PREFIX,x)
#endif /*_ESTREAM_PREFIX*/
#define estream_printf_out_t _ESTREAM_PREFIX(estream_printf_out_t)
#define estream_format _ESTREAM_PREFIX(estream_format)
#define estream_printf _ESTREAM_PREFIX(estream_printf)
#define estream_fprintf _ESTREAM_PREFIX(estream_fprintf)
#define estream_vfprintf _ESTREAM_PREFIX(estream_vfprintf)
#define estream_snprintf _ESTREAM_PREFIX(estream_snprintf)
#define estream_vsnprintf _ESTREAM_PREFIX(estream_vsnprintf)
#define estream_asprintf _ESTREAM_PREFIX(estream_asprintf)
#define estream_vasprintf _ESTREAM_PREFIX(estream_vasprintf)
#endif /*_ESTREAM_EXT_SYM_PREFIX*/
#ifndef _ESTREAM_GCC_A_PRINTF
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 )
# define _ESTREAM_GCC_A_PRINTF( f, a ) __attribute__ ((format (printf,f,a)))
#else
# define _ESTREAM_GCC_A_PRINTF( f, a )
#endif
#endif /*_ESTREAM_GCC_A_PRINTF*/
#ifdef __cplusplus
extern "C"
{
#if 0
}
#endif
#endif
typedef int (*estream_printf_out_t)
(void *outfncarg, const char *buf, size_t buflen);
int estream_format (estream_printf_out_t outfnc, void *outfncarg,
const char *format, va_list vaargs)
_ESTREAM_GCC_A_PRINTF(3,0);
int estream_printf (const char *format, ...)
_ESTREAM_GCC_A_PRINTF(1,2);
int estream_fprintf (FILE *fp, const char *format, ... )
_ESTREAM_GCC_A_PRINTF(2,3);
int estream_vfprintf (FILE *fp, const char *format, va_list arg_ptr)
_ESTREAM_GCC_A_PRINTF(2,0);
int estream_snprintf (char *buf, size_t bufsize, const char *format, ...)
_ESTREAM_GCC_A_PRINTF(3,4);
int estream_vsnprintf (char *buf,size_t bufsize,
const char *format, va_list arg_ptr)
_ESTREAM_GCC_A_PRINTF(3,0);
int estream_asprintf (char **bufp, const char *format, ...)
_ESTREAM_GCC_A_PRINTF(2,3);
int estream_vasprintf (char **bufp, const char *format, va_list arg_ptr)
_ESTREAM_GCC_A_PRINTF(2,0);
#ifdef __cplusplus
}
#endif
#endif /*ESTREAM_PRINTF_H*/

View File

@ -30,6 +30,7 @@
#define M_DBGINFO(a) "["__FILE__ ":" STR(a) "]" #define M_DBGINFO(a) "["__FILE__ ":" STR(a) "]"
#endif /* __riscos__ */ #endif /* __riscos__ */
#define xmalloc(n) m_debug_alloc((n), M_DBGINFO( __LINE__ ) ) #define xmalloc(n) m_debug_alloc((n), M_DBGINFO( __LINE__ ) )
#define xtrymalloc(n) m_debug_trymalloc ((n), M_DBGINFO( __LINE__ ))
#define xmalloc_clear(n) m_debug_alloc_clear((n), M_DBGINFO(__LINE__) ) #define xmalloc_clear(n) m_debug_alloc_clear((n), M_DBGINFO(__LINE__) )
#define xmalloc_secure(n) m_debug_alloc_secure(n), M_DBGINFO(__LINE__) ) #define xmalloc_secure(n) m_debug_alloc_secure(n), M_DBGINFO(__LINE__) )
#define xmalloc_secure_clear(n) m_debug_alloc_secure_clear((n), M_DBGINFO(__LINE__) ) #define xmalloc_secure_clear(n) m_debug_alloc_secure_clear((n), M_DBGINFO(__LINE__) )
@ -38,8 +39,10 @@
#define m_check(n) m_debug_check((n), M_DBGINFO(__LINE__) ) #define m_check(n) m_debug_check((n), M_DBGINFO(__LINE__) )
/*#define m_copy(a) m_debug_copy((a), M_DBGINFO(__LINE__) )*/ /*#define m_copy(a) m_debug_copy((a), M_DBGINFO(__LINE__) )*/
#define xstrdup(a) m_debug_strdup((a), M_DBGINFO(__LINE__) ) #define xstrdup(a) m_debug_strdup((a), M_DBGINFO(__LINE__) )
#define xtrystrdup(a) m_debug_trystrdup((a), M_DBGINFO(__LINE__) )
void *m_debug_alloc( size_t n, const char *info ); void *m_debug_alloc( size_t n, const char *info );
void *m_debug_trymalloc (size_t n, const char *info);
void *m_debug_alloc_clear( size_t n, const char *info ); void *m_debug_alloc_clear( size_t n, const char *info );
void *m_debug_alloc_secure( size_t n, const char *info ); void *m_debug_alloc_secure( size_t n, const char *info );
void *m_debug_alloc_secure_clear( size_t n, const char *info ); void *m_debug_alloc_secure_clear( size_t n, const char *info );
@ -48,9 +51,11 @@ void m_debug_free( void *p, const char *info );
void m_debug_check( const void *a, const char *info ); void m_debug_check( const void *a, const char *info );
/*void *m_debug_copy( const void *a, const char *info );*/ /*void *m_debug_copy( const void *a, const char *info );*/
char *m_debug_strdup( const char *a, const char *info ); char *m_debug_strdup( const char *a, const char *info );
char *m_debug_trystrdup (const char *a, const char *info);
#else #else
void *xmalloc( size_t n ); void *xmalloc( size_t n );
void *xtrymalloc (size_t n);
void *xmalloc_clear( size_t n ); void *xmalloc_clear( size_t n );
void *xmalloc_secure( size_t n ); void *xmalloc_secure( size_t n );
void *xmalloc_secure_clear( size_t n ); void *xmalloc_secure_clear( size_t n );
@ -59,6 +64,7 @@ void xfree( void *p );
void m_check( const void *a ); void m_check( const void *a );
/*void *m_copy( const void *a );*/ /*void *m_copy( const void *a );*/
char *xstrdup( const char * a); char *xstrdup( const char * a);
char *xtrystrdup (const char *a);
#endif #endif
size_t m_size( const void *a ); size_t m_size( const void *a );

View File

@ -131,10 +131,12 @@ typedef union {
double g; double g;
} PROPERLY_ALIGNED_TYPE; } PROPERLY_ALIGNED_TYPE;
typedef struct string_list { struct string_list {
struct string_list *next; struct string_list *next;
unsigned int flags; unsigned int flags;
char d[1]; char d[1];
} *STRLIST; };
typedef struct string_list *STRLIST;
typedef struct string_list *strlist_t;
#endif /*G10_TYPES_H*/ #endif /*G10_TYPES_H*/

View File

@ -240,11 +240,13 @@ char *read_w32_registry_string( const char *root,
int write_w32_registry_string(const char *root, const char *dir, int write_w32_registry_string(const char *root, const char *dir,
const char *name, const char *value); const char *name, const char *value);
/*-- strgutil.c --*/
int vasprintf (char **result, const char *format, va_list args);
int asprintf (char **buf, const char *fmt, ...);
#endif /*_WIN32*/ #endif /*_WIN32*/
/*-- strgutil.c --*/
char *xasprintf (const char *fmt, ...);
char *xtryasprintf (const char *fmt, ...);
/*-- pka.c --*/ /*-- pka.c --*/
char *get_pka_info (const char *address, unsigned char *fpr); char *get_pka_info (const char *address, unsigned char *fpr);
@ -252,6 +254,16 @@ char *get_pka_info (const char *address, unsigned char *fpr);
int get_cert(const char *name,size_t max_size,IOBUF *iobuf, int get_cert(const char *name,size_t max_size,IOBUF *iobuf,
unsigned char **fpr,size_t *fpr_len,char **url); unsigned char **fpr,size_t *fpr_len,char **url);
/*-- convert.c --*/
int hex2bin (const char *string, void *buffer, size_t length);
int hexcolon2bin (const char *string, void *buffer, size_t length);
char *bin2hex (const void *buffer, size_t length, char *stringbuf);
char *bin2hexcolon (const void *buffer, size_t length, char *stringbuf);
const char *hex2str (const char *hexstring,
char *buffer, size_t bufsize, size_t *buflen);
char *hex2str_alloc (const char *hexstring, size_t *r_count);
/**** other missing stuff ****/ /**** other missing stuff ****/
#ifndef HAVE_ATEXIT /* For SunOS */ #ifndef HAVE_ATEXIT /* For SunOS */
#define atexit(a) (on_exit((a),0)) #define atexit(a) (on_exit((a),0))

View File

@ -1,3 +1,7 @@
2009-07-21 Werner Koch <wk@g10code.com>
* estream.m4: New. Taken from libestream.
2007-12-17 Werner Koch <wk@g10code.com> 2007-12-17 Werner Koch <wk@g10code.com>
* ldap.m4: Test for ldap_start_tls_sA. * ldap.m4: Test for ldap_start_tls_sA.

View File

@ -6,6 +6,7 @@ EXTRA_DIST = glibc2.m4 intl.m4 intldir.m4 lock.m4 visibility.m4 intmax.m4 longdo
po.m4 progtest.m4 stdint_h.m4 uintmax_t.m4 ulonglong.m4 \ po.m4 progtest.m4 stdint_h.m4 uintmax_t.m4 ulonglong.m4 \
readline.m4 libcurl.m4 libusb.m4 tar-ustar.m4 \ readline.m4 libcurl.m4 libusb.m4 tar-ustar.m4 \
ldap.m4 \ ldap.m4 \
noexecstack.m4 autobuild.m4 noexecstack.m4 autobuild.m4 estream.m4

48
m4/estream.m4 Normal file
View File

@ -0,0 +1,48 @@
dnl Autoconf macros for libestream
dnl Copyright (C) 2007 g10 Code GmbH
dnl
dnl This file is free software; as a special exception the author gives
dnl unlimited permission to copy and/or distribute it, with or without
dnl modifications, as long as this notice is preserved.
dnl
dnl This file is distributed in the hope that it will be useful, but
dnl WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
dnl implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
dnl estream_PRINTF_INIT
dnl Prepare build of source included estream-printf.c
dnl
AC_DEFUN([estream_PRINTF_INIT],
[
AC_MSG_NOTICE([checking system features for estream-printf])
AC_TYPE_LONG_LONG_INT
AC_TYPE_LONG_DOUBLE
AC_TYPE_INTMAX_T
AC_TYPE_UINTMAX_T
AC_CHECK_TYPES([ptrdiff_t])
AC_CHECK_SIZEOF([unsigned long])
AC_CHECK_SIZEOF([void *])
AC_CACHE_CHECK([for nl_langinfo and THOUSANDS_SEP],
estream_cv_langinfo_thousands_sep,
[AC_TRY_LINK([#include <langinfo.h>],
[char* cs = nl_langinfo(THOUSANDS_SEP); return !cs;],
estream_cv_langinfo_thousands_sep=yes,
estream_cv_langinfo_thousands_sep=no)
])
if test $estream_cv_langinfo_thousands_sep = yes; then
AC_DEFINE(HAVE_LANGINFO_THOUSANDS_SEP, 1,
[Define if you have <langinfo.h> and nl_langinfo(THOUSANDS_SEP).])
fi
])
dnl estream_INIT
dnl Prepare build of source included estream.c
dnl
AC_DEFUN([estream_INIT],
[
AC_REQUIRE([estream_PRINTF_INIT])
AC_MSG_NOTICE([checking system features for estream])
])

View File

@ -1,3 +1,20 @@
2009-07-21 Werner Koch <wk@g10code.com>
* ttyio.c (tty_printf): Replace vasprintf by xtryasprintf.
(tty_fprintf): Ditto.
* strgutil.c: Include estream-printf.h.
(xasprintf, xtryasprintf): New.
(vasprintf, asprintf): Remove.
* estream-printf.c: New. Taken from libestream.
* Makefile.am (libutil_a_SOURCES): Add it.
* memory.c (trymalloc,trystrdup): New.
* convert.c: New. Taken from GnuPG 2.0 SVN.
* Makefile.am (libutil_a_SOURCES): Add it.
2009-05-26 David Shaw <dshaw@jabberwocky.com> 2009-05-26 David Shaw <dshaw@jabberwocky.com>
* http.c (send_request): Pass in a STRLIST for additional headers. * http.c (send_request): Pass in a STRLIST for additional headers.

View File

@ -23,7 +23,8 @@ noinst_LIBRARIES = libutil.a libcompat.a
libutil_a_SOURCES = logger.c fileutil.c miscutil.c strgutil.c \ libutil_a_SOURCES = logger.c fileutil.c miscutil.c strgutil.c \
ttyio.c argparse.c memory.c secmem.c errors.c iobuf.c \ ttyio.c argparse.c memory.c secmem.c errors.c iobuf.c \
dotlock.c http.c pka.c membuf.c cert.c \ dotlock.c http.c pka.c membuf.c cert.c convert.c \
estream-printf.c \
$(libcompat_a_SOURCES) $(libcompat_a_SOURCES)
if USE_SIMPLE_GETTEXT if USE_SIMPLE_GETTEXT

249
util/convert.c Normal file
View File

@ -0,0 +1,249 @@
/* convert.c - Hex conversion functions.
* Copyright (C) 2006, 2008 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 <stdlib.h>
#include <errno.h>
#include <ctype.h>
#include "util.h"
#define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A'))
/* Convert STRING consisting of hex characters into its binary
representation and store that at BUFFER. BUFFER needs to be of
LENGTH bytes. The function checks that the STRING will convert
exactly to LENGTH bytes. The string is delimited by either end of
string or a white space character. The function returns -1 on
error or the length of the parsed string. */
int
hex2bin (const char *string, void *buffer, size_t length)
{
int i;
const char *s = string;
for (i=0; i < length; )
{
if (!hexdigitp (s) || !hexdigitp (s+1))
return -1; /* Invalid hex digits. */
((unsigned char*)buffer)[i++] = xtoi_2 (s);
s += 2;
}
if (*s && (!isascii (*s) || !isspace (*s)) )
return -1; /* Not followed by Nul or white space. */
if (i != length)
return -1; /* Not of expected length. */
if (*s)
s++; /* Skip the delimiter. */
return s - string;
}
/* Convert STRING consisting of hex characters into its binary representation
and store that at BUFFER. BUFFER needs to be of LENGTH bytes. The
function check that the STRING will convert exactly to LENGTH
bytes. Colons inbetween the hex digits are allowed, if one colon
has been given a colon is expected very 2 characters. The string
is delimited by either end of string or a white space character.
The function returns -1 on error or the length of the parsed
string. */
int
hexcolon2bin (const char *string, void *buffer, size_t length)
{
int i;
const char *s = string;
int need_colon = 0;
for (i=0; i < length; )
{
if (i==1 && *s == ':') /* Skip colons between hex digits. */
{
need_colon = 1;
s++;
}
else if (need_colon && *s == ':')
s++;
else if (need_colon)
return -1; /* Colon expected. */
if (!hexdigitp (s) || !hexdigitp (s+1))
return -1; /* Invalid hex digits. */
((unsigned char*)buffer)[i++] = xtoi_2 (s);
s += 2;
}
if (*s == ':')
return -1; /* Trailing colons are not allowed. */
if (*s && (!isascii (*s) || !isspace (*s)) )
return -1; /* Not followed by Nul or white space. */
if (i != length)
return -1; /* Not of expected length. */
if (*s)
s++; /* Skip the delimiter. */
return s - string;
}
static char *
do_bin2hex (const void *buffer, size_t length, char *stringbuf, int with_colon)
{
const unsigned char *s;
char *p;
if (!stringbuf)
{
/* Not really correct for with_colon but we don't care about the
one wasted byte. */
size_t n = with_colon? 3:2;
size_t nbytes = n * length + 1;
if (length && (nbytes-1) / n != length)
{
errno = ENOMEM;
return NULL;
}
stringbuf = xtrymalloc (nbytes);
if (!stringbuf)
return NULL;
}
for (s = buffer, p = stringbuf; length; length--, s++)
{
if (with_colon && s != buffer)
*p++ = ':';
*p++ = tohex ((*s>>4)&15);
*p++ = tohex (*s&15);
}
*p = 0;
return stringbuf;
}
/* Convert LENGTH bytes of data in BUFFER into hex encoding and store
that at the provided STRINGBUF. STRINGBUF must be allocated of at
least (2*LENGTH+1) bytes or be NULL so that the function mallocs an
appropriate buffer. Returns STRINGBUF or NULL on error (which may
only occur if STRINGBUF has been NULL and the internal malloc
failed). */
char *
bin2hex (const void *buffer, size_t length, char *stringbuf)
{
return do_bin2hex (buffer, length, stringbuf, 0);
}
/* Convert LENGTH bytes of data in BUFFER into hex encoding and store
that at the provided STRINGBUF. STRINGBUF must be allocated of at
least (3*LENGTH+1) bytes or be NULL so that the function mallocs an
appropriate buffer. Returns STRINGBUF or NULL on error (which may
only occur if STRINGBUF has been NULL and the internal malloc
failed). */
char *
bin2hexcolon (const void *buffer, size_t length, char *stringbuf)
{
return do_bin2hex (buffer, length, stringbuf, 1);
}
/* Convert HEXSTRING consisting of hex characters into string and
store that at BUFFER. HEXSTRING is either delimited by end of
string or a white space character. The function makes sure that
the resulting string in BUFFER is terminated by a Nul character.
BUFSIZE is the availabe length of BUFFER; if the converted result
plus a possible required Nul character does not fit into this
buffer, the function returns NULL and won't change the existing
conent of buffer. In-place conversion is possible as long as
BUFFER points to HEXSTRING.
If BUFFER is NULL and bufsize is 0 the function scans HEXSTRING but
does not store anything. This may be used to find the end of
hexstring.
On sucess the function returns a pointer to the next character
after HEXSTRING (which is either end-of-string or a the next white
space). If BUFLEN is not NULL the strlen of buffer is stored
there; this will even be done if BUFFER has been passed as NULL. */
const char *
hex2str (const char *hexstring, char *buffer, size_t bufsize, size_t *buflen)
{
const char *s = hexstring;
int idx, count;
int need_nul = 0;
if (buflen)
*buflen = 0;
for (s=hexstring, count=0; hexdigitp (s) && hexdigitp (s+1); s += 2, count++)
;
if (*s && (!isascii (*s) || !isspace (*s)) )
return NULL; /* Not followed by Nul or white space. */
/* We need to append a nul character. However we don't want that if
the hexstring already ends with "00". */
need_nul = ((s == hexstring) || !(s[-2] == '0' && s[-1] == '0'));
if (need_nul)
count++;
if (buffer)
{
if (count > bufsize)
return NULL; /* Too long. */
for (s=hexstring, idx=0; hexdigitp (s) && hexdigitp (s+1); s += 2)
((unsigned char*)buffer)[idx++] = xtoi_2 (s);
if (need_nul)
buffer[idx] = 0;
}
if (buflen)
*buflen = count - 1;
return s;
}
/* Same as hex2str but this function allocated a new string. Returns
NULL on error. If R_COUNT is not NULL, the number of scanned bytes
will be stored there. ERRNO is set on error. */
char *
hex2str_alloc (const char *hexstring, size_t *r_count)
{
const char *tail;
size_t nbytes;
char *result;
tail = hex2str (hexstring, NULL, 0, &nbytes);
if (!tail)
{
if (r_count)
*r_count = 0;
errno = EINVAL;
return NULL;
}
if (r_count)
*r_count = tail - hexstring;
result = xtrymalloc (nbytes+1);
if (!result)
return NULL;
if (!hex2str (hexstring, result, nbytes+1, NULL))
BUG ();
return result;
}

2110
util/estream-printf.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -62,6 +62,7 @@
#define M_GUARD 1 #define M_GUARD 1
#endif #endif
#undef xmalloc #undef xmalloc
#undef xtrymalloc
#undef xmalloc_clear #undef xmalloc_clear
#undef xmalloc_secure #undef xmalloc_secure
#undef xmalloc_secure_clear #undef xmalloc_secure_clear
@ -69,6 +70,7 @@
#undef xfree #undef xfree
#undef m_check #undef m_check
#undef xstrdup #undef xstrdup
#undef xtrystrdup
#define FNAME(a) m_debug_ ##a #define FNAME(a) m_debug_ ##a
#define FNAMEX(a) m_debug_ ##a #define FNAMEX(a) m_debug_ ##a
#define FNAMEXM(a) m_debug_ ##a #define FNAMEXM(a) m_debug_ ##a
@ -444,6 +446,30 @@ FNAMEXM(alloc)( size_t n FNAMEPRT )
#endif #endif
} }
/* Allocate memory of size n. This function returns NULL if we do not
have enough memory. */
void *
FNAMEX(trymalloc)(size_t n FNAMEPRT)
{
#ifdef M_GUARD
char *p;
if (!n)
n = 1;
p = malloc (n + EXTRA_ALIGN+5);
if (!p)
return NULL;
store_len(p,n,0);
used_memory += n;
p[4+EXTRA_ALIGN+n] = MAGIC_END_BYTE;
return p+EXTRA_ALIGN+4;
#else
/* Mallocing zero bytes is undefined by ISO-C, so we better make
sure that it won't happen. */
return malloc (n? n: 1);
#endif
}
/**************** /****************
* Allocate memory of size n from the secure memory pool. * Allocate memory of size n from the secure memory pool.
* This function gives up if we do not have enough memory * This function gives up if we do not have enough memory
@ -616,6 +642,16 @@ FNAMEX(strdup)( const char *a FNAMEPRT )
return p; return p;
} }
char *
FNAMEX(trystrdup)(const char *a FNAMEPRT)
{
size_t n = strlen (a);
char *p = FNAMEX(trymalloc)(n+1 FNAMEARG);
if (p)
strcpy (p, a);
return p;
}
/* Wrapper around xmalloc_clear to take the usual 2 arguments of a /* Wrapper around xmalloc_clear to take the usual 2 arguments of a
calloc style function. */ calloc style function. */

View File

@ -1,6 +1,6 @@
/* strgutil.c - string utilities /* strgutil.c - string utilities
* Copyright (C) 1994, 1998, 1999, 2000, 2001, * Copyright (C) 1994, 1998, 1999, 2000, 2001,
* 2003, 2004, 2005 Free Software Foundation, Inc. * 2003, 2004, 2005, 2009 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -47,6 +47,14 @@
#include "memory.h" #include "memory.h"
#include "i18n.h" #include "i18n.h"
#include "dynload.h" #include "dynload.h"
#include "estream-printf.h"
/* Our xasprintf replacements are expected to work with our memory
allocator. Let's test for this here. */
#if !defined(_ESTREAM_PRINTF_MALLOC) || !defined(_ESTREAM_PRINTF_FREE)
#error Please define _ESTREAM_PRINTF_MALLOC and _FREE
#endif
#ifndef USE_GNUPG_ICONV #ifndef USE_GNUPG_ICONV
@ -1040,6 +1048,40 @@ utf8_to_native( const char *string, size_t length, int delim )
} }
} }
/* Same as asprintf but return an allocated buffer suitable to be
freed using xfree. This function simply dies on memory failure,
thus no extra check is required. */
char *
xasprintf (const char *fmt, ...)
{
va_list ap;
char *buf;
va_start (ap, fmt);
if (estream_vasprintf (&buf, fmt, ap) < 0)
log_fatal ("estream_asprintf failed: %s\n", strerror (errno));
va_end (ap);
return buf;
}
/* Same as above but return NULL on memory failure. */
char *
xtryasprintf (const char *fmt, ...)
{
int rc;
va_list ap;
char *buf;
va_start (ap, fmt);
rc = estream_vasprintf (&buf, fmt, ap);
va_end (ap);
if (rc < 0)
return NULL;
return buf;
}
/**************************************************** /****************************************************
******** locale insensitive ctype functions ******** ******** locale insensitive ctype functions ********
****************************************************/ ****************************************************/
@ -1127,111 +1169,6 @@ strncasecmp( const char *a, const char *b, size_t n )
#ifdef _WIN32 #ifdef _WIN32
/*
* Like vsprintf but provides a pointer to malloc'd storage, which
* must be freed by the caller (xfree). Taken from libiberty as
* found in gcc-2.95.2 and a little bit modernized.
* FIXME: Write a new CRT for W32.
*/
int
vasprintf (char **result, const char *format, va_list args)
{
const char *p = format;
/* Add one to make sure that it is never zero, which might cause malloc
to return NULL. */
int total_width = strlen (format) + 1;
va_list ap;
/* this is not really portable but works under Windows */
memcpy ( &ap, &args, sizeof (va_list));
while (*p != '\0')
{
if (*p++ == '%')
{
while (strchr ("-+ #0", *p))
++p;
if (*p == '*')
{
++p;
total_width += abs (va_arg (ap, int));
}
else
{
char *endp;
total_width += strtoul (p, &endp, 10);
p = endp;
}
if (*p == '.')
{
++p;
if (*p == '*')
{
++p;
total_width += abs (va_arg (ap, int));
}
else
{
char *endp;
total_width += strtoul (p, &endp, 10);
p = endp;
}
}
while (strchr ("hlL", *p))
++p;
/* Should be big enough for any format specifier except %s
and floats. */
total_width += 30;
switch (*p)
{
case 'd':
case 'i':
case 'o':
case 'u':
case 'x':
case 'X':
case 'c':
(void) va_arg (ap, int);
break;
case 'f':
case 'e':
case 'E':
case 'g':
case 'G':
(void) va_arg (ap, double);
/* Since an ieee double can have an exponent of 307, we'll
make the buffer wide enough to cover the gross case. */
total_width += 307;
case 's':
total_width += strlen (va_arg (ap, char *));
break;
case 'p':
case 'n':
(void) va_arg (ap, char *);
break;
}
}
}
*result = xmalloc (total_width);
if (*result != NULL)
return vsprintf (*result, format, args);
else
return 0;
}
int
asprintf (char **buf, const char *fmt, ...)
{
int status;
va_list ap;
va_start (ap, fmt);
status = vasprintf (buf, fmt, ap);
va_end (ap);
return status;
}
const char * const char *
w32_strerror (int w32_errno) w32_strerror (int w32_errno)
{ {

View File

@ -239,13 +239,14 @@ tty_printf( const char *fmt, ... )
va_start( arg_ptr, fmt ) ; va_start( arg_ptr, fmt ) ;
#ifdef _WIN32 #ifdef _WIN32
{ {
char *buf = NULL; char *buf;
int n; int n;
DWORD nwritten; DWORD nwritten;
n = vasprintf(&buf, fmt, arg_ptr); buf = xtryasprintf(fmt, arg_ptr);
if (!buf) if (!buf)
log_bug("vasprintf() failed\n"); log_bug("xtryasprintf() failed\n");
n = strlen (buf);
if (!WriteConsoleA (con.out, buf, n, &nwritten, NULL)) if (!WriteConsoleA (con.out, buf, n, &nwritten, NULL))
log_fatal ("WriteConsole failed: %s", w32_strerror (0)); log_fatal ("WriteConsole failed: %s", w32_strerror (0));
@ -286,13 +287,14 @@ tty_fprintf (FILE *fp, const char *fmt, ... )
va_start( arg_ptr, fmt ) ; va_start( arg_ptr, fmt ) ;
#ifdef _WIN32 #ifdef _WIN32
{ {
char *buf = NULL; char *buf;
int n; int n;
DWORD nwritten; DWORD nwritten;
n = vasprintf(&buf, fmt, arg_ptr); buf = xtryasprintf (fmt, arg_ptr);
if (!buf) if (!buf)
log_bug("vasprintf() failed\n"); log_bug ("xtryasprintf() failed\n");
n = strlen (buf);
if (!WriteConsoleA (con.out, buf, n, &nwritten, NULL)) if (!WriteConsoleA (con.out, buf, n, &nwritten, NULL))
log_fatal ("WriteConsole failed: %s", w32_strerror (0)); log_fatal ("WriteConsole failed: %s", w32_strerror (0));