mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
Some minor bug fixes, new test utilities and started support for other
smartcard applications.
This commit is contained in:
parent
203e1cc272
commit
eb24d8b751
@ -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
4
README
@ -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
1
TODO
@ -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.
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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 \
|
||||
|
@ -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.
|
||||
|
@ -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 \
|
||||
|
@ -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))
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -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
129
scd/app-dinsig.c
Normal 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
388
scd/app-nks.c
Normal 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;
|
||||
}
|
||||
|
||||
|
@ -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
131
scd/app.c
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
127
scd/command.c
127
scd/command.c
@ -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)
|
||||
|
192
scd/iso7816.c
192
scd/iso7816.c
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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*/
|
||||
|
@ -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));
|
||||
|
@ -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:
|
||||
;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
219
scd/tlv.c
Normal 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
84
scd/tlv.h
Normal 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
705
tools/gpgparsemail.c
Normal 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
1235
tools/rfc822parse.c
Normal file
File diff suppressed because it is too large
Load Diff
79
tools/rfc822parse.h
Normal file
79
tools/rfc822parse.h
Normal 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 */
|
Loading…
x
Reference in New Issue
Block a user