Some minor bug fixes, new test utilities and started support for other

smartcard applications.
This commit is contained in:
Werner Koch 2004-01-27 16:40:42 +00:00
parent 203e1cc272
commit eb24d8b751
33 changed files with 3862 additions and 278 deletions

View File

@ -1,3 +1,7 @@
2004-01-24 Werner Koch <wk@gnupg.org>
* configure.ac: Now requires libassuan 0.6.3.
2003-12-23 Werner Koch <wk@gnupg.org>
Released 1.9.3.

4
README
View File

@ -138,8 +138,8 @@ gpgsm:
--with-key-data
Displays extra information with the --list-keys commands. Especiall
a line tagged "grp" si printed which tells you the keygrip of a
Displays extra information with the --list-keys commands. Especially
a line tagged "grp" is printed which tells you the keygrip of a
key. This is string is for example used as the filename of the
secret key.

1
TODO
View File

@ -52,6 +52,7 @@ might want to have an agent context for each service request
* agent/protect-tool.c
** Export and import certificates along with the secret key.
** Make it more comfortable; i.e. copy files to the correct place.
** BUG? --p12-export seems to work only with unprotected keys
* Move pkcs-1 encoding into libgcrypt.

View File

@ -1,3 +1,7 @@
2004-01-27 Werner Koch <wk@gnupg.org>
* sexp-parse.h: Moved to ../common.
2004-01-24 Werner Koch <wk@gnupg.org>
* call-scd.c (atfork_cb): New.
@ -437,7 +441,7 @@
* protect.c (agent_get_shadow_info): New.
* protect.c (snext,sskip,smatch): Moved to
* sexp-parse.h: new file.
* sexp-parse.h: New file.
* divert-scd.c: New.
2002-02-27 Werner Koch <wk@gnupg.org>

View File

@ -41,8 +41,7 @@ gpg_agent_SOURCES = \
trustlist.c \
divert-scd.c \
call-scd.c \
learncard.c \
sexp-parse.h
learncard.c
gpg_agent_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \

View File

@ -1,3 +1,9 @@
2004-01-27 Werner Koch <wk@gnupg.org>
* sexp-parse.h: New; moved from../agent.
* util.h (xtoi_4): New.
2003-12-23 Werner Koch <wk@gnupg.org>
* maperror.c (map_assuan_err): Prepared for a new error code.

View File

@ -29,6 +29,7 @@ AM_CPPFLAGS = $(LIBGCRYPT_CFLAGS) $(KSBA_CFLAGS)
libcommon_a_SOURCES = \
util.h i18n.h \
errors.h \
sexp-parse.h \
maperror.c \
sysutils.c sysutils.h \
gettime.c \

View File

@ -134,13 +134,14 @@ int asprintf (char **result, const char *format, ...) JNLIB_GCC_A_PRINTF(2,3);
\v, but works for the purposes used here. */
#define ascii_isspace(a) ((a)==' ' || (a)=='\n' || (a)=='\r' || (a)=='\t')
/* the atoi macros assume that the buffer has only valid digits */
/* The atoi macros assume that the buffer has only valid digits. */
#define atoi_1(p) (*(p) - '0' )
#define atoi_2(p) ((atoi_1(p) * 10) + atoi_1((p)+1))
#define atoi_4(p) ((atoi_2(p) * 100) + atoi_2((p)+2))
#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \
*(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1))
#define xtoi_4(p) ((xtoi_2(p) * 256) + xtoi_2((p)+2))

View File

@ -31,7 +31,7 @@ AC_INIT(gnupg, 1.9.4-cvs, gnupg-devel@gnupg.org)
development_version=yes
NEED_GPG_ERROR_VERSION=0.6
NEED_LIBGCRYPT_VERSION=1.1.91
NEED_LIBASSUAN_VERSION=0.6.2
NEED_LIBASSUAN_VERSION=0.6.3
NEED_KSBA_VERSION=0.9.1
NEED_OPENSC_VERSION=0.8.0

View File

@ -1,3 +1,68 @@
2004-01-27 Werner Koch <wk@gnupg.org>
* command.c (cmd_readcert, cmd_readkey): Work on a copy of LINE.
* app-common.h (app_ctx_s): Added readcert field.
* app.c (app_readcert): New.
* tlv.c (parse_ber_header): Added; taken from libksba.
2004-01-26 Werner Koch <wk@gnupg.org>
* card.c (map_sc_err): Use SCD as the error source.
* command.c (open_card): ADD arg NAME to allow requesting a
specific application. Changed all callers.
(cmd_serialno): Allow optional argument to select the desired
application.
* app-nks.c: New.
* scdaemon.h (opt): Add READER_PORT.
* scdaemon.c (main): Set it here.
* app.c (app_set_default_reader_port): Removed.
(select_application): Add NAME arg and figure out a
default serial number from the GDO. Add SLOT arg and remove all
reader management.
(release_application): New.
(app_write_learn_status): Output an APPTYPE status line.
* command.c (open_card): Adapt for select_application change.
* app-openpgp.c (app_select_openpgp): Removed SN and SNLEN args
and set it directly. Changed all callers.
2004-01-25 Werner Koch <wk@gnupg.org>
* iso7816.c (iso7816_select_application): P1 kludge for OpenPGP
card.
* app-openpgp.c (find_tlv): Factor out this function to ..
* tlv.c, tlv.h: .. new.
* scdaemon.h: Introduced app_t and ctrl_t as the new types for APP
and CTRL.
2004-01-21 Werner Koch <wk@gnupg.org>
* apdu.c (apdu_send_le): Treat SW_EOF_REACHED as a warning.
2004-01-20 Werner Koch <wk@gnupg.org>
* iso7816.c (iso7816_read_binary): New.
(iso7816_select_file): New.
(iso7816_list_directory): New.
* sc-investigate.c: Add option -i.
(select_app, read_line, interactive_shell): New.
2004-01-16 Werner Koch <wk@gnupg.org>
* apdu.h: Add SW_FILE_NOT_FOUND.
* iso7816.c (map_sw): Map it to GPG_ERR_ENOENT.
* iso7816.c (iso7816_select_file): New.
* app-dinsig.c: New file w/o any real code yet.
* Makefile.am (scdaemon_SOURCES,sc_investigate_SOURCES): Add file.
* sc-investigate.c: Add option --disable-ccid.
2003-12-19 Werner Koch <wk@gnupg.org>
* apdu.c (apdu_send_le): Send a get_response with the indicated

View File

@ -26,6 +26,8 @@ bin_PROGRAMS = scdaemon sc-investigate sc-copykeys
AM_CPPFLAGS = -I$(top_srcdir)/common $(OPENSC_CFLAGS) $(LIBGCRYPT_CFLAGS) \
$(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS)
card_apps = app-openpgp.c app-nks.c app-dinsig.c
scdaemon_SOURCES = \
scdaemon.c scdaemon.h \
command.c card.c \
@ -34,8 +36,9 @@ scdaemon_SOURCES = \
apdu.c apdu.h \
ccid-driver.c ccid-driver.h \
iso7816.c iso7816.h \
app.c app-common.h \
app-openpgp.c
tlv.c tlv.h \
app.c app-common.h $(card_apps)
scdaemon_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \
$(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) \
@ -46,9 +49,9 @@ sc_investigate_SOURCES = \
apdu.c apdu.h \
ccid-driver.c ccid-driver.h \
iso7816.c iso7816.h \
app.c app-common.h \
app-openpgp.c \
atr.c atr.h
tlv.c tlv.h \
atr.c atr.h \
app.c app-common.h $(card_apps)
sc_investigate_LDADD = \
../jnlib/libjnlib.a ../common/libcommon.a \
@ -61,17 +64,12 @@ sc_copykeys_SOURCES = \
apdu.c apdu.h \
ccid-driver.c ccid-driver.h \
iso7816.c iso7816.h \
app.c app-common.h \
app-openpgp.c \
atr.c atr.h
tlv.c tlv.h \
atr.c atr.h \
app.c app-common.h $(card_apps)
sc_copykeys_LDADD = \
../jnlib/libjnlib.a ../common/libcommon.a \
../common/libsimple-pwquery.a \
$(OPENSC_LIBS) $(LIBGCRYPT_LIBS) $(LIBUSB_LIBS) \
-lgpg-error @INTLLIBS@ -ldl

View File

