mirror of
git://git.gnupg.org/gnupg.git
synced 2025-07-02 22:46:30 +02:00
This commit was manufactured by cvs2svn to create branch
'GNUPG-1-9-BRANCH'.
This commit is contained in:
parent
eba8c18657
commit
9ca4830a5b
76 changed files with 34899 additions and 0 deletions
242
scd/ChangeLog
Normal file
242
scd/ChangeLog
Normal file
|
@ -0,0 +1,242 @@
|
|||
2003-07-31 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* Makefile.am (scdaemon_LDADD): Added INTLLIBS.
|
||||
|
||||
2003-07-28 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* app-openpgp.c (do_setattr): Change implementation. Allow all
|
||||
useful DOs.
|
||||
|
||||
2003-07-27 Werner Koch <wk@gnupg.org>
|
||||
|
||||
Adjusted for gcry_mpi_print and gcry_mpi_scan API change.
|
||||
|
||||
2003-07-24 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* app-openpgp.c (do_learn_status): Print more status information.
|
||||
(app_select_openpgp): Store the card version.
|
||||
(store_fpr): Add argument card_version and fix DOs for old cards.
|
||||
(app_openpgp_storekey): Likewise.
|
||||
|
||||
2003-07-23 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* command.c (cmd_pkauth): New.
|
||||
(cmd_setdata): Check whether data was given at all to avoid
|
||||
passing 0 to malloc.
|
||||
|
||||
* app.c (app_auth): New.
|
||||
* app-openpgp.c (do_auth): New.
|
||||
|
||||
2003-07-22 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* command.c (cmd_passwd): New.
|
||||
* app.c (app_change_pin): New.
|
||||
* app-openpgp.c (do_change_pin): New.
|
||||
* iso7816.c (iso7816_reset_retry_counter): Implemented.
|
||||
|
||||
* sc-investigate.c (main): New option --gen-random.
|
||||
* iso7816.c (iso7816_get_challenge): Don't create APDUs with a
|
||||
length larger than 255.
|
||||
|
||||
2003-07-17 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* command.c (cmd_random): New command RANDOM.
|
||||
|
||||
* iso7816.c (map_sw): New. Use it in this file to return
|
||||
meaningful error messages. Changed all public fucntions to return
|
||||
a gpg_error_t.
|
||||
(iso7816_change_reference_data): New.
|
||||
* apdu.c (apdu_open_reader): Use faked status words for soem
|
||||
system errors.
|
||||
|
||||
2003-07-16 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* apdu.c (apdu_send_simple): Use apdu_send_le so that we can
|
||||
specify not to send Le as it should be.
|
||||
|
||||
2003-07-15 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* Makefile.am: Add sc-copykeys program.
|
||||
* sc-copykeys.c: New.
|
||||
* app-openpgp.c (app_openpgp_storekey): New.
|
||||
(app_openpgp_cardinfo): New.
|
||||
(count_bits): New.
|
||||
(store_fpr): And use it here to get the actual length in bit.
|
||||
|
||||
2003-07-03 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* app-openpgp.c (do_setattr): Add setting of the URL.
|
||||
(app_select_openpgp): Dump card data only in very verbose mode.
|
||||
(do_decipher): New.
|
||||
|
||||
2003-07-02 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* app-openpgp.c (get_sig_counter): New.
|
||||
(do_sign): Print the signature counter and enable the PIN callback.
|
||||
(do_genkey): Implement the PIN callback.
|
||||
|
||||
2003-07-01 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* app-openpgp.c (store_fpr): Fixed fingerprint calculation.
|
||||
|
||||
2003-06-26 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* app-openpgp.c (find_tlv): Fixed length header parsing.
|
||||
|
||||
* app.c (app_genkey): New.
|
||||
* command.c (cmd_genkey): New.
|
||||
|
||||
2003-06-25 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* command.c (percent_plus_unescape): New.
|
||||
(cmd_setattr): New.
|
||||
|
||||
2003-06-24 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* command.c (send_status_info): New.
|
||||
|
||||
* app-openpgp.c (app_select_openpgp): Replace SLOT arg by APP arg
|
||||
and setup the function pointers in APP on success. Changed callers.
|
||||
* app.c: New.
|
||||
* app-common.h: New.
|
||||
* scdaemon.h (APP): New type to handle applications.
|
||||
(server_control_s): Add an APP context field.
|
||||
|
||||
* command.c (cmd_serialno): Handle applications.
|
||||
(cmd_pksign): Ditto.
|
||||
(cmd_pkdecrypt): Ditto.
|
||||
(reset_notify): Ditto.
|
||||
(cmd_learn): For now return error for application contexts.
|
||||
(cmd_readcert): Ditto.
|
||||
(cmd_readkey): Ditto.
|
||||
|
||||
2003-06-04 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* card.c (map_sc_err): Renamed gpg_make_err to gpg_err_make.
|
||||
|
||||
Renamed error codes from INVALID to INV and removed _ERROR suffixes.
|
||||
|
||||
2003-06-03 Werner Koch <wk@gnupg.org>
|
||||
|
||||
Changed all error codes in all files to the new libgpg-error scheme.
|
||||
|
||||
* scdaemon.h: Include gpg-error.h and errno.h
|
||||
* card.c (map_sc_err): Use unknown for the error source.
|
||||
* Makefile.am: Link with libgpg-error
|
||||
|
||||
2003-05-14 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* atr.c, atr.h: New.
|
||||
* sc-investigate.c: Dump the ATR in a human readable format.
|
||||
|
||||
2003-05-08 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* scdaemon.h (DBG_CARD_IO_VALUE): New.
|
||||
|
||||
* sc-investigate.c: New.
|
||||
* scdaemon.c (main): Removed --print-atr option.
|
||||
|
||||
* iso7816.c, iso7816.h, app-openpgp.c: New.
|
||||
|
||||
2003-04-29 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* scdaemon.c: New options --print-atr and --reader-port
|
||||
* apdu.c, apdu.h: New
|
||||
|
||||
* card.c, card-p15.c, card-dinsig.c: Allow build without OpenSC.
|
||||
|
||||
* Makefile.am (LDFLAGS): Removed.
|
||||
|
||||
* command.c (register_commands): Adjusted for new Assuan semantics.
|
||||
|
||||
2002-08-21 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* scdaemon.c (main): New option --daemon so that the program is
|
||||
not accidently started in the background.
|
||||
|
||||
2002-08-16 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* scdaemon.c: Include i18n.h.
|
||||
|
||||
* card-common.h (struct p15_private_s): Forward declaration. Add
|
||||
it to card_ctx_s.
|
||||
* card.c (card_close): Make sure private data is released.
|
||||
(card_enum_certs): New.
|
||||
* card-p15.c (p15_release_private_data): New.
|
||||
(init_private_data): New to work around an OpenSC weirdness.
|
||||
(p15_enum_keypairs): Do an OpenSC get_objects only once.
|
||||
(p15_enum_certs): New.
|
||||
(card_p15_bind): Bind new function.
|
||||
* command.c (cmd_learn): Return information about the certificates.
|
||||
|
||||
2002-08-09 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* card.c (card_get_serial_and_stamp): Use the tokeinfo serial
|
||||
number as a fallback. Add a special prefix for serial numbers.
|
||||
|
||||
2002-07-30 Werner Koch <wk@gnupg.org>
|
||||
|
||||
Changes to cope with OpenSC 0.7.0:
|
||||
|
||||
* card.c: Removed the check for the packed opensc version.
|
||||
Changed include file names of opensc.
|
||||
(map_sc_err): Adjusted error codes for new opensc version.
|
||||
* card-p15.c: Changed include filename of opensc.
|
||||
* card-dinsig.c: Ditto.
|
||||
|
||||
* card-p15.c (p15_decipher): Add flags argument to OpenSC call.
|
||||
|
||||
2002-07-24 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* card.c (find_simple_tlv, find_iccsn): New.
|
||||
(card_get_serial_and_stamp): Improved serial number parser.
|
||||
|
||||
2002-06-27 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* scdaemon.c (main): Use GNUPG_DEFAULT_HOMEDIR constant.
|
||||
|
||||
2002-06-15 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* card-dinsig.c: Documented some stuff from the DIN norm.
|
||||
|
||||
2002-04-15 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* command.c (cmd_pksign, cmd_pkdecrypt): Use a copy of the key ID.
|
||||
|
||||
2002-04-12 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* scdaemon.c: New option --debug-sc N.
|
||||
* card.c (card_open): set it here.
|
||||
|
||||
* card-p15.c (p15_prepare_key): Factored out common code from ...
|
||||
(p15_sign, p15_decipher): here and made the decryption work the
|
||||
regular way.
|
||||
|
||||
2002-04-10 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* card.c (card_open): Return immediately when no reader is available.
|
||||
|
||||
2002-03-27 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* card.c (card_open, card_close): Adjusted for changes in OpenSC.
|
||||
|
||||
2002-03-10 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* card-p15.c, card-dinsig.c, card-common.h: New.
|
||||
* card.c: Factored most code out to the new modules, so that we
|
||||
can better support different types of card applications.
|
||||
|
||||
2002-01-26 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* scdaemon.c scdaemon.h, command.c: New. Based on the code from
|
||||
the gpg-agent.
|
||||
|
||||
Copyright 2002 Free Software Foundation, Inc.
|
||||
|
||||
This file is free software; as a special exception the author gives
|
||||
unlimited permission to copy and/or distribute it, with or without
|
||||
modifications, as long as this notice is preserved.
|
||||
|
||||
This file is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
72
scd/Makefile.am
Normal file
72
scd/Makefile.am
Normal file
|
@ -0,0 +1,72 @@
|
|||
# Copyright (C) 2002, 2003 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is part of GnuPG.
|
||||
#
|
||||
# GnuPG is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# GnuPG is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
|
||||
## Process this file with automake to produce Makefile.in
|
||||
|
||||
localedir = $(datadir)/locale
|
||||
INCLUDES = -I../intl -DLOCALEDIR=\"$(localedir)\"
|
||||
|
||||
bin_PROGRAMS = scdaemon sc-investigate sc-copykeys
|
||||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/common $(OPENSC_CFLAGS) $(LIBGCRYPT_CFLAGS) \
|
||||
$(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS)
|
||||
|
||||
scdaemon_SOURCES = \
|
||||
scdaemon.c scdaemon.h \
|
||||
command.c card.c \
|
||||
card-common.h \
|
||||
card-p15.c card-dinsig.c \
|
||||
apdu.c apdu.h \
|
||||
iso7816.c iso7816.h \
|
||||
app.c app-common.h \
|
||||
app-openpgp.c
|
||||
|
||||
scdaemon_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \
|
||||
$(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) \
|
||||
-lgpg-error @INTLLIBS@ -ldl
|
||||
|
||||
sc_investigate_SOURCES = \
|
||||
sc-investigate.c scdaemon.h \
|
||||
apdu.c apdu.h \
|
||||
iso7816.c iso7816.h \
|
||||
app.c app-common.h \
|
||||
app-openpgp.c \
|
||||
atr.c atr.h
|
||||
|
||||
sc_investigate_LDADD = \
|
||||
../jnlib/libjnlib.a ../common/libcommon.a \
|
||||
$(LIBGCRYPT_LIBS) @INTLLIBS@ -lgpg-error -ldl
|
||||
|
||||
|
||||
sc_copykeys_SOURCES = \
|
||||
sc-copykeys.c scdaemon.h \
|
||||
apdu.c apdu.h \
|
||||
iso7816.c iso7816.h \
|
||||
app.c app-common.h \
|
||||
app-openpgp.c \
|
||||
atr.c atr.h
|
||||
|
||||
sc_copykeys_LDADD = \
|
||||
../jnlib/libjnlib.a ../common/libcommon.a \
|
||||
../common/libsimple-pwquery.a \
|
||||
$(LIBGCRYPT_LIBS) -lgpg-error @INTLLIBS@ -ldl
|
||||
|
||||
|
||||
|
||||
|
||||
|
558
scd/apdu.c
Normal file
558
scd/apdu.c
Normal file
|
@ -0,0 +1,558 @@
|
|||
/* apdu.c - ISO 7816 APDU functions and low level I/O
|
||||
* Copyright (C) 2003 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
* GnuPG is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GnuPG is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dlfcn.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "scdaemon.h"
|
||||
#include "apdu.h"
|
||||
|
||||
#define HAVE_CTAPI 1
|
||||
|
||||
#define MAX_READER 4 /* Number of readers we support concurrently. */
|
||||
#define CARD_CONNECT_TIMEOUT 1 /* Number of seconds to wait for
|
||||
insertion of the card (1 = don't wait). */
|
||||
|
||||
|
||||
|
||||
/* A global table to keep track of active readers. */
|
||||
static struct {
|
||||
int used; /* True if slot is used. */
|
||||
unsigned short port; /* port number0 = unused, 1 - dev/tty */
|
||||
int status;
|
||||
unsigned char atr[33];
|
||||
size_t atrlen;
|
||||
} reader_table[MAX_READER];
|
||||
|
||||
|
||||
/* ct API function pointer. */
|
||||
static char (*CT_init) (unsigned short ctn, unsigned short Pn);
|
||||
static char (*CT_data) (unsigned short ctn, unsigned char *dad,
|
||||
unsigned char *sad, unsigned short lc,
|
||||
unsigned char *cmd, unsigned short *lr,
|
||||
unsigned char *rsp);
|
||||
static char (*CT_close) (unsigned short ctn);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Helper
|
||||
*/
|
||||
|
||||
|
||||
/* Find an unused reader slot for PORT and put it into the reader
|
||||
table. Return -1 on error or the index into the reader table. */
|
||||
static int
|
||||
new_reader_slot (int port)
|
||||
{
|
||||
int i, reader = -1;
|
||||
|
||||
if (port < 0 || port > 0xffff)
|
||||
{
|
||||
log_error ("new_reader_slot: invalid port %d requested\n", port);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i=0; i < MAX_READER; i++)
|
||||
{
|
||||
if (reader_table[i].used && reader_table[i].port == port)
|
||||
{
|
||||
log_error ("new_reader_slot: requested port %d already in use\n",
|
||||
reader);
|
||||
return -1;
|
||||
}
|
||||
else if (!reader_table[i].used && reader == -1)
|
||||
reader = i;
|
||||
}
|
||||
if (reader == -1)
|
||||
{
|
||||
log_error ("new_reader_slot: out of slots\n");
|
||||
return -1;
|
||||
}
|
||||
reader_table[reader].used = 1;
|
||||
reader_table[reader].port = port;
|
||||
return reader;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
dump_reader_status (int reader)
|
||||
{
|
||||
log_info ("reader %d: %s\n", reader,
|
||||
reader_table[reader].status == 1? "Processor ICC present" :
|
||||
reader_table[reader].status == 0? "Memory ICC present" :
|
||||
"ICC not present" );
|
||||
|
||||
if (reader_table[reader].status != -1)
|
||||
{
|
||||
log_info ("reader %d: ATR=", reader);
|
||||
log_printhex ("", reader_table[reader].atr,
|
||||
reader_table[reader].atrlen);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef HAVE_CTAPI
|
||||
/*
|
||||
ct API Interface
|
||||
*/
|
||||
|
||||
static const char *
|
||||
ct_error_string (int err)
|
||||
{
|
||||
switch (err)
|
||||
{
|
||||
case 0: return "okay";
|
||||
case -1: return "invalid data";
|
||||
case -8: return "ct error";
|
||||
case -10: return "transmission error";
|
||||
case -11: return "memory allocation error";
|
||||
case -128: return "HTSI error";
|
||||
default: return "unknown CT-API error";
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for the card in READER and activate it. Return -1 on error or
|
||||
0 on success. */
|
||||
static int
|
||||
ct_activate_card (int reader)
|
||||
{
|
||||
int rc, count;
|
||||
|
||||
for (count = 0; count < CARD_CONNECT_TIMEOUT; count++)
|
||||
{
|
||||
unsigned char dad[1], sad[1], cmd[11], buf[256];
|
||||
unsigned short buflen;
|
||||
|
||||
if (count)
|
||||
sleep (1); /* FIXME: we should use a more reliable timer. */
|
||||
|
||||
/* Check whether card has been inserted. */
|
||||
dad[0] = 1; /* Destination address: CT. */
|
||||
sad[0] = 2; /* Source address: Host. */
|
||||
|
||||
cmd[0] = 0x20; /* Class byte. */
|
||||
cmd[1] = 0x13; /* Request status. */
|
||||
cmd[2] = 0x00; /* From kernel. */
|
||||
cmd[3] = 0x80; /* Return card's DO. */
|
||||
cmd[4] = 0x00;
|
||||
|
||||
buflen = DIM(buf);
|
||||
|
||||
rc = CT_data (reader, dad, sad, 5, cmd, &buflen, buf);
|
||||
if (rc || buflen < 2 || buf[buflen-2] != 0x90)
|
||||
{
|
||||
log_error ("ct_activate_card: can't get status of reader %d: %s\n",
|
||||
reader, ct_error_string (rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (buf[0] == 0x05)
|
||||
{ /* Connected, now activate the card. */
|
||||
dad[0] = 1; /* Destination address: CT. */
|
||||
sad[0] = 2; /* Source address: Host. */
|
||||
|
||||
cmd[0] = 0x20; /* Class byte. */
|
||||
cmd[1] = 0x12; /* Request ICC. */
|
||||
cmd[2] = 0x01; /* From first interface. */
|
||||
cmd[3] = 0x01; /* Return card's ATR. */
|
||||
cmd[4] = 0x00;
|
||||
|
||||
buflen = DIM(buf);
|
||||
|
||||
rc = CT_data (reader, dad, sad, 5, cmd, &buflen, buf);
|
||||
if (rc || buflen < 2 || buf[buflen-2] != 0x90)
|
||||
{
|
||||
log_error ("ct_activate_card(%d): activation failed: %s\n",
|
||||
reader, ct_error_string (rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Store the type and the ATR. */
|
||||
if (buflen - 2 > DIM (reader_table[0].atr))
|
||||
{
|
||||
log_error ("ct_activate_card(%d): ATR too long\n", reader);
|
||||
return -1;
|
||||
}
|
||||
|
||||
reader_table[reader].status = buf[buflen - 1];
|
||||
memcpy (reader_table[reader].atr, buf, buflen - 2);
|
||||
reader_table[reader].atrlen = buflen - 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
log_info ("ct_activate_card(%d): timeout waiting for card\n", reader);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* Open a reader and return an internal handle for it. PORT is a
|
||||
non-negative value with the port number of the reader. USB readers
|
||||
do have port numbers starting at 32769. */
|
||||
static int
|
||||
open_ct_reader (int port)
|
||||
{
|
||||
int rc, reader;
|
||||
|
||||
reader = new_reader_slot (port);
|
||||
if (reader == -1)
|
||||
return reader;
|
||||
|
||||
rc = CT_init (reader, (unsigned short)port);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("apdu_open_ct_reader failed on port %d: %s\n",
|
||||
port, ct_error_string (rc));
|
||||
reader_table[reader].used = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = ct_activate_card (reader);
|
||||
if (rc)
|
||||
{
|
||||
reader_table[reader].used = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
dump_reader_status (reader);
|
||||
return reader;
|
||||
}
|
||||
|
||||
|
||||
/* Actually send the APDU of length APDULEN to SLOT and return a
|
||||
maximum of *BUFLEN data in BUFFER, the actual retruned size will be
|
||||
set to BUFLEN. Returns: CT API error code. */
|
||||
static int
|
||||
ct_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
|
||||
unsigned char *buffer, size_t *buflen)
|
||||
{
|
||||
int rc;
|
||||
unsigned char dad[1], sad[1];
|
||||
unsigned short ctbuflen;
|
||||
|
||||
dad[0] = 0; /* Destination address: Card. */
|
||||
sad[0] = 2; /* Source address: Host. */
|
||||
ctbuflen = *buflen;
|
||||
if (DBG_CARD_IO)
|
||||
log_printhex (" CT_data:", apdu, apdulen);
|
||||
rc = CT_data (slot, dad, sad, apdulen, apdu, &ctbuflen, buffer);
|
||||
*buflen = ctbuflen;
|
||||
|
||||
/* FIXME: map the errorcodes to GNUPG ones, so that they can be
|
||||
shared between CTAPI and PCSC. */
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
#endif /*HAVE_CTAPI*/
|
||||
|
||||
|
||||
#ifdef HAVE_PCSC
|
||||
/*
|
||||
PC/SC Interface
|
||||
*/
|
||||
|
||||
|
||||
#endif /*HAVE_PCSC*/
|
||||
|
||||
|
||||
/*
|
||||
Driver Access
|
||||
*/
|
||||
|
||||
/* Open the reader and return an internal slot number or -1 on
|
||||
error. */
|
||||
int
|
||||
apdu_open_reader (int port)
|
||||
{
|
||||
static int ct_api_loaded;
|
||||
|
||||
if (!ct_api_loaded)
|
||||
{
|
||||
void *handle;
|
||||
|
||||
handle = dlopen ("libtowitoko.so", RTLD_LAZY);
|
||||
if (!handle)
|
||||
{
|
||||
log_error ("apdu_open_reader: failed to open driver: %s",
|
||||
dlerror ());
|
||||
return -1;
|
||||
}
|
||||
CT_init = dlsym (handle, "CT_init");
|
||||
CT_data = dlsym (handle, "CT_data");
|
||||
CT_close = dlsym (handle, "CT_close");
|
||||
if (!CT_init || !CT_data || !CT_close)
|
||||
{
|
||||
log_error ("apdu_open_reader: invalid driver\n");
|
||||
dlclose (handle);
|
||||
return -1;
|
||||
}
|
||||
ct_api_loaded = 1;
|
||||
}
|
||||
return open_ct_reader (port);
|
||||
}
|
||||
|
||||
|
||||
unsigned char *
|
||||
apdu_get_atr (int slot, size_t *atrlen)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
|
||||
return NULL;
|
||||
|
||||
buf = xtrymalloc (reader_table[slot].atrlen);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
memcpy (buf, reader_table[slot].atr, reader_table[slot].atrlen);
|
||||
*atrlen = reader_table[slot].atrlen;
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
static const char *
|
||||
error_string (int slot, int rc)
|
||||
{
|
||||
#ifdef HAVE_CTAPI
|
||||
return ct_error_string (rc);
|
||||
#elif defined(HAVE_PCSC)
|
||||
return "?";
|
||||
#else
|
||||
return "?";
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* Dispatcher for the actual send_apdu fucntion. */
|
||||
static int
|
||||
send_apdu (int slot, unsigned char *apdu, size_t apdulen,
|
||||
unsigned char *buffer, size_t *buflen)
|
||||
{
|
||||
#ifdef HAVE_CTAPI
|
||||
return ct_send_apdu (slot, apdu, apdulen, buffer, buflen);
|
||||
#elif defined(HAVE_PCSC)
|
||||
return SW_HOST_NO_DRIVER;
|
||||
#else
|
||||
return SW_HOST_NO_DRIVER;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Send an APDU to the card in SLOT. The APDU is created from all
|
||||
given parameters: CLASS, INS, P0, P1, LC, DATA, LE. A value of -1
|
||||
for LC won't sent this field and the data field; in this case DATA
|
||||
must also be passed as NULL. The return value is the status word
|
||||
or -1 for an invalid SLOT or other non card related error. If
|
||||
RETBUF is not NULL, it will receive an allocated buffer with the
|
||||
returned data. The length of that data will be put into
|
||||
*RETBUFLEN. The caller is reponsible for releasing the buffer even
|
||||
in case of errors. */
|
||||
int
|
||||
apdu_send_le(int slot, int class, int ins, int p0, int p1,
|
||||
int lc, const char *data, int le,
|
||||
unsigned char **retbuf, size_t *retbuflen)
|
||||
{
|
||||
unsigned char result[256+10]; /* 10 extra in case of bugs in the driver. */
|
||||
size_t resultlen = 256;
|
||||
unsigned char apdu[5+256+1];
|
||||
size_t apdulen;
|
||||
int rc, sw;
|
||||
|
||||
if (DBG_CARD_IO)
|
||||
log_debug ("send apdu: c=%02X i=%02X p0=%02X p1=%02X lc=%d le=%d\n",
|
||||
class, ins, p0, p1, lc, le);
|
||||
|
||||
if (lc != -1 && (lc > 255 || lc < 0))
|
||||
return SW_WRONG_LENGTH;
|
||||
if (le != -1 && (le > 256 || le < 1))
|
||||
return SW_WRONG_LENGTH;
|
||||
if ((!data && lc != -1) || (data && lc == -1))
|
||||
return SW_HOST_INV_VALUE;
|
||||
|
||||
apdulen = 0;
|
||||
apdu[apdulen++] = class;
|
||||
apdu[apdulen++] = ins;
|
||||
apdu[apdulen++] = p0;
|
||||
apdu[apdulen++] = p1;
|
||||
if (lc != -1)
|
||||
{
|
||||
apdu[apdulen++] = lc;
|
||||
memcpy (apdu+apdulen, data, lc);
|
||||
apdulen += lc;
|
||||
}
|
||||
if (le != -1)
|
||||
apdu[apdulen++] = le; /* Truncation is okay becuase 0 means 256. */
|
||||
assert (sizeof (apdu) >= apdulen);
|
||||
/* As safeguard don't pass any garbage from the stack to the driver. */
|
||||
memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
|
||||
rc = send_apdu (slot, apdu, apdulen, result, &resultlen);
|
||||
if (rc || resultlen < 2)
|
||||
{
|
||||
log_error ("apdu_send_simple(%d) failed: %s\n",
|
||||
slot, error_string (slot, rc));
|
||||
return SW_HOST_INCOMPLETE_CARD_RESPONSE;
|
||||
}
|
||||
sw = (result[resultlen-2] << 8) | result[resultlen-1];
|
||||
/* store away the returned data but strip the statusword. */
|
||||
resultlen -= 2;
|
||||
if (DBG_CARD_IO)
|
||||
{
|
||||
log_debug (" response: sw=%04X datalen=%d\n", sw, resultlen);
|
||||
if ( !retbuf && (sw == SW_SUCCESS || (sw & 0xff00) == SW_MORE_DATA))
|
||||
log_printhex (" dump: ", result, resultlen);
|
||||
}
|
||||
|
||||
if (sw == SW_SUCCESS)
|
||||
{
|
||||
if (retbuf)
|
||||
{
|
||||
*retbuf = xtrymalloc (resultlen? resultlen : 1);
|
||||
if (!*retbuf)
|
||||
return SW_HOST_OUT_OF_CORE;
|
||||
*retbuflen = resultlen;
|
||||
memcpy (*retbuf, result, resultlen);
|
||||
}
|
||||
}
|
||||
else if ((sw & 0xff00) == SW_MORE_DATA)
|
||||
{
|
||||
unsigned char *p = NULL, *tmp;
|
||||
size_t bufsize = 4096;
|
||||
|
||||
/* It is likely that we need to return much more data, so we
|
||||
start off with a large buffer. */
|
||||
if (retbuf)
|
||||
{
|
||||
*retbuf = p = xtrymalloc (bufsize);
|
||||
if (!*retbuf)
|
||||
return SW_HOST_OUT_OF_CORE;
|
||||
assert (resultlen < bufsize);
|
||||
memcpy (p, result, resultlen);
|
||||
p += resultlen;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
int len = (sw & 0x00ff);
|
||||
|
||||
log_debug ("apdu_send_simple(%d): %d more bytes available\n",
|
||||
slot, len);
|
||||
apdulen = 0;
|
||||
apdu[apdulen++] = class;
|
||||
apdu[apdulen++] = 0xC0;
|
||||
apdu[apdulen++] = 0;
|
||||
apdu[apdulen++] = 0;
|
||||
apdu[apdulen++] = 64; /* that is 256 bytes for Le */
|
||||
memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
|
||||
rc = send_apdu (slot, apdu, apdulen, result, &resultlen);
|
||||
if (rc || resultlen < 2)
|
||||
{
|
||||
log_error ("apdu_send_simple(%d) for get response failed: %s\n",
|
||||
slot, error_string (slot, rc));
|
||||
return SW_HOST_INCOMPLETE_CARD_RESPONSE;
|
||||
}
|
||||
sw = (result[resultlen-2] << 8) | result[resultlen-1];
|
||||
resultlen -= 2;
|
||||
if (DBG_CARD_IO)
|
||||
{
|
||||
log_debug (" more: sw=%04X datalen=%d\n", sw, resultlen);
|
||||
if (!retbuf && (sw==SW_SUCCESS || (sw&0xff00)==SW_MORE_DATA))
|
||||
log_printhex (" dump: ", result, resultlen);
|
||||
}
|
||||
|
||||
if ((sw & 0xff00) == SW_MORE_DATA || sw == SW_SUCCESS)
|
||||
{
|
||||
if (retbuf)
|
||||
{
|
||||
if (p - *retbuf + resultlen > bufsize)
|
||||
{
|
||||
bufsize += resultlen > 4096? resultlen: 4096;
|
||||
tmp = xtryrealloc (*retbuf, bufsize);
|
||||
if (!tmp)
|
||||
return SW_HOST_OUT_OF_CORE;
|
||||
p = tmp + (p - *retbuf);
|
||||
*retbuf = tmp;
|
||||
}
|
||||
memcpy (p, result, resultlen);
|
||||
p += resultlen;
|
||||
}
|
||||
}
|
||||
else
|
||||
log_info ("apdu_send_simple(%d) "
|
||||
"got unexpected status %04X from get response\n",
|
||||
slot, sw);
|
||||
}
|
||||
while ((sw & 0xff00) == SW_MORE_DATA);
|
||||
|
||||
if (retbuf)
|
||||
{
|
||||
*retbuflen = p - *retbuf;
|
||||
tmp = xtryrealloc (*retbuf, *retbuflen);
|
||||
if (tmp)
|
||||
*retbuf = tmp;
|
||||
}
|
||||
}
|
||||
if (DBG_CARD_IO && retbuf && sw == SW_SUCCESS)
|
||||
log_printhex (" dump: ", *retbuf, *retbuflen);
|
||||
|
||||
return sw;
|
||||
}
|
||||
|
||||
/* Send an APDU to the card in SLOT. The APDU is created from all
|
||||
given parameters: CLASS, INS, P0, P1, LC, DATA. A value of -1 for
|
||||
LC won't sent this field and the data field; in this case DATA must
|
||||
also be passed as NULL. The return value is the status word or -1
|
||||
for an invalid SLOT or other non card related error. If RETBUF is
|
||||
not NULL, it will receive an allocated buffer with the returned
|
||||
data. The length of that data will be put into *RETBUFLEN. The
|
||||
caller is reponsible for releasing the buffer even in case of
|
||||
errors. */
|
||||
int
|
||||
apdu_send (int slot, int class, int ins, int p0, int p1,
|
||||
int lc, const char *data, unsigned char **retbuf, size_t *retbuflen)
|
||||
{
|
||||
return apdu_send_le (slot, class, ins, p0, p1, lc, data, 256,
|
||||
retbuf, retbuflen);
|
||||
}
|
||||
|
||||
/* Send an APDU to the card in SLOT. The APDU is created from all
|
||||
given parameters: CLASS, INS, P0, P1, LC, DATA. A value of -1 for
|
||||
LC won't sent this field and the data field; in this case DATA must
|
||||
also be passed as NULL. The return value is the status word or -1
|
||||
for an invalid SLOT or other non card related error. No data will be
|
||||
returned. */
|
||||
int
|
||||
apdu_send_simple (int slot, int class, int ins, int p0, int p1,
|
||||
int lc, const char *data)
|
||||
{
|
||||
return apdu_send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
73
scd/apdu.h
Normal file
73
scd/apdu.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
/* apdu.h - ISO 7816 APDU functions and low level I/O
|
||||
* Copyright (C) 2003 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
* GnuPG is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GnuPG is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#ifndef APDU_H
|
||||
#define APDU_H
|
||||
|
||||
/* ISO 7816 values for the statusword are defined here because they
|
||||
should not be visible to the users of the actual ISO command
|
||||
API. */
|
||||
enum {
|
||||
SW_MORE_DATA = 0x6100, /* Note: that the low byte must be
|
||||
masked of.*/
|
||||
SW_EEPROM_FAILURE = 0x6581,
|
||||
SW_WRONG_LENGTH = 0x6700,
|
||||
SW_CHV_WRONG = 0x6982,
|
||||
SW_CHV_BLOCKED = 0x6983,
|
||||
SW_USE_CONDITIONS = 0x6985,
|
||||
SW_NOT_SUPPORTED = 0x6a81,
|
||||
SW_BAD_PARAMETER = 0x6a80, /* (in the data field) */
|
||||
SW_REF_NOT_FOUND = 0x6a88,
|
||||
SW_BAD_P0_P1 = 0x6b00,
|
||||
SW_INS_NOT_SUP = 0x6d00,
|
||||
SW_CLA_NOT_SUP = 0x6e00,
|
||||
SW_SUCCESS = 0x9000,
|
||||
|
||||
/* The follwoing statuswords are no real ones but used to map host
|
||||
OS errors into status words. A status word is 16 bit so that
|
||||
those values can't be issued by a card. */
|
||||
SW_HOST_OUT_OF_CORE = 0x10001, /* No way yet to differentiate
|
||||
between errnos on a failed malloc. */
|
||||
SW_HOST_INV_VALUE = 0x10002,
|
||||
SW_HOST_INCOMPLETE_CARD_RESPONSE = 0x10003,
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* Note , that apdu_open_reader returns no status word but -1 on error. */
|
||||
int apdu_open_reader (int port);
|
||||
unsigned char *apdu_get_atr (int slot, size_t *atrlen);
|
||||
|
||||
|
||||
/* The apdu send functions do return status words. */
|
||||
int apdu_send_simple (int slot, int class, int ins, int p0, int p1,
|
||||
int lc, const char *data);
|
||||
int apdu_send (int slot, int class, int ins, int p0, int p1,
|
||||
int lc, const char *data,
|
||||
unsigned char **retbuf, size_t *retbuflen);
|
||||
int apdu_send_le (int slot, int class, int ins, int p0, int p1,
|
||||
int lc, const char *data, int le,
|
||||
unsigned char **retbuf, size_t *retbuflen);
|
||||
|
||||
|
||||
#endif /*APDU_H*/
|
||||
|
||||
|
||||
|
128
scd/app-common.h
Normal file
128
scd/app-common.h
Normal file
|
@ -0,0 +1,128 @@
|
|||
/* app-common.h - Common declarations for all card applications
|
||||
* Copyright (C) 2003 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
* GnuPG is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GnuPG is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#ifndef GNUPG_SCD_APP_COMMON_H
|
||||
#define GNUPG_SCD_APP_COMMON_H
|
||||
|
||||
struct app_ctx_s {
|
||||
int initialized; /* The application has been initialied and the
|
||||
function pointers may be used. Note that for
|
||||
unsupported operations the particular
|
||||
function pointer is set to NULL */
|
||||
int slot; /* Used reader. */
|
||||
unsigned char *serialno; /* Serialnumber in raw form, allocated. */
|
||||
size_t serialnolen; /* Length in octets of serialnumber. */
|
||||
unsigned int card_version;
|
||||
int did_chv1;
|
||||
int did_chv2;
|
||||
int did_chv3;
|
||||
struct {
|
||||
int (*learn_status) (APP app, CTRL ctrl);
|
||||
int (*setattr) (APP app, const char *name,
|
||||
int (*pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const unsigned char *value, size_t valuelen);
|
||||
int (*sign) (APP app,
|
||||
const char *keyidstr, int hashalgo,
|
||||
int (pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const void *indata, size_t indatalen,
|
||||
unsigned char **outdata, size_t *outdatalen );
|
||||
int (*auth) (APP app, const char *keyidstr,
|
||||
int (*pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const void *indata, size_t indatalen,
|
||||
unsigned char **outdata, size_t *outdatalen);
|
||||
int (*decipher) (APP app, const char *keyidstr,
|
||||
int (pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const void *indata, size_t indatalen,
|
||||
unsigned char **outdata, size_t *outdatalen);
|
||||
int (*genkey) (APP app, CTRL ctrl,
|
||||
const char *keynostr, unsigned int flags,
|
||||
int (*pincb)(void*, const char *, char **),
|
||||
void *pincb_arg);
|
||||
int (*change_pin) (APP app, CTRL ctrl,
|
||||
const char *chvnostr, int reset_mode,
|
||||
int (*pincb)(void*, const char *, char **),
|
||||
void *pincb_arg);
|
||||
} fnc;
|
||||
|
||||
|
||||
};
|
||||
|
||||
/*-- app.c --*/
|
||||
APP select_application (void);
|
||||
int app_get_serial_and_stamp (APP app, char **serial, time_t *stamp);
|
||||
int app_write_learn_status (APP app, CTRL ctrl);
|
||||
int app_setattr (APP app, const char *name,
|
||||
int (*pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const unsigned char *value, size_t valuelen);
|
||||
int app_sign (APP app, const char *keyidstr, int hashalgo,
|
||||
int (pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const void *indata, size_t indatalen,
|
||||
unsigned char **outdata, size_t *outdatalen );
|
||||
int app_auth (APP app, const char *keyidstr,
|
||||
int (*pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const void *indata, size_t indatalen,
|
||||
unsigned char **outdata, size_t *outdatalen);
|
||||
int app_decipher (APP app, const char *keyidstr,
|
||||
int (pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const void *indata, size_t indatalen,
|
||||
unsigned char **outdata, size_t *outdatalen );
|
||||
int app_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
|
||||
int (*pincb)(void*, const char *, char **),
|
||||
void *pincb_arg);
|
||||
int app_get_challenge (APP app, size_t nbytes, unsigned char *buffer);
|
||||
int app_change_pin (APP app, CTRL ctrl, const char *chvnostr, int reset_mode,
|
||||
int (*pincb)(void*, const char *, char **),
|
||||
void *pincb_arg);
|
||||
|
||||
|
||||
/*-- app-openpgp.c --*/
|
||||
int app_select_openpgp (APP app, unsigned char **sn, size_t *snlen);
|
||||
|
||||
int app_openpgp_cardinfo (APP app,
|
||||
char **serialno,
|
||||
char **disp_name,
|
||||
char **pubkey_url,
|
||||
unsigned char **fpr1,
|
||||
unsigned char **fpr2,
|
||||
unsigned char **fpr3);
|
||||
int app_openpgp_storekey (APP app, int keyno,
|
||||
unsigned char *template, size_t template_len,
|
||||
time_t created_at,
|
||||
const unsigned char *m, size_t mlen,
|
||||
const unsigned char *e, size_t elen,
|
||||
int (*pincb)(void*, const char *, char **),
|
||||
void *pincb_arg);
|
||||
int app_openpgp_readkey (APP app, int keyno,
|
||||
unsigned char **m, size_t *mlen,
|
||||
unsigned char **e, size_t *elen);
|
||||
|
||||
|
||||
#endif /*GNUPG_SCD_APP_COMMON_H*/
|
||||
|
||||
|
||||
|
1482
scd/app-openpgp.c
Normal file
1482
scd/app-openpgp.c
Normal file
File diff suppressed because it is too large
Load diff
278
scd/app.c
Normal file
278
scd/app.c
Normal file
|
@ -0,0 +1,278 @@
|
|||
/* app.c - Application selection.
|
||||
* Copyright (C) 2003 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
* GnuPG is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GnuPG is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "scdaemon.h"
|
||||
#include "app-common.h"
|
||||
#include "apdu.h"
|
||||
#include "iso7816.h"
|
||||
|
||||
/* The select the best fitting application and return a context.
|
||||
Returns NULL if no application was found or no card is present. */
|
||||
APP
|
||||
select_application (void)
|
||||
{
|
||||
int reader_port = 32768; /* First USB reader. */
|
||||
int slot;
|
||||
int rc;
|
||||
APP app;
|
||||
|
||||
slot = apdu_open_reader (reader_port);
|
||||
if (slot == -1)
|
||||
{
|
||||
log_error ("card reader not available\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
app = xtrycalloc (1, sizeof *app);
|
||||
if (!app)
|
||||
{
|
||||
rc = out_of_core ();
|
||||
log_info ("error allocating context: %s\n", gpg_strerror (rc));
|
||||
/*apdu_close_reader (slot);*/
|
||||
return NULL;
|
||||
}
|
||||
|
||||
app->slot = slot;
|
||||
rc = app_select_openpgp (app, &app->serialno, &app->serialnolen);
|
||||
if (rc)
|
||||
{
|
||||
/* apdu_close_reader (slot); */
|
||||
log_info ("selecting openpgp failed: %s\n", gpg_strerror (rc));
|
||||
xfree (app);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
app->initialized = 1;
|
||||
return app;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Retrieve the serial number and the time of the last update of the
|
||||
card. The serial number is returned as a malloced string (hex
|
||||
encoded) in SERIAL and the time of update is returned in STAMP. If
|
||||
no update time is available the returned value is 0. Caller must
|
||||
free SERIAL unless the function returns an error. */
|
||||
int
|
||||
app_get_serial_and_stamp (APP app, char **serial, time_t *stamp)
|
||||
{
|
||||
unsigned char *buf, *p;
|
||||
int i;
|
||||
|
||||
if (!app || !serial || !stamp)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
|
||||
*serial = NULL;
|
||||
*stamp = 0; /* not available */
|
||||
|
||||
buf = xtrymalloc (app->serialnolen * 2 + 1);
|
||||
if (!buf)
|
||||
return gpg_error_from_errno (errno);
|
||||
for (p=buf, i=0; i < app->serialnolen; p +=2, i++)
|
||||
sprintf (p, "%02X", app->serialno[i]);
|
||||
*p = 0;
|
||||
*serial = buf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Write out the application specifig status lines for the LEARN
|
||||
command. */
|
||||
int
|
||||
app_write_learn_status (APP app, CTRL ctrl)
|
||||
{
|
||||
if (!app)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
if (!app->initialized)
|
||||
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
|
||||
if (!app->fnc.learn_status)
|
||||
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
|
||||
return app->fnc.learn_status (app, ctrl);
|
||||
}
|
||||
|
||||
|
||||
/* Perform a SETATTR operation. */
|
||||
int
|
||||
app_setattr (APP app, const char *name,
|
||||
int (*pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const unsigned char *value, size_t valuelen)
|
||||
{
|
||||
if (!app || !name || !*name || !value)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
if (!app->initialized)
|
||||
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
|
||||
if (!app->fnc.setattr)
|
||||
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
|
||||
return app->fnc.setattr (app, name, pincb, pincb_arg, value, valuelen);
|
||||
}
|
||||
|
||||
/* Create the signature and return the allocated result in OUTDATA.
|
||||
If a PIN is required the PINCB will be used to ask for the PIN; it
|
||||
should return the PIN in an allocated buffer and put it into PIN. */
|
||||
int
|
||||
app_sign (APP app, const char *keyidstr, int hashalgo,
|
||||
int (pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const void *indata, size_t indatalen,
|
||||
unsigned char **outdata, size_t *outdatalen )
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!app || !indata || !indatalen || !outdata || !outdatalen || !pincb)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
if (!app->initialized)
|
||||
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
|
||||
if (!app->fnc.sign)
|
||||
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
|
||||
rc = app->fnc.sign (app, keyidstr, hashalgo,
|
||||
pincb, pincb_arg,
|
||||
indata, indatalen,
|
||||
outdata, outdatalen);
|
||||
if (opt.verbose)
|
||||
log_info ("operation sign result: %s\n", gpg_strerror (rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Create the signature using the INTERNAL AUTHENTICATE command and
|
||||
return the allocated result in OUTDATA. If a PIN is required the
|
||||
PINCB will be used to ask for the PIN; it should return the PIN in
|
||||
an allocated buffer and put it into PIN. */
|
||||
int
|
||||
app_auth (APP app, const char *keyidstr,
|
||||
int (pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const void *indata, size_t indatalen,
|
||||
unsigned char **outdata, size_t *outdatalen )
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!app || !indata || !indatalen || !outdata || !outdatalen || !pincb)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
if (!app->initialized)
|
||||
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
|
||||
if (!app->fnc.auth)
|
||||
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
|
||||
rc = app->fnc.auth (app, keyidstr,
|
||||
pincb, pincb_arg,
|
||||
indata, indatalen,
|
||||
outdata, outdatalen);
|
||||
if (opt.verbose)
|
||||
log_info ("operation auth result: %s\n", gpg_strerror (rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Decrypt the data in INDATA and return the allocated result in OUTDATA.
|
||||
If a PIN is required the PINCB will be used to ask for the PIN; it
|
||||
should return the PIN in an allocated buffer and put it into PIN. */
|
||||
int
|
||||
app_decipher (APP app, const char *keyidstr,
|
||||
int (pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const void *indata, size_t indatalen,
|
||||
unsigned char **outdata, size_t *outdatalen )
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!app || !indata || !indatalen || !outdata || !outdatalen || !pincb)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
if (!app->initialized)
|
||||
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
|
||||
if (!app->fnc.decipher)
|
||||
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
|
||||
rc = app->fnc.decipher (app, keyidstr,
|
||||
pincb, pincb_arg,
|
||||
indata, indatalen,
|
||||
outdata, outdatalen);
|
||||
if (opt.verbose)
|
||||
log_info ("operation decipher result: %s\n", gpg_strerror (rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Perform a SETATTR operation. */
|
||||
int
|
||||
app_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
|
||||
int (*pincb)(void*, const char *, char **),
|
||||
void *pincb_arg)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!app || !keynostr || !*keynostr || !pincb)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
if (!app->initialized)
|
||||
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
|
||||
if (!app->fnc.genkey)
|
||||
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
|
||||
rc = app->fnc.genkey (app, ctrl, keynostr, flags, pincb, pincb_arg);
|
||||
if (opt.verbose)
|
||||
log_info ("operation genkey result: %s\n", gpg_strerror (rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Perform a GET CHALLENGE operation. This fucntion is special as it
|
||||
directly accesses the card without any application specific
|
||||
wrapper. */
|
||||
int
|
||||
app_get_challenge (APP app, size_t nbytes, unsigned char *buffer)
|
||||
{
|
||||
if (!app || !nbytes || !buffer)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
if (!app->initialized)
|
||||
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
|
||||
return iso7816_get_challenge (app->slot, nbytes, buffer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Perform a CHANGE REFERENCE DATA or RESET RETRY COUNTER operation. */
|
||||
int
|
||||
app_change_pin (APP app, CTRL ctrl, const char *chvnostr, int reset_mode,
|
||||
int (*pincb)(void*, const char *, char **),
|
||||
void *pincb_arg)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!app || !chvnostr || !*chvnostr || !pincb)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
if (!app->initialized)
|
||||
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
|
||||
if (!app->fnc.change_pin)
|
||||
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
|
||||
rc = app->fnc.change_pin (app, ctrl, chvnostr, reset_mode, pincb, pincb_arg);
|
||||
if (opt.verbose)
|
||||
log_info ("operation change_pin result: %s\n", gpg_strerror (rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
73
scd/card-common.h
Normal file
73
scd/card-common.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
/* card-common.h - Common declarations for all card types
|
||||
* Copyright (C) 2001, 2002 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
* GnuPG is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GnuPG is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#ifndef CARD_COMMON_H
|
||||
#define CARD_COMMON_H
|
||||
|
||||
/* Declaration of private data structure used by card-p15.c */
|
||||
struct p15private_s;
|
||||
|
||||
|
||||
struct card_ctx_s {
|
||||
int reader; /* used reader */
|
||||
struct sc_context *ctx;
|
||||
struct sc_card *scard;
|
||||
struct sc_pkcs15_card *p15card; /* only if there is a pkcs15 application */
|
||||
struct p15private_s *p15priv; /* private data used by card-p15.c */
|
||||
|
||||
struct {
|
||||
int initialized; /* the card has been initialied and the function
|
||||
pointers may be used. However for
|
||||
unsupported operations the particular
|
||||
function pointer is set to NULL */
|
||||
|
||||
int (*enum_keypairs) (CARD card, int idx,
|
||||
unsigned char *keygrip, char **keyid);
|
||||
int (*enum_certs) (CARD card, int idx, char **certid, int *certtype);
|
||||
int (*read_cert) (CARD card, const char *certidstr,
|
||||
unsigned char **cert, size_t *ncert);
|
||||
int (*sign) (CARD card,
|
||||
const char *keyidstr, int hashalgo,
|
||||
int (pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const void *indata, size_t indatalen,
|
||||
unsigned char **outdata, size_t *outdatalen );
|
||||
int (*decipher) (CARD card, const char *keyidstr,
|
||||
int (pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const void *indata, size_t indatalen,
|
||||
unsigned char **outdata, size_t *outdatalen);
|
||||
} fnc;
|
||||
|
||||
};
|
||||
|
||||
/*-- card.c --*/
|
||||
gpg_error_t map_sc_err (int rc);
|
||||
int card_help_get_keygrip (KsbaCert cert, unsigned char *array);
|
||||
|
||||
/*-- card-15.c --*/
|
||||
void p15_release_private_data (CARD card);
|
||||
|
||||
/* constructors */
|
||||
void card_p15_bind (CARD card);
|
||||
void card_dinsig_bind (CARD card);
|
||||
|
||||
|
||||
#endif /*CARD_COMMON_H*/
|
502
scd/card-p15.c
Normal file
502
scd/card-p15.c
Normal file
|
@ -0,0 +1,502 @@
|
|||
/* card-p15.c - PKCS-15 based card access
|
||||
* Copyright (C) 2002 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
* GnuPG is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GnuPG is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef HAVE_OPENSC
|
||||
#include <opensc/pkcs15.h>
|
||||
#include <ksba.h>
|
||||
|
||||
#include "scdaemon.h"
|
||||
#include "card-common.h"
|
||||
|
||||
|
||||
struct p15private_s {
|
||||
int n_prkey_rsa_objs;
|
||||
struct sc_pkcs15_object *prkey_rsa_objs[32];
|
||||
int n_cert_objs;
|
||||
struct sc_pkcs15_object *cert_objs[32];
|
||||
};
|
||||
|
||||
|
||||
/* Allocate private data. */
|
||||
static int
|
||||
init_private_data (CARD card)
|
||||
{
|
||||
struct p15private_s *priv;
|
||||
int rc;
|
||||
|
||||
if (card->p15priv)
|
||||
return 0; /* already done. */
|
||||
|
||||
priv = xtrycalloc (1, sizeof *priv);
|
||||
if (!priv)
|
||||
return out_of_core ();
|
||||
|
||||
/* OpenSC (0.7.0) is a bit strange in that the get_objects functions
|
||||
tries to be a bit too clever and implicitly does an enumeration
|
||||
which eventually leads to the fact that every call to this
|
||||
fucntion returns one more macthing object. The old code in
|
||||
p15_enum_keypairs assume that it would alwyas return the same
|
||||
numer of objects and used this to figure out what the last object
|
||||
enumerated is. We now do an enum_objects just once and keep it
|
||||
in the private data. */
|
||||
rc = sc_pkcs15_get_objects (card->p15card, SC_PKCS15_TYPE_PRKEY_RSA,
|
||||
priv->prkey_rsa_objs,
|
||||
DIM (priv->prkey_rsa_objs));
|
||||
if (rc < 0)
|
||||
{
|
||||
log_error ("private keys enumeration failed: %s\n", sc_strerror (rc));
|
||||
xfree (priv);
|
||||
return gpg_error (GPG_ERR_CARD);
|
||||
}
|
||||
priv->n_prkey_rsa_objs = rc;
|
||||
|
||||
/* Read all certificate objects. */
|
||||
rc = sc_pkcs15_get_objects (card->p15card, SC_PKCS15_TYPE_CERT_X509,
|
||||
priv->cert_objs,
|
||||
DIM (priv->cert_objs));
|
||||
if (rc < 0)
|
||||
{
|
||||
log_error ("private keys enumeration failed: %s\n", sc_strerror (rc));
|
||||
xfree (priv);
|
||||
return gpg_error (GPG_ERR_CARD);
|
||||
}
|
||||
priv->n_cert_objs = rc;
|
||||
|
||||
card->p15priv = priv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Release private data used in this module. */
|
||||
void
|
||||
p15_release_private_data (CARD card)
|
||||
{
|
||||
if (!card->p15priv)
|
||||
return;
|
||||
xfree (card->p15priv);
|
||||
card->p15priv = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* See card.c for interface description */
|
||||
static int
|
||||
p15_enum_keypairs (CARD card, int idx,
|
||||
unsigned char *keygrip, char **keyid)
|
||||
{
|
||||
int rc;
|
||||
KsbaError krc;
|
||||
struct p15private_s *priv;
|
||||
struct sc_pkcs15_object *tmpobj;
|
||||
int nobjs;
|
||||
struct sc_pkcs15_prkey_info *pinfo;
|
||||
struct sc_pkcs15_cert_info *certinfo;
|
||||
struct sc_pkcs15_cert *certder;
|
||||
KsbaCert cert;
|
||||
|
||||
rc = init_private_data (card);
|
||||
if (rc)
|
||||
return rc;
|
||||
priv = card->p15priv;
|
||||
nobjs = priv->n_prkey_rsa_objs;
|
||||
rc = 0;
|
||||
if (idx >= nobjs)
|
||||
return -1;
|
||||
pinfo = priv->prkey_rsa_objs[idx]->data;
|
||||
|
||||
/* now we need to read the certificate so that we can calculate the
|
||||
keygrip */
|
||||
rc = sc_pkcs15_find_cert_by_id (card->p15card, &pinfo->id, &tmpobj);
|
||||
if (rc)
|
||||
{
|
||||
log_info ("certificate for private key %d not found: %s\n",
|
||||
idx, sc_strerror (rc));
|
||||
/* note, that we return the ID anyway */
|
||||
rc = gpg_error (GPG_ERR_MISSING_CERTIFICATE);
|
||||
goto return_keyid;
|
||||
}
|
||||
certinfo = tmpobj->data;
|
||||
rc = sc_pkcs15_read_certificate (card->p15card, certinfo, &certder);
|
||||
if (rc)
|
||||
{
|
||||
log_info ("failed to read certificate for private key %d: %s\n",
|
||||
idx, sc_strerror (rc));
|
||||
return gpg_error (GPG_ERR_CARD);
|
||||
}
|
||||
|
||||
cert = ksba_cert_new ();
|
||||
if (!cert)
|
||||
{
|
||||
gpg_error_t tmperr = out_of_core ();
|
||||
sc_pkcs15_free_certificate (certder);
|
||||
return tmperr;
|
||||
}
|
||||
krc = ksba_cert_init_from_mem (cert, certder->data, certder->data_len);
|
||||
sc_pkcs15_free_certificate (certder);
|
||||
if (krc)
|
||||
{
|
||||
log_error ("failed to parse the certificate for private key %d: %s\n",
|
||||
idx, ksba_strerror (krc));
|
||||
ksba_cert_release (cert);
|
||||
return gpg_error (GPG_ERR_CARD);
|
||||
}
|
||||
if (card_help_get_keygrip (cert, keygrip))
|
||||
{
|
||||
log_error ("failed to calculate the keygrip of private key %d\n", idx);
|
||||
ksba_cert_release (cert);
|
||||
return gpg_error (GPG_ERR_CARD);
|
||||
}
|
||||
ksba_cert_release (cert);
|
||||
|
||||
rc = 0;
|
||||
return_keyid:
|
||||
if (keyid)
|
||||
{
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
*keyid = p = xtrymalloc (9+pinfo->id.len*2+1);
|
||||
if (!*keyid)
|
||||
return out_of_core ();
|
||||
p = stpcpy (p, "P15-5015.");
|
||||
for (i=0; i < pinfo->id.len; i++, p += 2)
|
||||
sprintf (p, "%02X", pinfo->id.value[i]);
|
||||
*p = 0;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* See card.c for interface description */
|
||||
static int
|
||||
p15_enum_certs (CARD card, int idx, char **certid, int *type)
|
||||
{
|
||||
int rc;
|
||||
struct p15private_s *priv;
|
||||
struct sc_pkcs15_object *obj;
|
||||
struct sc_pkcs15_cert_info *cinfo;
|
||||
int nobjs;
|
||||
|
||||
rc = init_private_data (card);
|
||||
if (rc)
|
||||
return rc;
|
||||
priv = card->p15priv;
|
||||
nobjs = priv->n_cert_objs;
|
||||
rc = 0;
|
||||
if (idx >= nobjs)
|
||||
return -1;
|
||||
obj = priv->cert_objs[idx];
|
||||
cinfo = obj->data;
|
||||
|
||||
if (certid)
|
||||
{
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
*certid = p = xtrymalloc (9+cinfo->id.len*2+1);
|
||||
if (!*certid)
|
||||
return out_of_core ();
|
||||
p = stpcpy (p, "P15-5015.");
|
||||
for (i=0; i < cinfo->id.len; i++, p += 2)
|
||||
sprintf (p, "%02X", cinfo->id.value[i]);
|
||||
*p = 0;
|
||||
}
|
||||
if (type)
|
||||
{
|
||||
if (!obj->df)
|
||||
*type = 0; /* unknown */
|
||||
else if (obj->df->type == SC_PKCS15_CDF)
|
||||
*type = 100;
|
||||
else if (obj->df->type == SC_PKCS15_CDF_TRUSTED)
|
||||
*type = 101;
|
||||
else if (obj->df->type == SC_PKCS15_CDF_USEFUL)
|
||||
*type = 102;
|
||||
else
|
||||
*type = 0; /* error -> unknown */
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int
|
||||
idstr_to_id (const char *idstr, struct sc_pkcs15_id *id)
|
||||
{
|
||||
const char *s;
|
||||
int n;
|
||||
|
||||
/* For now we only support the standard DF */
|
||||
if (strncmp (idstr, "P15-5015.", 9) )
|
||||
return gpg_error (GPG_ERR_INV_ID);
|
||||
for (s=idstr+9, n=0; hexdigitp (s); s++, n++)
|
||||
;
|
||||
if (*s || (n&1))
|
||||
return gpg_error (GPG_ERR_INV_ID); /*invalid or odd number of digits*/
|
||||
n /= 2;
|
||||
if (!n || n > SC_PKCS15_MAX_ID_SIZE)
|
||||
return gpg_error (GPG_ERR_INV_ID); /* empty or too large */
|
||||
for (s=idstr+9, n=0; *s; s += 2, n++)
|
||||
id->value[n] = xtoi_2 (s);
|
||||
id->len = n;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* See card.c for interface description */
|
||||
static int
|
||||
p15_read_cert (CARD card, const char *certidstr,
|
||||
unsigned char **cert, size_t *ncert)
|
||||
{
|
||||
struct sc_pkcs15_object *tmpobj;
|
||||
struct sc_pkcs15_id certid;
|
||||
struct sc_pkcs15_cert_info *certinfo;
|
||||
struct sc_pkcs15_cert *certder;
|
||||
int rc;
|
||||
|
||||
if (!card || !certidstr || !cert || !ncert)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
if (!card->p15card)
|
||||
return gpg_error (GPG_ERR_NO_PKCS15_APP);
|
||||
|
||||
rc = idstr_to_id (certidstr, &certid);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = sc_pkcs15_find_cert_by_id (card->p15card, &certid, &tmpobj);
|
||||
if (rc)
|
||||
{
|
||||
log_info ("certificate '%s' not found: %s\n",
|
||||
certidstr, sc_strerror (rc));
|
||||
return -1;
|
||||
}
|
||||
certinfo = tmpobj->data;
|
||||
rc = sc_pkcs15_read_certificate (card->p15card, certinfo, &certder);
|
||||
if (rc)
|
||||
{
|
||||
log_info ("failed to read certificate '%s': %s\n",
|
||||
certidstr, sc_strerror (rc));
|
||||
return gpg_error (GPG_ERR_CARD);
|
||||
}
|
||||
|
||||
*cert = xtrymalloc (certder->data_len);
|
||||
if (!*cert)
|
||||
{
|
||||
gpg_error_t tmperr = out_of_core ();
|
||||
sc_pkcs15_free_certificate (certder);
|
||||
return tmperr;
|
||||
}
|
||||
memcpy (*cert, certder->data, certder->data_len);
|
||||
*ncert = certder->data_len;
|
||||
sc_pkcs15_free_certificate (certder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static int
|
||||
p15_prepare_key (CARD card, const char *keyidstr,
|
||||
int (pincb)(void*, const char *, char **),
|
||||
void *pincb_arg, struct sc_pkcs15_object **r_keyobj)
|
||||
{
|
||||
struct sc_pkcs15_id keyid;
|
||||
struct sc_pkcs15_pin_info *pin;
|
||||
struct sc_pkcs15_object *keyobj, *pinobj;
|
||||
char *pinvalue;
|
||||
int rc;
|
||||
|
||||
rc = idstr_to_id (keyidstr, &keyid);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = sc_pkcs15_find_prkey_by_id (card->p15card, &keyid, &keyobj);
|
||||
if (rc < 0)
|
||||
{
|
||||
log_error ("private key not found: %s\n", sc_strerror(rc));
|
||||
return gpg_error (GPG_ERR_NO_SECRET_KEY);
|
||||
}
|
||||
|
||||
rc = sc_pkcs15_find_pin_by_auth_id (card->p15card,
|
||||
&keyobj->auth_id, &pinobj);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("failed to find PIN by auth ID: %s\n", sc_strerror (rc));
|
||||
return gpg_error (GPG_ERR_BAD_PIN_METHOD);
|
||||
}
|
||||
pin = pinobj->data;
|
||||
|
||||
/* Fixme: pack this into a verification loop */
|
||||
/* Fixme: we might want to pass pin->min_length and
|
||||
pin->stored_length */
|
||||
rc = pincb (pincb_arg, pinobj->label, &pinvalue);
|
||||
if (rc)
|
||||
{
|
||||
log_info ("PIN callback returned error: %s\n", gnupg_strerror (rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = sc_pkcs15_verify_pin (card->p15card, pin,
|
||||
pinvalue, strlen (pinvalue));
|
||||
xfree (pinvalue);
|
||||
if (rc)
|
||||
{
|
||||
log_info ("PIN verification failed: %s\n", sc_strerror (rc));
|
||||
return gpg_error (GPG_ERR_BAD_PIN);
|
||||
}
|
||||
|
||||
/* fixme: check wheter we need to release KEYOBJ in case of an error */
|
||||
*r_keyobj = keyobj;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* See card.c for interface description */
|
||||
static int
|
||||
p15_sign (CARD card, const char *keyidstr, int hashalgo,
|
||||
int (pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const void *indata, size_t indatalen,
|
||||
unsigned char **outdata, size_t *outdatalen )
|
||||
{
|
||||
unsigned int cryptflags;
|
||||
struct sc_pkcs15_object *keyobj;
|
||||
int rc;
|
||||
unsigned char *outbuf = NULL;
|
||||
size_t outbuflen;
|
||||
|
||||
if (hashalgo != GCRY_MD_SHA1)
|
||||
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
|
||||
|
||||
rc = p15_prepare_key (card, keyidstr, pincb, pincb_arg, &keyobj);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
cryptflags = SC_ALGORITHM_RSA_PAD_PKCS1;
|
||||
|
||||
outbuflen = 1024;
|
||||
outbuf = xtrymalloc (outbuflen);
|
||||
if (!outbuf)
|
||||
return out_of_core ();
|
||||
|
||||
rc = sc_pkcs15_compute_signature (card->p15card, keyobj,
|
||||
cryptflags,
|
||||
indata, indatalen,
|
||||
outbuf, outbuflen );
|
||||
if (rc < 0)
|
||||
{
|
||||
log_error ("failed to create signature: %s\n", sc_strerror (rc));
|
||||
rc = gpg_error (GPG_ERR_CARD);
|
||||
}
|
||||
else
|
||||
{
|
||||
*outdatalen = rc;
|
||||
*outdata = outbuf;
|
||||
outbuf = NULL;
|
||||
rc = 0;
|
||||
}
|
||||
|
||||
xfree (outbuf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* See card.c for description */
|
||||
static int
|
||||
p15_decipher (CARD card, const char *keyidstr,
|
||||
int (pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const void *indata, size_t indatalen,
|
||||
unsigned char **outdata, size_t *outdatalen )
|
||||
{
|
||||
struct sc_pkcs15_object *keyobj;
|
||||
int rc;
|
||||
unsigned char *outbuf = NULL;
|
||||
size_t outbuflen;
|
||||
|
||||
rc = p15_prepare_key (card, keyidstr, pincb, pincb_arg, &keyobj);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (card && card->scard && card->scard->driver
|
||||
&& !strcasecmp (card->scard->driver->short_name, "tcos"))
|
||||
{
|
||||
/* very ugly hack to force the use of a local key. We need this
|
||||
until we have fixed the initialization code for TCOS cards */
|
||||
struct sc_pkcs15_prkey_info *prkey = keyobj->data;
|
||||
if ( !(prkey->key_reference & 0x80))
|
||||
{
|
||||
prkey->key_reference |= 0x80;
|
||||
log_debug ("using TCOS hack to force the use of local keys\n");
|
||||
}
|
||||
if (*keyidstr && keyidstr[strlen(keyidstr)-1] == '6')
|
||||
{
|
||||
prkey->key_reference |= 1;
|
||||
log_debug ("warning: using even more TCOS hacks\n");
|
||||
}
|
||||
}
|
||||
|
||||
outbuflen = indatalen < 256? 256 : indatalen;
|
||||
outbuf = xtrymalloc (outbuflen);
|
||||
if (!outbuf)
|
||||
return out_of_core ();
|
||||
|
||||
rc = sc_pkcs15_decipher (card->p15card, keyobj,
|
||||
0,
|
||||
indata, indatalen,
|
||||
outbuf, outbuflen);
|
||||
if (rc < 0)
|
||||
{
|
||||
log_error ("failed to decipher the data: %s\n", sc_strerror (rc));
|
||||
rc = gpg_error (GPG_ERR_CARD);
|
||||
}
|
||||
else
|
||||
{
|
||||
*outdatalen = rc;
|
||||
*outdata = outbuf;
|
||||
outbuf = NULL;
|
||||
rc = 0;
|
||||
}
|
||||
|
||||
xfree (outbuf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Bind our operations to the card */
|
||||
void
|
||||
card_p15_bind (CARD card)
|
||||
{
|
||||
card->fnc.enum_keypairs = p15_enum_keypairs;
|
||||
card->fnc.enum_certs = p15_enum_certs;
|
||||
card->fnc.read_cert = p15_read_cert;
|
||||
card->fnc.sign = p15_sign;
|
||||
card->fnc.decipher = p15_decipher;
|
||||
}
|
||||
#endif /*HAVE_OPENSC*/
|
564
scd/card.c
Normal file
564
scd/card.c
Normal file
|
@ -0,0 +1,564 @@
|
|||
/* card.c - SCdaemon card functions
|
||||
* Copyright (C) 2002 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
* GnuPG is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GnuPG is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifdef HAVE_OPENSC
|
||||
#include <opensc/pkcs15.h>
|
||||
#endif
|
||||
#include <ksba.h>
|
||||
|
||||
#include "scdaemon.h"
|
||||
#include "card-common.h"
|
||||
|
||||
/* Map the SC error codes to the GNUPG ones */
|
||||
gpg_error_t
|
||||
map_sc_err (int rc)
|
||||
{
|
||||
gpg_err_code_t e;
|
||||
|
||||
switch (rc)
|
||||
{
|
||||
case 0: e = 0; break;
|
||||
#ifdef HAVE_OPENSC
|
||||
case SC_ERROR_NOT_SUPPORTED: e = GPG_ERR_NOT_SUPPORTED; break;
|
||||
case SC_ERROR_PKCS15_APP_NOT_FOUND: e = GPG_ERR_NO_PKCS15_APP; break;
|
||||
case SC_ERROR_OUT_OF_MEMORY: e = GPG_ERR_ENOMEM; break;
|
||||
case SC_ERROR_CARD_NOT_PRESENT: e = GPG_ERR_CARD_NOT_PRESENT; break;
|
||||
case SC_ERROR_CARD_REMOVED: e = GPG_ERR_CARD_REMOVED; break;
|
||||
case SC_ERROR_INVALID_CARD: e = GPG_ERR_INV_CARD; break;
|
||||
#endif
|
||||
default: e = GPG_ERR_CARD; break;
|
||||
}
|
||||
return gpg_err_make (GPG_ERR_SOURCE_UNKNOWN, e);
|
||||
}
|
||||
|
||||
/* Get the keygrip from CERT, return 0 on success */
|
||||
int
|
||||
card_help_get_keygrip (KsbaCert cert, unsigned char *array)
|
||||
{
|
||||
gcry_sexp_t s_pkey;
|
||||
int rc;
|
||||
KsbaSexp p;
|
||||
size_t n;
|
||||
|
||||
p = ksba_cert_get_public_key (cert);
|
||||
if (!p)
|
||||
return -1; /* oops */
|
||||
n = gcry_sexp_canon_len (p, 0, NULL, NULL);
|
||||
if (!n)
|
||||
return -1; /* libksba did not return a proper S-expression */
|
||||
rc = gcry_sexp_sscan ( &s_pkey, NULL, p, n);
|
||||
xfree (p);
|
||||
if (rc)
|
||||
return -1; /* can't parse that S-expression */
|
||||
array = gcry_pk_get_keygrip (s_pkey, array);
|
||||
gcry_sexp_release (s_pkey);
|
||||
if (!array)
|
||||
return -1; /* failed to calculate the keygrip */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* Create a new context for the card and figures out some basic
|
||||
information of the card. Detects whgether a PKCS_15 application is
|
||||
stored.
|
||||
|
||||
Common errors: GPG_ERR_CARD_NOT_PRESENT */
|
||||
int
|
||||
card_open (CARD *rcard)
|
||||
{
|
||||
#ifdef HAVE_OPENSC
|
||||
CARD card;
|
||||
int rc;
|
||||
|
||||
card = xtrycalloc (1, sizeof *card);
|
||||
if (!card)
|
||||
return out_of_core ();
|
||||
card->reader = 0;
|
||||
|
||||
rc = sc_establish_context (&card->ctx, "scdaemon");
|
||||
if (rc)
|
||||
{
|
||||
log_error ("failed to establish SC context: %s\n", sc_strerror (rc));
|
||||
rc = map_sc_err (rc);
|
||||
goto leave;
|
||||
}
|
||||
if (card->reader >= card->ctx->reader_count)
|
||||
{
|
||||
log_error ("no card reader available\n");
|
||||
rc = gpg_error (GPG_ERR_CARD);
|
||||
goto leave;
|
||||
}
|
||||
card->ctx->error_file = log_get_stream ();
|
||||
card->ctx->debug = opt.debug_sc;
|
||||
card->ctx->debug_file = log_get_stream ();
|
||||
|
||||
if (sc_detect_card_presence (card->ctx->reader[card->reader], 0) != 1)
|
||||
{
|
||||
rc = gpg_error (GPG_ERR_CARD_NOT_PRESENT);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
rc = sc_connect_card (card->ctx->reader[card->reader], 0, &card->scard);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("failed to connect card in reader %d: %s\n",
|
||||
card->reader, sc_strerror (rc));
|
||||
rc = map_sc_err (rc);
|
||||
goto leave;
|
||||
}
|
||||
if (opt.verbose)
|
||||
log_info ("connected to card in reader %d using driver `%s'\n",
|
||||
card->reader, card->scard->driver->name);
|
||||
|
||||
rc = sc_lock (card->scard);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("can't lock card in reader %d: %s\n",
|
||||
card->reader, sc_strerror (rc));
|
||||
rc = map_sc_err (rc);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
|
||||
leave:
|
||||
if (rc)
|
||||
card_close (card);
|
||||
else
|
||||
*rcard = card;
|
||||
|
||||
return rc;
|
||||
#else
|
||||
return gpg_error (GPG_ERR_NOT_SUPPORTED);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* Close a card and release all resources */
|
||||
void
|
||||
card_close (CARD card)
|
||||
{
|
||||
if (card)
|
||||
{
|
||||
#ifdef HAVE_OPENSC
|
||||
if (card->p15card)
|
||||
{
|
||||
sc_pkcs15_unbind (card->p15card);
|
||||
card->p15card = NULL;
|
||||
}
|
||||
if (card->p15priv)
|
||||
p15_release_private_data (card);
|
||||
if (card->scard)
|
||||
{
|
||||
sc_unlock (card->scard);
|
||||
sc_disconnect_card (card->scard, 0);
|
||||
card->scard = NULL;
|
||||
}
|
||||
if (card->ctx)
|
||||
{
|
||||
sc_release_context (card->ctx);
|
||||
card->ctx = NULL;
|
||||
}
|
||||
#endif
|
||||
xfree (card);
|
||||
}
|
||||
}
|
||||
|
||||
/* Locate a simple TLV encoded data object in BUFFER of LENGTH and
|
||||
return a pointer to value as well as its length in NBYTES. Return
|
||||
NULL if it was not found. Note, that the function does not check
|
||||
whether the value fits into the provided buffer. */
|
||||
#ifdef HAVE_OPENSC
|
||||
static const char *
|
||||
find_simple_tlv (const unsigned char *buffer, size_t length,
|
||||
int tag, size_t *nbytes)
|
||||
{
|
||||
const char *s = buffer;
|
||||
size_t n = length;
|
||||
size_t len;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
buffer = s;
|
||||
if (n < 2)
|
||||
return NULL; /* buffer too short for tag and length. */
|
||||
len = s[1];
|
||||
s += 2; n -= 2;
|
||||
if (len == 255)
|
||||
{
|
||||
if (n < 2)
|
||||
return NULL; /* we expected 2 more bytes with the length. */
|
||||
len = (s[0] << 8) | s[1];
|
||||
s += 2; n -= 2;
|
||||
}
|
||||
if (*buffer == tag)
|
||||
{
|
||||
*nbytes = len;
|
||||
return s;
|
||||
}
|
||||
if (len > n)
|
||||
return NULL; /* buffer too short to skip to the next tag. */
|
||||
s += len; n -= len;
|
||||
}
|
||||
}
|
||||
#endif /*HAVE_OPENSC*/
|
||||
|
||||
/* Find the ICC Serial Number within the provided BUFFER of LENGTH
|
||||
(which should contain the GDO file) and return it as a hex encoded
|
||||
string and allocated string in SERIAL. Return an error code when
|
||||
the ICCSN was not found. */
|
||||
#ifdef HAVE_OPENSC
|
||||
static int
|
||||
find_iccsn (const unsigned char *buffer, size_t length, char **serial)
|
||||
{
|
||||
size_t n;
|
||||
const unsigned char *s;
|
||||
char *p;
|
||||
|
||||
s = find_simple_tlv (buffer, length, 0x5A, &n);
|
||||
if (!s)
|
||||
return gpg_error (GPG_ERR_CARD);
|
||||
length -= s - buffer;
|
||||
if (n > length)
|
||||
{
|
||||
/* Oops, it does not fit into the buffer. This is an invalid
|
||||
encoding (or the buffer is too short. However, I have some
|
||||
test cards with such an invalid encoding and therefore I use
|
||||
this ugly workaround to return something I can further
|
||||
experiment with. */
|
||||
if (n == 0x0D && length+1 == n)
|
||||
{
|
||||
log_debug ("enabling BMI testcard workaround\n");
|
||||
n--;
|
||||
}
|
||||
else
|
||||
return gpg_error (GPG_ERR_CARD); /* Bad encoding; does
|
||||
not fit into buffer. */
|
||||
}
|
||||
if (!n)
|
||||
return gpg_error (GPG_ERR_CARD); /* Well, that is too short. */
|
||||
|
||||
*serial = p = xtrymalloc (2*n+1);
|
||||
if (!*serial)
|
||||
return out_of_core ();
|
||||
for (; n; n--, p += 2, s++)
|
||||
sprintf (p, "%02X", *s);
|
||||
*p = 0;
|
||||
return 0;
|
||||
}
|
||||
#endif /*HAVE_OPENSC*/
|
||||
|
||||
/* Retrieve the serial number and the time of the last update of the
|
||||
card. The serial number is returned as a malloced string (hex
|
||||
encoded) in SERIAL and the time of update is returned in STAMP.
|
||||
If no update time is available the returned value is 0. The serial
|
||||
is mandatory for a PKCS_15 application and an error will be
|
||||
returned if this value is not availbale. For non-PKCS-15 cards a
|
||||
serial number is constructed by other means. Caller must free
|
||||
SERIAL unless the function returns an error. */
|
||||
int
|
||||
card_get_serial_and_stamp (CARD card, char **serial, time_t *stamp)
|
||||
{
|
||||
#ifdef HAVE_OPENSC
|
||||
int rc;
|
||||
struct sc_path path;
|
||||
struct sc_file *file;
|
||||
unsigned char buf[256];
|
||||
int buflen;
|
||||
#endif
|
||||
|
||||
if (!card || !serial || !stamp)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
|
||||
*serial = NULL;
|
||||
*stamp = 0; /* not available */
|
||||
|
||||
#ifdef HAVE_OPENSC
|
||||
if (!card->fnc.initialized)
|
||||
{
|
||||
card->fnc.initialized = 1;
|
||||
/* The first use of this card tries to figure out the type of the card
|
||||
and sets up the function pointers. */
|
||||
rc = sc_pkcs15_bind (card->scard, &card->p15card);
|
||||
if (rc)
|
||||
{
|
||||
if (rc != SC_ERROR_PKCS15_APP_NOT_FOUND)
|
||||
log_error ("binding of existing PKCS-15 failed in reader %d: %s\n",
|
||||
card->reader, sc_strerror (rc));
|
||||
card->p15card = NULL;
|
||||
rc = 0;
|
||||
}
|
||||
if (card->p15card)
|
||||
card_p15_bind (card);
|
||||
else
|
||||
card_dinsig_bind (card);
|
||||
card->fnc.initialized = 1;
|
||||
}
|
||||
|
||||
|
||||
/* We should lookup the iso 7812-1 and 8583-3 - argh ISO
|
||||
practice is suppressing innovation - IETF rules! So we
|
||||
always get the serialnumber from the 2F02 GDO file. */
|
||||
/* FIXME: in case we can't parse the 2F02 EF and we have a P15 card,
|
||||
we should get the serial number from the respective P15 file */
|
||||
sc_format_path ("3F002F02", &path);
|
||||
rc = sc_select_file (card->scard, &path, &file);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("sc_select_file failed: %s\n", sc_strerror (rc));
|
||||
return gpg_error (GPG_ERR_CARD);
|
||||
}
|
||||
if (file->type != SC_FILE_TYPE_WORKING_EF
|
||||
|| file->ef_structure != SC_FILE_EF_TRANSPARENT)
|
||||
{
|
||||
log_error ("wrong type or structure of GDO file\n");
|
||||
sc_file_free (file);
|
||||
return gpg_error (GPG_ERR_CARD);
|
||||
}
|
||||
|
||||
if (!file->size || file->size >= DIM(buf) )
|
||||
{ /* FIXME: Use a real parser */
|
||||
log_error ("unsupported size of GDO file (%d)\n", file->size);
|
||||
sc_file_free (file);
|
||||
return gpg_error (GPG_ERR_CARD);
|
||||
}
|
||||
buflen = file->size;
|
||||
|
||||
rc = sc_read_binary (card->scard, 0, buf, buflen, 0);
|
||||
sc_file_free (file);
|
||||
if (rc < 0)
|
||||
{
|
||||
log_error ("error reading GDO file: %s\n", sc_strerror (rc));
|
||||
return gpg_error (GPG_ERR_CARD);
|
||||
}
|
||||
if (rc != buflen)
|
||||
{
|
||||
log_error ("short read on GDO file\n");
|
||||
return gpg_error (GPG_ERR_CARD);
|
||||
}
|
||||
|
||||
rc = find_iccsn (buf, buflen, serial);
|
||||
if (gpg_err_code (rc) == GPG_ERR_CARD)
|
||||
log_error ("invalid structure of GDO file\n");
|
||||
if (!rc && card->p15card && !strcmp (*serial, "D27600000000000000000000"))
|
||||
{ /* This is a German card with a silly serial number. Try to get
|
||||
the serial number from the EF(TokenInfo). We indicate such a
|
||||
serial number by the using the prefix: "FF0100". */
|
||||
const char *efser = card->p15card->serial_number;
|
||||
char *p;
|
||||
|
||||
if (!efser)
|
||||
efser = "";
|
||||
|
||||
xfree (*serial);
|
||||
*serial = NULL;
|
||||
p = xtrymalloc (strlen (efser) + 7);
|
||||
if (!p)
|
||||
rc = out_of_core ();
|
||||
else
|
||||
{
|
||||
strcpy (p, "FF0100");
|
||||
strcpy (p+6, efser);
|
||||
*serial = p;
|
||||
}
|
||||
}
|
||||
else if (!rc && **serial == 'F' && (*serial)[1] == 'F')
|
||||
{ /* The serial number starts with our special prefix. This
|
||||
requires that we put our default prefix "FF0000" in front. */
|
||||
char *p = xtrymalloc (strlen (*serial) + 7);
|
||||
if (!p)
|
||||
{
|
||||
xfree (*serial);
|
||||
*serial = NULL;
|
||||
rc = out_of_core ();
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy (p, "FF0000");
|
||||
strcpy (p+6, *serial);
|
||||
xfree (*serial);
|
||||
*serial = p;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
#else
|
||||
return gpg_error (GPG_ERR_NOT_SUPPORTED);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* Enumerate all keypairs on the card and return the Keygrip as well
|
||||
as the internal identification of the key. KEYGRIP must be a
|
||||
caller provided buffer with a size of 20 bytes which will receive
|
||||
the KEYGRIP of the keypair. If KEYID is not NULL, it returns the
|
||||
ID field of the key in allocated memory; this is a string without
|
||||
spaces. The function returns -1 when all keys have been
|
||||
enumerated. Note that the error GPG_ERR_MISSING_CERTIFICATE may be
|
||||
returned if there is just the private key but no public key (ie.e a
|
||||
certificate) available. Applications might want to continue
|
||||
enumerating after this error.*/
|
||||
int
|
||||
card_enum_keypairs (CARD card, int idx,
|
||||
unsigned char *keygrip,
|
||||
char **keyid)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (keyid)
|
||||
*keyid = NULL;
|
||||
|
||||
if (!card || !keygrip)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
if (idx < 0)
|
||||
return gpg_error (GPG_ERR_INV_INDEX);
|
||||
if (!card->fnc.initialized)
|
||||
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
|
||||
if (!card->fnc.enum_keypairs)
|
||||
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
|
||||
rc = card->fnc.enum_keypairs (card, idx, keygrip, keyid);
|
||||
if (opt.verbose)
|
||||
log_info ("card operation enum_keypairs result: %s\n",
|
||||
gpg_strerror (rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Enumerate all trusted certificates available on the card, return
|
||||
their ID in CERT and the type in CERTTYPE. Types of certificates
|
||||
are:
|
||||
0 := Unknown
|
||||
100 := Regular X.509 cert
|
||||
101 := Trusted X.509 cert
|
||||
102 := Useful X.509 cert
|
||||
*/
|
||||
int
|
||||
card_enum_certs (CARD card, int idx, char **certid, int *certtype)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (certid)
|
||||
*certid = NULL;
|
||||
|
||||
if (!card)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
if (idx < 0)
|
||||
return gpg_error (GPG_ERR_INV_INDEX);
|
||||
if (!card->fnc.initialized)
|
||||
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
|
||||
if (!card->fnc.enum_certs)
|
||||
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
|
||||
rc = card->fnc.enum_certs (card, idx, certid, certtype);
|
||||
if (opt.verbose)
|
||||
log_info ("card operation enum_certs result: %s\n",
|
||||
gpg_strerror (rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Read the certificate identified by CERTIDSTR which is the
|
||||
hexadecimal encoded ID of the certificate, prefixed with the string
|
||||
"3F005015.". The certificate is return in DER encoded form in CERT
|
||||
and NCERT. */
|
||||
int
|
||||
card_read_cert (CARD card, const char *certidstr,
|
||||
unsigned char **cert, size_t *ncert)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!card || !certidstr || !cert || !ncert)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
if (!card->fnc.initialized)
|
||||
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
|
||||
if (!card->fnc.read_cert)
|
||||
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
|
||||
rc = card->fnc.read_cert (card, certidstr, cert, ncert);
|
||||
if (opt.verbose)
|
||||
log_info ("card operation read_cert result: %s\n", gpg_strerror (rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Create the signature and return the allocated result in OUTDATA.
|
||||
If a PIN is required the PINCB will be used to ask for the PIN; it
|
||||
should return the PIN in an allocated buffer and put it into PIN. */
|
||||
int
|
||||
card_sign (CARD card, const char *keyidstr, int hashalgo,
|
||||
int (pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const void *indata, size_t indatalen,
|
||||
unsigned char **outdata, size_t *outdatalen )
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!card || !indata || !indatalen || !outdata || !outdatalen || !pincb)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
if (!card->fnc.initialized)
|
||||
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
|
||||
if (!card->fnc.sign)
|
||||
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
|
||||
rc = card->fnc.sign (card, keyidstr, hashalgo,
|
||||
pincb, pincb_arg,
|
||||
indata, indatalen,
|
||||
outdata, outdatalen);
|
||||
if (opt.verbose)
|
||||
log_info ("card operation sign result: %s\n", gpg_strerror (rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Create the signature and return the allocated result in OUTDATA.
|
||||
If a PIN is required the PINCB will be used to ask for the PIN; it
|
||||
should return the PIN in an allocated buffer and put it into PIN. */
|
||||
int
|
||||
card_decipher (CARD card, const char *keyidstr,
|
||||
int (pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const void *indata, size_t indatalen,
|
||||
unsigned char **outdata, size_t *outdatalen )
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!card || !indata || !indatalen || !outdata || !outdatalen || !pincb)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
if (!card->fnc.initialized)
|
||||
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
|
||||
if (!card->fnc.decipher)
|
||||
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
|
||||
rc = card->fnc.decipher (card, keyidstr,
|
||||
pincb, pincb_arg,
|
||||
indata, indatalen,
|
||||
outdata, outdatalen);
|
||||
if (opt.verbose)
|
||||
log_info ("card operation decipher result: %s\n", gpg_strerror (rc));
|
||||
return rc;
|
||||
}
|
||||
|
1034
scd/command.c
Normal file
1034
scd/command.c
Normal file
File diff suppressed because it is too large
Load diff
371
scd/iso7816.c
Normal file
371
scd/iso7816.c
Normal file
|
@ -0,0 +1,371 @@
|
|||
/* iso7816.c - ISO 7816 commands
|
||||
* Copyright (C) 2003 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
* GnuPG is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GnuPG is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "scdaemon.h"
|
||||
#include "iso7816.h"
|
||||
#include "apdu.h"
|
||||
|
||||
#define CMD_SELECT_FILE 0xA4
|
||||
#define CMD_VERIFY 0x20
|
||||
#define CMD_CHANGE_REFERENCE_DATA 0x24
|
||||
#define CMD_RESET_RETRY_COUNTER 0x2C
|
||||
#define CMD_GET_DATA 0xCA
|
||||
#define CMD_PUT_DATA 0xDA
|
||||
#define CMD_PSO 0x2A
|
||||
#define CMD_INTERNAL_AUTHENTICATE 0x88
|
||||
#define CMD_GENERATE_KEYPAIR 0x47
|
||||
#define CMD_GET_CHALLENGE 0x84
|
||||
|
||||
static gpg_error_t
|
||||
map_sw (int sw)
|
||||
{
|
||||
gpg_err_code_t ec;
|
||||
|
||||
switch (sw)
|
||||
{
|
||||
case SW_EEPROM_FAILURE: ec = GPG_ERR_HARDWARE; break;
|
||||
case SW_WRONG_LENGTH: ec = GPG_ERR_INV_VALUE; break;
|
||||
case SW_CHV_WRONG: ec = GPG_ERR_BAD_PIN; break;
|
||||
case SW_CHV_BLOCKED: ec = GPG_ERR_PIN_BLOCKED; break;
|
||||
case SW_USE_CONDITIONS: ec = GPG_ERR_USE_CONDITIONS; break;
|
||||
case SW_NOT_SUPPORTED: ec = GPG_ERR_NOT_SUPPORTED; break;
|
||||
case SW_BAD_PARAMETER: ec = GPG_ERR_INV_VALUE; break;
|
||||
case SW_REF_NOT_FOUND: ec = GPG_ERR_NO_OBJ; break;
|
||||
case SW_BAD_P0_P1: ec = GPG_ERR_INV_VALUE; break;
|
||||
case SW_INS_NOT_SUP: ec = GPG_ERR_CARD; break;
|
||||
case SW_CLA_NOT_SUP: ec = GPG_ERR_CARD; break;
|
||||
case SW_SUCCESS: ec = 0; break;
|
||||
|
||||
case SW_HOST_OUT_OF_CORE: ec = GPG_ERR_ENOMEM; break;
|
||||
case SW_HOST_INV_VALUE: ec = GPG_ERR_INV_VALUE; break;
|
||||
case SW_HOST_INCOMPLETE_CARD_RESPONSE: ec = GPG_ERR_CARD; break;
|
||||
default:
|
||||
if ((sw & 0x010000))
|
||||
ec = GPG_ERR_GENERAL; /* Should not happen. */
|
||||
else if ((sw & 0xff00) == SW_MORE_DATA)
|
||||
ec = 0; /* This should actually never been seen here. */
|
||||
else
|
||||
ec = GPG_ERR_CARD;
|
||||
}
|
||||
return gpg_error (ec);
|
||||
}
|
||||
|
||||
/* This function is specialized version of the SELECT FILE command.
|
||||
SLOT is the card and reader as created for example by
|
||||
apdu_open_reader (), AID is a buffer of size AIDLEN holding the
|
||||
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
|
||||
for okay or GNUPG error code. Note that ISO error codes are
|
||||
internally mapped. */
|
||||
gpg_error_t
|
||||
iso7816_select_application (int slot, const char *aid, size_t aidlen)
|
||||
{
|
||||
int sw;
|
||||
|
||||
sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, 0, aidlen, aid);
|
||||
return map_sw (sw);
|
||||
}
|
||||
|
||||
|
||||
/* Perform a VERIFY command on SLOT using the card holder verification
|
||||
vector CHVNO with a CHV of lenght CHVLEN. Returns 0 on success. */
|
||||
gpg_error_t
|
||||
iso7816_verify (int slot, int chvno, const char *chv, size_t chvlen)
|
||||
{
|
||||
int sw;
|
||||
|
||||
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
|
||||
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
|
||||
iso7816_change_reference_data (int slot, int chvno,
|
||||
const char *oldchv, size_t oldchvlen,
|
||||
const char *newchv, size_t newchvlen)
|
||||
{
|
||||
int sw;
|
||||
char *buf;
|
||||
|
||||
if ((!oldchv && oldchvlen)
|
||||
|| (oldchv && !oldchvlen)
|
||||
|| !newchv || !newchvlen )
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
|
||||
buf = xtrymalloc (oldchvlen + newchvlen);
|
||||
if (!buf)
|
||||
return out_of_core ();
|
||||
if (oldchvlen)
|
||||
memcpy (buf, oldchv, oldchvlen);
|
||||
memcpy (buf+oldchvlen, newchv, newchvlen);
|
||||
|
||||
sw = apdu_send_simple (slot, 0x00, CMD_CHANGE_REFERENCE_DATA,
|
||||
oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf);
|
||||
xfree (buf);
|
||||
return map_sw (sw);
|
||||
|
||||
}
|
||||
|
||||
gpg_error_t
|
||||
iso7816_reset_retry_counter (int slot, int chvno,
|
||||
const char *newchv, size_t newchvlen)
|
||||
{
|
||||
int sw;
|
||||
|
||||
if (!newchv || !newchvlen )
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
|
||||
sw = apdu_send_simple (slot, 0x00, CMD_RESET_RETRY_COUNTER,
|
||||
2, chvno, newchvlen, newchv);
|
||||
return map_sw (sw);
|
||||
}
|
||||
|
||||
|
||||
/* Perform a GET DATA command requesting TAG and storing the result in
|
||||
a newly allocated buffer at the address passed by RESULT. Return
|
||||
the length of this data at the address of RESULTLEN. */
|
||||
gpg_error_t
|
||||
iso7816_get_data (int slot, int tag,
|
||||
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 (slot, 0x00, CMD_GET_DATA,
|
||||
((tag >> 8) & 0xff), (tag & 0xff), -1, NULL,
|
||||
result, resultlen);
|
||||
if (sw != SW_SUCCESS)
|
||||
{
|
||||
/* Make sure that pending buffers are released. */
|
||||
xfree (*result);
|
||||
*result = NULL;
|
||||
*resultlen = 0;
|
||||
return map_sw (sw);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Perform a PUT DATA command on card in SLOT. Write DATA of length
|
||||
DATALEN to TAG. */
|
||||
gpg_error_t
|
||||
iso7816_put_data (int slot, int tag,
|
||||
const unsigned char *data, size_t datalen)
|
||||
{
|
||||
int sw;
|
||||
|
||||
sw = apdu_send_simple (slot, 0x00, CMD_PUT_DATA,
|
||||
((tag >> 8) & 0xff), (tag & 0xff),
|
||||
datalen, data);
|
||||
return map_sw (sw);
|
||||
}
|
||||
|
||||
|
||||
/* Perform the security operation COMPUTE DIGITAL SIGANTURE. On
|
||||
success 0 is returned and the data is availavle in a newly
|
||||
allocated buffer stored at RESULT with its length stored at
|
||||
RESULTLEN. */
|
||||
gpg_error_t
|
||||
iso7816_compute_ds (int slot, const unsigned char *data, size_t datalen,
|
||||
unsigned char **result, size_t *resultlen)
|
||||
{
|
||||
int sw;
|
||||
|
||||
if (!data || !datalen || !result || !resultlen)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
*result = NULL;
|
||||
*resultlen = 0;
|
||||
|
||||
sw = apdu_send (slot, 0x00, CMD_PSO, 0x9E, 0x9A, datalen, data,
|
||||
result, resultlen);
|
||||
if (sw != SW_SUCCESS)
|
||||
{
|
||||
/* Make sure that pending buffers are released. */
|
||||
xfree (*result);
|
||||
*result = NULL;
|
||||
*resultlen = 0;
|
||||
return map_sw (sw);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Perform the security operation DECIPHER. On
|
||||
success 0 is returned and the plaintext is available in a newly
|
||||
allocated buffer stored at RESULT with its length stored at
|
||||
RESULTLEN. */
|
||||
gpg_error_t
|
||||
iso7816_decipher (int slot, const unsigned char *data, size_t datalen,
|
||||
unsigned char **result, size_t *resultlen)
|
||||
{
|
||||
int sw;
|
||||
unsigned char *buf;
|
||||
|
||||
if (!data || !datalen || !result || !resultlen)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
*result = NULL;
|
||||
*resultlen = 0;
|
||||
|
||||
/* We need to prepend the padding indicator. */
|
||||
buf = xtrymalloc (datalen + 1);
|
||||
if (!buf)
|
||||
return out_of_core ();
|
||||
*buf = 0; /* Padding indicator. */
|
||||
memcpy (buf+1, data, datalen);
|
||||
sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86, datalen+1, buf,
|
||||
result, resultlen);
|
||||
xfree (buf);
|
||||
if (sw != SW_SUCCESS)
|
||||
{
|
||||
/* Make sure that pending buffers are released. */
|
||||
xfree (*result);
|
||||
*result = NULL;
|
||||
*resultlen = 0;
|
||||
return map_sw (sw);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
gpg_error_t
|
||||
iso7816_internal_authenticate (int slot,
|
||||
const unsigned char *data, size_t datalen,
|
||||
unsigned char **result, size_t *resultlen)
|
||||
{
|
||||
int sw;
|
||||
|
||||
if (!data || !datalen || !result || !resultlen)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
*result = NULL;
|
||||
*resultlen = 0;
|
||||
|
||||
sw = apdu_send (slot, 0x00, CMD_INTERNAL_AUTHENTICATE, 0, 0,
|
||||
datalen, data, result, resultlen);
|
||||
if (sw != SW_SUCCESS)
|
||||
{
|
||||
/* Make sure that pending buffers are released. */
|
||||
xfree (*result);
|
||||
*result = NULL;
|
||||
*resultlen = 0;
|
||||
return map_sw (sw);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static gpg_error_t
|
||||
generate_keypair (int slot, int readonly,
|
||||
const unsigned char *data, size_t datalen,
|
||||
unsigned char **result, size_t *resultlen)
|
||||
{
|
||||
int sw;
|
||||
|
||||
if (!data || !datalen || !result || !resultlen)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
*result = NULL;
|
||||
*resultlen = 0;
|
||||
|
||||
sw = apdu_send (slot, 0x00, CMD_GENERATE_KEYPAIR, readonly? 0x81:0x80, 0,
|
||||
datalen, data, result, resultlen);
|
||||
if (sw != SW_SUCCESS)
|
||||
{
|
||||
/* Make sure that pending buffers are released. */
|
||||
xfree (*result);
|
||||
*result = NULL;
|
||||
*resultlen = 0;
|
||||
return map_sw (sw);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
gpg_error_t
|
||||
iso7816_generate_keypair (int slot,
|
||||
const unsigned char *data, size_t datalen,
|
||||
unsigned char **result, size_t *resultlen)
|
||||
{
|
||||
return generate_keypair (slot, 0, data, datalen, result, resultlen);
|
||||
}
|
||||
|
||||
|
||||
gpg_error_t
|
||||
iso7816_read_public_key (int slot,
|
||||
const unsigned char *data, size_t datalen,
|
||||
unsigned char **result, size_t *resultlen)
|
||||
{
|
||||
return generate_keypair (slot, 1, data, datalen, result, resultlen);
|
||||
}
|
||||
|
||||
|
||||
|
||||
gpg_error_t
|
||||
iso7816_get_challenge (int slot, int length, unsigned char *buffer)
|
||||
{
|
||||
int sw;
|
||||
unsigned char *result;
|
||||
size_t resultlen, n;
|
||||
|
||||
if (!buffer || length < 1)
|
||||
return gpg_error (GPG_ERR_INV_VALUE);
|
||||
|
||||
do
|
||||
{
|
||||
result = NULL;
|
||||
n = length > 254? 254 : length;
|
||||
sw = apdu_send_le (slot, 0x00, CMD_GET_CHALLENGE, 0, 0, -1, NULL,
|
||||
n,
|
||||
&result, &resultlen);
|
||||
if (sw != SW_SUCCESS)
|
||||
{
|
||||
/* Make sure that pending buffers are released. */
|
||||
xfree (result);
|
||||
return map_sw (sw);
|
||||
}
|
||||
if (resultlen > n)
|
||||
resultlen = n;
|
||||
memcpy (buffer, result, resultlen);
|
||||
buffer += resultlen;
|
||||
length -= resultlen;
|
||||
xfree (result);
|
||||
}
|
||||
while (length > 0);
|
||||
|
||||
return 0;
|
||||
}
|
56
scd/iso7816.h
Normal file
56
scd/iso7816.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
/* iso7816.h - ISO 7816 commands
|
||||
* Copyright (C) 2003 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
* GnuPG is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GnuPG is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#ifndef ISO7816_H
|
||||
#define ISO7816_H
|
||||
|
||||
gpg_error_t iso7816_select_application (int slot,
|
||||
const char *aid, size_t aidlen);
|
||||
gpg_error_t iso7816_verify (int slot,
|
||||
int chvno, const char *chv, size_t chvlen);
|
||||
gpg_error_t iso7816_change_reference_data (int slot, int chvno,
|
||||
const char *oldchv, size_t oldchvlen,
|
||||
const char *newchv, size_t newchvlen);
|
||||
gpg_error_t iso7816_reset_retry_counter (int slot, int chvno,
|
||||
const char *newchv, size_t newchvlen);
|
||||
gpg_error_t iso7816_get_data (int slot, int tag,
|
||||
unsigned char **result, size_t *resultlen);
|
||||
gpg_error_t iso7816_put_data (int slot, int tag,
|
||||
const unsigned char *data, size_t datalen);
|
||||
gpg_error_t iso7816_compute_ds (int slot,
|
||||
const unsigned char *data, size_t datalen,
|
||||
unsigned char **result, size_t *resultlen);
|
||||
gpg_error_t iso7816_decipher (int slot,
|
||||
const unsigned char *data, size_t datalen,
|
||||
unsigned char **result, size_t *resultlen);
|
||||
gpg_error_t iso7816_internal_authenticate (int slot,
|
||||
const unsigned char *data, size_t datalen,
|
||||
unsigned char **result, size_t *resultlen);
|
||||
gpg_error_t iso7816_generate_keypair (int slot,
|
||||
const unsigned char *data, size_t datalen,
|
||||
unsigned char **result, size_t *resultlen);
|
||||
gpg_error_t iso7816_read_public_key (int slot,
|
||||
const unsigned char *data, size_t datalen,
|
||||
unsigned char **result, size_t *resultlen);
|
||||
gpg_error_t iso7816_get_challenge (int slot,
|
||||
int length, unsigned char *buffer);
|
||||
|
||||
|
||||
#endif /*ISO7816_H*/
|
731
scd/sc-copykeys.c
Normal file
731
scd/sc-copykeys.c
Normal file
|
@ -0,0 +1,731 @@
|
|||
/* sc-copykeys.c - A tool to store keys on a smartcard.
|
||||
* Copyright (C) 2003 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
* GnuPG is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GnuPG is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define JNLIB_NEED_LOG_LOGV
|
||||
#include "scdaemon.h"
|
||||
#include <gcrypt.h>
|
||||
|
||||
#include "../common/ttyio.h"
|
||||
#include "../common/simple-pwquery.h"
|
||||
#include "apdu.h" /* for open_reader */
|
||||
#include "atr.h"
|
||||
#include "app-common.h"
|
||||
|
||||
#define _(a) (a)
|
||||
|
||||
|
||||
enum cmd_and_opt_values
|
||||
{ oVerbose = 'v',
|
||||
oReaderPort = 500,
|
||||
oDebug,
|
||||
oDebugAll,
|
||||
|
||||
aTest };
|
||||
|
||||
|
||||
static ARGPARSE_OPTS opts[] = {
|
||||
|
||||
{ 301, NULL, 0, "@Options:\n " },
|
||||
|
||||
{ oVerbose, "verbose", 0, "verbose" },
|
||||
{ oReaderPort, "reader-port", 1, "|N|connect to reader at port N"},
|
||||
{ oDebug, "debug" ,4|16, "set debugging flags"},
|
||||
{ oDebugAll, "debug-all" ,0, "enable full debugging"},
|
||||
{0}
|
||||
};
|
||||
|
||||
|
||||
static void copykeys (APP app, const char *fname);
|
||||
|
||||
|
||||
static const char *
|
||||
my_strusage (int level)
|
||||
{
|
||||
const char *p;
|
||||
switch (level)
|
||||
{
|
||||
case 11: p = "sc-copykeys (GnuPG)";
|
||||
break;
|
||||
case 13: p = VERSION; break;
|
||||
case 17: p = PRINTABLE_OS_NAME; break;
|
||||
case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
|
||||
break;
|
||||
case 1:
|
||||
case 40: p = _("Usage: sc-copykeys [options] (-h for help)\n");
|
||||
break;
|
||||
case 41: p = _("Syntax: sc-copykeys [options] "
|
||||
"file-with-key\n"
|
||||
"Copy keys to a smartcards\n");
|
||||
break;
|
||||
|
||||
default: p = NULL;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Used by gcry for logging */
|
||||
static void
|
||||
my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr)
|
||||
{
|
||||
/* translate the log levels */
|
||||
switch (level)
|
||||
{
|
||||
case GCRY_LOG_CONT: level = JNLIB_LOG_CONT; break;
|
||||
case GCRY_LOG_INFO: level = JNLIB_LOG_INFO; break;
|
||||
case GCRY_LOG_WARN: level = JNLIB_LOG_WARN; break;
|
||||
case GCRY_LOG_ERROR:level = JNLIB_LOG_ERROR; break;
|
||||
case GCRY_LOG_FATAL:level = JNLIB_LOG_FATAL; break;
|
||||
case GCRY_LOG_BUG: level = JNLIB_LOG_BUG; break;
|
||||
case GCRY_LOG_DEBUG:level = JNLIB_LOG_DEBUG; break;
|
||||
default: level = JNLIB_LOG_ERROR; break;
|
||||
}
|
||||
log_logv (level, fmt, arg_ptr);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main (int argc, char **argv )
|
||||
{
|
||||
ARGPARSE_ARGS pargs;
|
||||
int slot, rc;
|
||||
int reader_port = 32768; /* First USB reader. */
|
||||
struct app_ctx_s appbuf;
|
||||
|
||||
memset (&appbuf, 0, sizeof appbuf);
|
||||
|
||||
set_strusage (my_strusage);
|
||||
gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
|
||||
log_set_prefix ("sc-copykeys", 1);
|
||||
|
||||
/* check that the libraries are suitable. Do it here because
|
||||
the option parsing may need services of the library */
|
||||
if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
|
||||
{
|
||||
log_fatal( _("libgcrypt is too old (need %s, have %s)\n"),
|
||||
NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
|
||||
}
|
||||
|
||||
gcry_set_log_handler (my_gcry_logger, NULL);
|
||||
gcry_control (GCRYCTL_DISABLE_SECMEM, 0); /* FIXME - we want to use it */
|
||||
/* FIXME? gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);*/
|
||||
|
||||
pargs.argc = &argc;
|
||||
pargs.argv = &argv;
|
||||
pargs.flags= 1; /* do not remove the args */
|
||||
while (arg_parse (&pargs, opts) )
|
||||
{
|
||||
switch (pargs.r_opt)
|
||||
{
|
||||
case oVerbose: opt.verbose++; break;
|
||||
case oDebug: opt.debug |= pargs.r.ret_ulong; break;
|
||||
case oDebugAll: opt.debug = ~0; break;
|
||||
default : pargs.err = 2; break;
|
||||
}
|
||||
}
|
||||
if (log_get_errorcount(0))
|
||||
exit(2);
|
||||
|
||||
if (argc != 1)
|
||||
usage (1);
|
||||
|
||||
slot = apdu_open_reader (reader_port);
|
||||
if (slot == -1)
|
||||
exit (1);
|
||||
|
||||
/* FIXME: Use select_application. */
|
||||
appbuf.slot = slot;
|
||||
rc = app_select_openpgp (&appbuf, &appbuf.serialno, &appbuf.serialnolen);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("selecting openpgp failed: %s\n", gpg_strerror (rc));
|
||||
exit (1);
|
||||
}
|
||||
appbuf.initialized = 1;
|
||||
log_info ("openpgp application selected\n");
|
||||
|
||||
copykeys (&appbuf, *argv);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
send_status_info (CTRL ctrl, const char *keyword, ...)
|
||||
{
|
||||
/* DUMMY */
|
||||
}
|
||||
|
||||
|
||||
|
||||
static char *
|
||||
read_file (const char *fname, size_t *r_length)
|
||||
{
|
||||
FILE *fp;
|
||||
struct stat st;
|
||||
char *buf;
|
||||
size_t buflen;
|
||||
|
||||
fp = fname? fopen (fname, "rb") : stdin;
|
||||
if (!fp)
|
||||
{
|
||||
log_error ("can't open `%s': %s\n",
|
||||
fname? fname: "[stdin]", strerror (errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fstat (fileno(fp), &st))
|
||||
{
|
||||
log_error ("can't stat `%s': %s\n",
|
||||
fname? fname: "[stdin]", strerror (errno));
|
||||
if (fname)
|
||||
fclose (fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buflen = st.st_size;
|
||||
buf = xmalloc (buflen+1);
|
||||
if (fread (buf, buflen, 1, fp) != 1)
|
||||
{
|
||||
log_error ("error reading `%s': %s\n",
|
||||
fname? fname: "[stdin]", strerror (errno));
|
||||
if (fname)
|
||||
fclose (fp);
|
||||
xfree (buf);
|
||||
return NULL;
|
||||
}
|
||||
if (fname)
|
||||
fclose (fp);
|
||||
|
||||
*r_length = buflen;
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
static gcry_sexp_t
|
||||
read_key (const char *fname)
|
||||
{
|
||||
char *buf;
|
||||
size_t buflen;
|
||||
gcry_sexp_t private;
|
||||
int rc;
|
||||
|
||||
buf = read_file (fname, &buflen);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
|
||||
rc = gcry_sexp_new (&private, buf, buflen, 1);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("gcry_sexp_new failed: %s\n", gpg_strerror (rc));
|
||||
return NULL;
|
||||
}
|
||||
xfree (buf);
|
||||
|
||||
return private;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static gcry_mpi_t *
|
||||
sexp_to_kparms (gcry_sexp_t sexp, unsigned long *created)
|
||||
{
|
||||
gcry_sexp_t list, l2;
|
||||
const char *name;
|
||||
const char *s;
|
||||
size_t n;
|
||||
int i, idx;
|
||||
const char *elems;
|
||||
gcry_mpi_t *array;
|
||||
|
||||
*created = 0;
|
||||
list = gcry_sexp_find_token (sexp, "private-key", 0 );
|
||||
if(!list)
|
||||
return NULL;
|
||||
|
||||
/* quick hack to get the creation time. */
|
||||
l2 = gcry_sexp_find_token (list, "created", 0);
|
||||
if (l2 && (name = gcry_sexp_nth_data (l2, 1, &n)))
|
||||
{
|
||||
char *tmp = xmalloc (n+1);
|
||||
memcpy (tmp, name, n);
|
||||
tmp[n] = 0;
|
||||
*created = strtoul (tmp, NULL, 10);
|
||||
xfree (tmp);
|
||||
}
|
||||
gcry_sexp_release (l2);
|
||||
l2 = gcry_sexp_cadr (list);
|
||||
gcry_sexp_release (list);
|
||||
list = l2;
|
||||
name = gcry_sexp_nth_data (list, 0, &n);
|
||||
if(!name || n != 3 || memcmp (name, "rsa", 3))
|
||||
{
|
||||
gcry_sexp_release (list);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Parameter names used with RSA. */
|
||||
elems = "nedpqu";
|
||||
array = xcalloc (strlen(elems) + 1, sizeof *array);
|
||||
for (idx=0, s=elems; *s; s++, idx++ )
|
||||
{
|
||||
l2 = gcry_sexp_find_token (list, s, 1);
|
||||
if (!l2)
|
||||
{
|
||||
for (i=0; i<idx; i++)
|
||||
gcry_mpi_release (array[i]);
|
||||
xfree (array);
|
||||
gcry_sexp_release (list);
|
||||
return NULL; /* required parameter not found */
|
||||
}
|
||||
array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
|
||||
gcry_sexp_release (l2);
|
||||
if (!array[idx])
|
||||
{
|
||||
for (i=0; i<idx; i++)
|
||||
gcry_mpi_release (array[i]);
|
||||
xfree (array);
|
||||
gcry_sexp_release (list);
|
||||
return NULL; /* required parameter is invalid */
|
||||
}
|
||||
}
|
||||
|
||||
gcry_sexp_release (list);
|
||||
return array;
|
||||
}
|
||||
|
||||
|
||||
/* Return true if the SHA1 fingerprint FPR consists only of zeroes. */
|
||||
static int
|
||||
fpr_is_zero (const char *fpr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i < 20 && !fpr[i]; i++)
|
||||
;
|
||||
return (i == 20);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
show_sha1_fpr (const unsigned char *fpr)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (fpr)
|
||||
{
|
||||
for (i=0; i < 20 ; i+=2, fpr += 2 )
|
||||
{
|
||||
if (i == 10 )
|
||||
tty_printf (" ");
|
||||
tty_printf (" %02X%02X", *fpr, fpr[1]);
|
||||
}
|
||||
}
|
||||
else
|
||||
tty_printf (" [none]");
|
||||
tty_printf ("\n");
|
||||
}
|
||||
|
||||
/* Query the card, show a list of already stored keys and ask the user
|
||||
where to store the key. Returns the key number or 0 for cancel
|
||||
operation. */
|
||||
static int
|
||||
query_card (APP app)
|
||||
{
|
||||
int keyno = 0;
|
||||
char *serialno, *disp_name, *pubkey_url;
|
||||
unsigned char *fpr1, *fpr2, *fpr3;
|
||||
|
||||
|
||||
if (app_openpgp_cardinfo (app,
|
||||
&serialno,
|
||||
&disp_name,
|
||||
&pubkey_url,
|
||||
&fpr1, &fpr2, &fpr3))
|
||||
return 0;
|
||||
|
||||
|
||||
for (;;)
|
||||
{
|
||||
char *answer;
|
||||
|
||||
tty_printf ("\n");
|
||||
|
||||
tty_printf ("Serial number ....: %s\n",
|
||||
serialno? serialno : "[none]");
|
||||
tty_printf ("Name of cardholder: %s\n",
|
||||
disp_name && *disp_name? disp_name : "[not set]");
|
||||
tty_printf ("URL of public key : %s\n",
|
||||
pubkey_url && *pubkey_url? pubkey_url : "[not set]");
|
||||
tty_printf ("Signature key ....:");
|
||||
show_sha1_fpr (fpr1);
|
||||
tty_printf ("Encryption key....:");
|
||||
show_sha1_fpr (fpr2);
|
||||
tty_printf ("Authentication key:");
|
||||
show_sha1_fpr (fpr3);
|
||||
|
||||
tty_printf ("\n"
|
||||
"1 - store as signature key and reset usage counter\n"
|
||||
"2 - store as encryption key\n"
|
||||
"3 - store as authentication key\n"
|
||||
"Q - quit\n"
|
||||
"\n");
|
||||
|
||||
answer = tty_get("Your selection? ");
|
||||
tty_kill_prompt();
|
||||
if (strlen (answer) != 1)
|
||||
;
|
||||
else if ( *answer == '1' )
|
||||
{
|
||||
if ( (fpr1 && !fpr_is_zero (fpr1)) )
|
||||
{
|
||||
tty_printf ("\n");
|
||||
log_error ("WARNING: signature key does already exists!\n");
|
||||
tty_printf ("\n");
|
||||
if ( tty_get_answer_is_yes ("Replace existing key? ") )
|
||||
{
|
||||
keyno = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
keyno = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ( *answer == '2' )
|
||||
{
|
||||
if ( (fpr2 && !fpr_is_zero (fpr2)) )
|
||||
{
|
||||
tty_printf ("\n");
|
||||
log_error ("WARNING: encryption key does already exists!\n");
|
||||
tty_printf ("\n");
|
||||
if ( tty_get_answer_is_yes ("Replace existing key? ") )
|
||||
{
|
||||
keyno = 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
keyno = 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ( *answer == '3' )
|
||||
{
|
||||
if ( (fpr3 && !fpr_is_zero (fpr3)) )
|
||||
{
|
||||
tty_printf ("\n");
|
||||
log_error ("WARNING: authentication key does already exists!\n");
|
||||
tty_printf ("\n");
|
||||
if ( tty_get_answer_is_yes ("Replace existing key? ") )
|
||||
{
|
||||
keyno = 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
keyno = 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ( *answer == 'q' || *answer == 'Q')
|
||||
{
|
||||
keyno = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
xfree (serialno);
|
||||
xfree (disp_name);
|
||||
xfree (pubkey_url);
|
||||
xfree (fpr1);
|
||||
xfree (fpr2);
|
||||
xfree (fpr3);
|
||||
|
||||
return keyno;
|
||||
}
|
||||
|
||||
|
||||
/* Callback function to ask for a PIN. */
|
||||
static int
|
||||
pincb (void *arg, const char *prompt, char **pinvalue)
|
||||
{
|
||||
char *pin = xstrdup ("12345678");
|
||||
|
||||
/* pin = simple_pwquery (NULL, NULL, prompt, */
|
||||
/* "We need the admin's PIN to store the key on the card", */
|
||||
/* NULL); */
|
||||
/* if (!pin) */
|
||||
/* return gpg_error (GPG_ERR_CANCELED); */
|
||||
|
||||
|
||||
|
||||
*pinvalue = pin;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* This function expects a file (or NULL for stdin) with the secret
|
||||
and public key parameters. This file should consist of an
|
||||
S-expression as used by gpg-agent. Only the unprotected format is
|
||||
supported. Example:
|
||||
|
||||
(private-key
|
||||
(rsa
|
||||
(n #00e0ce9..[some bytes not shown]..51#)
|
||||
(e #010001#)
|
||||
(d #046129F..[some bytes not shown]..81#)
|
||||
(p #00e861b..[some bytes not shown]..f1#)
|
||||
(q #00f7a7c..[some bytes not shown]..61#)
|
||||
(u #304559a..[some bytes not shown]..9b#))
|
||||
(uri http://foo.bar x-foo:whatever_you_want))
|
||||
|
||||
*/
|
||||
static void
|
||||
copykeys (APP app, const char *fname)
|
||||
{
|
||||
int rc;
|
||||
gcry_sexp_t private;
|
||||
gcry_mpi_t *mpis, rsa_n, rsa_e, rsa_p, rsa_q;
|
||||
unsigned int nbits;
|
||||
size_t n;
|
||||
unsigned char *template, *tp;
|
||||
unsigned char m[128], e[4];
|
||||
size_t mlen, elen;
|
||||
unsigned long creation_date;
|
||||
time_t created_at;
|
||||
int keyno;
|
||||
|
||||
if (!strcmp (fname, "-"))
|
||||
fname = NULL;
|
||||
|
||||
private = read_key (fname);
|
||||
if (!private)
|
||||
exit (1);
|
||||
|
||||
mpis = sexp_to_kparms (private, &creation_date);
|
||||
if (!creation_date)
|
||||
{
|
||||
log_info ("no creation date found - assuming current date\n");
|
||||
created_at = time (NULL);
|
||||
}
|
||||
else
|
||||
created_at = creation_date;
|
||||
gcry_sexp_release (private);
|
||||
if (!mpis)
|
||||
{
|
||||
log_error ("invalid structure of key file or not RSA\n");
|
||||
exit (1);
|
||||
}
|
||||
/* MPIS is now an array with the key parameters as defined by OpenPGP. */
|
||||
rsa_n = mpis[0];
|
||||
rsa_e = mpis[1];
|
||||
gcry_mpi_release (mpis[2]);
|
||||
rsa_p = mpis[3];
|
||||
rsa_q = mpis[4];
|
||||
gcry_mpi_release (mpis[5]);
|
||||
xfree (mpis);
|
||||
|
||||
nbits = gcry_mpi_get_nbits (rsa_e);
|
||||
if (nbits < 2 || nbits > 32)
|
||||
{
|
||||
log_error ("public exponent too large (more than 32 bits)\n");
|
||||
goto failure;
|
||||
}
|
||||
nbits = gcry_mpi_get_nbits (rsa_p);
|
||||
if (nbits != 512)
|
||||
{
|
||||
log_error ("length of first RSA prime is not 512\n");
|
||||
goto failure;
|
||||
}
|
||||
nbits = gcry_mpi_get_nbits (rsa_q);
|
||||
if (nbits != 512)
|
||||
{
|
||||
log_error ("length of second RSA prime is not 512\n");
|
||||
goto failure;
|
||||
}
|
||||
|
||||
nbits = gcry_mpi_get_nbits (rsa_n);
|
||||
if (nbits != 1024)
|
||||
{
|
||||
log_error ("length of RSA modulus is not 1024\n");
|
||||
goto failure;
|
||||
}
|
||||
|
||||
keyno = query_card (app);
|
||||
if (!keyno)
|
||||
goto failure;
|
||||
|
||||
/* Build the private key template as described in section 4.3.3.6 of
|
||||
the specs.
|
||||
0xC0 <length> public exponent
|
||||
0xC1 <length> prime p
|
||||
0xC2 <length> prime q */
|
||||
template = tp = xmalloc (1+2 + 1+1+4 + 1+1+64 + 1+1+64);
|
||||
*tp++ = 0xC0;
|
||||
*tp++ = 4;
|
||||
rc = gcry_mpi_print (GCRYMPI_FMT_USG, tp, 4, &n, rsa_e);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("mpi_print failed: %s\n", gpg_strerror (rc));
|
||||
goto failure;
|
||||
}
|
||||
assert (n <= 4);
|
||||
memcpy (e, tp, n);
|
||||
elen = n;
|
||||
if (n != 4)
|
||||
{
|
||||
memmove (tp+4-n, tp, 4-n);
|
||||
memset (tp, 0, 4-n);
|
||||
}
|
||||
tp += 4;
|
||||
|
||||
*tp++ = 0xC1;
|
||||
*tp++ = 64;
|
||||
rc = gcry_mpi_print (GCRYMPI_FMT_USG, tp, 64, &n, rsa_p);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("mpi_print failed: %s\n", gpg_strerror (rc));
|
||||
goto failure;
|
||||
}
|
||||
assert (n == 64);
|
||||
tp += 64;
|
||||
|
||||
*tp++ = 0xC2;
|
||||
*tp++ = 64;
|
||||
rc = gcry_mpi_print (GCRYMPI_FMT_USG, tp, 64, &n, rsa_q);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("mpi_print failed: %s\n", gpg_strerror (rc));
|
||||
goto failure;
|
||||
}
|
||||
assert (n == 64);
|
||||
tp += 64;
|
||||
assert (tp - template == 138);
|
||||
|
||||
/* (we need the modulus to calculate the fingerprint) */
|
||||
rc = gcry_mpi_print (GCRYMPI_FMT_USG, m, 128, &n, rsa_n);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("mpi_print failed: %s\n", gpg_strerror (rc));
|
||||
goto failure;
|
||||
}
|
||||
assert (n == 128);
|
||||
mlen = 128;
|
||||
|
||||
|
||||
rc = app_openpgp_storekey (app, keyno,
|
||||
template, tp - template,
|
||||
created_at,
|
||||
m, mlen,
|
||||
e, elen,
|
||||
pincb, NULL);
|
||||
|
||||
if (rc)
|
||||
{
|
||||
log_error ("error storing key: %s\n", gpg_strerror (rc));
|
||||
goto failure;
|
||||
}
|
||||
log_info ("key successfully stored\n");
|
||||
{
|
||||
unsigned char *mm, *ee;
|
||||
size_t mmlen, eelen;
|
||||
int i;
|
||||
|
||||
rc = app_openpgp_readkey (app, keyno, &mm, &mmlen, &ee, &eelen);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("error reading key back: %s\n", gpg_strerror (rc));
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* Strip leading zeroes. */
|
||||
for (i=0; i < mmlen && !mm[i]; i++)
|
||||
;
|
||||
mmlen -= i;
|
||||
memmove (mm, mm+i, mmlen);
|
||||
for (i=0; i < eelen && !ee[i]; i++)
|
||||
;
|
||||
eelen -= i;
|
||||
memmove (ee, ee+i, eelen);
|
||||
|
||||
if (eelen != elen || mmlen != mlen)
|
||||
{
|
||||
log_error ("key parameter length mismatch (n=%u/%u, e=%u/%u)\n",
|
||||
(unsigned int)mlen, (unsigned int)mmlen,
|
||||
(unsigned int)elen, (unsigned int)eelen);
|
||||
xfree (mm);
|
||||
xfree (ee);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
if (memcmp (m, mm, mlen))
|
||||
{
|
||||
log_error ("key parameter n mismatch\n");
|
||||
log_printhex ("original n: ", m, mlen);
|
||||
log_printhex (" copied n: ", mm, mlen);
|
||||
xfree (mm);
|
||||
xfree (ee);
|
||||
goto failure;
|
||||
}
|
||||
if (memcmp (e, ee, elen))
|
||||
{
|
||||
log_error ("key parameter e mismatch\n");
|
||||
log_printhex ("original e: ", e, elen);
|
||||
log_printhex (" copied e: ", ee, elen);
|
||||
xfree (mm);
|
||||
xfree (ee);
|
||||
goto failure;
|
||||
}
|
||||
xfree (mm);
|
||||
xfree (ee);
|
||||
}
|
||||
|
||||
|
||||
gcry_mpi_release (rsa_e);
|
||||
gcry_mpi_release (rsa_p);
|
||||
gcry_mpi_release (rsa_q);
|
||||
gcry_mpi_release (rsa_n);
|
||||
return;
|
||||
|
||||
failure:
|
||||
gcry_mpi_release (rsa_e);
|
||||
gcry_mpi_release (rsa_p);
|
||||
gcry_mpi_release (rsa_q);
|
||||
gcry_mpi_release (rsa_n);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
|
209
scd/sc-investigate.c
Normal file
209
scd/sc-investigate.c
Normal file
|
@ -0,0 +1,209 @@
|
|||
/* sc-investigate.c - A tool to look around on smartcards.
|
||||
* Copyright (C) 2003 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
* GnuPG is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GnuPG is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define JNLIB_NEED_LOG_LOGV
|
||||
#include "scdaemon.h"
|
||||
#include <gcrypt.h>
|
||||
|
||||
#include "apdu.h" /* for open_reader */
|
||||
#include "atr.h"
|
||||
#include "app-common.h"
|
||||
|
||||
#define _(a) (a)
|
||||
|
||||
|
||||
enum cmd_and_opt_values
|
||||
{ oVerbose = 'v',
|
||||
oReaderPort = 500,
|
||||
oDebug,
|
||||
oDebugAll,
|
||||
|
||||
oGenRandom,
|
||||
|
||||
aTest };
|
||||
|
||||
|
||||
static ARGPARSE_OPTS opts[] = {
|
||||
|
||||
{ 301, NULL, 0, "@Options:\n " },
|
||||
|
||||
{ oVerbose, "verbose", 0, "verbose" },
|
||||
{ oReaderPort, "reader-port", 1, "|N|connect to reader at port N"},
|
||||
{ oDebug, "debug" ,4|16, "set debugging flags"},
|
||||
{ oDebugAll, "debug-all" ,0, "enable full debugging"},
|
||||
{ oGenRandom, "gen-random", 4, "|N|generate N bytes of random"},
|
||||
{0}
|
||||
};
|
||||
|
||||
static const char *
|
||||
my_strusage (int level)
|
||||
{
|
||||
const char *p;
|
||||
switch (level)
|
||||
{
|
||||
case 11: p = "sc-investigate (GnuPG)";
|
||||
break;
|
||||
case 13: p = VERSION; break;
|
||||
case 17: p = PRINTABLE_OS_NAME; break;
|
||||
case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
|
||||
break;
|
||||
case 1:
|
||||
case 40: p = _("Usage: sc-investigate [options] (-h for help)\n");
|
||||
break;
|
||||
case 41: p = _("Syntax: sc-investigate [options] [args]]\n"
|
||||
"Have a look at smartcards\n");
|
||||
break;
|
||||
|
||||
default: p = NULL;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Used by gcry for logging */
|
||||
static void
|
||||
my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr)
|
||||
{
|
||||
/* translate the log levels */
|
||||
switch (level)
|
||||
{
|
||||
case GCRY_LOG_CONT: level = JNLIB_LOG_CONT; break;
|
||||
case GCRY_LOG_INFO: level = JNLIB_LOG_INFO; break;
|
||||
case GCRY_LOG_WARN: level = JNLIB_LOG_WARN; break;
|
||||
case GCRY_LOG_ERROR:level = JNLIB_LOG_ERROR; break;
|
||||
case GCRY_LOG_FATAL:level = JNLIB_LOG_FATAL; break;
|
||||
case GCRY_LOG_BUG: level = JNLIB_LOG_BUG; break;
|
||||
case GCRY_LOG_DEBUG:level = JNLIB_LOG_DEBUG; break;
|
||||
default: level = JNLIB_LOG_ERROR; break;
|
||||
}
|
||||
log_logv (level, fmt, arg_ptr);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main (int argc, char **argv )
|
||||
{
|
||||
ARGPARSE_ARGS pargs;
|
||||
int slot, rc;
|
||||
int reader_port = 32768; /* First USB reader. */
|
||||
struct app_ctx_s appbuf;
|
||||
unsigned long gen_random = 0;
|
||||
|
||||
memset (&appbuf, 0, sizeof appbuf);
|
||||
|
||||
set_strusage (my_strusage);
|
||||
gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
|
||||
log_set_prefix ("sc-investigate", 1);
|
||||
|
||||
/* check that the libraries are suitable. Do it here because
|
||||
the option parsing may need services of the library */
|
||||
if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
|
||||
{
|
||||
log_fatal( _("libgcrypt is too old (need %s, have %s)\n"),
|
||||
NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
|
||||
}
|
||||
|
||||
gcry_set_log_handler (my_gcry_logger, NULL);
|
||||
/* FIXME? gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);*/
|
||||
|
||||
pargs.argc = &argc;
|
||||
pargs.argv = &argv;
|
||||
pargs.flags= 1; /* do not remove the args */
|
||||
while (arg_parse (&pargs, opts) )
|
||||
{
|
||||
switch (pargs.r_opt)
|
||||
{
|
||||
case oVerbose: opt.verbose++; break;
|
||||
case oDebug: opt.debug |= pargs.r.ret_ulong; break;
|
||||
case oDebugAll: opt.debug = ~0; break;
|
||||
case oGenRandom: gen_random = pargs.r.ret_ulong; break;
|
||||
default : pargs.err = 2; break;
|
||||
}
|
||||
}
|
||||
if (log_get_errorcount(0))
|
||||
exit(2);
|
||||
|
||||
if (opt.verbose < 2)
|
||||
opt.verbose = 2; /* hack to let select_openpgp print some info. */
|
||||
|
||||
if (argc)
|
||||
usage (1);
|
||||
|
||||
slot = apdu_open_reader (reader_port);
|
||||
if (slot == -1)
|
||||
exit (1);
|
||||
|
||||
if (!gen_random)
|
||||
{
|
||||
rc = atr_dump (slot, stdout);
|
||||
if (rc)
|
||||
log_error ("can't dump ATR: %s\n", gpg_strerror (rc));
|
||||
}
|
||||
|
||||
appbuf.slot = slot;
|
||||
rc = app_select_openpgp (&appbuf, NULL, NULL);
|
||||
if (rc)
|
||||
log_error ("selecting openpgp failed: %s\n", gpg_strerror (rc));
|
||||
else
|
||||
{
|
||||
appbuf.initialized = 1;
|
||||
log_info ("openpgp application selected\n");
|
||||
|
||||
if (gen_random)
|
||||
{
|
||||
size_t nbytes;
|
||||
unsigned char *buffer;
|
||||
|
||||
buffer = xmalloc (4096);
|
||||
do
|
||||
{
|
||||
nbytes = gen_random > 4096? 4096 : gen_random;
|
||||
rc = app_get_challenge (&appbuf, nbytes, buffer);
|
||||
if (rc)
|
||||
log_error ("app_get_challenge failed: %s\n",gpg_strerror (rc));
|
||||
else
|
||||
{
|
||||
if (fwrite (buffer, nbytes, 1, stdout) != 1)
|
||||
log_error ("writing to stdout failed: %s\n",
|
||||
strerror (errno));
|
||||
gen_random -= nbytes;
|
||||
}
|
||||
}
|
||||
while (gen_random && !log_get_errorcount (0));
|
||||
xfree (buffer);
|
||||
}
|
||||
}
|
||||
|
||||
return log_get_errorcount (0)? 2:0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
send_status_info (CTRL ctrl, const char *keyword, ...)
|
||||
{
|
||||
/* DUMMY */
|
||||
}
|
638
scd/scdaemon.c
Normal file
638
scd/scdaemon.c
Normal file
|
@ -0,0 +1,638 @@
|
|||
/* scdaemon.c - The GnuPG Smartcard Daemon
|
||||
* Copyright (C) 2001, 2002 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
* GnuPG is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GnuPG is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
#define JNLIB_NEED_LOG_LOGV
|
||||
#include "scdaemon.h"
|
||||
#include <ksba.h>
|
||||
#include <gcrypt.h>
|
||||
|
||||
#include <assuan.h> /* malloc hooks */
|
||||
|
||||
#include "i18n.h"
|
||||
#include "sysutils.h"
|
||||
|
||||
|
||||
|
||||
enum cmd_and_opt_values
|
||||
{ aNull = 0,
|
||||
oCsh = 'c',
|
||||
oQuiet = 'q',
|
||||
oSh = 's',
|
||||
oVerbose = 'v',
|
||||
|
||||
oNoVerbose = 500,
|
||||
oOptions,
|
||||
oDebug,
|
||||
oDebugAll,
|
||||
oDebugWait,
|
||||
oDebugSC,
|
||||
oNoGreeting,
|
||||
oNoOptions,
|
||||
oHomedir,
|
||||
oNoDetach,
|
||||
oNoGrab,
|
||||
oLogFile,
|
||||
oServer,
|
||||
oDaemon,
|
||||
oBatch,
|
||||
oReaderPort,
|
||||
|
||||
aTest };
|
||||
|
||||
|
||||
|
||||
static ARGPARSE_OPTS opts[] = {
|
||||
|
||||
{ 301, NULL, 0, N_("@Options:\n ") },
|
||||
|
||||
{ oServer, "server", 0, N_("run in server mode (foreground)") },
|
||||
{ oDaemon, "daemon", 0, N_("run in daemon mode (background)") },
|
||||
{ oVerbose, "verbose", 0, N_("verbose") },
|
||||
{ oQuiet, "quiet", 0, N_("be somewhat more quiet") },
|
||||
{ oSh, "sh", 0, N_("sh-style command output") },
|
||||
{ oCsh, "csh", 0, N_("csh-style command output") },
|
||||
{ oOptions, "options" , 2, N_("read options from file")},
|
||||
{ oDebug, "debug" ,4|16, N_("set debugging flags")},
|
||||
{ oDebugAll, "debug-all" ,0, N_("enable full debugging")},
|
||||
{ oDebugWait,"debug-wait",1, "@"},
|
||||
{ oDebugSC, "debug-sc", 1, N_("|N|set OpenSC debug level to N")},
|
||||
{ oNoDetach, "no-detach" ,0, N_("do not detach from the console")},
|
||||
{ oLogFile, "log-file" ,2, N_("use a log file for the server")},
|
||||
{ oReaderPort, "reader-port", 1, N_("|N|connect to reader at port N")},
|
||||
|
||||
{0}
|
||||
};
|
||||
|
||||
|
||||
static volatile int caught_fatal_sig = 0;
|
||||
|
||||
/* It is possible that we are currently running under setuid permissions */
|
||||
static int maybe_setuid = 1;
|
||||
|
||||
/* Name of the communication socket */
|
||||
static char socket_name[128];
|
||||
|
||||
static const char *
|
||||
my_strusage (int level)
|
||||
{
|
||||
const char *p;
|
||||
switch (level)
|
||||
{
|
||||
case 11: p = "scdaemon (GnuPG)";
|
||||
break;
|
||||
case 13: p = VERSION; break;
|
||||
case 17: p = PRINTABLE_OS_NAME; break;
|
||||
case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
|
||||
break;
|
||||
case 1:
|
||||
case 40: p = _("Usage: scdaemon [options] (-h for help)");
|
||||
break;
|
||||
case 41: p = _("Syntax: scdaemon [options] [command [args]]\n"
|
||||
"Smartcard daemon for GnuPG\n");
|
||||
break;
|
||||
|
||||
default: p = NULL;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
i18n_init (void)
|
||||
{
|
||||
#ifdef USE_SIMPLE_GETTEXT
|
||||
set_gettext_file( PACKAGE );
|
||||
#else
|
||||
#ifdef ENABLE_NLS
|
||||
setlocale (LC_ALL, "");
|
||||
bindtextdomain (PACKAGE, LOCALEDIR);
|
||||
textdomain (PACKAGE);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Used by gcry for logging */
|
||||
static void
|
||||
my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr)
|
||||
{
|
||||
/* translate the log levels */
|
||||
switch (level)
|
||||
{
|
||||
case GCRY_LOG_CONT: level = JNLIB_LOG_CONT; break;
|
||||
case GCRY_LOG_INFO: level = JNLIB_LOG_INFO; break;
|
||||
case GCRY_LOG_WARN: level = JNLIB_LOG_WARN; break;
|
||||
case GCRY_LOG_ERROR:level = JNLIB_LOG_ERROR; break;
|
||||
case GCRY_LOG_FATAL:level = JNLIB_LOG_FATAL; break;
|
||||
case GCRY_LOG_BUG: level = JNLIB_LOG_BUG; break;
|
||||
case GCRY_LOG_DEBUG:level = JNLIB_LOG_DEBUG; break;
|
||||
default: level = JNLIB_LOG_ERROR; break;
|
||||
}
|
||||
log_logv (level, fmt, arg_ptr);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
cleanup (void)
|
||||
{
|
||||
if (*socket_name)
|
||||
{
|
||||
char *p;
|
||||
|
||||
remove (socket_name);
|
||||
p = strrchr (socket_name, '/');
|
||||
if (p)
|
||||
{
|
||||
*p = 0;
|
||||
rmdir (socket_name);
|
||||
*p = '/';
|
||||
}
|
||||
*socket_name = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static RETSIGTYPE
|
||||
cleanup_sh (int sig)
|
||||
{
|
||||
if (caught_fatal_sig)
|
||||
raise (sig);
|
||||
caught_fatal_sig = 1;
|
||||
|
||||
/* gcry_control( GCRYCTL_TERM_SECMEM );*/
|
||||
cleanup ();
|
||||
|
||||
#ifndef HAVE_DOSISH_SYSTEM
|
||||
{ /* reset action to default action and raise signal again */
|
||||
struct sigaction nact;
|
||||
nact.sa_handler = SIG_DFL;
|
||||
sigemptyset( &nact.sa_mask );
|
||||
nact.sa_flags = 0;
|
||||
sigaction( sig, &nact, NULL);
|
||||
}
|
||||
#endif
|
||||
raise( sig );
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv )
|
||||
{
|
||||
ARGPARSE_ARGS pargs;
|
||||
int orig_argc;
|
||||
int may_coredump;
|
||||
char **orig_argv;
|
||||
FILE *configfp = NULL;
|
||||
char *configname = NULL;
|
||||
const char *shell;
|
||||
unsigned configlineno;
|
||||
int parse_debug = 0;
|
||||
int default_config =1;
|
||||
int greeting = 0;
|
||||
int nogreeting = 0;
|
||||
int pipe_server = 0;
|
||||
int is_daemon = 0;
|
||||
int nodetach = 0;
|
||||
int csh_style = 0;
|
||||
char *logfile = NULL;
|
||||
int debug_wait = 0;
|
||||
int reader_port = 32768; /* First USB reader. */
|
||||
|
||||
set_strusage (my_strusage);
|
||||
gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
|
||||
/* Please note that we may running SUID(ROOT), so be very CAREFUL
|
||||
when adding any stuff between here and the call to INIT_SECMEM()
|
||||
somewhere after the option parsing */
|
||||
log_set_prefix ("scdaemon", 1|4);
|
||||
i18n_init ();
|
||||
|
||||
/* check that the libraries are suitable. Do it here because
|
||||
the option parsing may need services of the library */
|
||||
if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
|
||||
{
|
||||
log_fatal( _("libgcrypt is too old (need %s, have %s)\n"),
|
||||
NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
|
||||
}
|
||||
|
||||
ksba_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free);
|
||||
assuan_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free);
|
||||
gcry_set_log_handler (my_gcry_logger, NULL);
|
||||
gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
|
||||
|
||||
may_coredump = disable_core_dumps ();
|
||||
|
||||
shell = getenv ("SHELL");
|
||||
if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") )
|
||||
csh_style = 1;
|
||||
|
||||
/* FIXME: Using this homedir option does only make sense when not
|
||||
running as a system service. We might want to check for this by
|
||||
looking at the uid or ebtter use an explict option for this */
|
||||
opt.homedir = getenv("GNUPGHOME");
|
||||
if (!opt.homedir || !*opt.homedir)
|
||||
opt.homedir = GNUPG_DEFAULT_HOMEDIR;
|
||||
|
||||
/* check whether we have a config file on the commandline */
|
||||
orig_argc = argc;
|
||||
orig_argv = argv;
|
||||
pargs.argc = &argc;
|
||||
pargs.argv = &argv;
|
||||
pargs.flags= 1|(1<<6); /* do not remove the args, ignore version */
|
||||
while (arg_parse( &pargs, opts))
|
||||
{
|
||||
if (pargs.r_opt == oDebug || pargs.r_opt == oDebugAll)
|
||||
parse_debug++;
|
||||
else if (pargs.r_opt == oOptions)
|
||||
{ /* yes there is one, so we do not try the default one, but
|
||||
read the option file when it is encountered at the
|
||||
commandline */
|
||||
default_config = 0;
|
||||
}
|
||||
else if (pargs.r_opt == oNoOptions)
|
||||
default_config = 0; /* --no-options */
|
||||
else if (pargs.r_opt == oHomedir)
|
||||
opt.homedir = pargs.r.ret_str;
|
||||
}
|
||||
|
||||
/* initialize the secure memory. */
|
||||
gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
|
||||
maybe_setuid = 0;
|
||||
|
||||
/*
|
||||
Now we are working under our real uid
|
||||
*/
|
||||
|
||||
|
||||
if (default_config)
|
||||
configname = make_filename (opt.homedir, "scdaemon.conf", NULL );
|
||||
|
||||
argc = orig_argc;
|
||||
argv = orig_argv;
|
||||
pargs.argc = &argc;
|
||||
pargs.argv = &argv;
|
||||
pargs.flags= 1; /* do not remove the args */
|
||||
next_pass:
|
||||
if (configname)
|
||||
{
|
||||
configlineno = 0;
|
||||
configfp = fopen (configname, "r");
|
||||
if (!configfp)
|
||||
{
|
||||
if (default_config)
|
||||
{
|
||||
if( parse_debug )
|
||||
log_info (_("NOTE: no default option file `%s'\n"),
|
||||
configname );
|
||||
}
|
||||
else
|
||||
{
|
||||
log_error (_("option file `%s': %s\n"),
|
||||
configname, strerror(errno) );
|
||||
exit(2);
|
||||
}
|
||||
xfree (configname);
|
||||
configname = NULL;
|
||||
}
|
||||
if (parse_debug && configname )
|
||||
log_info (_("reading options from `%s'\n"), configname );
|
||||
default_config = 0;
|
||||
}
|
||||
|
||||
while (optfile_parse( configfp, configname, &configlineno, &pargs, opts) )
|
||||
{
|
||||
switch (pargs.r_opt)
|
||||
{
|
||||
case oQuiet: opt.quiet = 1; break;
|
||||
case oVerbose: opt.verbose++; break;
|
||||
case oBatch: opt.batch=1; break;
|
||||
|
||||
case oDebug: opt.debug |= pargs.r.ret_ulong; break;
|
||||
case oDebugAll: opt.debug = ~0; break;
|
||||
case oDebugWait: debug_wait = pargs.r.ret_int; break;
|
||||
case oDebugSC: opt.debug_sc = pargs.r.ret_int; break;
|
||||
|
||||
case oOptions:
|
||||
/* config files may not be nested (silently ignore them) */
|
||||
if (!configfp)
|
||||
{
|
||||
xfree(configname);
|
||||
configname = xstrdup(pargs.r.ret_str);
|
||||
goto next_pass;
|
||||
}
|
||||
break;
|
||||
case oNoGreeting: nogreeting = 1; break;
|
||||
case oNoVerbose: opt.verbose = 0; break;
|
||||
case oNoOptions: break; /* no-options */
|
||||
case oHomedir: opt.homedir = pargs.r.ret_str; break;
|
||||
case oNoDetach: nodetach = 1; break;
|
||||
case oLogFile: logfile = pargs.r.ret_str; break;
|
||||
case oCsh: csh_style = 1; break;
|
||||
case oSh: csh_style = 0; break;
|
||||
case oServer: pipe_server = 1; break;
|
||||
case oDaemon: is_daemon = 1; break;
|
||||
|
||||
case oReaderPort: reader_port = pargs.r.ret_int; break;
|
||||
|
||||
default : pargs.err = configfp? 1:2; break;
|
||||
}
|
||||
}
|
||||
if (configfp)
|
||||
{
|
||||
fclose( configfp );
|
||||
configfp = NULL;
|
||||
xfree(configname);
|
||||
configname = NULL;
|
||||
goto next_pass;
|
||||
}
|
||||
xfree (configname);
|
||||
configname = NULL;
|
||||
if (log_get_errorcount(0))
|
||||
exit(2);
|
||||
if (nogreeting )
|
||||
greeting = 0;
|
||||
|
||||
if (greeting)
|
||||
{
|
||||
fprintf (stderr, "%s %s; %s\n",
|
||||
strusage(11), strusage(13), strusage(14) );
|
||||
fprintf (stderr, "%s\n", strusage(15) );
|
||||
}
|
||||
#ifdef IS_DEVELOPMENT_VERSION
|
||||
log_info ("NOTE: this is a development version!\n");
|
||||
#endif
|
||||
|
||||
|
||||
if (atexit (cleanup))
|
||||
{
|
||||
log_error ("atexit failed\n");
|
||||
cleanup ();
|
||||
exit (1);
|
||||
}
|
||||
|
||||
|
||||
if (debug_wait && pipe_server)
|
||||
{
|
||||
log_debug ("waiting for debugger - my pid is %u .....\n",
|
||||
(unsigned int)getpid());
|
||||
sleep (debug_wait);
|
||||
log_debug ("... okay\n");
|
||||
}
|
||||
|
||||
/* now start with logging to a file if this is desired */
|
||||
if (logfile)
|
||||
{
|
||||
log_set_file (logfile);
|
||||
log_set_prefix (NULL, 1|2|4);
|
||||
}
|
||||
|
||||
|
||||
if (pipe_server)
|
||||
{ /* this is the simple pipe based server */
|
||||
scd_command_handler (-1);
|
||||
}
|
||||
else if (!is_daemon)
|
||||
{
|
||||
log_info (_("please use the option `--daemon'"
|
||||
" to run the program in the background\n"));
|
||||
}
|
||||
else
|
||||
{ /* regular server mode */
|
||||
int fd;
|
||||
pid_t pid;
|
||||
int i;
|
||||
int len;
|
||||
struct sockaddr_un serv_addr;
|
||||
char *p;
|
||||
|
||||
/* fixme: if there is already a running gpg-agent we should
|
||||
share the same directory - and vice versa */
|
||||
*socket_name = 0;
|
||||
snprintf (socket_name, DIM(socket_name)-1,
|
||||
"/tmp/gpg-XXXXXX/S.scdaemon");
|
||||
socket_name[DIM(socket_name)-1] = 0;
|
||||
p = strrchr (socket_name, '/');
|
||||
if (!p)
|
||||
BUG ();
|
||||
*p = 0;;
|
||||
if (!mkdtemp(socket_name))
|
||||
{
|
||||
log_error ("can't create directory `%s': %s\n",
|
||||
socket_name, strerror(errno) );
|
||||
exit (1);
|
||||
}
|
||||
*p = '/';
|
||||
|
||||
if (strchr (socket_name, ':') )
|
||||
{
|
||||
log_error ("colons are not allowed in the socket name\n");
|
||||
exit (1);
|
||||
}
|
||||
if (strlen (socket_name)+1 >= sizeof serv_addr.sun_path )
|
||||
{
|
||||
log_error ("name of socket to long\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
|
||||
fd = socket (AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd == -1)
|
||||
{
|
||||
log_error ("can't create socket: %s\n", strerror(errno) );
|
||||
exit (1);
|
||||
}
|
||||
|
||||
memset (&serv_addr, 0, sizeof serv_addr);
|
||||
serv_addr.sun_family = AF_UNIX;
|
||||
strcpy (serv_addr.sun_path, socket_name);
|
||||
len = (offsetof (struct sockaddr_un, sun_path)
|
||||
+ strlen(serv_addr.sun_path) + 1);
|
||||
|
||||
if (bind (fd, (struct sockaddr*)&serv_addr, len) == -1)
|
||||
{
|
||||
log_error ("error binding socket to `%s': %s\n",
|
||||
serv_addr.sun_path, strerror (errno) );
|
||||
close (fd);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (listen (fd, 5 ) == -1)
|
||||
{
|
||||
log_error ("listen() failed: %s\n", strerror (errno));
|
||||
close (fd);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (opt.verbose)
|
||||
log_info ("listening on socket `%s'\n", socket_name );
|
||||
|
||||
|
||||
fflush (NULL);
|
||||
pid = fork ();
|
||||
if (pid == (pid_t)-1)
|
||||
{
|
||||
log_fatal ("fork failed: %s\n", strerror (errno) );
|
||||
exit (1);
|
||||
}
|
||||
else if (pid)
|
||||
{ /* we are the parent */
|
||||
char *infostr;
|
||||
|
||||
close (fd);
|
||||
|
||||
/* create the info string: <name>:<pid>:<protocol_version> */
|
||||
if (asprintf (&infostr, "SCDAEMON_INFO=%s:%lu:1",
|
||||
socket_name, (ulong)pid ) < 0)
|
||||
{
|
||||
log_error ("out of core\n");
|
||||
kill (pid, SIGTERM);
|
||||
exit (1);
|
||||
}
|
||||
*socket_name = 0; /* don't let cleanup() remove the socket -
|
||||
the child should do this from now on */
|
||||
if (argc)
|
||||
{ /* run the program given on the commandline */
|
||||
if (putenv (infostr))
|
||||
{
|
||||
log_error ("failed to set environment: %s\n",
|
||||
strerror (errno) );
|
||||
kill (pid, SIGTERM );
|
||||
exit (1);
|
||||
}
|
||||
execvp (argv[0], argv);
|
||||
log_error ("failed to run the command: %s\n", strerror (errno));
|
||||
kill (pid, SIGTERM);
|
||||
exit (1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* print the environment string, so that the caller can use
|
||||
shell's eval to set it */
|
||||
if (csh_style)
|
||||
{
|
||||
*strchr (infostr, '=') = ' ';
|
||||
printf ( "setenv %s\n", infostr);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ( "%s; export SCDAEMON_INFO;\n", infostr);
|
||||
}
|
||||
free (infostr);
|
||||
exit (0);
|
||||
}
|
||||
/* NOTREACHED */
|
||||
} /* end parent */
|
||||
|
||||
/* this is the child */
|
||||
|
||||
/* detach from tty and put process into a new session */
|
||||
if (!nodetach )
|
||||
{ /* close stdin, stdout and stderr unless it is the log stream */
|
||||
for (i=0; i <= 2; i++)
|
||||
{
|
||||
if ( log_get_fd () != i)
|
||||
close (i);
|
||||
}
|
||||
if (setsid() == -1)
|
||||
{
|
||||
log_error ("setsid() failed: %s\n", strerror(errno) );
|
||||
cleanup ();
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
|
||||
/* setup signals */
|
||||
{
|
||||
struct sigaction oact, nact;
|
||||
|
||||
nact.sa_handler = cleanup_sh;
|
||||
sigemptyset (&nact.sa_mask);
|
||||
nact.sa_flags = 0;
|
||||
|
||||
sigaction (SIGHUP, NULL, &oact);
|
||||
if (oact.sa_handler != SIG_IGN)
|
||||
sigaction (SIGHUP, &nact, NULL);
|
||||
sigaction( SIGTERM, NULL, &oact );
|
||||
if (oact.sa_handler != SIG_IGN)
|
||||
sigaction (SIGTERM, &nact, NULL);
|
||||
nact.sa_handler = SIG_IGN;
|
||||
sigaction (SIGPIPE, &nact, NULL);
|
||||
sigaction (SIGINT, &nact, NULL);
|
||||
}
|
||||
|
||||
if (chdir("/"))
|
||||
{
|
||||
log_error ("chdir to / failed: %s\n", strerror (errno));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
scd_command_handler (fd);
|
||||
|
||||
close (fd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
scd_exit (int rc)
|
||||
{
|
||||
#if 0
|
||||
#warning no update_random_seed_file
|
||||
update_random_seed_file();
|
||||
#endif
|
||||
#if 0
|
||||
/* at this time a bit annoying */
|
||||
if (opt.debug & DBG_MEMSTAT_VALUE)
|
||||
{
|
||||
gcry_control( GCRYCTL_DUMP_MEMORY_STATS );
|
||||
gcry_control( GCRYCTL_DUMP_RANDOM_STATS );
|
||||
}
|
||||
if (opt.debug)
|
||||
gcry_control (GCRYCTL_DUMP_SECMEM_STATS );
|
||||
#endif
|
||||
gcry_control (GCRYCTL_TERM_SECMEM );
|
||||
rc = rc? rc : log_get_errorcount(0)? 2 : 0;
|
||||
exit (rc);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
scd_init_default_ctrl (CTRL ctrl)
|
||||
{
|
||||
|
||||
}
|
||||
|
127
scd/scdaemon.h
Normal file
127
scd/scdaemon.h
Normal file
|
@ -0,0 +1,127 @@
|
|||
/* scdaemon.h - Global definitions for the SCdaemon
|
||||
* Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
* GnuPG is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GnuPG is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
*/
|
||||
|
||||
#ifndef SCDAEMON_H
|
||||
#define SCDAEMON_H
|
||||
|
||||
#ifdef GPG_ERR_SOURCE_DEFAULT
|
||||
#error GPG_ERR_SOURCE_DEFAULT already defined
|
||||
#endif
|
||||
#define GPG_ERR_SOURCE_DEFAULT GPG_ERR_SOURCE_SCD
|
||||
#include <gpg-error.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <time.h>
|
||||
#include <gcrypt.h>
|
||||
#include "../common/util.h"
|
||||
#include "../common/errors.h"
|
||||
|
||||
/* Convenience funcion to be used instead of returning the old
|
||||
GNUPG_Out_Of_Core. */
|
||||
static __inline__ gpg_error_t
|
||||
out_of_core (void)
|
||||
{
|
||||
return gpg_error (gpg_err_code_from_errno (errno));
|
||||
}
|
||||
|
||||
|
||||
#define MAX_DIGEST_LEN 24
|
||||
|
||||
/* A large struct name "opt" to keep global flags */
|
||||
struct {
|
||||
unsigned int debug; /* debug flags (DBG_foo_VALUE) */
|
||||
int debug_sc; /* OpenSC debug level */
|
||||
int verbose; /* verbosity level */
|
||||
int quiet; /* be as quiet as possible */
|
||||
int dry_run; /* don't change any persistent data */
|
||||
int batch; /* batch mode */
|
||||
const char *homedir; /* configuration directory name */
|
||||
} opt;
|
||||
|
||||
|
||||
#define DBG_COMMAND_VALUE 1 /* debug commands i/o */
|
||||
#define DBG_MPI_VALUE 2 /* debug mpi details */
|
||||
#define DBG_CRYPTO_VALUE 4 /* debug low level crypto */
|
||||
#define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */
|
||||
#define DBG_CACHE_VALUE 64 /* debug the caching */
|
||||
#define DBG_MEMSTAT_VALUE 128 /* show memory statistics */
|
||||
#define DBG_HASHING_VALUE 512 /* debug hashing operations */
|
||||
#define DBG_ASSUAN_VALUE 1024
|
||||
#define DBG_CARD_IO_VALUE 2048
|
||||
|
||||
#define DBG_COMMAND (opt.debug & DBG_COMMAND_VALUE)
|
||||
#define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE)
|
||||
#define DBG_MEMORY (opt.debug & DBG_MEMORY_VALUE)
|
||||
#define DBG_CACHE (opt.debug & DBG_CACHE_VALUE)
|
||||
#define DBG_HASHING (opt.debug & DBG_HASHING_VALUE)
|
||||
#define DBG_ASSUAN (opt.debug & DBG_ASSUAN_VALUE)
|
||||
#define DBG_CARD_IO (opt.debug & DBG_CARD_IO_VALUE)
|
||||
|
||||
struct server_local_s;
|
||||
struct card_ctx_s;
|
||||
struct app_ctx_s;
|
||||
|
||||
struct server_control_s {
|
||||
struct server_local_s *server_local;
|
||||
struct card_ctx_s *card_ctx;
|
||||
struct app_ctx_s *app_ctx;
|
||||
struct {
|
||||
unsigned char *value;
|
||||
int valuelen;
|
||||
} in_data; /* helper to store the value we are going to sign */
|
||||
|
||||
};
|
||||
|
||||
typedef struct server_control_s *CTRL;
|
||||
typedef struct card_ctx_s *CARD;
|
||||
typedef struct app_ctx_s *APP;
|
||||
|
||||
/*-- scdaemon.c --*/
|
||||
void scd_exit (int rc);
|
||||
void scd_init_default_ctrl (CTRL ctrl);
|
||||
|
||||
/*-- command.c --*/
|
||||
void scd_command_handler (int);
|
||||
void send_status_info (CTRL ctrl, const char *keyword, ...);
|
||||
|
||||
/*-- card.c --*/
|
||||
int card_open (CARD *rcard);
|
||||
void card_close (CARD card);
|
||||
int card_get_serial_and_stamp (CARD card, char **serial, time_t *stamp);
|
||||
int card_enum_keypairs (CARD card, int idx,
|
||||
unsigned char *keygrip,
|
||||
char **keyid);
|
||||
int card_enum_certs (CARD card, int idx, char **certid, int *certtype);
|
||||
int card_read_cert (CARD card, const char *certidstr,
|
||||
unsigned char **cert, size_t *ncert);
|
||||
int card_sign (CARD card,
|
||||
const char *keyidstr, int hashalgo,
|
||||
int (pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const void *indata, size_t indatalen,
|
||||
unsigned char **outdata, size_t *outdatalen );
|
||||
int card_decipher (CARD card, const char *keyidstr,
|
||||
int (pincb)(void*, const char *, char **),
|
||||
void *pincb_arg,
|
||||
const void *indata, size_t indatalen,
|
||||
unsigned char **outdata, size_t *outdatalen);
|
||||
|
||||
|
||||
#endif /*SCDAEMON_H*/
|
Loading…
Add table
Add a link
Reference in a new issue