@ -1168,7 +1168,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
log_printhex (" dump: ", result, resultlen);
}
if (sw == SW_SUCCESS)
if (sw == SW_SUCCESS || sw == SW_EOF_REACHED)
{
if (retbuf)
{

View File

@ -27,13 +27,16 @@
enum {
SW_MORE_DATA = 0x6100, /* Note: that the low byte must be
masked of.*/
SW_EOF_REACHED = 0x6282,
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_NOT_SUPPORTED = 0x6a81,
SW_FILE_NOT_FOUND = 0x6a82,
SW_RECORD_NOT_FOUND = 0x6a83,
SW_REF_NOT_FOUND = 0x6a88,
SW_BAD_P0_P1 = 0x6b00,
SW_INS_NOT_SUP = 0x6d00,

View File

@ -29,43 +29,46 @@ struct app_ctx_s {
int slot; /* Used reader. */
unsigned char *serialno; /* Serialnumber in raw form, allocated. */
size_t serialnolen; /* Length in octets of serialnumber. */
const char *apptype;
unsigned int card_version;
int did_chv1;
int force_chv1; /* True if the card does not cache CHV1. */
int did_chv2;
int did_chv3;
struct {
int (*learn_status) (APP app, CTRL ctrl);
int (*getattr) (APP app, CTRL ctrl, const char *name);
int (*setattr) (APP app, const char *name,
int (*learn_status) (app_t app, ctrl_t ctrl);
int (*readcert) (app_t app, const char *certid,
unsigned char **cert, size_t *certlen);
int (*getattr) (app_t app, ctrl_t ctrl, const char *name);
int (*setattr) (app_t app, const char *name,
int (*pincb)(void*, const char *, char **),
void *pincb_arg,
const unsigned char *value, size_t valuelen);
int (*sign) (APP app,
int (*sign) (app_t 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 (*auth) (app_t 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 (*decipher) (app_t 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,
int (*genkey) (app_t app, ctrl_t ctrl,
const char *keynostr, unsigned int flags,
int (*pincb)(void*, const char *, char **),
void *pincb_arg);
int (*change_pin) (APP app, CTRL ctrl,
int (*change_pin) (app_t app, ctrl_t ctrl,
const char *chvnostr, int reset_mode,
int (*pincb)(void*, const char *, char **),
void *pincb_arg);
int (*check_pin) (APP app, const char *keyidstr,
int (*check_pin) (app_t app, const char *keyidstr,
int (pincb)(void*, const char *, char **),
void *pincb_arg);
} fnc;
@ -74,66 +77,77 @@ struct app_ctx_s {
};
#if GNUPG_MAJOR_VERSION == 1
int app_select_openpgp (APP app, unsigned char **sn, size_t *snlen);
int app_get_serial_and_stamp (APP app, char **serial, time_t *stamp);
int app_select_openpgp (app_t app);
int app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp);
#else
/*-- app.c --*/
void app_set_default_reader_port (const char *portstr);
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_getattr (APP app, CTRL ctrl, const char *name);
int app_setattr (APP app, const char *name,
app_t select_application (ctrl_t ctrl, int slot, const char *name);
void release_application (app_t app);
int app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp);
int app_write_learn_status (app_t app, ctrl_t ctrl);
int app_readcert (app_t app, const char *certid,
unsigned char **cert, size_t *certlen);
int app_getattr (app_t app, ctrl_t ctrl, const char *name);
int app_setattr (app_t 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 app_sign (app_t 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 app_auth (app_t 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 app_decipher (app_t 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 app_genkey (app_t app, ctrl_t 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 app_get_challenge (app_t app, size_t nbytes, unsigned char *buffer);
int app_change_pin (app_t app, ctrl_t ctrl,
const char *chvnostr, int reset_mode,
int (*pincb)(void*, const char *, char **),
void *pincb_arg);
int app_check_pin (APP app, const char *keyidstr,
int app_check_pin (app_t app, const char *keyidstr,
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_select_openpgp (app_t app);
int app_openpgp_cardinfo (APP app,
int app_openpgp_cardinfo (app_t 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,
int app_openpgp_storekey (app_t 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,
int app_openpgp_readkey (app_t app, int keyno,
unsigned char **m, size_t *mlen,
unsigned char **e, size_t *elen);
/*-- app-nks.c --*/
int app_select_nks (app_t app);
/*-- app-dinsig.c --*/
int app_select_dinsig (app_t app);
#endif

129
scd/app-dinsig.c Normal file
View File

@ -0,0 +1,129 @@
/* app-dinsig.c - The DINSIG (DIN V 66291-1) card application.
* Copyright (C) 2004 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
/* The German signature law and its bylaw (SigG and SigV) is currently
used with an interface specification described in DIN V 66291-1.
The AID to be used is: 'D27600006601'.
The file IDs for certificates utilize the generic format:
Cxyz
C being the hex digit 'C' (12).
x being the service indicator:
'0' := SigG conform digital signature.
'1' := entity authentication.
'2' := key encipherment.
'3' := data encipherment.
'4' := key agreement.
other values are reserved for future use.
y being the security environment number using '0' for cards
not supporting a SE number.
z being the certificate type:
'0' := C.CH (base certificate of card holder) or C.ICC.
'1' .. '7' := C.CH (business or professional certificate
of card holder.
'8' .. 'D' := C.CA (certificate of a CA issue by the Root-CA).
'E' := C.RCA (self certified certificate of the Root-CA).
'F' := reserved.
The file IDs used by default are:
'1F00' EF.SSD (security service descriptor). [o,o]
'2F02' EF.GDO (global data objects) [m,m]
'A000' EF.PROT (signature log). Cyclic file with 20 records of 53 byte.
Read and update after user authentication. [o,o]
'B000' EF.PK.RCA.DS (public keys of Root-CA). Size is 512b or size
of keys. [m (unless a 'C00E' is present),m]
'B001' EF.PK.CA.DS (public keys of CAs). Size is 512b or size
of keys. [o,o]
'C00n' EF.C.CH.DS (digital signature certificate of card holder)
with n := 0 .. 7. Size is 2k or size of cert. Read and
update allowed after user authentication. [m,m]
'C00m' EF.C.CA.DS (digital signature certificate of CA)
with m := 8 .. E. Size is 1k or size of cert. Read always
allowed, update after user authentication. [o,o]
'C100' EF.C.ICC.AUT (AUT certificate of ICC) [o,m]
'C108' EF.C.CA.AUT (AUT certificate of CA) [o,m]
'D000' EF.DM (display message) [-,m]
The letters in brackets indicate optional or mandatory files: The
first for card terminals under full control and the second for
"business" card terminals.
FIXME: Needs a lot more explanation.
*/
#include <config.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <time.h>
#include "scdaemon.h"
#include "iso7816.h"
#include "app-common.h"
static int
do_learn_status (APP app, CTRL ctrl)
{
return 0;
}
/* Select the DINSIG application on the card in SLOT. This function
must be used before any other DINSIG application functions. */
int
app_select_dinsig (APP app)
{
static char const aid[] = { 0xD2, 0x76, 0x00, 0x00, 0x66, 0x01 };
int slot = app->slot;
int rc;
rc = iso7816_select_application (slot, aid, sizeof aid);
if (!rc)
{
app->apptype = "DINSIG";
app->fnc.learn_status = do_learn_status;
app->fnc.getattr = NULL;
app->fnc.setattr = NULL;
app->fnc.genkey = NULL;
app->fnc.sign = NULL;
app->fnc.auth = NULL;
app->fnc.decipher = NULL;
app->fnc.change_pin = NULL;
app->fnc.check_pin = NULL;
}
return rc;
}

388
scd/app-nks.c Normal file
View File

@ -0,0 +1,388 @@
/* app-nks.c - The Telesec NKS 2.0 card application.
* Copyright (C) 2004 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <time.h>
#include "scdaemon.h"
#include "iso7816.h"
#include "app-common.h"
#include "tlv.h"
static struct {
int fid; /* File ID. */
int certtype; /* Type of certificate or 0 if it is not a certificate. */
int iskeypair; /* If true has the FID of the correspoding certificate. */
} filelist[] = {
{ 0x4531, 0, 0xC000 },
{ 0xC000, 101 },
{ 0x4331, 100 },
{ 0x4332, 100 },
{ 0xB000, 110 },
{ 0x45B1, 0, 0xC200 },
{ 0xC200, 101 },
{ 0x43B1, 100 },
{ 0x43B2, 100 },
{ 0, 0 }
};
/* Given the slot and the File Id FID, return the length of the
certificate contained in that file. Returns 0 if the file does not
exists or does not contain a certificate. */
static size_t
get_length_of_cert (int slot, int fid)
{
gpg_error_t err;
unsigned char *buffer;
const unsigned char *p;
size_t buflen, n;
int class, tag, constructed, ndef;
size_t objlen, hdrlen;
err = iso7816_select_file (slot, fid, 0, NULL, NULL);
if (err)
{
log_info ("error selecting FID 0x%04X: %s\n", fid, gpg_strerror (err));
return 0;
}
err = iso7816_read_binary (slot, 0, 32, &buffer, &buflen);
if (err)
{
log_info ("error reading certificate from FID 0x%04X: %s\n",
fid, gpg_strerror (err));
return 0;
}
if (!buflen || *buffer == 0xff)
{
log_info ("no certificate contained in FID 0x%04X\n", fid);
xfree (buffer);
return 0;
}
p = buffer;
n = buflen;
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (err)
{
log_info ("error parsing certificate in FID 0x%04X: %s\n",
fid, gpg_strerror (err));
xfree (buffer);
return 0;
}
/* All certificates should commence with a SEQUENCE expect fro the
special ROOT CA which are enclosed in a SET. */
if ( !(class == CLASS_UNIVERSAL && constructed
&& (tag == TAG_SEQUENCE || tag == TAG_SET)))
{
log_info ("contents of FID 0x%04X does not look like a certificate\n",
fid);
return 0;
}
return objlen + hdrlen;
}
/* Read the file with FID, assume it contains a public key and return
its keygrip in the caller provided 41 byte buffer R_GRIPSTR. */
static gpg_error_t
keygripstr_from_pk_file (int slot, int fid, char *r_gripstr)
{
gpg_error_t err;
unsigned char grip[20];
unsigned char *buffer[2];
size_t buflen[2];
gcry_sexp_t sexp;
int i;
err = iso7816_select_file (slot, fid, 0, NULL, NULL);
if (err)
return err;
err = iso7816_read_record (slot, 1, 1, &buffer[0], &buflen[0]);
if (err)
return err;
err = iso7816_read_record (slot, 2, 1, &buffer[1], &buflen[1]);
if (err)
{
xfree (buffer[0]);
return err;
}
for (i=0; i < 2; i++)
{
/* Check that the value appears like an integer encoded as
Simple-TLV. We don't check the tag because the tests cards I
have use 1 for both, the modulus and the exponent - the
example in the documentation gives 2 for the exponent. */
if (buflen[i] < 3)
err = gpg_error (GPG_ERR_TOO_SHORT);
else if (buffer[i][1] != buflen[i]-2 )
err = gpg_error (GPG_ERR_INV_OBJ);
}
if (!err)
err = gcry_sexp_build (&sexp, NULL,
"(public-key (rsa (n %b) (e %b)))",
(int)buflen[0]-2, buffer[0]+2,
(int)buflen[1]-2, buffer[1]+2);
xfree (buffer[0]);
xfree (buffer[1]);
if (err)
return err;
if (!gcry_pk_get_keygrip (sexp, grip))
{
err = gpg_error (GPG_ERR_INTERNAL); /* i.e. RSA not supported by
libgcrypt. */
}
else
{
for (i=0; i < 20; i++)
sprintf (r_gripstr+i*2, "%02X", grip[i]);
}
gcry_sexp_release (sexp);
return err;
}
static int
do_learn_status (APP app, CTRL ctrl)
{
gpg_error_t err;
char ct_buf[100], id_buf[100];
int i;
/* Output information about all useful objects. */
for (i=0; filelist[i].fid; i++)
{
if (filelist[i].certtype)
{
size_t len = get_length_of_cert (app->slot, filelist[i].fid);
if (len)
{
/* FIXME: We should store the length in the application's
context so that a following readcert does only need to
read that many bytes. */
sprintf (ct_buf, "%d", filelist[i].certtype);
sprintf (id_buf, "NKS-DF01.%04X", filelist[i].fid);
send_status_info (ctrl, "CERTINFO",
ct_buf, strlen (ct_buf),
id_buf, strlen (id_buf),
NULL, (size_t)0);
}
}
else if (filelist[i].iskeypair)
{
char gripstr[40+1];
err = keygripstr_from_pk_file (app->slot, filelist[i].fid, gripstr);
if (err)
log_error ("can't get keygrip from FID 0x%04X: %s\n",
filelist[i].fid, gpg_strerror (err));
else
{
sprintf (id_buf, "NKS-DF01.%04X", filelist[i].fid);
send_status_info (ctrl, "KEYPAIRINFO",
gripstr, 40,
id_buf, strlen (id_buf),
NULL, (size_t)0);
}
}
}
return 0;
}
/* Read the certificate with id CERTID (as returned by learn_status in
the CERTINFO status lines) and return it in the freshly allocated
buffer put into CERT and the length of the certificate put into
CERTLEN. */
static int
do_readcert (app_t app, const char *certid,
unsigned char **cert, size_t *certlen)
{
int i, fid;
gpg_error_t err;
unsigned char *buffer;
const unsigned char *p;
size_t buflen, n;
int class, tag, constructed, ndef;
size_t totobjlen, objlen, hdrlen;
int rootca = 0;
*cert = NULL;
*certlen = 0;
if (strncmp (certid, "NKS-DF01.", 9) )
return gpg_error (GPG_ERR_INV_ID);
certid += 9;
if (!hexdigitp (certid) || !hexdigitp (certid+1)
|| !hexdigitp (certid+2) || !hexdigitp (certid+3)
|| certid[4])
return gpg_error (GPG_ERR_INV_ID);
fid = xtoi_4 (certid);
for (i=0; filelist[i].fid; i++)
if ((filelist[i].certtype || filelist[i].iskeypair)
&& filelist[i].fid == fid)
break;
if (!filelist[i].fid)
return gpg_error (GPG_ERR_NOT_FOUND);
/* If the requested objects is a plain public key, redirect it to
the corresponding certificate. The whole system is a bit messy
becuase we sometime use the key directly or let the caller
retrieve the key from the certificate. The valid point behind
that is to support not-yet stored certificates. */
if (filelist[i].iskeypair)
fid = filelist[i].iskeypair;
/* Read the entire file. fixme: This could be optimized by first
reading the header to figure out how long the certificate
actually is. */
err = iso7816_select_file (app->slot, fid, 0, NULL, NULL);
if (err)
{
log_error ("error selecting FID 0x%04X: %s\n", fid, gpg_strerror (err));
return err;
}
err = iso7816_read_binary (app->slot, 0, 0, &buffer, &buflen);
if (err)
{
log_error ("error reading certificate from FID 0x%04X: %s\n",
fid, gpg_strerror (err));
return err;
}
if (!buflen || *buffer == 0xff)
{
log_info ("no certificate contained in FID 0x%04X\n", fid);
err = gpg_error (GPG_ERR_NOT_FOUND);
goto leave;
}
/* Now figure something out about the object. */
p = buffer;
n = buflen;
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (err)
goto leave;
if ( class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed )
;
else if ( class == CLASS_UNIVERSAL && tag == TAG_SET && constructed )
rootca = 1;
else
return gpg_error (GPG_ERR_INV_OBJ);
totobjlen = objlen + hdrlen;
assert (totobjlen <= buflen);
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (err)
goto leave;
if (rootca)
;
else if (class == CLASS_UNIVERSAL && tag == TAG_OBJECT_ID && !constructed)
{
const unsigned char *save_p;
/* The certificate seems to be contained in a userCertificate
container. Skip this and assume the following sequence is
the certificate. */
if (n < objlen)
{
err = gpg_error (GPG_ERR_INV_OBJ);
goto leave;
}
p += objlen;
n -= objlen;
save_p = p;
err = parse_ber_header (&p, &n, &class, &tag, &constructed,
&ndef, &objlen, &hdrlen);
if (err)
goto leave;
if ( !(class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed) )
return gpg_error (GPG_ERR_INV_OBJ);
totobjlen = objlen + hdrlen;
assert (save_p + totobjlen <= buffer + buflen);
memmove (buffer, save_p, totobjlen);
}
*cert = buffer;
buffer = NULL;
*certlen = totobjlen;
leave:
xfree (buffer);
return err;
}
/* Select the NKS 2.0 application on the card in SLOT. */
int
app_select_nks (APP app)
{
static char const aid[] = { 0xD2, 0x76, 0x00, 0x00, 0x03, 0x01, 0x02 };
int slot = app->slot;
int rc;
rc = iso7816_select_application (slot, aid, sizeof aid);
if (!rc)
{
app->apptype = "NKS";
app->fnc.learn_status = do_learn_status;
app->fnc.readcert = do_readcert;
app->fnc.getattr = NULL;
app->fnc.setattr = NULL;
app->fnc.genkey = NULL;
app->fnc.sign = NULL;
app->fnc.auth = NULL;
app->fnc.decipher = NULL;
app->fnc.change_pin = NULL;
app->fnc.check_pin = NULL;
}
return rc;
}

View File

@ -1,5 +1,5 @@
/* app-openpgp.c - The OpenPGP card application.
* Copyright (C) 2003 Free Software Foundation, Inc.
* Copyright (C) 2003, 2004 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@ -42,7 +42,7 @@
#include "iso7816.h"
#include "app-common.h"
#include "tlv.h"
static struct {
@ -80,94 +80,6 @@ static unsigned long convert_sig_counter_value (const unsigned char *value,
static unsigned long get_sig_counter (APP app);
/* Locate a 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.
FIXME: Move this to an extra file, it is mostly duplicated from card.c.
*/
static const unsigned char *
find_tlv (const unsigned char *buffer, size_t length,
int tag, size_t *nbytes, int nestlevel)
{
const unsigned char *s = buffer;
size_t n = length;
size_t len;
int this_tag;
int composite;
for (;;)
{
buffer = s;
if (n < 2)
return NULL; /* buffer definitely too short for tag and length. */
if (!*s || *s == 0xff)
{ /* Skip optional filler between TLV objects. */
s++;
n--;
continue;
}
composite = !!(*s & 0x20);
if ((*s & 0x1f) == 0x1f)
{ /* more tag bytes to follow */
s++;
n--;
if (n < 2)
return NULL; /* buffer definitely too short for tag and length. */
if ((*s & 0x1f) == 0x1f)
return NULL; /* We support only up to 2 bytes. */
this_tag = (s[-1] << 8) | (s[0] & 0x7f);
}
else
this_tag = s[0];
len = s[1];
s += 2; n -= 2;
if (len < 0x80)
;
else if (len == 0x81)
{ /* One byte length follows. */
if (!n)
return NULL; /* we expected 1 more bytes with the length. */
len = s[0];
s++; n--;
}
else if (len == 0x82)
{ /* Two byte length follows. */
if (n < 2)
return NULL; /* we expected 2 more bytes with the length. */
len = (s[0] << 8) | s[1];
s += 2; n -= 2;
}
else
return NULL; /* APDU limit is 65535, thus it does not make
sense to assume longer length fields. */
if (composite && nestlevel < 100)
{ /* Dive into this composite DO after checking for too deep
nesting. */
const unsigned char *tmp_s;
size_t tmp_len;
tmp_s = find_tlv (s, len, tag, &tmp_len, nestlevel+1);
if (tmp_s)
{
*nbytes = tmp_len;
return tmp_s;
}
}
if (this_tag == tag)
{
*nbytes = len;
return s;
}
if (len > n)
return NULL; /* buffer too short to skip to the next tag. */
s += len; n -= len;
}
}
/* Get the DO identified by TAG from the card in SLOT and return a
buffer with its content in RESULT and NBYTES. The return value is
@ -197,7 +109,7 @@ get_one_do (int slot, int tag, unsigned char **result, size_t *nbytes)
{
const unsigned char *s;
s = find_tlv (buffer, buflen, tag, &valuelen, 0);
s = find_tlv (buffer, buflen, tag, &valuelen);
if (!s)
value = NULL; /* not found */
else if (valuelen > buflen - (s - buffer))
@ -271,7 +183,7 @@ dump_all_do (int slot)
if (j==i || data_objects[i].tag != data_objects[j].get_from)
continue;
value = find_tlv (buffer, buflen,
data_objects[j].tag, &valuelen, 0);
data_objects[j].tag, &valuelen);
if (!value)
; /* not found */
else if (valuelen > buflen - (value - buffer))
@ -443,7 +355,7 @@ do_getattr (APP app, CTRL ctrl, const char *name)
{
/* The serial number is very special. We could have used the
AID DO to retrieve it, but we have it already in the app
context and the stanmp argument is required anyway which we
context and the stamp argument is required anyway which we
can't by other means. The AID DO is available anyway but not
hex formatted. */
char *serial;
@ -772,7 +684,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
log_error ("error reading application data\n");
return gpg_error (GPG_ERR_GENERAL);
}
fpr = find_tlv (buffer, buflen, 0x00C5, &n, 0);
fpr = find_tlv (buffer, buflen, 0x00C5, &n);
if (!fpr || n != 60)
{
rc = gpg_error (GPG_ERR_GENERAL);
@ -820,7 +732,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
}
log_info ("key generation completed (%d seconds)\n",
(int)(time (NULL) - start_at));
keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen, 0);
keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen);
if (!keydata)
{
rc = gpg_error (GPG_ERR_CARD);
@ -828,7 +740,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
goto leave;
}
m = find_tlv (keydata, keydatalen, 0x0081, &mlen, 0);
m = find_tlv (keydata, keydatalen, 0x0081, &mlen);
if (!m)
{
rc = gpg_error (GPG_ERR_CARD);
@ -838,7 +750,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
/* log_printhex ("RSA n:", m, mlen); */
send_key_data (ctrl, "n", m, mlen);
e = find_tlv (keydata, keydatalen, 0x0082, &elen, 0);
e = find_tlv (keydata, keydatalen, 0x0082, &elen);
if (!e)
{
rc = gpg_error (GPG_ERR_CARD);
@ -913,7 +825,7 @@ compare_fingerprint (APP app, int keyno, unsigned char *sha1fpr)
log_error ("error reading application data\n");
return gpg_error (GPG_ERR_GENERAL);
}
fpr = find_tlv (buffer, buflen, 0x00C5, &n, 0);
fpr = find_tlv (buffer, buflen, 0x00C5, &n);
if (!fpr || n != 60)
{
xfree (buffer);
@ -1268,7 +1180,7 @@ do_check_pin (APP app, const char *keyidstr,
/* Select the OpenPGP application on the card in SLOT. This function
must be used before any other OpenPGP application functions. */
int
app_select_openpgp (APP app, unsigned char **sn, size_t *snlen)
app_select_openpgp (APP app)
{
static char const aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 };
int slot = app->slot;
@ -1280,10 +1192,17 @@ app_select_openpgp (APP app, unsigned char **sn, size_t *snlen)
rc = iso7816_select_application (slot, aid, sizeof aid);
if (!rc)
{
app->apptype = "OPENPGP";
app->did_chv1 = 0;
app->did_chv2 = 0;
app->did_chv3 = 0;
/* The OpenPGP card returns the serial number as part of the
AID; because we prefer to use OpenPGP serial numbers, we
repalce a possibly already set one from a EF.GDO with this
one. Note, that for current OpenPGP cards, no EF.GDO exists
and thus it won't matter at all. */
rc = iso7816_get_data (slot, 0x004F, &buffer, &buflen);
if (rc)
goto leave;
@ -1293,15 +1212,12 @@ app_select_openpgp (APP app, unsigned char **sn, size_t *snlen)
log_printhex ("", buffer, buflen);
}
if (sn)
{
*sn = buffer;
*snlen = buflen;
app->card_version = buffer[6] << 8;
app->card_version |= buffer[7];
}
else
xfree (buffer);
app->card_version = buffer[6] << 8;
app->card_version |= buffer[7];
xfree (app->serialno);
app->serialno = buffer;
app->serialnolen = buflen;
buffer = NULL;
relptr = get_one_do (app->slot, 0x00C4, &buffer, &buflen);
if (!relptr)
@ -1316,6 +1232,7 @@ app_select_openpgp (APP app, unsigned char **sn, size_t *snlen)
dump_all_do (slot);
app->fnc.learn_status = do_learn_status;
app->fnc.readcert = NULL;
app->fnc.getattr = do_getattr;
app->fnc.setattr = do_setattr;
app->fnc.genkey = do_genkey;
@ -1498,7 +1415,7 @@ app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen,
goto leave;
}
keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen, 0);
keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen);
if (!keydata)
{
log_error ("response does not contain the public key data\n");
@ -1506,7 +1423,7 @@ app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen,
goto leave;
}
a = find_tlv (keydata, keydatalen, 0x0081, &alen, 0);
a = find_tlv (keydata, keydatalen, 0x0081, &alen);
if (!a)
{
log_error ("response does not contain the RSA modulus\n");
@ -1517,7 +1434,7 @@ app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen,
*m = xmalloc (alen);
memcpy (*m, a, alen);
a = find_tlv (keydata, keydatalen, 0x0082, &alen, 0);
a = find_tlv (keydata, keydatalen, 0x0082, &alen);
if (!e)
{
log_error ("response does not contain the RSA public exponent\n");

131
scd/app.c
View File

@ -29,49 +29,78 @@
#include "app-common.h"
#include "apdu.h"
#include "iso7816.h"
#include "dynload.h"
static char *default_reader_port;
void
app_set_default_reader_port (const char *portstr)
{
xfree (default_reader_port);
default_reader_port = portstr? xstrdup (portstr): NULL;
}
#include "tlv.h"
/* The select the best fitting application and return a context.
Returns NULL if no application was found or no card is present. */
/* If called with NAME as NULL, select the best fitting application
and return a context; otherwise select the application with NAME
and return a context. SLOT identifies the reader device. Returns
NULL if no application was found or no card is present. */
APP
select_application (void)
select_application (ctrl_t ctrl, int slot, const char *name)
{
int slot;
int rc;
APP app;
slot = apdu_open_reader (default_reader_port);
if (slot == -1)
{
log_error ("card reader not available\n");
return NULL;
}
unsigned char *result = NULL;
size_t resultlen;
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);
/* Fixme: We should now first check whether a card is at all
present. */
/* Try to read the GDO file first to get a default serial number. */
rc = iso7816_select_file (slot, 0x3F00, 1, NULL, NULL);
if (!rc)
rc = iso7816_select_file (slot, 0x2F02, 0, NULL, NULL);
if (!rc)
rc = iso7816_read_binary (slot, 0, 0, &result, &resultlen);
if (!rc)
{
size_t n;
const unsigned char *p;
p = find_tlv (result, resultlen, 0x5A, &n);
if (p && n && n >= (resultlen - (p - result)))
{
/* The GDO file is pretty short, thus we simply reuse it for
storing the serial number. */
memmove (result, p, n);
app->serialno = result;
app->serialnolen = n;
}
else
xfree (result);
result = NULL;
}
rc = gpg_error (GPG_ERR_NOT_FOUND);
if (!name || !strcmp (name, "openpgp"))
rc = app_select_openpgp (app);
if (rc && (!name || !strcmp (name, "nks")))
rc = app_select_nks (app);
if (rc && (!name || !strcmp (name, "dinsig")))
rc = app_select_dinsig (app);
if (rc && name)
rc = gpg_error (GPG_ERR_NOT_SUPPORTED);
if (rc)
{
/* apdu_close_reader (slot); */
log_info ("selecting openpgp failed: %s\n", gpg_strerror (rc));
if (name)
log_info ("can't select application `%s': %s\n",
name, gpg_strerror (rc));
else
log_info ("no supported card application found: %s\n",
gpg_strerror (rc));
xfree (app);
return NULL;
}
@ -81,23 +110,36 @@ select_application (void)
}
void
release_application (app_t app)
{
if (!app)
return;
xfree (app->serialno);
xfree (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. */
free SERIAL unless the function returns an error. If STAMP is not
of interest, NULL may be passed. */
int
app_get_serial_and_stamp (APP app, char **serial, time_t *stamp)
app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp)
{
unsigned char *buf, *p;
int i;
if (!app || !serial || !stamp)
if (!app || !serial)
return gpg_error (GPG_ERR_INV_VALUE);
*serial = NULL;
*stamp = 0; /* not available */
if (stamp)
*stamp = 0; /* not available */
buf = xtrymalloc (app->serialnolen * 2 + 1);
if (!buf)
@ -121,10 +163,34 @@ app_write_learn_status (APP app, CTRL ctrl)
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
if (!app->fnc.learn_status)
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
if (app->apptype)
send_status_info (ctrl, "APPTYPE",
app->apptype, strlen (app->apptype), NULL, 0);
return app->fnc.learn_status (app, ctrl);
}
/* Read the certificate with id CERTID (as returned by learn_status in
the CERTINFO status lines) and return it in the freshly allocated
buffer put into CERT and the length of the certificate put into
CERTLEN. */
int
app_readcert (app_t app, const char *certid,
unsigned char **cert, size_t *certlen)
{
if (!app)
return gpg_error (GPG_ERR_INV_VALUE);
if (!app->initialized)
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
if (!app->fnc.readcert)
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
return app->fnc.readcert (app, certid, cert, certlen);
}
/* Perform a GETATTR operation. */
int
app_getattr (APP app, CTRL ctrl, const char *name)
@ -317,8 +383,3 @@ app_check_pin (APP app, const char *keyidstr,
return rc;
}

View File

@ -53,7 +53,10 @@ map_sc_err (int rc)
#endif
default: e = GPG_ERR_CARD; break;
}
return gpg_err_make (GPG_ERR_SOURCE_UNKNOWN, e);
/* It does not make much sense to further distingusih the error
source between OpenSC and SCD. Thus we use SCD as source
here. */
return gpg_err_make (GPG_ERR_SOURCE_SCD, e);
}
/* Get the keygrip from CERT, return 0 on success */
@ -462,6 +465,7 @@ card_enum_keypairs (CARD card, int idx,
100 := Regular X.509 cert
101 := Trusted X.509 cert
102 := Useful X.509 cert
110 := Root CA cert (DINSIG)
*/
int
card_enum_certs (CARD card, int idx, char **certid, int *certtype)

View File

@ -98,6 +98,11 @@
# include "scdaemon.h"
# endif
/* Disable all debgging output for now. */
#undef DBG_CARD_IO
#define DBG_CARD_IO 0
# define DEBUGOUT(t) do { if (DBG_CARD_IO) \
log_debug (DRVNAME t); } while (0)
# define DEBUGOUT_1(t,a) do { if (DBG_CARD_IO) \
@ -944,7 +949,9 @@ ccid_transceive (ccid_driver_t handle,
{
if (n > maxresplen)
{
DEBUGOUT ("provided buffer too short for received data\n");
DEBUGOUT_2 ("provided buffer too short for received data "
"(%u/%u)\n",
(unsigned int)n, (unsigned int)maxresplen);
return -1;
}

View File

@ -1,5 +1,5 @@
/* command.c - SCdaemon command handler
* Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
* Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@ -31,6 +31,7 @@
#include "scdaemon.h"
#include <ksba.h>
#include "app-common.h"
#include "apdu.h" /* Required for apdu_*_reader (). */
/* maximum length aloowed as a PIN; used for INQUIRE NEEDPIN */
#define MAXLEN_PIN 100
@ -90,17 +91,34 @@ option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value)
function returns an Assuan error, so don't map the error a second
time */
static AssuanError
open_card (CTRL ctrl)
open_card (CTRL ctrl, const char *apptype)
{
int slot;
if (ctrl->app_ctx)
return 0; /* Already initialized for one specific application. */
if (ctrl->card_ctx)
return 0; /* Already initialized using a card context. */
ctrl->app_ctx = select_application ();
slot = apdu_open_reader (opt.reader_port);
if (slot != -1)
{
ctrl->app_ctx = select_application (ctrl, slot, apptype);
if (!ctrl->app_ctx)
apdu_close_reader (slot);
}
if (!ctrl->app_ctx)
{ /* No application found - fall back to old mode. */
int rc = card_open (&ctrl->card_ctx);
/* Note that we should rework the old code to use the
application paradigma too. */
int rc;
/* If an APPTYPE was requested and it is not pkcs#15, we return
an error here. */
if (apptype && !(!strcmp (apptype, "P15") || !strcmp (apptype, "p15")))
rc = gpg_error (GPG_ERR_NOT_SUPPORTED);
else
rc = card_open (&ctrl->card_ctx);
if (rc)
return map_to_assuan_status (rc);
}
@ -143,11 +161,17 @@ percent_plus_unescape (unsigned char *string)
/* SERIALNO
/* SERIALNO [APPTYPE]
Return the serial number of the card using a status reponse. This
functon should be used to check for the presence of a card.
If APPTYPE is given, an application of that type is selected and an
error is returned if the application is not supported or available.
The default is to auto-select the application using a hardwired
preference system. Note, that a future extension to this function
may allow to specify a list and order of applications to try.
This function is special in that it can be used to reset the card.
Most other functions will return an error when a card change has
been detected and the use of this function is therefore required.
@ -165,7 +189,7 @@ cmd_serialno (ASSUAN_CONTEXT ctx, char *line)
char *serial;
time_t stamp;
if ((rc = open_card (ctrl)))
if ((rc = open_card (ctrl, *line? line:NULL)))
return rc;
if (ctrl->app_ctx)
@ -223,6 +247,7 @@ cmd_serialno (ASSUAN_CONTEXT ctx, char *line)
100 := Regular X.509 cert
101 := Trusted X.509 cert
102 := Useful X.509 cert
110 := Root CA cert (DINSIG)
For certain cards, more information will be returned:
@ -240,7 +265,7 @@ cmd_serialno (ASSUAN_CONTEXT ctx, char *line)
S DISP-NAME <name_of_card_holder>
The name of the card holder as stored on the card; percent
aescaping takes place, spaces are encoded as '+'
escaping takes place, spaces are encoded as '+'
S PUBKEY-URL <url>
@ -254,7 +279,7 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
int rc = 0;
int idx;
if ((rc = open_card (ctrl)))
if ((rc = open_card (ctrl, NULL)))
return rc;
/* Unless the force option is used we try a shortcut by identifying
@ -305,10 +330,15 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
free (serial_and_stamp);
}
/* Return information about the certificates. */
if (ctrl->app_ctx)
rc = -1; /* This information is not yet available for applications. */
for (idx=0; !rc; idx++)
/* If we are using the modern application paradigma, let the
application print out its collection of useful status
information. */
if (!rc && ctrl->app_ctx)
rc = app_write_learn_status (ctrl->app_ctx, ctrl);
/* Return information about the certificates. FIXME: Move this into
an app-p15.c*/
for (idx=0; !rc && !ctrl->app_ctx; idx++)
{
char *certid;
int certtype;
@ -333,11 +363,9 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
if (rc == -1)
rc = 0;
/* Return information about the keys. */
if (ctrl->app_ctx)
rc = -1; /* This information is not yet available for applications. */
for (idx=0; !rc; idx++)
/* Return information about the keys. FIXME: Move this into an
app-p15.c */
for (idx=0; !rc && !ctrl->app_ctx; idx++)
{
unsigned char keygrip[20];
char *keyid;
@ -346,7 +374,7 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
rc = card_enum_keypairs (ctrl->card_ctx, idx, keygrip, &keyid);
if (gpg_err_code (rc) == GPG_ERR_MISSING_CERT && keyid)
{
/* this does happen with an incomplete personalized
/* This does happen with an incomplete personalized
card; i.e. during the time we have stored the key on the
card but not stored the certificate; probably becuase it
has not yet been received back from the CA. Note that we
@ -383,10 +411,6 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
if (rc == -1)
rc = 0;
if (!rc && ctrl->app_ctx)
rc = app_write_learn_status (ctrl->app_ctx, ctrl);
return map_to_assuan_status (rc);
}
@ -403,17 +427,24 @@ cmd_readcert (ASSUAN_CONTEXT ctx, char *line)
unsigned char *cert;
size_t ncert;
if ((rc = open_card (ctrl)))
if ((rc = open_card (ctrl, NULL)))
return rc;
line = xstrdup (line); /* Need a copy of the line. */
if (ctrl->app_ctx)
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert);
if (rc)
{
log_error ("card_read_cert failed: %s\n", gpg_strerror (rc));
rc = app_readcert (ctrl->app_ctx, line, &cert, &ncert);
if (rc)
log_error ("app_readcert failed: %s\n", gpg_strerror (rc));
}
else
{
rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert);
if (rc)
log_error ("card_read_cert failed: %s\n", gpg_strerror (rc));
}
xfree (line);
line = NULL;
if (!rc)
{
rc = assuan_send_data (ctx, cert, ncert);
@ -440,18 +471,26 @@ cmd_readkey (ASSUAN_CONTEXT ctx, char *line)
ksba_cert_t kc = NULL;
ksba_sexp_t p;
if ((rc = open_card (ctrl)))
if ((rc = open_card (ctrl, NULL)))
return rc;
line = xstrdup (line); /* Need a copy of the line. */
if (ctrl->app_ctx)
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert);
if (rc)
{
log_error ("card_read_cert failed: %s\n", gpg_strerror (rc));
goto leave;
rc = app_readcert (ctrl->app_ctx, line, &cert, &ncert);
if (rc)
log_error ("app_readcert failed: %s\n", gpg_strerror (rc));
}
else
{
rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert);
if (rc)
log_error ("card_read_cert failed: %s\n", gpg_strerror (rc));
}
xfree (line);
line = NULL;
if (rc)
goto leave;
rc = ksba_cert_new (&kc);
if (rc)
@ -569,7 +608,7 @@ cmd_pksign (ASSUAN_CONTEXT ctx, char *line)
size_t outdatalen;
char *keyidstr;
if ((rc = open_card (ctrl)))
if ((rc = open_card (ctrl, NULL)))
return rc;
/* We have to use a copy of the key ID because the function may use
@ -619,7 +658,7 @@ cmd_pkauth (ASSUAN_CONTEXT ctx, char *line)
size_t outdatalen;
char *keyidstr;
if ((rc = open_card (ctrl)))
if ((rc = open_card (ctrl, NULL)))
return rc;
if (!ctrl->app_ctx)
@ -665,7 +704,7 @@ cmd_pkdecrypt (ASSUAN_CONTEXT ctx, char *line)
size_t outdatalen;
char *keyidstr;
if ((rc = open_card (ctrl)))
if ((rc = open_card (ctrl, NULL)))
return rc;
keyidstr = xtrystrdup (line);
@ -718,7 +757,7 @@ cmd_getattr (ASSUAN_CONTEXT ctx, char *line)
int rc;
char *keyword;
if ((rc = open_card (ctrl)))
if ((rc = open_card (ctrl, NULL)))
return rc;
keyword = line;
@ -757,7 +796,7 @@ cmd_setattr (ASSUAN_CONTEXT ctx, char *orig_line)
size_t nbytes;
char *line, *linebuf;
if ((rc = open_card (ctrl)))
if ((rc = open_card (ctrl, NULL)))
return rc;
/* We need to use a copy of LINE, because PIN_CB uses the same
@ -823,7 +862,7 @@ cmd_genkey (ASSUAN_CONTEXT ctx, char *line)
line++;
*line = 0;
if ((rc = open_card (ctrl)))
if ((rc = open_card (ctrl, NULL)))
return rc;
if (!ctrl->app_ctx)
@ -854,7 +893,7 @@ cmd_random (ASSUAN_CONTEXT ctx, char *line)
return set_error (Parameter_Error, "number of requested bytes missing");
nbytes = strtoul (line, NULL, 0);
if ((rc = open_card (ctrl)))
if ((rc = open_card (ctrl, NULL)))
return rc;
if (!ctrl->app_ctx)
@ -904,7 +943,7 @@ cmd_passwd (ASSUAN_CONTEXT ctx, char *line)
line++;
*line = 0;
if ((rc = open_card (ctrl)))
if ((rc = open_card (ctrl, NULL)))
return rc;
if (!ctrl->app_ctx)
@ -931,7 +970,7 @@ cmd_checkpin (ASSUAN_CONTEXT ctx, char *line)
int rc;
char *keyidstr;
if ((rc = open_card (ctrl)))
if ((rc = open_card (ctrl, NULL)))
return rc;
if (!ctrl->app_ctx)

View File

@ -51,6 +51,8 @@
#define CMD_INTERNAL_AUTHENTICATE 0x88
#define CMD_GENERATE_KEYPAIR 0x47
#define CMD_GET_CHALLENGE 0x84
#define CMD_READ_BINARY 0xB0
#define CMD_READ_RECORD 0xB2
static gpg_error_t
map_sw (int sw)
@ -66,6 +68,8 @@ map_sw (int sw)
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_FILE_NOT_FOUND: ec = GPG_ERR_ENOENT; break;
case SW_RECORD_NOT_FOUND:ec= GPG_ERR_NOT_FOUND; 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;
@ -91,18 +95,79 @@ map_sw (int sw)
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
for okay or a GPG error code. Note that ISO error codes are
internally mapped. */
gpg_error_t
iso7816_select_application (int slot, const char *aid, size_t aidlen)
{
static char const openpgp_aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 };
int sw;
int p1 = 0x0C; /* No FCI to be returned. */
if (aidlen == sizeof openpgp_aid
&& !memcmp (aid, openpgp_aid, sizeof openpgp_aid))
p1 = 0; /* The current openpgp cards don't allow 0x0c. */
sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, 0, aidlen, aid);
sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, p1, aidlen, aid);
return map_sw (sw);
}
gpg_error_t
iso7816_select_file (int slot, int tag, int is_dir,
unsigned char **result, size_t *resultlen)
{
int sw, p0, p1;
unsigned char tagbuf[2];
tagbuf[0] = (tag >> 8) & 0xff;
tagbuf[1] = tag & 0xff;
if (result || resultlen)
{
*result = NULL;
*resultlen = 0;
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
}
else
{
p0 = (tag == 0x3F00)? 0: is_dir? 1:2;
p1 = 0x0c; /* No FC return. */
sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE,
p0, p1, 2, tagbuf );
return map_sw (sw);
}
return 0;
}
/* This is a private command currently only working for TCOS cards. */
gpg_error_t
iso7816_list_directory (int slot, int list_dirs,
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, 0x80, 0xAA, list_dirs? 1:2, 0, -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);
}
/* 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
@ -381,3 +446,126 @@ iso7816_get_challenge (int slot, int length, unsigned char *buffer)
return 0;
}
/* Perform a READ BINARY command requesting a maximum of NMAX bytes
from OFFSET. With NMAX = 0 the entire file is read. The result is
stored in a newly allocated buffer at the address passed by RESULT.
Returns the length of this data at the address of RESULTLEN. */
gpg_error_t
iso7816_read_binary (int slot, size_t offset, size_t nmax,
unsigned char **result, size_t *resultlen)
{
int sw;
unsigned char *buffer;
size_t bufferlen;
if (!result || !resultlen)
return gpg_error (GPG_ERR_INV_VALUE);
*result = NULL;
*resultlen = 0;
/* We can only encode 15 bits in p0,p1 to indicate an offset. Thus
we check for this limit. */
if (offset > 32767 || nmax > 254)
return gpg_error (GPG_ERR_INV_VALUE);
do
{
buffer = NULL;
bufferlen = 0;
/* Fixme: Either the ccid driver of the TCOS cards have problems
with an Le of 0. */
sw = apdu_send_le (slot, 0x00, CMD_READ_BINARY,
((offset>>8) & 0xff), (offset & 0xff) , -1, NULL,
nmax? nmax : 254, &buffer, &bufferlen);
if (sw != SW_SUCCESS && sw != SW_EOF_REACHED)
{
/* Make sure that pending buffers are released. */
xfree (buffer);
xfree (*result);
*result = NULL;
*resultlen = 0;
return map_sw (sw);
}
if (*result) /* Need to extend the buffer. */
{
unsigned char *p = xtryrealloc (*result, *resultlen + bufferlen);
if (!p)
{
gpg_error_t err = gpg_error_from_errno (errno);
xfree (buffer);
xfree (*result);
*result = NULL;
*resultlen = 0;
return err;
}
*result = p;
memcpy (*result + *resultlen, buffer, bufferlen);
*resultlen += bufferlen;
xfree (buffer);
buffer = NULL;
}
else /* Transfer the buffer into our result. */
{
*result = buffer;
*resultlen = bufferlen;
}
offset += bufferlen;
if (offset > 32767)
break; /* We simply truncate the result for too large
files. */
}
while (!nmax && sw != SW_EOF_REACHED);
return 0;
}
/* Perform a READ RECORD command. RECNO gives the record number to
read with 0 indicating the current record. RECCOUNT must be 1 (not
all cards support reading of more than one record). The result is
stored in a newly allocated buffer at the address passed by RESULT.
Returns the length of this data at the address of RESULTLEN. */
gpg_error_t
iso7816_read_record (int slot, int recno, int reccount,
unsigned char **result, size_t *resultlen)
{
int sw;
unsigned char *buffer;
size_t bufferlen;
if (!result || !resultlen)
return gpg_error (GPG_ERR_INV_VALUE);
*result = NULL;
*resultlen = 0;
/* We can only encode 15 bits in p0,p1 to indicate an offset. Thus
we check for this limit. */
if (recno < 0 || recno > 255 || reccount != 1)
return gpg_error (GPG_ERR_INV_VALUE);
buffer = NULL;
bufferlen = 0;
/* Fixme: Either the ccid driver of the TCOS cards have problems
with an Le of 0. */
sw = apdu_send_le (slot, 0x00, CMD_READ_RECORD,
recno,
0x04,
-1, NULL,
254, &buffer, &bufferlen);
if (sw != SW_SUCCESS && sw != SW_EOF_REACHED)
{
/* Make sure that pending buffers are released. */
xfree (buffer);
xfree (*result);
*result = NULL;
*resultlen = 0;
return map_sw (sw);
}
*result = buffer;
*resultlen = bufferlen;
return 0;
}

View File

@ -27,6 +27,10 @@
gpg_error_t iso7816_select_application (int slot,
const char *aid, size_t aidlen);
gpg_error_t iso7816_select_file (int slot, int tag, int is_dir,
unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_list_directory (int slot, int list_dirs,
unsigned char **result, size_t *resultlen);
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,
@ -56,5 +60,9 @@ gpg_error_t iso7816_read_public_key (int slot,
gpg_error_t iso7816_get_challenge (int slot,
int length, unsigned char *buffer);
gpg_error_t iso7816_read_binary (int slot, size_t offset, size_t nmax,
unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_read_record (int slot, int recno, int reccount,
unsigned char **result, size_t *resultlen);
#endif /*ISO7816_H*/

View File

@ -165,7 +165,7 @@ main (int argc, char **argv )
/* FIXME: Use select_application. */
appbuf.slot = slot;
rc = app_select_openpgp (&appbuf, &appbuf.serialno, &appbuf.serialnolen);
rc = app_select_openpgp (&appbuf);
if (rc)
{
log_error ("selecting openpgp failed: %s\n", gpg_strerror (rc));

View File

@ -24,6 +24,13 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <unistd.h>
#ifdef HAVE_READLINE_READLINE_H
#include <readline/readline.h>
#include <readline/history.h>
#endif
#define JNLIB_NEED_LOG_LOGV
#include "scdaemon.h"
@ -32,17 +39,25 @@
#include "apdu.h" /* for open_reader */
#include "atr.h"
#include "app-common.h"
#include "iso7816.h"
#define _(a) (a)
#define CONTROL_D ('D' - 'A' + 1)
enum cmd_and_opt_values
{ oVerbose = 'v',
{
oInteractive = 'i',
oVerbose = 'v',
oReaderPort = 500,
octapiDriver,
oDebug,
oDebugAll,
oDisableCCID,
oGenRandom,
aTest };
@ -52,15 +67,27 @@ static ARGPARSE_OPTS opts[] = {
{ 301, NULL, 0, "@Options:\n " },
{ oInteractive, "interactive", 0, "start in interactive explorer mode"},
{ oVerbose, "verbose", 0, "verbose" },
{ oReaderPort, "reader-port", 2, "|N|connect to reader at port N"},
{ octapiDriver, "ctapi-driver", 2, "NAME|use NAME as ctAPI driver"},
{ oDisableCCID, "disable-ccid", 0,
#ifdef HAVE_LIBUSB
"do not use the internal CCID driver"
#else
"@"
#endif
},
{ 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 void interactive_shell (int slot);
static const char *
my_strusage (int level)
{
@ -111,10 +138,8 @@ main (int argc, char **argv )
ARGPARSE_ARGS pargs;
int slot, rc;
const char *reader_port = NULL;
struct app_ctx_s appbuf;
unsigned long gen_random = 0;
memset (&appbuf, 0, sizeof appbuf);
int interactive = 0;
set_strusage (my_strusage);
gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
@ -143,7 +168,9 @@ main (int argc, char **argv )
case oDebugAll: opt.debug = ~0; break;
case oReaderPort: reader_port = pargs.r.ret_str; break;
case octapiDriver: opt.ctapi_driver = pargs.r.ret_str; break;
case oDisableCCID: opt.disable_ccid = 1; break;
case oGenRandom: gen_random = pargs.r.ret_ulong; break;
case oInteractive: interactive = 1; break;
default : pargs.err = 2; break;
}
}
@ -151,7 +178,7 @@ main (int argc, char **argv )
exit(2);
if (opt.verbose < 2)
opt.verbose = 2; /* hack to let select_openpgp print some info. */
opt.verbose = 2; /* Hack to let select_openpgp print some info. */
if (argc)
usage (1);
@ -167,40 +194,61 @@ main (int argc, char **argv )
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));
if (interactive)
interactive_shell (slot);
else
{
appbuf.initialized = 1;
log_info ("openpgp application selected\n");
struct app_ctx_s appbuf;
if (gen_random)
/* Fixme: We better use app.c directly. */
memset (&appbuf, 0, sizeof appbuf);
appbuf.slot = slot;
rc = app_select_openpgp (&appbuf);
if (rc)
{
size_t nbytes;
unsigned char *buffer;
buffer = xmalloc (4096);
do
log_info ("selecting openpgp failed: %s\n", gpg_strerror (rc));
memset (&appbuf, 0, sizeof appbuf);
appbuf.slot = slot;
rc = app_select_dinsig (&appbuf);
if (rc)
log_info ("selecting dinsig failed: %s\n", gpg_strerror (rc));
else
{
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;
}
appbuf.initialized = 1;
log_info ("dinsig application selected\n");
}
}
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);
}
while (gen_random && !log_get_errorcount (0));
xfree (buffer);
}
}
return log_get_errorcount (0)? 2:0;
}
@ -211,3 +259,377 @@ send_status_info (CTRL ctrl, const char *keyword, ...)
{
/* DUMMY */
}
/* Dump BUFFER of length NBYTES in a nicely human readable format. */
static void
dump_buffer (const unsigned char *buffer, size_t nbytes)
{
int i;
while (nbytes)
{
for (i=0; i < 16 && i < nbytes; i++)
printf ("%02X%s ", buffer[i], i==8? " ":"");
for (; i < 16; i++)
printf (" %s ", i==8? " ":"");
putchar (' ');
putchar (' ');
for (i=0; i < 16 && i < nbytes; i++)
if (isprint (buffer[i]))
putchar (buffer[i]);
else
putchar ('.');
nbytes -= i;
buffer += i;
for (; i < 16; i++)
putchar (' ');
putchar ('\n');
}
}
static void
dump_or_store_buffer (const char *arg,
const unsigned char *buffer, size_t nbytes)
{
const char *s = strchr (arg, '>');
int append;
FILE *fp;
if (!s)
{
dump_buffer (buffer, nbytes);
return;
}
if ((append = (*++s == '>')))
s++;
fp = fopen (s, append? "ab":"wb");
if (!fp)
{
log_error ("failed to create `%s': %s\n", s, strerror (errno));
return;
}
if (nbytes && fwrite (buffer, nbytes, 1, fp) != 1)
log_error ("failed to write to `%s': %s\n", s, strerror (errno));
if (fclose (fp))
log_error ("failed to close `%s': %s\n", s, strerror (errno));
}
/* Convert STRING into a a newly allocated buffer and return the
length of the buffer in R_LENGTH. Detect xx:xx:xx... sequence and
unhexify that one. */
static unsigned char *
pin_to_buffer (const char *string, size_t *r_length)
{
unsigned char *buffer = xmalloc (strlen (string)+1);
const char *s;
size_t n;
for (s=string, n=0; *s; s += 3)
{
if (hexdigitp (s) && hexdigitp (s+1) && (s[2]==':'||!s[2]))
{
buffer[n++] = xtoi_2 (s);
if (!s[2])
break;
}
else
{
memcpy (buffer, string, strlen (string));
*r_length = strlen (string);
return buffer;
}
}
*r_length = n;
return buffer;
}
static char *
read_line (int use_readline, char *prompt)
{
static char buf[256];
#ifdef HAVE_READLINE
if (use_readline)
{
char *line = readline (prompt);
if (line)
trim_spaces (line);
if (line && strlen (line) > 2 )
add_history (line);
return line;
}
#endif
/* Either we don't have readline or we are not running
interactively */
#ifndef HAVE_READLINE
printf ("%s", prompt );
#endif
fflush(stdout);
if (!fgets(buf, sizeof(buf), stdin))
return NULL;
if (!strlen(buf))
return NULL;
if (buf[strlen (buf)-1] == '\n')
buf[strlen (buf)-1] = 0;
trim_spaces (buf);
return buf;
}
/* Run a shell for interactive exploration of the card. */
static void
interactive_shell (int slot)
{
enum cmdids
{
cmdNOP = 0,
cmdQUIT, cmdHELP,
cmdSELECT,
cmdCHDIR,
cmdLS,
cmdAPP,
cmdREAD,
cmdREADREC,
cmdDEBUG,
cmdVERIFY,
cmdCHANGEREF,
cmdINVCMD
};
static struct
{
const char *name;
enum cmdids id;
const char *desc;
} cmds[] = {
{ "quit" , cmdQUIT , "quit this menu" },
{ "q" , cmdQUIT , NULL },
{ "help" , cmdHELP , "show this help" },
{ "?" , cmdHELP , NULL },
{ "debug" , cmdDEBUG, "set debugging flags" },
{ "select" , cmdSELECT, "select file (EF)" },
{ "s" , cmdSELECT, NULL },
{ "chdir" , cmdCHDIR, "change directory (select DF)"},
{ "cd" , cmdCHDIR, NULL },
{ "ls" , cmdLS, "list directory (some cards only)"},
{ "app" , cmdAPP, "select application"},
{ "read" , cmdREAD, "read binary" },
{ "rb" , cmdREAD, NULL },
{ "readrec", cmdREADREC, "read record(s)" },
{ "rr" , cmdREADREC, NULL },
{ "verify" , cmdVERIFY, "verify CHVNO PIN" },
{ "ver" , cmdVERIFY, NULL },
{ "changeref", cmdCHANGEREF, "change reference data" },
{ NULL, cmdINVCMD }
};
enum cmdids cmd = cmdNOP;
int use_readline = isatty (fileno(stdin));
char *line;
gpg_error_t err = 0;
unsigned char *result = NULL;
size_t resultlen;
#ifdef HAVE_READLINE
if (use_readline)
using_history ();
#endif
for (;;)
{
int arg_number;
const char *arg_string = "";
const char *arg_next = "";
char *p;
int i;
if (err)
printf ("command failed: %s\n", gpg_strerror (err));
err = 0;
xfree (result);
result = NULL;
printf ("\n");
do
{
line = read_line (use_readline, "cmd> ");
}
while ( line && *line == '#' );
arg_number = 0;
if (!line || *line == CONTROL_D)
cmd = cmdQUIT;
else if (!*line)
cmd = cmdNOP;
else {
if ((p=strchr (line,' ')))
{
char *endp;
*p++ = 0;
trim_spaces (line);
trim_spaces (p);
arg_number = strtol (p, &endp, 0);
arg_string = p;
if (endp != p)
{
arg_next = endp;
while ( spacep (arg_next) )
arg_next++;
}
}
for (i=0; cmds[i].name; i++ )
if (!ascii_strcasecmp (line, cmds[i].name ))
break;
cmd = cmds[i].id;
}
switch (cmd)
{
case cmdHELP:
for (i=0; cmds[i].name; i++ )
if (cmds[i].desc)
printf("%-10s %s\n", cmds[i].name, _(cmds[i].desc) );
break;
case cmdQUIT:
goto leave;
case cmdNOP:
break;
case cmdDEBUG:
if (!*arg_string)
opt.debug = opt.debug? 0 : 2048;
else
opt.debug = arg_number;
break;
case cmdSELECT:
err = iso7816_select_file (slot, arg_number, 0, NULL, NULL);
break;
case cmdCHDIR:
err = iso7816_select_file (slot, arg_number, 1, NULL, NULL);
break;
case cmdLS:
err = iso7816_list_directory (slot, 1, &result, &resultlen);
if (!err || gpg_err_code (err) == GPG_ERR_ENOENT)
err = iso7816_list_directory (slot, 0, &result, &resultlen);
/* FIXME: Do something with RESULT. */
break;
case cmdAPP:
{
app_t app;
app = select_application (NULL, slot, *arg_string? arg_string:NULL);
if (app)
{
char *sn;
app_get_serial_and_stamp (app, &sn, NULL);
log_info ("application `%s' ready; sn=%s\n",
app->apptype?app->apptype:"?", sn? sn:"[none]");
release_application (app);
}
}
break;
case cmdREAD:
err = iso7816_read_binary (slot, 0, 0, &result, &resultlen);
if (!err)
dump_or_store_buffer (arg_string, result, resultlen);
break;
case cmdREADREC:
if (*arg_string == '*' && (!arg_string[1] || arg_string[1] == ' '))
{
/* Fixme: Can't write to a file yet. */
for (i=1, err=0; !err; i++)
{
xfree (result); result = NULL;
err = iso7816_read_record (slot, i, 1, &result, &resultlen);
if (!err)
dump_buffer (result, resultlen);
}
if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
err = 0;
}
else
{
err = iso7816_read_record (slot, arg_number, 1,
&result, &resultlen);
if (!err)
dump_or_store_buffer (arg_string, result, resultlen);
}
break;
case cmdVERIFY:
if (arg_number < 0 || arg_number > 255 || (arg_number & 127) > 31)
printf ("error: invalid CHVNO\n");
else
{
unsigned char *pin;
size_t pinlen;
pin = pin_to_buffer (arg_next, &pinlen);
err = iso7816_verify (slot, arg_number, pin, pinlen);
xfree (pin);
}
break;
case cmdCHANGEREF:
{
const char *newpin = arg_next;
while ( *newpin && !spacep (newpin) )
newpin++;
while ( spacep (newpin) )
newpin++;
if (arg_number < 0 || arg_number > 255 || (arg_number & 127) > 31)
printf ("error: invalid CHVNO\n");
else if (!*arg_next || !*newpin || newpin == arg_next)
printf ("usage: changeref CHVNO OLDPIN NEWPIN\n");
else
{
char *oldpin = xstrdup (arg_next);
unsigned char *oldpin_buf, *newpin_buf;
size_t oldpin_len, newpin_len;
for (p=oldpin; *p && !spacep (p); p++ )
;
*p = 0;
oldpin_buf = pin_to_buffer (oldpin, &oldpin_len);
newpin_buf = pin_to_buffer (newpin, &newpin_len);
err = iso7816_change_reference_data (slot, arg_number,
oldpin_buf, oldpin_len,
newpin_buf, newpin_len);
xfree (newpin_buf);
xfree (oldpin_buf);
xfree (oldpin);
}
}
break;
case cmdINVCMD:
default:
printf ("\n");
printf ("Invalid command (try \"help\")\n");
break;
} /* End command switch. */
} /* End of main menu loop. */
leave:
;
}

View File

@ -100,7 +100,7 @@ static ARGPARSE_OPTS opts[] = {
{ oReaderPort, "reader-port", 2, N_("|N|connect to reader at port N")},
{ octapiDriver, "ctapi-driver", 2, N_("NAME|use NAME as ct-API driver")},
{ opcscDriver, "pcsc-driver", 2, N_("NAME|use NAME as PC/SC driver")},
{ oDisableCCID, "disable-ccidc", 0,
{ oDisableCCID, "disable-ccid", 0,
#ifdef HAVE_LIBUSB
N_("do not use the internal CCID driver")
#else
@ -397,7 +397,7 @@ main (int argc, char **argv )
case oServer: pipe_server = 1; break;
case oDaemon: is_daemon = 1; break;
case oReaderPort: app_set_default_reader_port (pargs.r.ret_str); break;
case oReaderPort: opt.reader_port = pargs.r.ret_str; break;
case octapiDriver: opt.ctapi_driver = pargs.r.ret_str; break;
case opcscDriver: opt.pcsc_driver = pargs.r.ret_str; break;
case oDisableCCID: opt.disable_ccid = 1; break;

View File

@ -55,6 +55,7 @@ struct {
const char *homedir; /* configuration directory name */
const char *ctapi_driver; /* Library to access the ctAPI. */
const char *pcsc_driver; /* Library to access the PC/SC system. */
const char *reader_port; /* NULL or reder port to use. */
int disable_opensc; /* Disable the use of the OpenSC framework. */
int disable_ccid; /* Disable the use of the internal CCID driver. */
int allow_admin; /* Allow the use of admin commands for certain
@ -96,8 +97,10 @@ struct server_control_s {
};
typedef struct server_control_s *CTRL;
typedef struct server_control_s *ctrl_t;
typedef struct card_ctx_s *CARD;
typedef struct app_ctx_s *APP;
typedef struct app_ctx_s *app_t;
/*-- scdaemon.c --*/
void scd_exit (int rc);

219
scd/tlv.c Normal file
View File

@ -0,0 +1,219 @@
/* tlv.c - Tag-Length-Value Utilities
* Copyright (C) 2003, 2004 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <gpg-error.h>
#include "tlv.h"
static const unsigned char *
do_find_tlv (const unsigned char *buffer, size_t length,
int tag, size_t *nbytes, int nestlevel)
{
const unsigned char *s = buffer;
size_t n = length;
size_t len;
int this_tag;
int composite;
for (;;)
{
buffer = s;
if (n < 2)
return NULL; /* Buffer definitely too short for tag and length. */
if (!*s || *s == 0xff)
{ /* Skip optional filler between TLV objects. */
s++;
n--;
continue;
}
composite = !!(*s & 0x20);
if ((*s & 0x1f) == 0x1f)
{ /* more tag bytes to follow */
s++;
n--;
if (n < 2)
return NULL; /* buffer definitely too short for tag and length. */
if ((*s & 0x1f) == 0x1f)
return NULL; /* We support only up to 2 bytes. */
this_tag = (s[-1] << 8) | (s[0] & 0x7f);
}
else
this_tag = s[0];
len = s[1];
s += 2; n -= 2;
if (len < 0x80)
;
else if (len == 0x81)
{ /* One byte length follows. */
if (!n)
return NULL; /* we expected 1 more bytes with the length. */
len = s[0];
s++; n--;
}
else if (len == 0x82)
{ /* Two byte length follows. */
if (n < 2)
return NULL; /* We expected 2 more bytes with the length. */
len = (s[0] << 8) | s[1];
s += 2; n -= 2;
}
else
return NULL; /* APDU limit is 65535, thus it does not make
sense to assume longer length fields. */
if (composite && nestlevel < 100)
{ /* Dive into this composite DO after checking for a too deep
nesting. */
const unsigned char *tmp_s;
size_t tmp_len;
tmp_s = do_find_tlv (s, len, tag, &tmp_len, nestlevel+1);
if (tmp_s)
{
*nbytes = tmp_len;
return tmp_s;
}
}
if (this_tag == tag)
{
*nbytes = len;
return s;
}
if (len > n)
return NULL; /* Buffer too short to skip to the next tag. */
s += len; n -= len;
}
}
/* Locate a 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. */
const unsigned char *
find_tlv (const unsigned char *buffer, size_t length,
int tag, size_t *nbytes)
{
return do_find_tlv (buffer, length, tag, nbytes, 0);
}
/* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag
and the length part from the TLV triplet. Update BUFFER and SIZE
on success. */
gpg_error_t
parse_ber_header (unsigned char const **buffer, size_t *size,
int *r_class, int *r_tag,
int *r_constructed, int *r_ndef,
size_t *r_length, size_t *r_nhdr)
{
int c;
unsigned long tag;
const unsigned char *buf = *buffer;
size_t length = *size;
*r_ndef = 0;
*r_length = 0;
*r_nhdr = 0;
/* Get the tag. */
if (!length)
return gpg_error (GPG_ERR_EOF);
c = *buf++; length--; ++*r_nhdr;
*r_class = (c & 0xc0) >> 6;
*r_constructed = !!(c & 0x20);
tag = c & 0x1f;
if (tag == 0x1f)
{
tag = 0;
do
{
/* Simple check against overflow. We limit our maximim tag
value more than needed but that should not be a problem
because I have nver encountered such large value. We
assume at least 32 bit integers. */
if (tag > (1 << 24))
return gpg_error (GPG_ERR_TOO_LARGE);
tag <<= 7;
if (!length)
return gpg_error (GPG_ERR_EOF);
c = *buf++; length--; ++*r_nhdr;
tag |= c & 0x7f;
}
while (c & 0x80);
}
*r_tag = tag;
/* Get the length. */
if (!length)
return gpg_error (GPG_ERR_EOF);
c = *buf++; length--; ++*r_nhdr;
if ( !(c & 0x80) )
*r_length = c;
else if (c == 0x80)
*r_ndef = 1;
else if (c == 0xff)
return gpg_error (GPG_ERR_BAD_BER);
else
{
unsigned long len = 0;
int count = c & 0x7f;
for (; count; count--)
{
/* Simple check against overflow. We limit our maximim
length more than needed but that should not be a problem
because I have never encountered such large value and
well they are managed in memory and thus we would run
into memory problems anyway. We assume at least 32 bit
integers. */
if (len > (1 << 24))
return gpg_error (GPG_ERR_TOO_LARGE);
len <<= 8;
if (!length)
return gpg_error (GPG_ERR_EOF);
c = *buf++; length--; ++*r_nhdr;
len |= c & 0xff;
}
*r_length = len;
}
/* Without this kludge some example certs can't be parsed. */
if (*r_class == CLASS_UNIVERSAL && !*r_tag)
*r_length = 0;
*buffer = buf;
*size = length;
return 0;
}

84
scd/tlv.h Normal file
View File

@ -0,0 +1,84 @@
/* tlv.h - Tag-Length-Value Utilities
* Copyright (C) 2004 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#ifndef SCD_TLV_H
#define SCD_TLV_H 1
enum tlv_tag_class {
CLASS_UNIVERSAL = 0,
CLASS_APPLICATION = 1,
CLASS_CONTEXT = 2,
CLASS_PRIVATE =3
};
enum tlv_tag_type {
TAG_NONE = 0,
TAG_BOOLEAN = 1,
TAG_INTEGER = 2,
TAG_BIT_STRING = 3,
TAG_OCTET_STRING = 4,
TAG_NULL = 5,
TAG_OBJECT_ID = 6,
TAG_OBJECT_DESCRIPTOR = 7,
TAG_EXTERNAL = 8,
TAG_REAL = 9,
TAG_ENUMERATED = 10,
TAG_EMBEDDED_PDV = 11,
TAG_UTF8_STRING = 12,
TAG_REALTIVE_OID = 13,
TAG_SEQUENCE = 16,
TAG_SET = 17,
TAG_NUMERIC_STRING = 18,
TAG_PRINTABLE_STRING = 19,
TAG_TELETEX_STRING = 20,
TAG_VIDEOTEX_STRING = 21,
TAG_IA5_STRING = 22,
TAG_UTC_TIME = 23,
TAG_GENERALIZED_TIME = 24,
TAG_GRAPHIC_STRING = 25,
TAG_VISIBLE_STRING = 26,
TAG_GENERAL_STRING = 27,
TAG_UNIVERSAL_STRING = 28,
TAG_CHARACTER_STRING = 29,
TAG_BMP_STRING = 30
};
/* Locate a 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.*/
const unsigned char *find_tlv (const unsigned char *buffer, size_t length,
int tag, size_t *nbytes);
/* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag
and the length part from the TLV triplet. Update BUFFER and SIZE
on success. */
gpg_error_t parse_ber_header (unsigned char const **buffer, size_t *size,
int *r_class, int *r_tag,
int *r_constructed,
int *r_ndef, size_t *r_length, size_t *r_nhdr);
#endif /* SCD_TLV_H */

705
tools/gpgparsemail.c Normal file
View File

@ -0,0 +1,705 @@
/* gpgparsemail.c - Standalone crypto mail parser
* 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
*/
/* This utility prints an RFC8222, possible MIME structured, message
in an annotated format with the first column having an indicator
for the content of the line.. Several options are available to
scrutinize the message. S/MIME and OpenPGP suuport is included. */
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include <assert.h>
#include <time.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include "rfc822parse.h"
#define PGM "gpgparsemail"
/* Option flags. */
static int verbose;
static int debug;
static int opt_crypto; /* Decrypt or verify messages. */
static int opt_no_header; /* Don't output the header lines. */
/* Structure used to communicate with the parser callback. */
struct parse_info_s {
int show_header; /* Show the header lines. */
int show_data; /* Show the data lines. */
unsigned int skip_show; /* Temporary disable above for these
number of lines. */
int show_data_as_note; /* The next data line should be shown
as a note. */
int show_boundary;
int nesting_level;
int gpgsm_mime; /* gpgsm shall be used from S/MIME. */
char *signing_protocol;
int hashing_level; /* The nesting level we are hashing. */
int hashing;
FILE *hash_file;
FILE *sig_file;
int verify_now; /* Falg set when all signature data is
available. */
};
/* Print diagnostic message and exit with failure. */
static void
die (const char *format, ...)
{
va_list arg_ptr;
fflush (stdout);
fprintf (stderr, "%s: ", PGM);
va_start (arg_ptr, format);
vfprintf (stderr, format, arg_ptr);
va_end (arg_ptr);
putc ('\n', stderr);
exit (1);
}
/* Print diagnostic message. */
static void
err (const char *format, ...)
{
va_list arg_ptr;
fflush (stdout);
fprintf (stderr, "%s: ", PGM);
va_start (arg_ptr, format);
vfprintf (stderr, format, arg_ptr);
va_end (arg_ptr);
putc ('\n', stderr);
}
static void *
xmalloc (size_t n)
{
void *p = malloc (n);
if (!p)
die ("out of core: %s", strerror (errno));
return p;
}
/* static void * */
/* xcalloc (size_t n, size_t m) */
/* { */
/* void *p = calloc (n, m); */
/* if (!p) */
/* die ("out of core: %s", strerror (errno)); */
/* return p; */
/* } */
/* static void * */
/* xrealloc (void *old, size_t n) */
/* { */
/* void *p = realloc (old, n); */
/* if (!p) */
/* die ("out of core: %s", strerror (errno)); */
/* return p; */
/* } */
static char *
xstrdup (const char *string)
{
void *p = malloc (strlen (string)+1);
if (!p)
die ("out of core: %s", strerror (errno));
strcpy (p, string);
return p;
}
static char *
stpcpy (char *a,const char *b)
{
while (*b)
*a++ = *b++;
*a = 0;
return (char*)a;
}
static int
run_gnupg (int smime, int sig_fd, int data_fd, int *close_list)
{
int rp[2];
pid_t pid;
int i, c, is_status;
unsigned int pos;
char status_buf[10];
const char *cmd = smime? "gpgsm":"gpg";
FILE *fp;
if (pipe (rp) == -1)
die ("error creating a pipe: %s", strerror (errno));
pid = fork ();
if (pid == -1)
die ("error forking process: %s", strerror (errno));
if (!pid)
{ /* Child. */
char data_fd_buf[50];
int fd;
/* Connect our signature fd to stdin. */
if (sig_fd != 0)
{
if (dup2 (sig_fd, 0) == -1)
die ("dup2 stdin failed: %s", strerror (errno));
}
/* Keep our data fd and format it for gpg/gpgsm use. */
sprintf (data_fd_buf, "-&%d", data_fd);
/* Send stdout to the bit bucket. */
fd = open ("/dev/null", O_WRONLY);
if (fd == -1)
die ("can't open `/dev/null': %s", strerror (errno));
if (fd != 1)
{
if (dup2 (fd, 1) == -1)
die ("dup2 stderr failed: %s", strerror (errno));
}
/* Connect stderr to our pipe. */
if (rp[1] != 2)
{
if (dup2 (rp[1], 2) == -1)
die ("dup2 stderr failed: %s", strerror (errno));
}
/* Close other files. */
for (i=0; (fd=close_list[i]) != -1; i++)
if (fd > 2 && fd != data_fd)
close (fd);
errno = 0;
execlp (cmd, cmd,
"--enable-special-filenames",
"--status-fd", "2",
"--assume-base64",
"--verify",
"--",
"-", data_fd_buf,
NULL);
die ("failed to exec the crypto command: %s", strerror (errno));
}
/* Parent. */
close (rp[1]);
fp = fdopen (rp[0], "r");
if (!fp)
die ("can't fdopen pipe for reading: %s", strerror (errno));
pos = 0;
is_status = 0;
assert (sizeof status_buf > 9);
while ((c=getc (fp)) != EOF)
{
if (pos < 9)
status_buf[pos] = c;
else
{
if (pos == 9)
{
is_status = !memcmp (status_buf, "[GNUPG:] ", 9);
if (is_status)
fputs ( "c ", stdout);
else if (verbose)
fputs ( "# ", stdout);
fwrite (status_buf, 9, 1, stdout);
}
putchar (c);
}
if (c == '\n')
{
if (verbose && pos < 9)
{
fputs ( "# ", stdout);
fwrite (status_buf, pos+1, 1, stdout);
}
pos = 0;
}
else
pos++;
}
if (pos)
{
if (verbose && pos < 9)
{
fputs ( "# ", stdout);
fwrite (status_buf, pos+1, 1, stdout);
}
putchar ('\n');
}
fclose (fp);
while ( (i=waitpid (pid, NULL, 0)) == -1 && errno == EINTR)
;
if (i == -1)
die ("waiting for child failed: %s", strerror (errno));
return 0;
}
/* Verify the signature in the current temp files. */
static void
verify_signature (struct parse_info_s *info)
{
int close_list[10];
assert (info->hash_file);
assert (info->sig_file);
rewind (info->hash_file);
rewind (info->sig_file);
/* printf ("# Begin hashed data\n"); */
/* while ( (c=getc (info->hash_file)) != EOF) */
/* putchar (c); */
/* printf ("# End hashed data signature\n"); */
/* printf ("# Begin signature\n"); */
/* while ( (c=getc (info->sig_file)) != EOF) */
/* putchar (c); */
/* printf ("# End signature\n"); */
/* rewind (info->hash_file); */
/* rewind (info->sig_file); */
close_list[0] = -1;
run_gnupg (1, fileno (info->sig_file), fileno (info->hash_file), close_list);
}
/* Prepare for a multipart/signed.
FIELD_CTX is the parsed context of the content-type header.*/
static void
mime_signed_begin (struct parse_info_s *info, rfc822parse_t msg,
rfc822parse_field_t field_ctx)
{
const char *s;
s = rfc822parse_query_parameter (field_ctx, "protocol", 1);
if (s)
{
printf ("h signed.protocol: %s\n", s);
if (!strcmp (s, "application/pkcs7-signature")
|| !strcmp (s, "application/x-pkcs7-signature"))
{
if (info->gpgsm_mime)
err ("note: ignoring nested pkcs7-signature");
else
{
info->gpgsm_mime = 1;
free (info->signing_protocol);
info->signing_protocol = xstrdup (s);
}
}
else if (verbose)
printf ("# this protocol is not supported\n");
}
}
/* Prepare for a multipart/encrypted.
FIELD_CTX is the parsed context of the content-type header.*/
static void
mime_encrypted_begin (struct parse_info_s *info, rfc822parse_t msg,
rfc822parse_field_t field_ctx)
{
const char *s;
s = rfc822parse_query_parameter (field_ctx, "protocol", 0);
if (s)
printf ("h encrypted.protocol: %s\n", s);
}
/* Print the event received by the parser for debugging as comment
line. */
static void
show_event (rfc822parse_event_t event)
{
const char *s;
switch (event)
{
case RFC822PARSE_OPEN: s= "Open"; break;
case RFC822PARSE_CLOSE: s= "Close"; break;
case RFC822PARSE_CANCEL: s= "Cancel"; break;
case RFC822PARSE_T2BODY: s= "T2Body"; break;
case RFC822PARSE_FINISH: s= "Finish"; break;
case RFC822PARSE_RCVD_SEEN: s= "Rcvd_Seen"; break;
case RFC822PARSE_LEVEL_DOWN: s= "Level_Down"; break;
case RFC822PARSE_LEVEL_UP: s= "Level_Up"; break;
case RFC822PARSE_BOUNDARY: s= "Boundary"; break;
case RFC822PARSE_LAST_BOUNDARY: s= "Last_Boundary"; break;
case RFC822PARSE_BEGIN_HEADER: s= "Begin_Header"; break;
case RFC822PARSE_PREAMBLE: s= "Preamble"; break;
case RFC822PARSE_EPILOGUE: s= "Epilogue"; break;
default: s= "[unknown event]"; break;
}
printf ("# *** got RFC822 event %s\n", s);
}
/* This function is called by the parser to communicate events. This
callback comminucates with the main program using a structure
passed in OPAQUE. Should retrun 0 or set errno and return -1. */
static int
message_cb (void *opaque, rfc822parse_event_t event, rfc822parse_t msg)
{
struct parse_info_s *info = opaque;
if (debug)
show_event (event);
if (event == RFC822PARSE_OPEN)
{
/* Initialize for a new message. */
info->show_header = 1;
}
else if (event == RFC822PARSE_T2BODY)
{
rfc822parse_field_t ctx;
ctx = rfc822parse_parse_field (msg, "Content-Type", -1);
if (ctx)
{
const char *s1, *s2;
s1 = rfc822parse_query_media_type (ctx, &s2);
if (s1)
{
printf ("h media: %*s%s %s\n",
info->nesting_level*2, "", s1, s2);
if (info->gpgsm_mime == 3)
{
char *buf = xmalloc (strlen (s1) + strlen (s2) + 2);
strcpy (stpcpy (stpcpy (buf, s1), "/"), s2);
assert (info->signing_protocol);
if (strcmp (buf, info->signing_protocol))
err ("invalid S/MIME structure; expected `%s', found `%s'",
info->signing_protocol, buf);
else
{
printf ("c begin_signature\n");
info->gpgsm_mime++;
if (opt_crypto)
{
assert (!info->sig_file);
info->sig_file = tmpfile ();
if (!info->sig_file)
die ("error creating temp file: %s",
strerror (errno));
}
}
free (buf);
}
else if (!strcmp (s1, "multipart"))
{
if (!strcmp (s2, "signed"))
mime_signed_begin (info, msg, ctx);
else if (!strcmp (s2, "encrypted"))
mime_encrypted_begin (info, msg, ctx);
}
}
else
printf ("h media: %*s none\n", info->nesting_level*2, "");
rfc822parse_release_field (ctx);
}
else
printf ("h media: %*stext plain [assumed]\n",
info->nesting_level*2, "");
info->show_header = 0;
info->show_data = 1;
info->skip_show = 1;
}
else if (event == RFC822PARSE_PREAMBLE)
info->show_data_as_note = 1;
else if (event == RFC822PARSE_LEVEL_DOWN)
{
printf ("b down\n");
info->nesting_level++;
}
else if (event == RFC822PARSE_LEVEL_UP)
{
printf ("b up\n");
if (info->nesting_level)
info->nesting_level--;
else
err ("invalid structure (bad nesting level)");
}
else if (event == RFC822PARSE_BOUNDARY || event == RFC822PARSE_LAST_BOUNDARY)
{
info->show_data = 0;
info->show_boundary = 1;
if (event == RFC822PARSE_BOUNDARY)
{
info->show_header = 1;
info->skip_show = 1;
printf ("b part\n");
}
else
printf ("b last\n");
if (info->gpgsm_mime == 2 && info->nesting_level == info->hashing_level)
{
printf ("c end_hash\n");
info->gpgsm_mime++;
info->hashing = 0;
}
else if (info->gpgsm_mime == 4)
{
printf ("c end_signature\n");
info->verify_now = 1;
}
}
else if (event == RFC822PARSE_BEGIN_HEADER)
{
if (info->gpgsm_mime == 1)
{
printf ("c begin_hash\n");
info->hashing = 1;
info->hashing_level = info->nesting_level;
info->gpgsm_mime++;
if (opt_crypto)
{
assert (!info->hash_file);
info->hash_file = tmpfile ();
if (!info->hash_file)
die ("failed to create temporary file: %s", strerror (errno));
}
}
}
return 0;
}
/* Read a message from FP and process it according to the global
options. */
static void
parse_message (FILE *fp)
{
char line[5000];
size_t length;
rfc822parse_t msg;
unsigned int lineno = 0;
int no_cr_reported = 0;
struct parse_info_s info;
memset (&info, 0, sizeof info);
msg = rfc822parse_open (message_cb, &info);
if (!msg)
die ("can't open parser: %s", strerror (errno));
/* Fixme: We should not use fgets becuase it can't cope with
embedded nul characters. */
while (fgets (line, sizeof (line), fp))
{
lineno++;
if (lineno == 1 && !strncmp (line, "From ", 5))
continue; /* We better ignore a leading From line. */
length = strlen (line);
if (length && line[length - 1] == '\n')
line[--length] = 0;
else
err ("line number %u too long or last line not terminated", lineno);
if (length && line[length - 1] == '\r')
line[--length] = 0;
else if (verbose && !no_cr_reported)
{
err ("non canonical ended line detected (line %u)", lineno);
no_cr_reported = 1;
}
if (rfc822parse_insert (msg, line, length))
die ("parser failed: %s", strerror (errno));
if (info.hashing)
{
/* Delay hashing of the CR/LF because the last line ending
belongs to the next boundary. */
if (debug)
printf ("# hashing %s`%s'\n", info.hashing==2?"CR,LF+":"", line);
if (opt_crypto)
{
if (info.hashing == 2)
fputs ("\r\n", info.hash_file);
fputs (line, info.hash_file);
if (ferror (info.hash_file))
die ("error writing to temporary file: %s", strerror (errno));
}
info.hashing = 2;
}
if (info.sig_file && opt_crypto)
{
if (info.verify_now)
{
verify_signature (&info);
fclose (info.hash_file);
info.hash_file = NULL;
fclose (info.sig_file);
info.sig_file = NULL;
info.gpgsm_mime = 0;
}
else
{
fputs (line, info.sig_file);
fputs ("\r\n", info.sig_file);
if (ferror (info.sig_file))
die ("error writing to temporary file: %s", strerror (errno));
}
}
if (info.show_boundary)
{
if (!opt_no_header)
printf (":%s\n", line);
info.show_boundary = 0;
}
if (info.skip_show)
info.skip_show--;
else if (info.show_data)
{
if (info.show_data_as_note)
{
if (verbose)
printf ("# DATA: %s\n", line);
info.show_data_as_note = 0;
}
else
printf (" %s\n", line);
}
else if (info.show_header && !opt_no_header)
printf (".%s\n", line);
}
rfc822parse_close (msg);
}
int
main (int argc, char **argv)
{
int last_argc = -1;
if (argc)
{
argc--; argv++;
}
while (argc && last_argc != argc )
{
last_argc = argc;
if (!strcmp (*argv, "--"))
{
argc--; argv++;
break;
}
else if (!strcmp (*argv, "--help"))
{
puts (
"Usage: " PGM " [OPTION] [FILE]\n"
"Parse a mail message into an annotated format.\n\n"
" --crypto decrypt or verify messages\n"
" --no-header don't output the header lines\n"
" --verbose enable extra informational output\n"
" --debug enable additional debug output\n"
" --help display this help and exit\n\n"
"With no FILE, or when FILE is -, read standard input.\n\n"
"Report bugs to <bug-gnupg@gnu.org>.");
exit (0);
}
else if (!strcmp (*argv, "--verbose"))
{
verbose = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--debug"))
{
verbose = debug = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--crypto"))
{
opt_crypto = 1;
argc--; argv++;
}
else if (!strcmp (*argv, "--no-header"))
{
opt_no_header = 1;
argc--; argv++;
}
}
if (argc > 1)
die ("usage: " PGM " [OPTION] [FILE] (try --help for more information)\n");
signal (SIGPIPE, SIG_IGN);
if (argc && strcmp (*argv, "-"))
{
FILE *fp = fopen (*argv, "rb");
if (!fp)
die ("can't open `%s': %s", *argv, strerror (errno));
parse_message (fp);
fclose (fp);
}
else
parse_message (stdin);
return 0;
}
/*
Local Variables:
compile-command: "gcc -Wall -g -o gpgparsemail rfc822parse.c gpgparsemail.c"
End:
*/

1235
tools/rfc822parse.c Normal file

File diff suppressed because it is too large Load Diff

79
tools/rfc822parse.h Normal file
View File

@ -0,0 +1,79 @@
/* rfc822parse.h - Simple mail and MIME parser
* Copyright (C) 1999 Werner Koch, Duesseldorf
* Copyright (C) 2003, g10 Code GmbH
*
* This program 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.
*
* This program 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 RFC822PARSE_H
#define RFC822PARSE_H
struct rfc822parse_context;
typedef struct rfc822parse_context *rfc822parse_t;
typedef enum
{
RFC822PARSE_OPEN = 1,
RFC822PARSE_CLOSE,
RFC822PARSE_CANCEL,
RFC822PARSE_T2BODY,
RFC822PARSE_FINISH,
RFC822PARSE_RCVD_SEEN,
RFC822PARSE_LEVEL_DOWN,
RFC822PARSE_LEVEL_UP,
RFC822PARSE_BOUNDARY,
RFC822PARSE_LAST_BOUNDARY,
RFC822PARSE_BEGIN_HEADER,
RFC822PARSE_PREAMBLE,
RFC822PARSE_EPILOGUE
}
rfc822parse_event_t;
struct rfc822parse_field_context;
typedef struct rfc822parse_field_context *rfc822parse_field_t;
typedef int (*rfc822parse_cb_t) (void *opaque,
rfc822parse_event_t event,
rfc822parse_t msg);
rfc822parse_t rfc822parse_open (rfc822parse_cb_t cb, void *opaque_value);
void rfc822parse_close (rfc822parse_t msg);
void rfc822parse_cancel (rfc822parse_t msg);
int rfc822parse_finish (rfc822parse_t msg);
int rfc822parse_insert (rfc822parse_t msg,
const unsigned char *line, size_t length);
char *rfc822parse_get_field (rfc822parse_t msg, const char *name, int which);
const char *rfc822parse_enum_header_lines (rfc822parse_t msg, void **context);
rfc822parse_field_t rfc822parse_parse_field (rfc822parse_t msg,
const char *name,
int which);
void rfc822parse_release_field (rfc822parse_field_t field);
const char *rfc822parse_query_parameter (rfc822parse_field_t ctx,
const char *attr, int lower_value);
const char *rfc822parse_query_media_type (rfc822parse_field_t ctx,
const char **subtype);
#endif /*RFC822PARSE_H */