Page MenuHome GnuPG

No OneTemporary

diff --git a/scd/ChangeLog b/scd/ChangeLog
index eb898c185..c93a17a2b 100644
--- a/scd/ChangeLog
+++ b/scd/ChangeLog
@@ -1,2007 +1,2011 @@
+2009-01-05 Werner Koch <wk@g10code.com>
+
+ * apdu.c (apdu_get_status): Save the last status.
+
2008-12-18 Werner Koch <wk@g10code.com>
* ccid-driver.c (abort_cmd): New.
(bulk_in): Call abort_cmd after severe errors.
* apdu.c (reader_table_s): Add field ANY_STATUS.
(new_reader_slot): Clear it.
(apdu_get_status): Use ANY_STATUS to update the change counter.
Remove the use of the flag bit from LAST_STATUS everywhere.
* command.c (update_reader_status_file): Factor code out to ...
(send_client_notifications): New. Track signals already sent.
(update_reader_status_file): Shutdown the reader after a failed
apdu_get_status.
2008-12-09 Werner Koch <wk@g10code.com>
* scdaemon.c (main): Call i18n_init before init_common_subsystems.
2008-12-08 Werner Koch <wk@g10code.com>
* scdaemon.c (handle_connections): Sync ticker to the next full
interval.
(TIMERTICK_INTERVAL_USEC): Change to 500ms.
2008-12-05 Werner Koch <wk@g10code.com>
* app-openpgp.c (app_local_s): Add field ALGO_ATTR_CHANGE.
(app_select_openpgp): Parse new capability.
(show_caps): Show new capability.
2008-12-03 Werner Koch <wk@g10code.com>
* scdaemon.c (opts): Use ARGPARSE_ macros. Add option
--card-timeout.
* command.c (update_reader_status_file): Implement it.
2008-11-18 Werner Koch <wk@g10code.com>
* scdaemon.c (make_libversion): New.
(my_strusage): Print libgcrypt and libksba version.
2008-11-03 Werner Koch <wk@g10code.com>
* command.c (server_local_s): Add field DISCONNECT_ALLOWED.
(cmd_disconnect): Implement command.
(open_card): Reset disconnect flag.
(update_reader_status_file): Disconnect if allowed.
* app-common.h (app_ctx_s): Remove INITIALIZED. Make REF_COUNT
unsigned.
* app.c (select_application): Remove INITIALIZED.
(app_write_learn_status, app_readcert, app_readkey, app_getattr)
(app_setattr, app_sign, app_decipher, app_writecert)
(app_writekey, app_get_challenge, app_change_pin, app_check_pin):
Replace INITIALIZED by REF_COUNT check.
(application_notify_card_removed): Rename to ..
(application_notify_card_reset): .. this. Change all callers.
* command.c (do_reset): Call application_notify_card_reset after
sending a reset.
(update_reader_status_file): Add arg SET_CARD_REMOVED.
(scd_update_reader_status_file): Pass true for new flag.
(do_reset): Pass false for new flag.
* app.c (app_get_serial_and_stamp): Use bin2hex.
* app-help.c (app_help_get_keygrip_string): Ditto.
* app-p15.c (send_certinfo, send_keypairinfo, do_getattr): Ditto.
* app-openpgp.c (send_fpr_if_not_null, send_key_data)
(retrieve_fpr_from_card, send_keypair_info): Ditto.
* app-nks.c (keygripstr_from_pk_file): Ditto.
* command.c (cmd_apdu): Ditto.
2008-10-21 Marcus Brinkmann <marcus@g10code.com>
* command.c (open_card): If connect error is SW_HOST_NO_CARD,
return a more descriptive error.
2008-10-20 Werner Koch <wk@g10code.com>
* pcsc-wrapper.c (read_32): Use provided arg and not stdin. Is
called with stdin, though.
(handle_close): Mark unused arg.
(handle_status, handle_reset): Ditto.
* ccid-driver.c (ccid_check_card_presence): Mark not yet used arg.
* scdaemon.c (scd_deinit_default_ctrl): Mark unused arg.
* command.c (cmd_unlock, cmd_restart, cmd_disconnect): Ditto.
* apdu.c (ct_get_status): Ditto.
(ct_send_apdu, pcsc_send_apdu_wrapped)
(apdu_open_remote_reader): Ditto.
* app.c (select_application): Ditto.
* app-openpgp.c (do_writecert, do_change_pin, do_writekey): Ditto.
* app-nks.c (do_change_pin, do_check_pin): Ditto.
2008-10-16 Werner Koch <wk@g10code.com>
* command.c (cmd_disconnect): New dummy command.
(register_commands): Register command.
2008-10-15 Werner Koch <wk@g10code.com>
* command.c (scd_command_handler): Return true if there is no more
active session.
* scdaemon.c (start_connection_thread): Set shutdown flag if
requested by command handler.
(main): Make PIPE_SERVER module global.
(handle_connections): Disable listen_fd if a shutdown is pending.
2008-10-14 Werner Koch <wk@g10code.com>
* apdu.c (reader_table_s): Add fields connect_card and
disconnect_card.
(new_reader_slot): Set them to NULL.
(apdu_connect, apdu_disconnect): New.
(apdu_close_reader, apdu_shutdown_reader): Call apdu_disconnect.
(connect_pcsc_card, disconnect_pcsc_card): new.
(reset_pcsc_reader_direct): Implement in terms of
disconnect_pcsc_card and connect_pcsc_card.
(apdu_get_atr): Return NULL if there is no ATR.
* sc-copykeys.c (main): Add call to apdu_connect.
* command.c (open_card): Ditto.
* apdu.h (SW_HOST_ALREADY_CONNECTED): New.
(APDU_CARD_USABLE, APDU_CARD_PRESENT, APDU_CARD_ACTIVE): New.
* apdu.c: Replace constants by the new macros.
(open_pcsc_reader): Factor code out to ...
(open_pcsc_reader_direct, open_pcsc_reader_wrapped): New.
(reset_pcsc_reader): Factor code out to ...
(reset_pcsc_reader_direct, reset_pcsc_reader_wrapped): New.
(pcsc_get_status): Factor code out to ...
(pcsc_get_status_direct, pcsc_get_status_wrapped): New.
(pcsc_send_apdu): Factor code out to ...
(pcsc_send_apdu_direct, pcsc_send_apdu_wrapped): New.
(close_pcsc_reader): Factor code out to ...
(close_pcsc_reader_direct, close_pcsc_reader_wrapped): New.
* command.c (update_reader_status_file): Open the reader if not
yet done.
* scdaemon.c (TIMERTICK_INTERVAL_SEC, TIMERTICK_INTERVAL_USEC):
New to replace TIMERTICK_INTERVAL. Chnage from 2s (4 under W32)
to 250ms.
2008-10-13 Werner Koch <wk@g10code.com>
* command.c (option_handler) [W32]: Use strtoul with base 16.
(update_reader_status_file) [W32]: Set Event.
(scd_command_handler): Use INT2FD to silent warning.
2008-09-29 Werner Koch <wk@g10code.com>
* scdaemon.h (GCRY_MD_USER): Rename to GCRY_MODULE_ID_USER.
(GCRY_MD_USER_TLS_MD5SHA1): Rename to MD_USER_TLS_MD5SHA1 and
change all users.
2008-09-28 Marcus Brinkmann <marcus@g10code.com>
* apdu.c (pcsc_get_status): Fix last change.
2008-09-25 Werner Koch <wk@g10code.com>
* app-openpgp.c (do_setattr): Do not allow setting of the reset
code.
(do_change_pin): Allow setting of the reset code.
2008-09-24 Werner Koch <wk@g10code.com>
* app-openpgp.c (verify_chv3): Set the did_chv3 flag which was
accidently removed on 2008-03-26.
(verify_chv2): Revert last change.
(do_change_pin): Do not change CHV2. Add reset code logic for v2
cards.
* iso7816.c (iso7816_reset_retry_counter_with_rc): New.
* app-openpgp.c (add_tlv, build_privkey_template): New.
(do_writekey): Support v2 keys and other key lengths than 1024.
* iso7816.c (iso7816_put_data_odd): New.
2008-09-23 Werner Koch <wk@g10code.com>
* app-openpgp.c (do_sign): Support SHA-2 digests.
(verify_chv2): No CHV auto-sync for v2 cards.
(do_auth): Allow 2048 bit keys.
(parse_algorithm_attribute): New.
(rsa_key_format_t): New.
(struct app_local_s): Add struct KEYATTR.
2008-09-23 Marcus Brinkmann <marcus@g10code.com>
* apdu.c (pcsc_get_status): Be more relaxed with the usable flag
under Windows.
2008-09-23 Werner Koch <wk@g10code.com>
* app-openpgp.c (do_setattr): Use command chaining for long
values.
* iso7816.c (iso7816_put_data): Add arg EXTENDED_MODE. Change all
callers.
* apdu.c (apdu_send_simple): Add arg EXTENDED_MODE. Change all
callers.
(send_le): Implement command chaining.
* ccid-driver.c (ccid_transceive_apdu_level): Increase allowed
APDU size.
(ccid_transceive): Alow for APDUS of up to 259 bytes.
* apdu.h: Add new SW_ codes.
2008-09-16 Werner Koch <wk@g10code.com>
* command.c (cmd_writecert): New.
(register_commands): Register it.
* app-common.h (app_ctx_s): Add member WRITECERT.
* app.c (app_writecert): New.
* app-openpgp.c (do_writecert): New.
(parse_historical): New.
(show_extcap): New.
(dump_all_do): Print only the length of longs DOs.
* command.c (cmd_writekey, cmd_apdu, cmd_pksign)
(cmd_passwd): Replace open coding by skip_options.
2008-08-30 Moritz <moritz@gnu.org>
* scdaemon.c (main): Use estream_asprintf instead of asprintf.
* command.c (update_reader_status_file): Likewise.
(cmd_serialno): Use estream_asprintf instead of asprintf
and xfree instead of free to release memory allocated
through (estream_)asprintf.
(cmd_learn): Likewise.
(pin_cb): Likewise.
* app-openpgp.c (get_public_key): Likewise.
2008-08-18 Werner Koch <wk@g10code.com>
* app-openpgp.c (do_setattr): Fix test for v2 cards.
2008-08-11 Werner Koch <wk@g10code.com>
* apdu.c (reset_pcsc_reader, open_pcsc_reader)
(reset_rapdu_reader, open_rapdu_reader): Allow ATRs of up to 33
bytes. Provide maximum size of ATR buffer using DIM. Such long
ATR are never seen in reality but the PC/SC library of MAC OS X is
just too buggy. Reported by Ludovic Rousseau. Fixes bug #948.
2008-07-30 Werner Koch <wk@g10code.com>
* app-openpgp.c (verify_a_chv): Use xtrymalloc and make the prompt
for CHV2 more user friendly.
2008-07-03 Werner Koch <wk@g10code.com>
* app-openpgp.c (do_readcert): New.
(app_local_s): Add fields IS_V2 and MAX_CERTLEN_3.
(app_select_openpgp): Set them and register do_readcert.
(do_setattr): Allow storing of the certificate.
2008-06-25 Werner Koch <wk@g10code.com>
* app-dinsig.c (do_sign): Allow for SHA256.
2008-06-24 Werner Koch <wk@g10code.com>
* app-common.h (app_ctx_s): Renamed reset_mode parameter of
change_pin to mode_Flags and make it an unsigned int.
(APP_CHANGE_FLAG_RESET, APP_CHANGE_FLAG_NULLPIN): New.
* app-openpgp.c (do_change_pin): Adjust for that.
* command.c (cmd_passwd): Add option --nullpin.
* app-nks.c (do_check_pin, do_change_pin): New.
(app_select_nks): Register new functions.
2008-04-21 Moritz Schulte <mo@g10code.com> (wk)
* app-openpgp.c (verify_a_chv): Make use of the default CHV flag.
2008-03-26 Werner Koch <wk@g10code.com>
* app-openpgp.c (verify_chv3): Support the keypad.
2008-02-09 Marcus Brinkmann <marcus@g10code.de>
* scdaemon.c (main): Use CONFIG_FILENAME as filename if it is set
in gpgconf-list output.
2007-12-10 Werner Koch <wk@g10code.com>
* app-openpgp.c (do_decipher): Take care of cryptograms shorter
that 128 bytes. Fixes bug#851.
2007-11-14 Werner Koch <wk@g10code.com>
* scdaemon.c (main): Pass STANDARD_SOCKET flag to
create_server_socket.
2007-11-13 Werner Koch <wk@g10code.com>
* scdaemon.c (start_connection_thread): Do not call
assuan_sock_check_nonce if we are running in --server mode.
2007-11-07 Werner Koch <wk@g10code.com>
* scdaemon.h: Remove errors.h.
2007-10-02 Werner Koch <wk@g10code.com>
* command.c (cmd_getinfo): Add "pid" subcommand.
2007-10-01 Werner Koch <wk@g10code.com>
* scdaemon.c (create_server_socket): Use Assuan socket wrappers
and remove Windows specific code.
(socket_nonce): New.
(start_connection_thread): Check nonce.
2007-09-14 Marcus Brinkmann <marcus@g10code.de>
* scdaemon.c (main): New variable STANDARD_SOCKET, which is 1 for
W32 targets. Use it for create_socket_name.
2007-08-07 Werner Koch <wk@g10code.com>
* tlv.c, tlv.h: Move to ../common/.
2007-08-02 Werner Koch <wk@g10code.com>
* scdaemon.c: Include gc-opt-flags.h and remove their definition
here.
2007-08-01 Werner Koch <wk@g10code.com>
* apdu.c (send_le): Implement exact length hack. Suggested by
Sten Lindgren.
2007-07-05 Werner Koch <wk@g10code.com>
* command.c (has_option_name, skip_options): New.
(cmd_genkey): Add option --timestamp.
(cmd_writekey): Enter confidential mode while inquiring the key data.
* app.c (app_genkey): Add arg CREATETIME.
* app-common.h (app_ctx_s): Likewise
* app-openpgp.c (do_genkey): Ditto. Use it.
2007-07-04 Werner Koch <wk@g10code.com>
* command.c (cmd_getinfo): New subcommand "version".
* scdaemon.c (TIMERTICK_INTERVAL): New.
(handle_connections) [W32]: Enable a dummy sigs event.
(handle_connections): Use a proper count for select and not
FD_SETSIZE.
(fixed_gcry_pth_init, main): Kludge to fix pth initialization.
2007-06-21 Werner Koch <wk@g10code.com>
* scdaemon.h (ctrl_t): Remove. It is now declared in ../common/util.h.
2007-06-18 Marcus Brinkmann <marcus@g10code.de>
* scdaemon.c (main): Percent escape output of --gpgconf-list.
2007-06-12 Werner Koch <wk@g10code.com>
* scdaemon.c (main): Replace some calls by init_common_subsystems.
2007-06-11 Werner Koch <wk@g10code.com>
* Makefile.am (scdaemon_LDADD): Use libcommonpth macro.
* command.c (initialize_module_command): New.
* scdaemon.c (main) [W32]: Do not use sigpipe code.
(main): Call initialize_module_command.
2007-06-06 Werner Koch <wk@g10code.com>
* app-openpgp.c (do_sign): Fix arithmetic on void*.
* app.c (dump_mutex_state) [W32]: Handle the W32Pth case.
* apdu.c: Remove dynload.h.
* scdaemon.c (i18n_init): Remove.
2007-04-20 Werner Koch <wk@g10code.com>
* sc-copykeys.c (my_gcry_logger): Removed.
(main): Call setup_libgcrypt_logging helper.
* scdaemon.c (my_gcry_logger): Removed.
(main): Call setup_libgcrypt_logging helper.
2007-04-03 Werner Koch <wk@g10code.com>
* command.c (cmd_getinfo): New subcommand "reader_list".
* ccid-driver.c (scan_or_find_devices): Ignore EBUSY in scan mode
for special transports.
2007-03-07 Werner Koch <wk@g10code.com>
* app-dinsig.c: Include i18n.h.
(verify_pin): Support PIN pads.
* app-nks.c (verify_pin): Ditto.
* ccid-driver.c (bulk_in): Handle time extension before checking
the message type.
(ccid_transceive_secure): Support the Cherry XX44 keyboard.
Kudos to the nice folks at Cherry for helping with that.
2007-02-18 Werner Koch <wk@g10code.com>
* scdaemon.c (DEFAULT_PCSC_DRIVER): Add a default for OS X.
2007-01-25 Werner Koch <wk@g10code.com>
* Makefile.am (scdaemon_LDADD): Added LIBICONV. Noted by Billy
Halsey.
2006-12-21 Werner Koch <wk@g10code.com>
* app-openpgp.c (verify_chv2): Factored most code out into...
(verify_a_chv): ... new.
(do_sign): Factored verification code out to new function and
take care of a keypad entered PIN.
(compare_fingerprint): Print an additional diagnostic.
2006-11-28 Werner Koch <wk@g10code.com>
* apdu.c (send_le, apdu_send_direct): Increase RESULTLEN to 258 to
allow for full 256 byte and the status word. This might break
some old PC/SC drivers or cards, but we will see. Suggested by
Kenneth Wang.
2006-11-23 Werner Koch <wk@g10code.com>
* command.c (scd_command_handler): Fixed use of CTRL.
2006-11-21 Werner Koch <wk@g10code.com>
* Makefile.am (libexec_PROGRAMS): Put pscs-wrapper into libexec.
Renamed to gnupg-pcsc-wrapper.
* apdu.c (open_pcsc_reader): Use GNUPG_LIBEXECDIR to accces the
wrapper. Suggested by Eric Dorland.
2006-11-20 Werner Koch <wk@g10code.com>
* app-openpgp.c (verify_chv2): Support for keypads (only CHV2).
* ccid-driver.c (ccid_transceive_secure): Made it work for Kaan
and SCM.
2006-11-17 Werner Koch <wk@g10code.com>
* ccid-driver.c (scan_or_find_devices): Use DEBUGOUT_2 instead of
log_debug. Removed few other log_debug.
* iso7816.c (iso7816_check_keypad): Allow for a SW of 0.
* command.c (pin_cb): New mode to prompt for a keypad entry.
* scdaemon.c (main) <gpgconf-list>: Add disable-keypad.
2006-11-15 Werner Koch <wk@g10code.com>
* app-p15.c (read_ef_odf): Cast one printf arg.
* scdaemon.h (struct server_control_s): Add field THREAD_STARTUP.
* command.c (scd_command_handler): Add new arg CTRL.
* scdaemon.c (scd_init_default_ctrl): Made static.
(scd_deinit_default_ctrl): New.
(start_connection_thread): Call init/deinit of ctrl.
(handle_connections): Allocate CTRL.
* apdu.c (PCSC_ERR_MASK): New.
(reset_pcsc_reader, pcsc_get_status, pcsc_send_apdu)
(close_pcsc_reader, open_pcsc_reader): Use it after shifting error
values. Reported by Henrik Nordstrom. Fixes bug #724.
2006-10-24 Werner Koch <wk@g10code.com>
* scdaemon.h (GCRY_MD_USER_TLS_MD5SHA1): New.
(MAX_DIGEST_LEN): Increased to 36.
* app-p15.c (do_sign): Support for TLS_MD5SHA1.
(do_auth): Detect TLS_MD5SHA1.
(do_sign): Tweaks for that digest.
2006-10-23 Werner Koch <wk@g10code.com>
* scdaemon.c (main): New command --gpgconf-test.
2006-10-17 Werner Koch <wk@g10code.com>
* Makefile.am (scdaemon_LDADD): Link against libcommonpth.
2006-10-12 Werner Koch <wk@g10code.com>
* apdu.c: Include pth.h after unistd.h for the sake of newer Pth
versions.
2006-10-11 Werner Koch <wk@g10code.com>
* app-openpgp.c (do_sign): Redirect to do_auth for OpenPGP.3.
2006-10-06 Werner Koch <wk@g10code.com>
* Makefile.am (AM_CFLAGS): Use PTH version of libassuan.
(scdaemon_LDADD): Ditto.
* scdaemon.h (send_status_info): Mark with sentinel attribute.
2006-10-02 Marcus Brinkmann <marcus@g10code.de>
* command.c (update_reader_status_file): Increase buffer of
NUMBUF2 (fixing typo).
2006-09-24 Marcus Brinkmann <marcus@g10code.de>
* app-openpgp.c (do_sign): Advance INDATA by the SHA1 resp. RMD160
prefix length.
2006-09-14 Werner Koch <wk@g10code.com>
Replaced all call gpg_error_from_errno(errno) by
gpg_error_from_syserror().
* command.c (scd_command_handler): Replaced
init_connected_socket_server by init_socket_server_ext.
2006-09-07 Werner Koch <wk@g10code.com>
* command.c (update_reader_status_file): Execute an event handler
if available.
2006-09-06 Werner Koch <wk@g10code.com>
* apdu.c (pcsc_end_transaction):
* pcsc-wrapper.c (pcsc_end_transaction: Fixed dclaration.
Reported by Bob Dunlop.
* scdaemon.h (CTRL,APP): Removed and changed everywhere to
ctrl_t/app_t.
Replaced all Assuan error codes by libgpg-error codes. Removed
all map_to_assuan_status and map_assuan_err.
* scdaemon.c (main): Call assuan_set_assuan_err_source to have Assuan
switch to gpg-error codes.
* command.c (set_error): Adjusted.
2006-09-02 Marcus Brinkmann <marcus@g10code.de>
* command.c (get_reader_slot): Return the slot_table index, not
the APDU slot number.
(update_reader_status_file): Use the slot_table index in the
update_card_removed invocation.
2006-09-01 Marcus Brinkmann <marcus@g10code.de>
* command.c (cmd_getinfo): Handle status command.
2006-08-30 Marcus Brinkmann <marcus@g10code.de>
* command.c (do_reset): Delay resetting CTRL->reader_slot until
after update_card_removed invocation.
2006-08-28 Marcus Brinkmann <marcus@g10code.de>
* app-openpgp.c (do_decipher, do_sign): Allow "OPENPGP.2"
resp. "OPENPGP.1" for KEYIDSTR.
2006-08-21 Werner Koch <wk@g10code.com>
* pcsc-wrapper.c (handle_open, handle_close): Reset card and
protocol on error/close.
(handle_status): Don't set the state if the state is unknown.
(handle_reset): Ignore an error if already disconnected. May
happen due to system wake-up after hibernation. Suggested by Bob
Dunlop.
2006-06-28 Werner Koch <wk@g10code.com>
* app-openpgp.c (do_writekey): Fixed computation of memmove
length. This led to garbled keys if E was larger than one byte.
Thanks to Achim Pietig for hinting at the garbled E.
2006-06-09 Marcus Brinkmann <marcus@g10code.de>
* Makefile.am (scdaemon_LDADD): Add $(NETLIBS).
2006-04-14 Marcus Brinkmann <marcus@g10code.de>
* app.c (select_application): Cover up a slot mismatch error in
case it happens (it shouldn't happen).
(release_application): Use APP->slot. Lock the reader.
(application_notify_card_removed): Lock the reader.
2006-04-11 Werner Koch <wk@g10code.com>
* command.c (hex_to_buffer): New.
(cmd_apdu): New.
2006-04-03 Werner Koch <wk@g10code.com>
* scdaemon.c [__GLIBC__]: Default to libpcsclite.so.1.
2006-03-21 Werner Koch <wk@g10code.com>
* command.c (cmd_pksign): Add --hash option.
2006-03-01 Werner Koch <wk@g10code.com>
* command.c (status_file_update_lock): New.
(scd_update_reader_status_file): Use lock and factor existing code
out to ..
(update_reader_status_file): .. this.
(do_reset): Use the lock and call update_reader_status_file.
2006-02-20 Werner Koch <wk@g10code.com>
* apdu.c (open_pcsc_reader): Fixed double free. Thanks to Moritz.
2006-02-09 Werner Koch <wk@g10code.com>
* command.c (get_reader_slot, do_reset)
(scd_update_reader_status_file): Rewrote.
* app.c (release_application): Factored code out to ..
(deallocate_app): new function.
(select_application): Introduce new saved application stuff.
(application_notify_card_removed): New.
* command.c (update_card_removed): Call it here.
(do_reset): And here.
* app.c (check_application_conflict): New.
* command.c (open_card): Use it here.
(cmd_restart): New command.
* command.c (cmd_lock): Fixed --wait option to actually terminate.
2006-02-08 Werner Koch <wk@g10code.com>
* ccid-driver.c (ccid_get_atr): Read Parameter and select T=1
using these parameters.
(scan_or_find_devices): Check for NULL r_fd.
2006-02-02 Werner Koch <wk@g10code.com>
* ccid-driver.c (special_transport): New
(ccid_open_reader, do_close_reader, ccid_shutdown_reader)
(bulk_out, bulk_in): Add support for CardMan 4040 reader.
* ccid-driver.c (scan_or_find_devices): Factored most code out to
(scan_or_find_usb_device): .. new.
(make_reader_id): Fixed vendor mask.
2006-01-01 Werner Koch <wk@g10code.com>
* app-openpgp.c (do_sign): Give user error if hash algorithm is
not supported by the card.
2005-12-06 Werner Koch <wk@g10code.com>
* apdu.c (open_pcsc_reader): Check that pcsc-wrapper is actually
installed.
2005-11-23 Werner Koch <wk@g10code.com>
* app-nks.c (verify_pin): Give a special error message for a Nullpin.
2005-10-29 Werner Koch <wk@g10code.com>
* ccid-driver.c (send_escape_cmd): New args RESULT, RESULTLEN and
RESULTMAX. Changed all callers.
(ccid_transceive_escape): New.
2005-10-27 Werner Koch <wk@g10code.com>
* apdu.c [__CYGWIN__]: Make cygwin environment similar to _WIN32.
Suggested by John P. Clizbe.
* scdaemon.c [__CYGWIN__]: Set default PC/SC driver to winscard.dll.
2005-10-19 Werner Koch <wk@g10code.com>
* ccid-driver.h (CCID_DRIVER_ERR_NO_KEYPAD): New.
* apdu.h (SW_HOST_NO_KEYPAD): New.
* iso7816.h (struct iso7816_pininfo_s): New.
* iso7816.c (map_sw): Support new code.
(iso7816_check_keypad): New.
(iso7816_verify_kp, iso7816_change_reference_data_kp)
(iso7816_reset_retry_counter_kp): New. Extended versions of the
original functions.
* apdu.c (host_sw_string): Support new code.
(reader_table_s): New field CHECK_KEYPAD.
(new_reader_slot, open_ct_reader, open_pcsc_reader)
(open_ccid_reader, open_rapdu_reader): Initialize it.
(check_ccid_keypad): New.
(apdu_check_keypad): New.
(apdu_send_le): Factored all code out to ...
(send_le): .. new. Takes an additional arg; changed all callers
of the orginal function to use this one with a NULL for the new
arg.
(apdu_send_simple_kp): New.
(ct_send_apdu, pcsc_send_apdu, my_rapdu_send_apdu)
(send_apdu_ccid): New arg PININFO.
(send_apdu_ccid): Use the new arg.
* scdaemon.c: New option --disable-keypad.
2005-10-08 Marcus Brinkmann <marcus@g10code.de>
* Makefile.am (scdaemon_LDADD): Add ../gl/libgnu.a after
../common/libcommon.a.
2005-09-20 Werner Koch <wk@g10code.com>
* app-dinsig.c (verify_pin): Try ISO 9564 BCD encoding.
* iso7816.c (iso7816_select_application): Add arg FLAGS. Changed
all callers to pass 0.
* app-openpgp.c (app_select_openpgp): But this one requires a
special flag.
* app-p15.c (app_select_p15): Don't use select application for the
BELPIC.
2005-09-09 Werner Koch <wk@g10code.com>
* pcsc-wrapper.c (main): Removed bogus free.
* app-p15.c (do_auth): New.
(do_getattr): New attribs $AUTHKEYID and $DISPSERIALNO.
* app-openpgp.c (do_getattr): Ditto.
2005-09-08 Werner Koch <wk@g10code.com>
* app-openpgp.c (do_getattr): New key $AUTHKEYID.
2005-09-06 Werner Koch <wk@g10code.com>
* app-p15.c (do_sign): Tweaked for BELPIC cards.
(read_home_df): New arg R_BELPIC.
(app_select_p15): Set card type for BELPIC.
2005-09-05 Werner Koch <wk@g10code.com>
* iso7816.c (iso7816_select_path): New.
* app-p15.c (select_ef_by_path): Allow for direct path selection.
(app_select_p15): Try using the Belgian variant of pkcs#15.
(read_home_df): New.
(read_ef_odf): Generalized.
(read_ef_tokeninfo): New.
(read_p15_info): Set serialnumber from TokenInfo.
(app_select_p15): Don't munge serialNumber - that must be done
only once.
* iso7816.c (iso7816_read_binary): Use Le=0 when reading all
data. Handle 6C00 error and take 6B00 as indication for EOF.
* apdu.h (SW_EXACT_LENGTH_P): New.
* apdu.c (new_reader_slot, reset_pcsc_reader, pcsc_get_status)
(open_pcsc_reader): Set new reader state IS_T0.
(apdu_send_le): When doing T=0 make sure not to send Lc and Le.
Problem reported by Carl Meijer.
(apdu_send_direct): Initialize RESULTLEN.
* pcsc-wrapper.c (handle_status): Return the current protocol as
a new third word.
2005-08-05 Werner Koch <wk@g10code.com>
* apdu.c (open_rapdu_reader): Set the reader number.
2005-07-05 Werner Koch <wk@g10code.com>
* app-openpgp.c (do_readkey): Return a mallcoed copy of the key as
required by the description. Thanks to Moritz for tracking this
problem down.
2005-06-21 Werner Koch <wk@g10code.com>
* scdaemon.c (main): ifdef call to ccid_set_debug_level.
* apdu.c (reset_pcsc_reader, open_pcsc_reader): Cast size_t to
ulong for printf.
2005-06-06 Werner Koch <wk@g10code.com>
* scdaemon.c (main): New option --debug-allow-core-dump.
2005-06-03 Werner Koch <wk@g10code.com>
* scdaemon.c (handle_connections): Make sure that the signals we
are handling are not blocked.Block signals while creating new
threads.
(handle_connections): Include the file descriptor into the name of
the thread.
2005-06-02 Werner Koch <wk@g10code.com>
* app.c (app_dump_state, dump_mutex_state): New.
* scdaemon.c (handle_signal): Print it on SIGUSR1.
* app-openpgp.c (do_writekey): Typo fix.
* command.c (open_card): Check for locked state even if an
application context is available.
* app-common.h: Add REF_COUNT field.
* app.c (release_application, select_application): Implement
reference counting to share the context beween connections.
* app.c (lock_reader, unlock_reader): Take SLOT instead of APP as
argument. Changed all callers.
(select_application): Unlock the reader on error. This should fix
the hangs I noticed last week.
* scdaemon.h: Removed card_ctx_t cruft.
2005-06-01 Werner Koch <wk@g10code.com>
* scdaemon.c: Include mkdtemp.h.
2005-05-31 Werner Koch <wk@g10code.com>
* tlv.c [GNUPG_MAJOR_VERSION==1]: Define constants instead of
including a gnupg 1.4 header.
2005-05-30 Werner Koch <wk@g10code.com>
* tlv.c: Add hack to compile without gpg-error.h when used with
GnuPG 1.4.
2005-05-23 Werner Koch <wk@g10code.com>
* Makefile.am: Do not build sc-copykeys anymore.
* app-openpgp.c (app_openpgp_storekey, app_openpgp_readkey)
(app_openpgp_cardinfo): Removed.
* ccid-driver.c (parse_ccid_descriptor): SCR335 FW version 5.14 is
good.
(do_close_reader): Never do a reset. The caller should instead
make sure that the reader has been closed properly. The new retry
code in ccid_slot_status will make sure that the readersatrts up
fine even if the last process didn't closed the USB connection
properly.
(ccid_get_atr): For certain readers try switching to ISO mode.
Thanks to Ludovic Rousseau for this hint and the magic numbers.
(print_command_failed): New.
(bulk_in): Use it here. Add new arg NO_DEBUG.
(ccid_slot_status): Disabled debugging.
2005-05-21 Werner Koch <wk@g10code.com>
* scdaemon.c (handle_signal): Print thread info on SIGUSR1.
2005-05-20 Werner Koch <wk@g10code.com>
* ccid-driver.c: Replaced macro DEBUG_T1 by a new debug level.
(parse_ccid_descriptor): Mark SCR335 firmware version 5.18 good.
(ccid_transceive): Arghhh. The seqno is another bit in the
R-block than in the I block, this was wrong at one place.
* scdaemon.c: New options --debug-ccid-driver and
--debug-disable-ticker.
* app-openpgp.c (do_genkey, do_writekey): Factored code to check
for existing key out into ..
(does_key_exist): .. New function.
2005-05-19 Werner Koch <wk@g10code.com>
* tlv.c (parse_sexp): New.
* command.c (cmd_writekey): New.
* app.c (app_writekey): New.
* app-common.c (app_t): Add function ptr WRITEKEY.
* app-openpgp.c (do_writekey): New.
* app-openpgp.c (do_readkey) [GNUPG_MAJOR_VERSION==1]: Return error.
* app-common.h (app_t) [GNUPG_MAJOR_VERSION==1]: Add a field to
store the Assuan context.
2005-05-17 Werner Koch <wk@g10code.com>
* scdaemon.c: Removed non-pth code paths.
(create_socket_name, create_server_socket): New. Taken from
../agent/gpg-agent.
(cleanup): Changed to adjust for SOCKET_NAME now being malloced.
(ticker_thread): Always use pth_event_occurred; it is again
defined for all decent PTH versions.
(handle_connections): New. Based on the gpg-agent code.
(start_connection_thread): Ditto.
(ticker_thread): Removed.
(cleanup_sh): Removed.
(main): Run the handler for the pipe server in a separate
thread. This replaces the old ticker thread.
(scd_get_socket_name): New.
* command.c (cmd_getinfo): New command GETINFO.
(scd_command_handler): Renamed argument and changed code to use an
already connected FD.
2005-05-15 Werner Koch <wk@g10code.com>
* app.c, app-common.h, app-nks.c, app-p15.c, app-dinsig.c
* app-openpgp.c: Change most function return types from int to
gpg_error_t.
* command.c (pin_cb): Ditto.
* sc-copykeys.c (pincb): Ditto.
* app.c (lock_reader, unlock_reader): New. Changed call handler
wrappers to make use of these functions.
2005-05-07 Werner Koch <wk@g10code.com>
* ccid-driver.c (do_close_reader): Don't do a reset before close.
Some folks reported that it makes the SCR335 hang less often.
Look at the source on how to re-enable it.
2005-04-27 Werner Koch <wk@g10code.com>
* app-p15.c (micardo_mse): New.
(do_sign): Call it.
* iso7816.c (iso7816_manage_security_env): Allow passing DATA as
NULL to indicate an empty Lc.
* tlv.c (find_tlv): Check that a found object fits into the
buffer.
(find_tlv_unchecked): New as replacement for the old non-checking
variant.
* app.c (select_application): Keep on using the non-checking
variant.
* app-openpgp.c (get_one_do, dump_all_do): Ditto.
Removal of the old OpenSC based code.
* app-p15.c: New. Basic support for pkcs15 cards without OpenSC.
There are quite a couple of things missing but at least I can use
my old TCOS cards from the Aegypten-1 development for signing.
* app.c (select_application): Detect pkcs15 applications.
* Makefile.am (scdaemon_SOURCES): Removed card.c, card-common.h
and card-p15.c because they are now obsolete. Added app-p15.c.
Removed all OpenSC stuff.
* command.c (do_reset, open_card, cmd_serialno, cmd_learn)
(cmd_readcert, cmd_readkey, cmd_pksign, cmd_pkdecrypt): Removed
all special cases for the old card.c based mechanisms.
* scdaemon.c, apdu.c: Removed all special cases for OpenSC.
2005-04-20 Werner Koch <wk@g10code.com>
* command.c: Use GPG_ERR_LOCKED instead of EBUSY.
2005-04-14 Werner Koch <wk@g10code.com>
* app-openpgp.c (retrieve_key_material): Rewritten. Return a
proper error code.
(retrieve_next_token): Removed.
(retrieve_fpr_from_card): Rewritten to make use of DO caching and
to take the KEYNO as arg.
(get_public_key): Renamed variable for clarity.
2005-04-12 Werner Koch <wk@g10code.com>
Basic support for several sessions.
* command.c (scd_command_handler): Replace the primary_connection
stuff by a real connection list. Release the local context on
exit.
(scd_update_reader_status_file): Update accordingly. Send signal
to all connections who registered an event signal.
(cmd_lock, cmd_unlock, register_commands): New commands LOCK and
UNLOCK.
(cmd_setdata, cmd_pksign, cmd_pkauth, cmd_pkdecrypt, cmd_setattr)
(cmd_genkey, cmd_passwd, cmd_checkpin): Return an error if reader
is locked.
(do_reset): Handle locking.
(open_card): Ditto. Share the reader slot with other sessions.
(get_reader_slot): New.
(update_card_removed): New. Use it in the TEST_CARD_REMOVAL macro.
2005-04-07 Werner Koch <wk@g10code.com>
* app-openpgp.c (do_check_pin): Add hack to allow verification of
CHV3.
(get_public_key): Don't use gcry functions to create S-expressions.
(do_deinit, do_readkey, do_genkey, send_keypair_info): Adjust for
above change.
2005-03-29 Moritz Schulte <moritz@g10code.com>
* app-openpgp.c (retrieve_fpr_from_card): New function.
(retrieve_next_token): New function.
(retrieve_key_material): New function.
(get_public_key): Implement retrival of key through expernal
helper (gpg) in case the openpgp card is not cooperative enough.
2005-03-16 Werner Koch <wk@g10code.com>
* ccid-driver.c (parse_ccid_descriptor): Make SCM workaround
reader type specific.
(scan_or_find_devices): Do not check the interface subclass in the
SPR532 kludge, as this depends on the firmware version.
(ccid_get_atr): Get the Slot status first. This solves the
problem with readers hanging on recent Linux 2.6.x.
(bulk_in): Add argument TIMEOUT and changed all callers to pass an
appropriate one. Change the standard timeout from 10 to 5 seconds.
(ccid_slot_status): Add a retry code with an initial short timeout.
(do_close_reader): Do an usb_reset before closing the reader.
2005-02-25 Werner Koch <wk@g10code.com>
* app-openpgp.c (get_public_key): Make sure not to return negative
numbers.
(do_sign): Allow passing of indata with algorithm prefix.
(do_auth): Allow OPENPGP.3 as an alternative ID.
* app.c (app_getattr): Return just the S/N but not the timestamp.
2005-02-24 Werner Koch <wk@g10code.com>
* app.c (app_getattr): Return APPTYPE or SERIALNO type even if the
application does dot support the getattr call.
* app-openpgp.c (get_one_do): Never try to get a non cacheable
object from the cache.
(get_one_do): Add new arg to return an error code. Changed all
callers.
(do_getattr): Let it return a proper error code.
* app.c (select_application): Return an error code and the
application context in an new arg.
* command.c (open_card): Adjusted for that. Don't use the
fallback if no card is present. Return an error if the card has
been removed without a reset.
(do_reset, cmd_serialno): Clear that error flag.
(TEST_CARD_REMOVAL): New. Use it with all command handlers.
(scd_update_reader_status_file): Set the error flag on all changes.
* scdaemon.c (ticker_thread): Termintate if a shutdown is pending.
* apdu.c: Added some PCSC error codes.
(pcsc_error_to_sw): New.
(reset_pcsc_reader, pcsc_get_status, pcsc_send_apdu)
(open_pcsc_reader): Do proper error code mapping.
2005-03-16 Werner Koch <wk@g10code.com>
* ccid-driver.c (parse_ccid_descriptor): Make SCM workaround
reader type specific.
(scan_or_find_devices): Do not check the interface subclass in the
SPR532 kludge, as this depends on the firmware version.
(ccid_get_atr): Get the Slot status first. This solves the
problem with readers hanging on recent Linux 2.6.x.
2005-02-22 Werner Koch <wk@g10code.com>
* app-openpgp.c (app_local_s): New field PK.
(do_deinit, do_genkey, app_openpgp_storekey): Clear it.
(get_public_key, send_keypair_info): New.
(do_learn_status): Send KEYPAIR info
* app-common.h (app_ctx_t): Add function pointer READKEY.
* app.c (app_readkey): New.
* command.c (cmd_readkey): Use READKEY function if possible.
2005-01-26 Werner Koch <wk@g10code.com>
* ccid-driver.c (parse_ccid_descriptor): Need the CSM workaround
also for newer firmware versions. Need to get a list of fixed
firmware versions and use that.
2005-01-25 Werner Koch <wk@g10code.com>
* apdu.c (apdu_send_le, apdu_send_direct): Fix some compiler
warnings.
* app-openpgp.c (get_cached_data): New arg GET_IMMEDIATE to bypass
the cache. Changed all callers.
(get_one_do): Bypass the cache if the value would have been read
directly for v1.1 cards.It makes things a bit slower but obnly for
1.0 cards and there are not that many cards out in the wild. This
is required to fix a caching bug when generating new keys; as a
side effect of the retrieval of the the C4 DO from the 6E DO the
cached fingerprint will get updated to the old value and later
when signing the generated key the checking of the fingerprint
fails because it won't match the new one. Thanks to Moritz for
analyzing this problem.
(verify_chv3): Removed the CHV status reread logic because we
won't cache the C4 DO anymore.
2004-12-28 Werner Koch <wk@g10code.com>
* ccid-driver.c (find_endpoint): New.
(scan_or_find_devices): Add new args to return endpoint info and
interface number.
(ccid_open_reader, ccid_shutdown_reader): Take care of these new
args.
(bulk_in, bulk_out): Use the correct endpoints.
(ccid_transceive_apdu_level): New.
(ccid_transceive): Divert to above.
(parse_ccid_descriptor): Allow APDU level exchange mode.
(do_close_reader): Pass the interface number to usb_release_interface.
2004-12-21 Werner Koch <wk@g10code.com>
* scdaemon.c (main): Use default_homedir().
2004-12-18 Werner Koch <wk@g10code.com>
* scdaemon.c (main) [W32]: Remove special Pth initialize..
* scdaemon.h (map_assuan_err): Define in terms of
map_assuan_err_with_source.
2004-12-15 Werner Koch <wk@g10code.com>
* scdaemon.c [W32]: Various hacks to make it run under W32.
* command.c (scd_update_reader_status_file) [W32]: Don't use kill.
* apdu.c [W32]: Disable use of pcsc_wrapper.
* Makefile.am (scdaemon_LDADD): Reorder libs.
(sc_copykeys_LDADD): Add libassuan because it is needed for W32.
2004-12-06 Werner Koch <wk@g10code.com>
* Makefile.am (pkglib_PROGRAMS): Build only for W32.
2004-10-22 Werner Koch <wk@g10code.com>
* app-openpgp.c (verify_chv3): The minium length for CHV3 is
8. Changed string to match the other ones.
2004-10-21 Werner Koch <wk@g10code.com>
* app-openpgp.c (do_sign): Replace asprintf by direct allocation.
This avoids problems with missing vasprintf implementations in
gnupg 1.4.
* app-common.h (app_openpgp_storekey: Add prototype.
2004-10-20 Werner Koch <wk@g10code.com>
* sc-investigate: Removed.
* Makefile.am (sc_investigate): Removed.
* pcsc-wrapper.c (load_pcsc_driver): Load get_status_change func.
(handle_open): Succeed even without a present card.
(handle_status, handle_reset): New.
* apdu.c (apdu_open_reader): Load pcsc_get_status_change fucntion.
(pcsc_get_status): Implemented.
(reset_pcsc_reader): Implemented.
(open_pcsc_reader): Succeed even with no card inserted.
(open_ccid_reader): Set LAST_STATUS.
* iso7816.c (iso7816_select_application): Always use 0 for P1.
2004-10-18 Werner Koch <wk@g10code.com>
* ccid-driver.c (ccid_get_atr): Reset T=1 state info.
2004-10-14 Werner Koch <wk@g10code.com>
* app-openpgp.c (parse_login_data): New.
(app_select_openpgp): Call it.
(do_setattr): Reparse it after change.
2004-10-06 Werner Koch <wk@g10code.de>
* ccid-driver.c (ccid_open_reader): Store the vendor ID.
(ccid_transceive_secure): New.
(parse_ccid_descriptor): Workaround for an SCM reader problem.
2004-10-04 Werner Koch <wk@g10code.de>
* ccid-driver.c (send_escape_cmd): New.
2004-09-30 Werner Koch <wk@g10code.com>
* Makefile.am: Adjusted for gettext 0.14.
* app-openpgp.c (do_sign): Add the error string to the verify
failed messages.
2004-09-27 Werner Koch <wk@g10code.com>
From gnupg 1.3
* app-openpgp.c: Made all strings translatable.
(verify_chv3) [GNUPG_MAJOR_VERSION]: Make opt.allow_admin
available for use in gnupg 2.
(verify_chv3): Reimplemented countdown showing to use only
functions from this module. Flush the CVH status cache on a
successful read.
(get_one_do): Hack to bypass the cache for cards versions > 1.0.
(store_fpr): Store the creation date for card version > 1.0.
* app-openpgp.c (app_openpgp_storekey): Call flush_cache.
(get_cached_data): Move local data initialization to ..
(app_select_openpgp): .. here. Read some flags for later use.
(do_getattr): New read-only attribute EXTCAP.
* apdu.c (open_pcsc_reader): Do not print empty reader string.
* ccid-driver.c (do_close_reader): Factored some code out from ...
(ccid_close_reader): ..here.
(ccid_shutdown_reader): New.
* apdu.c (apdu_shutdown_reader): New.
(shutdown_ccid_reader): New.
* apdu.c (open_ccid_reader): New arg PORTSTR. Pass it to
ccid_open_reader.
(apdu_open_reader): Pass portstr to open_ccid_reader.
(apdu_open_reader): No fallback if a full CCID reader id has been
given.
* ccid-driver.c (ccid_get_reader_list): New.
(ccid_open_reader): Changed API to take a string for the reader.
Removed al the cruft for the libusb development vesion which seems
not to be maintained anymore and there are no packages anyway.
The stable library works just fine.
(struct ccid_reader_id_s): Deleted and replaced everywhere by a
simple string.
(usb_get_string_simple): Removed.
(bulk_in): Do valgrind hack here and not just everywhere.
* ccid-driver.c (read_device_info): Removed.
(make_reader_id, scan_or_find_devices): New.
(ccid_open_reader): Simplified by make use of the new functions.
(ccid_set_debug_level): New. Changed the macros to make use of
it. It has turned out that it is often useful to enable debugging
at runtime so I added this option.
From gnupg 1.3 - David Shaw <dshaw@jabberwocky.com>
* app-openpgp.c (verify_chv3): Show a countdown of how many wrong
admin PINs can be entered before the card is locked.
* app-openpgp.c (get_cached_data): Avoid mallocing zero since it
breaks us when using --enable-m-guard.
* ccid-driver.c (usb_get_string_simple): Replacement function to
work with older libusb.
* ccid-driver.c (read_device_info): Fix segfault when usb device
is not accessible.
(ccid_open_reader): Allow working with an even older version of
libusb (usb_busses global instead of usb_get_busses()).
2004-09-11 Werner Koch <wk@g10code.com>
* app-openpgp.c (app_select_openpgp): Its app_munge_serialno and
not app_number_serialno.
2004-08-20 Werner Koch <wk@g10code.de>
* app.c (select_application): Fixed serial number extraction and
added the BMI card workaround.
(app_munge_serialno): New.
* app-openpgp.c (app_select_openpgp): Try munging serialno.
2004-08-05 Werner Koch <wk@g10code.de>
* scdaemon.c (main): New option --disable-application.
* app.c (is_app_allowed): New.
(select_application): Use it to check for disabled applications.
* ccid-driver.h (CCID_DRIVER_ERR_ABORTED): New.
* ccid-driver.c (ccid_open_reader): Support the stable 0.1 version
of libusb.
(ccid_get_atr): Handle short messages.
* apdu.c (my_rapdu_get_status): Implemented.
2004-07-27 Moritz Schulte <moritz@g10code.com>
* apdu.c: Include <signal.h>.
* Makefile.am: Use @DL_LIBS@ instead of -ldl.
2004-07-22 Werner Koch <wk@g10code.de>
* Makefile.am: Make OpenSC lib link after libgcrypt. Do not link
to pth.
* apdu.c: Don't use Pth if we use OpenSC.
* sc-investigate.c, scdaemon.c: Disable use of pth if OpenSC is used.
* scdaemon.c (main): Bumbed thread stack size up to 512k.
2004-07-16 Werner Koch <wk@gnupg.org>
* apdu.c (reader_table_s): Add function pointers for the backends.
(apdu_close_reader, apdu_get_status, apdu_activate)
(send_apdu): Make use of them.
(new_reader_slot): Intialize them to NULL.
(dump_ccid_reader_status, ct_dump_reader_status): New.
(dump_pcsc_reader_status): New.
(open_ct_reader, open_pcsc_reader, open_ccid_reader)
(open_osc_reader, open_rapdu_reader): Intialize function pointers.
(ct_activate_card, ct_send_apdu, pcsc_send_apdu, osc_send_apdu)
(error_string): Removed. Replaced by apdu_strerror.
(get_ccid_error_string): Removed.
(ct_activate_card): Remove the unused loop.
(reset_ct_reader): Implemented.
(ct_send_apdu): Activate the card if not yet done.
(pcsc_send_apdu): Ditto.
2004-07-15 Werner Koch <wk@gnupg.org>
* ccid-driver.h: Add error codes.
* ccid-driver.c: Implement more or less proper error codes all
over the place.
* apdu.c (apdu_send_direct): New.
(get_ccid_error_string): Add some error code mappings.
(send_apdu): Pass error codes along for drivers already supporting
them.
(host_sw_string): New.
(get_ccid_error_string): Use above.
(send_apdu_ccid): Reset the reader if it has not yet been done.
(open_ccid_reader): Don't care if the ATR can't be read.
(apdu_activate_card): New.
(apdu_strerror): New.
(dump_reader_status): Only enable it with opt.VERBOSE.
* iso7816.c (map_sw): Add mappings for the new error codes.
2004-07-02 Werner Koch <wk@gnupg.org>
* apdu.c (open_ct_reader, open_pcsc_reader, open_ccid_reader)
(reset_ccid_reader, open_osc_reader): Call dump_reader_status only
in verbose mode.
2004-07-01 Werner Koch <wk@gnupg.org>
* sc-investigate.c: Initialize Pth which is now required.
(interactive_shell): New command "readpk".
* app-openpgp.c (do_getattr): Fix for sending CA-FPR.
2004-06-30 Werner Koch <wk@gnupg.org>
* app-openpgp.c (app_openpgp_readkey): Fixed check for valid
exponent.
2004-06-18 Werner Koch <wk@g10code.com>
* sc-investigate.c (my_read_line): Renamed from read_line.
2004-06-16 Werner Koch <wk@gnupg.org>
* apdu.c (osc_get_status): Fixed type in function name. Noted by
Axel Thimm. Yes, I didn't tested it with OpenSC :-(.
2004-04-28 Werner Koch <wk@gnupg.org>
* app-openpgp.c (do_setattr): Sync FORCE_CHV1.
2004-04-27 Werner Koch <wk@gnupg.org>
* app-common.h: Do not include ksba.h for gnupg 1.
2004-04-26 Werner Koch <wk@gnupg.org>
* app-common.h: New members FNC.DEINIT and APP_LOCAL.
* app.c (release_application): Call new deconstructor.
* app-openpgp.c (do_deinit): New.
(get_cached_data, flush_cache_item, flush_cache_after_error)
(flush_cache): New.
(get_one_do): Replaced arg SLOT by APP. Make used of cached data.
(verify_chv2, verify_chv3): Flush some cache item after error.
(do_change_pin): Ditto.
(do_sign): Ditto.
(do_setattr): Flush cache item.
(do_genkey): Flush the entire cache.
(compare_fingerprint): Use cached data.
* scdaemon.c (main): Do the last change the usual way. This is so
that we can easily test for versioned config files above.
2004-04-26 Marcus Brinkmann <marcus@g10code.de>
* scdaemon.c (main): For now, always print default filename for
--gpgconf-list, and never /dev/null.
2004-04-21 Werner Koch <wk@gnupg.org>
* command.c (scd_update_reader_status_file): Send a signal back to
the client.
(option_handler): Parse the new event-signal option.
* scdaemon.c (handle_signal): Do not use SIGUSR{1,2} anymore for
changing the verbosity.
2004-04-20 Werner Koch <wk@gnupg.org>
* command.c (scd_update_reader_status_file): Write status files.
* app-help.c (app_help_read_length_of_cert): Fixed calculation of
R_CERTOFF.
* pcsc-wrapper.c: New.
* Makefile.am (pkglib_PROGRAMS): Install it here.
* apdu.c (writen, readn): New.
(open_pcsc_reader, pcsc_send_apdu, close_pcsc_reader): Use the
pcsc-wrapper if we are using Pth.
(apdu_send_le): Reinitialize RESULTLEN. Handle SW_EOF_REACHED
like SW_SUCCESS.
2004-04-19 Werner Koch <wk@gnupg.org>
* ccid-driver.c (parse_ccid_descriptor): Store some of the reader
features away. New arg HANDLE
(read_device_info): New arg HANDLE. Changed caller.
(bulk_in): Handle time extension requests.
(ccid_get_atr): Setup parameters and the IFSD.
(compute_edc): New. Factored out code.
(ccid_transceive): Use default NADs when required.
2004-04-14 Werner Koch <wk@gnupg.org>
* scdaemon.h (server_control_s): Add member READER_SLOT.
* scdaemon.c (scd_init_default_ctrl): Initialize READER_SLOT to -1.
* command.c (open_card): Reuse an open slot.
(reset_notify): Just reset the slot if supported by the reader.
(do_reset): Factored code from above out.
(scd_command_handler): Use it for cleanup.
* apdu.h: New pseudo stati SW_HOST_NOT_SUPPORTED,
SW_HOST_LOCKING_FAILED and SW_HOST_BUSY.
* iso7816.c (map_sw): Map it.
* ccid-driver.c (ccid_slot_status): Add arg STATUSBITS.
* apdu.c (apdu_get_status): New.
(ct_get_status, pcsc_get_status, ocsc_get_status): New stubs.
(get_status_ccid): New.
(apdu_reset): New.
(reset_ct_reader, reset_pcsc_reader, reset_osc_reader): New stubs.
(reset_ccid_reader): New.
(apdu_enum_reader): New.
* apdu.c (lock_slot, trylock_slot, unlock_slot): New helpers.
(new_reader_slot) [USE_GNU_PTH]: Init mutex.
(apdu_reset, apdu_get_status, apdu_send_le): Run functions
in locked mode.
* command.c (scd_update_reader_status_file): New.
* scdaemon.c (handle_tick): Call it.
2004-04-13 Werner Koch <wk@gnupg.org>
* scdaemon.c: Convert to a Pth application.
(handle_signal, ticker_thread, handle_tick): New.
(main): Fire up the ticker thread in server mode.
2004-03-23 Werner Koch <wk@gnupg.org>
* scdaemon.c (main) <gpgconf_list>: Fixed output for pcsc_driver.
2004-03-17 Werner Koch <wk@gnupg.org>
* tlv.c (parse_ber_header): Do not check for tag overflow - it
does not make sense. Simplified the check for length overflow.
* scdaemon.c (main) <gpgconf>: Fixed default value quoting.
2004-03-16 Werner Koch <wk@gnupg.org>
* app-dinsig.c: Implemented. Based on app-nks.c and card-dinsig.c
* app-nks.c (get_length_of_cert): Removed.
* app-help.c: New.
(app_help_read_length_of_cert): New. Code taken from above. New
optional arg R_CERTOFF.
* card-dinsig.c: Removed.
* card.c (card_get_serial_and_stamp): Do not bind to the old and
never finsiged card-dinsig.c.
* iso7816.c (iso7816_read_binary): Allow for an NMAX > 254.
2004-03-11 Werner Koch <wk@gnupg.org>
* scdaemon.h (out_of_core): Removed. Replaced callers by standard
gpg_error function.
* apdu.c, iso7816.c, ccid-driver.c [GNUPG_SCD_MAIN_HEADER]: Allow
to include a header defined by the compiler. This helps us to
reuse the source in other software.
2004-03-10 Werner Koch <wk@gnupg.org>
* iso7816.c (iso7816_read_record): New arg SHORT_EF. Changed all
callers.
2004-02-18 Werner Koch <wk@gnupg.org>
* sc-investigate.c (main): Setup the used character set.
* scdaemon.c (main): Ditto.
* scdaemon.c (set_debug): New. Add option --debug-level.
(main): Add option --gpgconf-list.
2004-02-12 Werner Koch <wk@gnupg.org>
* Makefile.am: Include cmacros.am for common flags.
2004-01-29 Werner Koch <wk@gnupg.org>
* command.c (reset_notify): Release the application context and
close the reader.
2004-01-28 Werner Koch <wk@gnupg.org>
* iso7816.c (iso7816_manage_security_env): New.
(iso7816_decipher): Add PADIND argument.
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
length and not the 64 bytes we used for testing.
* app-openpgp.c (verify_chv2, verify_chv3, do_sign): Check the
minimum length of the passphrase, so that we don't need to
decrement the retry counter.
2003-12-17 Werner Koch <wk@gnupg.org>
* card-p15.c (p15_enum_keypairs): Replaced KRC by RC.
* card-dinsig.c (dinsig_enum_keypairs): Ditto.
2003-12-16 Werner Koch <wk@gnupg.org>
* scdaemon.c (main): Set the prefixes for assuan logging.
2003-11-17 Werner Koch <wk@gnupg.org>
* scdaemon.c, scdaemon.h: New options --allow-admin and --deny-admin.
* app-openpgp.c (verify_chv3): Check it here.
2003-11-12 Werner Koch <wk@gnupg.org>
Adjusted for API changes in Libksba.
2003-10-30 Werner Koch <wk@gnupg.org>
* apdu.c (close_ct_reader, close_pcsc_reader): Implemented.
(get_ccid_error_string): New. Not very useful messages, though.
2003-10-25 Werner Koch <wk@gnupg.org>
* ccid-driver.c (ccid_open_reader): Return an error if no USB
devices are found.
* command.c (cmd_genkey, cmd_passwd): Fixed faulty use of
!spacep().
* apdu.c (apdu_open_reader): Hacks for PC/SC under Windows.
2003-10-20 Werner Koch <wk@gnupg.org>
* command.c (cmd_checkpin): New.
(register_commands): Add command CHECKPIN.
* app.c (app_check_pin): New.
* app-openpgp.c (check_against_given_fingerprint): New. Factored
out that code elsewhere.
(do_check_pin): New.
2003-10-10 Werner Koch <wk@gnupg.org>
* ccid-driver.c (ccid_close_reader): New.
* apdu.c (close_ccid_reader, close_ct_reader, close_csc_reader)
(close_osc_reader, apdu_close_reader): New. Not all are properly
implemented yet.
2003-10-09 Werner Koch <wk@gnupg.org>
* ccid-driver.c (ccid_transceive): Add T=1 chaining for sending.
2003-10-08 Werner Koch <wk@gnupg.org>
* app-openpgp.c (do_getattr): Support SERIALNO and AID.
2003-10-01 Werner Koch <wk@gnupg.org>
* ccid-driver.c: Detect GnuPG 1.3 and include appropriate files.
* apdu.c: Ditto.
* app-openpgp.c: Ditto.
* iso7816.c: Ditto.
(generate_keypair): Renamed to ..
(do_generate_keypair): .. this.
* app-common.h [GNUPG_MAJOR_VERSION]: New.
* iso7816.h [GNUPG_MAJOR_VERSION]: Include cardglue.h
2003-09-30 Werner Koch <wk@gnupg.org>
* command.c (cmd_getattr): New command GETATTR.
* app.c (app_setattr): New.
(do_getattr): New.
(do_learn_status): Reimplemented in terms of do_getattr.
* app-openpgp.c (do_change_pin): Make sure CVH1 and CHV2 are
always synced.
(verify_chv2, verify_chv3): New. Factored out common code.
(do_setattr, do_sign, do_auth, do_decipher): Change the names of
the prompts to match that we have only 2 different PINs.
(app_select_openpgp): Check whether the card enforced CHV1.
(convert_sig_counter_value): New. Factor out code from
get_sig_counter.
2003-09-28 Werner Koch <wk@gnupg.org>
* app-openpgp.c (dump_all_do): Use gpg_err_code and not gpg_error.
2003-09-19 Werner Koch <wk@gnupg.org>
* ccid-driver.c (parse_ccid_descriptor): New.
(read_device_info): New.
(ccid_open_reader): Check that the device has all required features.
2003-09-06 Werner Koch <wk@gnupg.org>
* scdaemon.c (main): --pcsc-driver again defaults to pcsclite.
David Corcoran was so kind to remove the GPL incompatible
advertisng clause from pcsclite.
* apdu.c (apdu_open_reader): Actually make pcsc-driver option work.
2003-09-05 Werner Koch <wk@gnupg.org>
* ccid-driver.c: More work, data can now actually be retrieved.
* ccid-driver.c, ccid-driver.h: Alternativley allow use under BSD
conditions.
2003-09-02 Werner Koch <wk@gnupg.org>
* scdaemon.c, scdaemon.h: New option --pcsc-ccid.
* ccid-driver.c, ccid-driver.h: New but far from being useful.
* Makefile.am: Add above.
* apdu.c: Add support for that ccid driver.
2003-08-26 Timo Schulz <twoaday@freakmail.de>
* apdu.c (new_reader_slot): Only set 'is_osc' when OpenSC
is used.
2003-08-25 Werner Koch <wk@gnupg.org>
* command.c (cmd_setattr): Use a copy of LINE.
(cmd_genkey): Use a copy of KEYNO.
(cmd_passwd): Use a copy of CHVNOSTR.
(cmd_pksign, cmd_pkauth, cmd_pkdecrypt): s/strdup/xtrystrdup/.
2003-08-19 Werner Koch <wk@gnupg.org>
* scdaemon.c, scdaemon.h: New option --pcsc-driver.
* apdu.c (apdu_open_reader): Use that option here instead of a
hardcoded one.
2003-08-18 Werner Koch <wk@gnupg.org>
* Makefile.am: Add OPENSC_LIBS to all programs.
* scdaemon.c, scdaemon.h: New option --disable-opensc.
* card.c (card_open): Implement it.
* apdu.c (open_osc_reader, osc_send_apdu): New.
(apdu_open_reader) [HAVE_OPENSC]: Use the opensc driver if not
disabled.
(error_string) [HAVE_OPENSC]: Use sc_strerror.
(send_apdu) [HAVE_OPENSC]: Call osc_apdu_send.
* card-p15.c (p15_enum_keypairs, p15_prepare_key): Adjusted for
libgpg-error.
2003-08-14 Timo Schulz <twoaday@freakmail.de>
* apdu.c (ct_activate_card): Change the code a little to avoid
problems with other readers.
* Always use 'dynload.h' instead of 'dlfcn.h'.
2003-08-05 Werner Koch <wk@gnupg.org>
* app-openpgp.c (dump_all_do): Don't analyze constructed DOs after
an error.
2003-08-04 Werner Koch <wk@gnupg.org>
* app.c (app_set_default_reader_port): New.
(select_application): Use it here.
* scdaemon.c (main): and here.
* sc-copykeys.c: --reader-port does now take a string.
* sc-investigate.c, scdaemon.c: Ditto.
* apdu.c (apdu_open_reader): Ditto. Load pcsclite if no ctapi
driver is configured. Always include code for ctapi.
(new_reader_slot): Don't test for already used ports and remove
port arg.
(open_pcsc_reader, pcsc_send_apdu, pcsc_error_string): New.
(apdu_send_le): Changed RC to long to cope with PC/SC.
* scdaemon.c, scdaemon.h: New option --ctapi-driver.
* sc-investigate.c, sc-copykeys.c: Ditto.
2003-07-31 Werner Koch <wk@gnupg.org>
* Makefile.am (scdaemon_LDADD): Added INTLLIBS.
2003-07-28 Werner Koch <wk@gnupg.org>
* app-openpgp.c (do_setattr): Change implementation. Allow all
useful DOs.
2003-07-27 Werner Koch <wk@gnupg.org>
Adjusted for gcry_mpi_print and gcry_mpi_scan API change.
2003-07-24 Werner Koch <wk@gnupg.org>
* app-openpgp.c (do_learn_status): Print more status information.
(app_select_openpgp): Store the card version.
(store_fpr): Add argument card_version and fix DOs for old cards.
(app_openpgp_storekey): Likewise.
2003-07-23 Werner Koch <wk@gnupg.org>
* command.c (cmd_pkauth): New.
(cmd_setdata): Check whether data was given at all to avoid
passing 0 to malloc.
* app.c (app_auth): New.
* app-openpgp.c (do_auth): New.
2003-07-22 Werner Koch <wk@gnupg.org>
* command.c (cmd_passwd): New.
* app.c (app_change_pin): New.
* app-openpgp.c (do_change_pin): New.
* iso7816.c (iso7816_reset_retry_counter): Implemented.
* sc-investigate.c (main): New option --gen-random.
* iso7816.c (iso7816_get_challenge): Don't create APDUs with a
length larger than 255.
2003-07-17 Werner Koch <wk@gnupg.org>
* command.c (cmd_random): New command RANDOM.
* iso7816.c (map_sw): New. Use it in this file to return
meaningful error messages. Changed all public fucntions to return
a gpg_error_t.
(iso7816_change_reference_data): New.
* apdu.c (apdu_open_reader): Use faked status words for soem
system errors.
2003-07-16 Werner Koch <wk@gnupg.org>
* apdu.c (apdu_send_simple): Use apdu_send_le so that we can
specify not to send Le as it should be.
2003-07-15 Werner Koch <wk@gnupg.org>
* Makefile.am: Add sc-copykeys program.
* sc-copykeys.c: New.
* app-openpgp.c (app_openpgp_storekey): New.
(app_openpgp_cardinfo): New.
(count_bits): New.
(store_fpr): And use it here to get the actual length in bit.
2003-07-03 Werner Koch <wk@gnupg.org>
* app-openpgp.c (do_setattr): Add setting of the URL.
(app_select_openpgp): Dump card data only in very verbose mode.
(do_decipher): New.
2003-07-02 Werner Koch <wk@gnupg.org>
* app-openpgp.c (get_sig_counter): New.
(do_sign): Print the signature counter and enable the PIN callback.
(do_genkey): Implement the PIN callback.
2003-07-01 Werner Koch <wk@gnupg.org>
* app-openpgp.c (store_fpr): Fixed fingerprint calculation.
2003-06-26 Werner Koch <wk@gnupg.org>
* app-openpgp.c (find_tlv): Fixed length header parsing.
* app.c (app_genkey): New.
* command.c (cmd_genkey): New.
2003-06-25 Werner Koch <wk@gnupg.org>
* command.c (percent_plus_unescape): New.
(cmd_setattr): New.
2003-06-24 Werner Koch <wk@gnupg.org>
* command.c (send_status_info): New.
* app-openpgp.c (app_select_openpgp): Replace SLOT arg by APP arg
and setup the function pointers in APP on success. Changed callers.
* app.c: New.
* app-common.h: New.
* scdaemon.h (APP): New type to handle applications.
(server_control_s): Add an APP context field.
* command.c (cmd_serialno): Handle applications.
(cmd_pksign): Ditto.
(cmd_pkdecrypt): Ditto.
(reset_notify): Ditto.
(cmd_learn): For now return error for application contexts.
(cmd_readcert): Ditto.
(cmd_readkey): Ditto.
2003-06-04 Werner Koch <wk@gnupg.org>
* card.c (map_sc_err): Renamed gpg_make_err to gpg_err_make.
Renamed error codes from INVALID to INV and removed _ERROR suffixes.
2003-06-03 Werner Koch <wk@gnupg.org>
Changed all error codes in all files to the new libgpg-error scheme.
* scdaemon.h: Include gpg-error.h and errno.h
* card.c (map_sc_err): Use unknown for the error source.
* Makefile.am: Link with libgpg-error
2003-05-14 Werner Koch <wk@gnupg.org>
* atr.c, atr.h: New.
* sc-investigate.c: Dump the ATR in a human readable format.
2003-05-08 Werner Koch <wk@gnupg.org>
* scdaemon.h (DBG_CARD_IO_VALUE): New.
* sc-investigate.c: New.
* scdaemon.c (main): Removed --print-atr option.
* iso7816.c, iso7816.h, app-openpgp.c: New.
2003-04-29 Werner Koch <wk@gnupg.org>
* scdaemon.c: New options --print-atr and --reader-port
* apdu.c, apdu.h: New
* card.c, card-p15.c, card-dinsig.c: Allow build without OpenSC.
* Makefile.am (LDFLAGS): Removed.
* command.c (register_commands): Adjusted for new Assuan semantics.
2002-08-21 Werner Koch <wk@gnupg.org>
* scdaemon.c (main): New option --daemon so that the program is
not accidently started in the background.
2002-08-16 Werner Koch <wk@gnupg.org>
* scdaemon.c: Include i18n.h.
* card-common.h (struct p15_private_s): Forward declaration. Add
it to card_ctx_s.
* card.c (card_close): Make sure private data is released.
(card_enum_certs): New.
* card-p15.c (p15_release_private_data): New.
(init_private_data): New to work around an OpenSC weirdness.
(p15_enum_keypairs): Do an OpenSC get_objects only once.
(p15_enum_certs): New.
(card_p15_bind): Bind new function.
* command.c (cmd_learn): Return information about the certificates.
2002-08-09 Werner Koch <wk@gnupg.org>
* card.c (card_get_serial_and_stamp): Use the tokeinfo serial
number as a fallback. Add a special prefix for serial numbers.
2002-07-30 Werner Koch <wk@gnupg.org>
Changes to cope with OpenSC 0.7.0:
* card.c: Removed the check for the packed opensc version.
Changed include file names of opensc.
(map_sc_err): Adjusted error codes for new opensc version.
* card-p15.c: Changed include filename of opensc.
* card-dinsig.c: Ditto.
* card-p15.c (p15_decipher): Add flags argument to OpenSC call.
2002-07-24 Werner Koch <wk@gnupg.org>
* card.c (find_simple_tlv, find_iccsn): New.
(card_get_serial_and_stamp): Improved serial number parser.
2002-06-27 Werner Koch <wk@gnupg.org>
* scdaemon.c (main): Use GNUPG_DEFAULT_HOMEDIR constant.
2002-06-15 Werner Koch <wk@gnupg.org>
* card-dinsig.c: Documented some stuff from the DIN norm.
2002-04-15 Werner Koch <wk@gnupg.org>
* command.c (cmd_pksign, cmd_pkdecrypt): Use a copy of the key ID.
2002-04-12 Werner Koch <wk@gnupg.org>
* scdaemon.c: New option --debug-sc N.
* card.c (card_open): set it here.
* card-p15.c (p15_prepare_key): Factored out common code from ...
(p15_sign, p15_decipher): here and made the decryption work the
regular way.
2002-04-10 Werner Koch <wk@gnupg.org>
* card.c (card_open): Return immediately when no reader is available.
2002-03-27 Werner Koch <wk@gnupg.org>
* card.c (card_open, card_close): Adjusted for changes in OpenSC.
2002-03-10 Werner Koch <wk@gnupg.org>
* card-p15.c, card-dinsig.c, card-common.h: New.
* card.c: Factored most code out to the new modules, so that we
can better support different types of card applications.
2002-01-26 Werner Koch <wk@gnupg.org>
* scdaemon.c scdaemon.h, command.c: New. Based on the code from
the gpg-agent.
Copyright 2002, 2003, 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
This file is free software; as a special exception the author gives
unlimited permission to copy and/or distribute it, with or without
modifications, as long as this notice is preserved.
This file is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/scd/apdu.c b/scd/apdu.c
index e14f2b2ca..f92565d7d 100644
--- a/scd/apdu.c
+++ b/scd/apdu.c
@@ -1,3283 +1,3284 @@
/* apdu.c - ISO 7816 APDU functions and low level I/O
* Copyright (C) 2003, 2004, 2008 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*
* $Id$
*/
/* NOTE: This module is also used by other software, thus the use of
the macro USE_GNU_PTH is mandatory. For GnuPG this macro is
guaranteed to be defined true. */
#include <config.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <signal.h>
#ifdef USE_GNU_PTH
# include <unistd.h>
# include <fcntl.h>
# include <pth.h>
#endif
/* If requested include the definitions for the remote APDU protocol
code. */
#ifdef USE_G10CODE_RAPDU
#include "rapdu.h"
#endif /*USE_G10CODE_RAPDU*/
#if defined(GNUPG_SCD_MAIN_HEADER)
#include GNUPG_SCD_MAIN_HEADER
#elif GNUPG_MAJOR_VERSION == 1
/* This is used with GnuPG version < 1.9. The code has been source
copied from the current GnuPG >= 1.9 and is maintained over
there. */
#include "options.h"
#include "errors.h"
#include "memory.h"
#include "util.h"
#include "i18n.h"
#include "cardglue.h"
#else /* GNUPG_MAJOR_VERSION != 1 */
#include "scdaemon.h"
#endif /* GNUPG_MAJOR_VERSION != 1 */
#include "apdu.h"
#include "ccid-driver.h"
/* Due to conflicting use of threading libraries we usually can't link
against libpcsclite. Instead we use a wrapper program. */
#ifdef USE_GNU_PTH
#if !defined(HAVE_W32_SYSTEM) && !defined(__CYGWIN__)
#define NEED_PCSC_WRAPPER 1
#endif
#endif
#define MAX_READER 4 /* Number of readers we support concurrently. */
#if defined(_WIN32) || defined(__CYGWIN__)
#define DLSTDCALL __stdcall
#else
#define DLSTDCALL
#endif
#ifdef _POSIX_OPEN_MAX
#define MAX_OPEN_FDS _POSIX_OPEN_MAX
#else
#define MAX_OPEN_FDS 20
#endif
/* Helper to pass parameters related to keypad based operations. */
struct pininfo_s
{
int mode;
int minlen;
int maxlen;
int padlen;
};
/* A structure to collect information pertaining to one reader
slot. */
struct reader_table_s {
int used; /* True if slot is used. */
unsigned short port; /* Port number: 0 = unused, 1 - dev/tty */
/* Function pointers intialized to the various backends. */
int (*connect_card)(int);
int (*disconnect_card)(int);
int (*close_reader)(int);
int (*shutdown_reader)(int);
int (*reset_reader)(int);
int (*get_status_reader)(int, unsigned int *);
int (*send_apdu_reader)(int,unsigned char *,size_t,
unsigned char *, size_t *, struct pininfo_s *);
int (*check_keypad)(int, int, int, int, int, int);
void (*dump_status_reader)(int);
struct {
ccid_driver_t handle;
} ccid;
struct {
unsigned long context;
unsigned long card;
unsigned long protocol;
#ifdef NEED_PCSC_WRAPPER
int req_fd;
int rsp_fd;
pid_t pid;
#endif /*NEED_PCSC_WRAPPER*/
} pcsc;
#ifdef USE_G10CODE_RAPDU
struct {
rapdu_t handle;
} rapdu;
#endif /*USE_G10CODE_RAPDU*/
char *rdrname; /* Name of the connected reader or NULL if unknown. */
int any_status; /* True if we have seen any status. */
int last_status;
int status;
int is_t0; /* True if we know that we are running T=0. */
unsigned char atr[33];
size_t atrlen; /* A zero length indicates that the ATR has
not yet been read; i.e. the card is not
ready for use. */
unsigned int change_counter;
#ifdef USE_GNU_PTH
int lock_initialized;
pth_mutex_t lock;
#endif
};
typedef struct reader_table_s *reader_table_t;
/* A global table to keep track of active readers. */
static struct reader_table_s reader_table[MAX_READER];
/* ct API function pointer. */
static char (* DLSTDCALL CT_init) (unsigned short ctn, unsigned short Pn);
static char (* DLSTDCALL CT_data) (unsigned short ctn, unsigned char *dad,
unsigned char *sad, unsigned short lc,
unsigned char *cmd, unsigned short *lr,
unsigned char *rsp);
static char (* DLSTDCALL CT_close) (unsigned short ctn);
/* PC/SC constants and function pointer. */
#define PCSC_SCOPE_USER 0
#define PCSC_SCOPE_TERMINAL 1
#define PCSC_SCOPE_SYSTEM 2
#define PCSC_SCOPE_GLOBAL 3
#define PCSC_PROTOCOL_T0 1
#define PCSC_PROTOCOL_T1 2
#define PCSC_PROTOCOL_RAW 4
#define PCSC_SHARE_EXCLUSIVE 1
#define PCSC_SHARE_SHARED 2
#define PCSC_SHARE_DIRECT 3
#define PCSC_LEAVE_CARD 0
#define PCSC_RESET_CARD 1
#define PCSC_UNPOWER_CARD 2
#define PCSC_EJECT_CARD 3
#define PCSC_UNKNOWN 0x0001
#define PCSC_ABSENT 0x0002 /* Card is absent. */
#define PCSC_PRESENT 0x0004 /* Card is present. */
#define PCSC_SWALLOWED 0x0008 /* Card is present and electrical connected. */
#define PCSC_POWERED 0x0010 /* Card is powered. */
#define PCSC_NEGOTIABLE 0x0020 /* Card is awaiting PTS. */
#define PCSC_SPECIFIC 0x0040 /* Card is ready for use. */
#define PCSC_STATE_UNAWARE 0x0000 /* Want status. */
#define PCSC_STATE_IGNORE 0x0001 /* Ignore this reader. */
#define PCSC_STATE_CHANGED 0x0002 /* State has changed. */
#define PCSC_STATE_UNKNOWN 0x0004 /* Reader unknown. */
#define PCSC_STATE_UNAVAILABLE 0x0008 /* Status unavailable. */
#define PCSC_STATE_EMPTY 0x0010 /* Card removed. */
#define PCSC_STATE_PRESENT 0x0020 /* Card inserted. */
#define PCSC_STATE_ATRMATCH 0x0040 /* ATR matches card. */
#define PCSC_STATE_EXCLUSIVE 0x0080 /* Exclusive Mode. */
#define PCSC_STATE_INUSE 0x0100 /* Shared mode. */
#define PCSC_STATE_MUTE 0x0200 /* Unresponsive card. */
/* Some PC/SC error codes. */
#define PCSC_E_CANCELLED 0x80100002
#define PCSC_E_CANT_DISPOSE 0x8010000E
#define PCSC_E_INSUFFICIENT_BUFFER 0x80100008
#define PCSC_E_INVALID_ATR 0x80100015
#define PCSC_E_INVALID_HANDLE 0x80100003
#define PCSC_E_INVALID_PARAMETER 0x80100004
#define PCSC_E_INVALID_TARGET 0x80100005
#define PCSC_E_INVALID_VALUE 0x80100011
#define PCSC_E_NO_MEMORY 0x80100006
#define PCSC_E_UNKNOWN_READER 0x80100009
#define PCSC_E_TIMEOUT 0x8010000A
#define PCSC_E_SHARING_VIOLATION 0x8010000B
#define PCSC_E_NO_SMARTCARD 0x8010000C
#define PCSC_E_UNKNOWN_CARD 0x8010000D
#define PCSC_E_PROTO_MISMATCH 0x8010000F
#define PCSC_E_NOT_READY 0x80100010
#define PCSC_E_SYSTEM_CANCELLED 0x80100012
#define PCSC_E_NOT_TRANSACTED 0x80100016
#define PCSC_E_READER_UNAVAILABLE 0x80100017
#define PCSC_W_REMOVED_CARD 0x80100069
/* The PC/SC error is defined as a long as per specs. Due to left
shifts bit 31 will get sign extended. We use this mask to fix
it. */
#define PCSC_ERR_MASK(a) ((a) & 0xffffffff)
struct pcsc_io_request_s
{
unsigned long protocol;
unsigned long pci_len;
};
typedef struct pcsc_io_request_s *pcsc_io_request_t;
struct pcsc_readerstate_s
{
const char *reader;
void *user_data;
unsigned long current_state;
unsigned long event_state;
unsigned long atrlen;
unsigned char atr[33];
};
typedef struct pcsc_readerstate_s *pcsc_readerstate_t;
long (* DLSTDCALL pcsc_establish_context) (unsigned long scope,
const void *reserved1,
const void *reserved2,
unsigned long *r_context);
long (* DLSTDCALL pcsc_release_context) (unsigned long context);
long (* DLSTDCALL pcsc_list_readers) (unsigned long context,
const char *groups,
char *readers, unsigned long*readerslen);
long (* DLSTDCALL pcsc_get_status_change) (unsigned long context,
unsigned long timeout,
pcsc_readerstate_t readerstates,
unsigned long nreaderstates);
long (* DLSTDCALL pcsc_connect) (unsigned long context,
const char *reader,
unsigned long share_mode,
unsigned long preferred_protocols,
unsigned long *r_card,
unsigned long *r_active_protocol);
long (* DLSTDCALL pcsc_reconnect) (unsigned long card,
unsigned long share_mode,
unsigned long preferred_protocols,
unsigned long initialization,
unsigned long *r_active_protocol);
long (* DLSTDCALL pcsc_disconnect) (unsigned long card,
unsigned long disposition);
long (* DLSTDCALL pcsc_status) (unsigned long card,
char *reader, unsigned long *readerlen,
unsigned long *r_state,
unsigned long *r_protocol,
unsigned char *atr, unsigned long *atrlen);
long (* DLSTDCALL pcsc_begin_transaction) (unsigned long card);
long (* DLSTDCALL pcsc_end_transaction) (unsigned long card,
unsigned long disposition);
long (* DLSTDCALL pcsc_transmit) (unsigned long card,
const pcsc_io_request_t send_pci,
const unsigned char *send_buffer,
unsigned long send_len,
pcsc_io_request_t recv_pci,
unsigned char *recv_buffer,
unsigned long *recv_len);
long (* DLSTDCALL pcsc_set_timeout) (unsigned long context,
unsigned long timeout);
/* Prototypes. */
static int pcsc_get_status (int slot, unsigned int *status);
static int reset_pcsc_reader (int slot);
/*
Helper
*/
/* Find an unused reader slot for PORTSTR and put it into the reader
table. Return -1 on error or the index into the reader table. */
static int
new_reader_slot (void)
{
int i, reader = -1;
for (i=0; i < MAX_READER; i++)
{
if (!reader_table[i].used && reader == -1)
reader = i;
}
if (reader == -1)
{
log_error ("new_reader_slot: out of slots\n");
return -1;
}
#ifdef USE_GNU_PTH
if (!reader_table[reader].lock_initialized)
{
if (!pth_mutex_init (&reader_table[reader].lock))
{
log_error ("error initializing mutex: %s\n", strerror (errno));
return -1;
}
reader_table[reader].lock_initialized = 1;
}
#endif /*USE_GNU_PTH*/
reader_table[reader].connect_card = NULL;
reader_table[reader].disconnect_card = NULL;
reader_table[reader].close_reader = NULL;
reader_table[reader].shutdown_reader = NULL;
reader_table[reader].reset_reader = NULL;
reader_table[reader].get_status_reader = NULL;
reader_table[reader].send_apdu_reader = NULL;
reader_table[reader].check_keypad = NULL;
reader_table[reader].dump_status_reader = NULL;
reader_table[reader].used = 1;
reader_table[reader].any_status = 0;
reader_table[reader].last_status = 0;
reader_table[reader].is_t0 = 1;
#ifdef NEED_PCSC_WRAPPER
reader_table[reader].pcsc.req_fd = -1;
reader_table[reader].pcsc.rsp_fd = -1;
reader_table[reader].pcsc.pid = (pid_t)(-1);
#endif
return reader;
}
static void
dump_reader_status (int slot)
{
if (!opt.verbose)
return;
if (reader_table[slot].dump_status_reader)
reader_table[slot].dump_status_reader (slot);
if (reader_table[slot].status != -1
&& reader_table[slot].atrlen)
{
log_info ("slot %d: ATR=", slot);
log_printhex ("", reader_table[slot].atr, reader_table[slot].atrlen);
}
}
static const char *
host_sw_string (long err)
{
switch (err)
{
case 0: return "okay";
case SW_HOST_OUT_OF_CORE: return "out of core";
case SW_HOST_INV_VALUE: return "invalid value";
case SW_HOST_NO_DRIVER: return "no driver";
case SW_HOST_NOT_SUPPORTED: return "not supported";
case SW_HOST_LOCKING_FAILED: return "locking failed";
case SW_HOST_BUSY: return "busy";
case SW_HOST_NO_CARD: return "no card";
case SW_HOST_CARD_INACTIVE: return "card inactive";
case SW_HOST_CARD_IO_ERROR: return "card I/O error";
case SW_HOST_GENERAL_ERROR: return "general error";
case SW_HOST_NO_READER: return "no reader";
case SW_HOST_ABORTED: return "aborted";
case SW_HOST_NO_KEYPAD: return "no keypad";
case SW_HOST_ALREADY_CONNECTED: return "already connected";
default: return "unknown host status error";
}
}
const char *
apdu_strerror (int rc)
{
switch (rc)
{
case SW_EOF_REACHED : return "eof reached";
case SW_EEPROM_FAILURE : return "eeprom failure";
case SW_WRONG_LENGTH : return "wrong length";
case SW_CHV_WRONG : return "CHV wrong";
case SW_CHV_BLOCKED : return "CHV blocked";
case SW_USE_CONDITIONS : return "use conditions not satisfied";
case SW_BAD_PARAMETER : return "bad parameter";
case SW_NOT_SUPPORTED : return "not supported";
case SW_FILE_NOT_FOUND : return "file not found";
case SW_RECORD_NOT_FOUND:return "record not found";
case SW_REF_NOT_FOUND : return "reference not found";
case SW_BAD_P0_P1 : return "bad P0 or P1";
case SW_INS_NOT_SUP : return "instruction not supported";
case SW_CLA_NOT_SUP : return "class not supported";
case SW_SUCCESS : return "success";
default:
if ((rc & ~0x00ff) == SW_MORE_DATA)
return "more data available";
if ( (rc & 0x10000) )
return host_sw_string (rc);
return "unknown status error";
}
}
/*
ct API Interface
*/
static const char *
ct_error_string (long err)
{
switch (err)
{
case 0: return "okay";
case -1: return "invalid data";
case -8: return "ct error";
case -10: return "transmission error";
case -11: return "memory allocation error";
case -128: return "HTSI error";
default: return "unknown CT-API error";
}
}
static void
ct_dump_reader_status (int slot)
{
log_info ("reader slot %d: %s\n", slot,
reader_table[slot].status == 1? "Processor ICC present" :
reader_table[slot].status == 0? "Memory ICC present" :
"ICC not present" );
}
/* Wait for the card in SLOT and activate it. Return a status word
error or 0 on success. */
static int
ct_activate_card (int slot)
{
int rc;
unsigned char dad[1], sad[1], cmd[11], buf[256];
unsigned short buflen;
/* Check whether card has been inserted. */
dad[0] = 1; /* Destination address: CT. */
sad[0] = 2; /* Source address: Host. */
cmd[0] = 0x20; /* Class byte. */
cmd[1] = 0x13; /* Request status. */
cmd[2] = 0x00; /* From kernel. */
cmd[3] = 0x80; /* Return card's DO. */
cmd[4] = 0x00;
buflen = DIM(buf);
rc = CT_data (slot, dad, sad, 5, cmd, &buflen, buf);
if (rc || buflen < 2 || buf[buflen-2] != 0x90)
{
log_error ("ct_activate_card: can't get status of reader %d: %s\n",
slot, ct_error_string (rc));
return SW_HOST_CARD_IO_ERROR;
}
/* Connected, now activate the card. */
dad[0] = 1; /* Destination address: CT. */
sad[0] = 2; /* Source address: Host. */
cmd[0] = 0x20; /* Class byte. */
cmd[1] = 0x12; /* Request ICC. */
cmd[2] = 0x01; /* From first interface. */
cmd[3] = 0x01; /* Return card's ATR. */
cmd[4] = 0x00;
buflen = DIM(buf);
rc = CT_data (slot, dad, sad, 5, cmd, &buflen, buf);
if (rc || buflen < 2 || buf[buflen-2] != 0x90)
{
log_error ("ct_activate_card(%d): activation failed: %s\n",
slot, ct_error_string (rc));
if (!rc)
log_printhex (" received data:", buf, buflen);
return SW_HOST_CARD_IO_ERROR;
}
/* Store the type and the ATR. */
if (buflen - 2 > DIM (reader_table[0].atr))
{
log_error ("ct_activate_card(%d): ATR too long\n", slot);
return SW_HOST_CARD_IO_ERROR;
}
reader_table[slot].status = buf[buflen - 1];
memcpy (reader_table[slot].atr, buf, buflen - 2);
reader_table[slot].atrlen = buflen - 2;
return 0;
}
static int
close_ct_reader (int slot)
{
CT_close (slot);
reader_table[slot].used = 0;
return 0;
}
static int
reset_ct_reader (int slot)
{
/* FIXME: Check is this is sufficient do do a reset. */
return ct_activate_card (slot);
}
static int
ct_get_status (int slot, unsigned int *status)
{
(void)slot;
/* The status we returned is wrong but we don't care becuase ctAPI
is not anymore required. */
*status = APDU_CARD_USABLE|APDU_CARD_PRESENT|APDU_CARD_ACTIVE;
return 0;
}
/* Actually send the APDU of length APDULEN to SLOT and return a
maximum of *BUFLEN data in BUFFER, the actual retruned size will be
set to BUFLEN. Returns: CT API error code. */
static int
ct_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
unsigned char *buffer, size_t *buflen, struct pininfo_s *pininfo)
{
int rc;
unsigned char dad[1], sad[1];
unsigned short ctbuflen;
(void)pininfo;
/* If we don't have an ATR, we need to reset the reader first. */
if (!reader_table[slot].atrlen
&& (rc = reset_ct_reader (slot)))
return rc;
dad[0] = 0; /* Destination address: Card. */
sad[0] = 2; /* Source address: Host. */
ctbuflen = *buflen;
if (DBG_CARD_IO)
log_printhex (" CT_data:", apdu, apdulen);
rc = CT_data (slot, dad, sad, apdulen, apdu, &ctbuflen, buffer);
*buflen = ctbuflen;
return rc? SW_HOST_CARD_IO_ERROR: 0;
}
/* Open a reader and return an internal handle for it. PORT is a
non-negative value with the port number of the reader. USB readers
do have port numbers starting at 32769. */
static int
open_ct_reader (int port)
{
int rc, reader;
if (port < 0 || port > 0xffff)
{
log_error ("open_ct_reader: invalid port %d requested\n", port);
return -1;
}
reader = new_reader_slot ();
if (reader == -1)
return reader;
reader_table[reader].port = port;
rc = CT_init (reader, (unsigned short)port);
if (rc)
{
log_error ("apdu_open_ct_reader failed on port %d: %s\n",
port, ct_error_string (rc));
reader_table[reader].used = 0;
return -1;
}
/* Only try to activate the card. */
rc = ct_activate_card (reader);
if (rc)
{
reader_table[reader].atrlen = 0;
rc = 0;
}
reader_table[reader].close_reader = close_ct_reader;
reader_table[reader].reset_reader = reset_ct_reader;
reader_table[reader].get_status_reader = ct_get_status;
reader_table[reader].send_apdu_reader = ct_send_apdu;
reader_table[reader].check_keypad = NULL;
reader_table[reader].dump_status_reader = ct_dump_reader_status;
dump_reader_status (reader);
return reader;
}
/*
PC/SC Interface
*/
#ifdef NEED_PCSC_WRAPPER
static int
writen (int fd, const void *buf, size_t nbytes)
{
size_t nleft = nbytes;
int nwritten;
/* log_printhex (" writen:", buf, nbytes); */
while (nleft > 0)
{
#ifdef USE_GNU_PTH
nwritten = pth_write (fd, buf, nleft);
#else
nwritten = write (fd, buf, nleft);
#endif
if (nwritten < 0 && errno == EINTR)
continue;
if (nwritten < 0)
return -1;
nleft -= nwritten;
buf = (const char*)buf + nwritten;
}
return 0;
}
/* Read up to BUFLEN bytes from FD and return the number of bytes
actually read in NREAD. Returns -1 on error or 0 on success. */
static int
readn (int fd, void *buf, size_t buflen, size_t *nread)
{
size_t nleft = buflen;
int n;
/* void *orig_buf = buf; */
while (nleft > 0)
{
#ifdef USE_GNU_PTH
n = pth_read (fd, buf, nleft);
#else
n = read (fd, buf, nleft);
#endif
if (n < 0 && errno == EINTR)
continue;
if (n < 0)
return -1; /* read error. */
if (!n)
break; /* EOF */
nleft -= n;
buf = (char*)buf + n;
}
if (nread)
*nread = buflen - nleft;
/* log_printhex (" readn:", orig_buf, *nread); */
return 0;
}
#endif /*NEED_PCSC_WRAPPER*/
static const char *
pcsc_error_string (long err)
{
const char *s;
if (!err)
return "okay";
if ((err & 0x80100000) != 0x80100000)
return "invalid PC/SC error code";
err &= 0xffff;
switch (err)
{
case 0x0002: s = "cancelled"; break;
case 0x000e: s = "can't dispose"; break;
case 0x0008: s = "insufficient buffer"; break;
case 0x0015: s = "invalid ATR"; break;
case 0x0003: s = "invalid handle"; break;
case 0x0004: s = "invalid parameter"; break;
case 0x0005: s = "invalid target"; break;
case 0x0011: s = "invalid value"; break;
case 0x0006: s = "no memory"; break;
case 0x0013: s = "comm error"; break;
case 0x0001: s = "internal error"; break;
case 0x0014: s = "unknown error"; break;
case 0x0007: s = "waited too long"; break;
case 0x0009: s = "unknown reader"; break;
case 0x000a: s = "timeout"; break;
case 0x000b: s = "sharing violation"; break;
case 0x000c: s = "no smartcard"; break;
case 0x000d: s = "unknown card"; break;
case 0x000f: s = "proto mismatch"; break;
case 0x0010: s = "not ready"; break;
case 0x0012: s = "system cancelled"; break;
case 0x0016: s = "not transacted"; break;
case 0x0017: s = "reader unavailable"; break;
case 0x0065: s = "unsupported card"; break;
case 0x0066: s = "unresponsive card"; break;
case 0x0067: s = "unpowered card"; break;
case 0x0068: s = "reset card"; break;
case 0x0069: s = "removed card"; break;
case 0x006a: s = "inserted card"; break;
case 0x001f: s = "unsupported feature"; break;
case 0x0019: s = "PCI too small"; break;
case 0x001a: s = "reader unsupported"; break;
case 0x001b: s = "duplicate reader"; break;
case 0x001c: s = "card unsupported"; break;
case 0x001d: s = "no service"; break;
case 0x001e: s = "service stopped"; break;
default: s = "unknown PC/SC error code"; break;
}
return s;
}
/* Map PC/SC error codes to our special host status words. */
static int
pcsc_error_to_sw (long ec)
{
int rc;
switch ( PCSC_ERR_MASK (ec) )
{
case 0: rc = 0; break;
case PCSC_E_CANCELLED: rc = SW_HOST_ABORTED; break;
case PCSC_E_NO_MEMORY: rc = SW_HOST_OUT_OF_CORE; break;
case PCSC_E_TIMEOUT: rc = SW_HOST_CARD_IO_ERROR; break;
case PCSC_E_SHARING_VIOLATION: rc = SW_HOST_LOCKING_FAILED; break;
case PCSC_E_NO_SMARTCARD: rc = SW_HOST_NO_CARD; break;
case PCSC_W_REMOVED_CARD: rc = SW_HOST_NO_CARD; break;
case PCSC_E_INVALID_TARGET:
case PCSC_E_INVALID_VALUE:
case PCSC_E_INVALID_HANDLE:
case PCSC_E_INVALID_PARAMETER:
case PCSC_E_INSUFFICIENT_BUFFER: rc = SW_HOST_INV_VALUE; break;
default: rc = SW_HOST_GENERAL_ERROR; break;
}
return rc;
}
static void
dump_pcsc_reader_status (int slot)
{
if (reader_table[slot].pcsc.card)
{
log_info ("reader slot %d: active protocol:", slot);
if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T0))
log_printf (" T0");
else if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1))
log_printf (" T1");
else if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_RAW))
log_printf (" raw");
log_printf ("\n");
}
else
log_info ("reader slot %d: not connected\n", slot);
}
#ifndef NEED_PCSC_WRAPPER
static int
pcsc_get_status_direct (int slot, unsigned int *status)
{
long err;
struct pcsc_readerstate_s rdrstates[1];
memset (rdrstates, 0, sizeof *rdrstates);
rdrstates[0].reader = reader_table[slot].rdrname;
rdrstates[0].current_state = PCSC_STATE_UNAWARE;
err = pcsc_get_status_change (reader_table[slot].pcsc.context,
0,
rdrstates, 1);
if (err == PCSC_E_TIMEOUT)
err = 0; /* Timeout is no error error here. */
if (err)
{
log_error ("pcsc_get_status_change failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
return pcsc_error_to_sw (err);
}
/* log_debug */
/* ("pcsc_get_status_change: %s%s%s%s%s%s%s%s%s%s\n", */
/* (rdrstates[0].event_state & PCSC_STATE_IGNORE)? " ignore":"", */
/* (rdrstates[0].event_state & PCSC_STATE_CHANGED)? " changed":"", */
/* (rdrstates[0].event_state & PCSC_STATE_UNKNOWN)? " unknown":"", */
/* (rdrstates[0].event_state & PCSC_STATE_UNAVAILABLE)?" unavail":"", */
/* (rdrstates[0].event_state & PCSC_STATE_EMPTY)? " empty":"", */
/* (rdrstates[0].event_state & PCSC_STATE_PRESENT)? " present":"", */
/* (rdrstates[0].event_state & PCSC_STATE_ATRMATCH)? " atr":"", */
/* (rdrstates[0].event_state & PCSC_STATE_EXCLUSIVE)? " excl":"", */
/* (rdrstates[0].event_state & PCSC_STATE_INUSE)? " unuse":"", */
/* (rdrstates[0].event_state & PCSC_STATE_MUTE)? " mute":"" ); */
*status = 0;
if ( (rdrstates[0].event_state & PCSC_STATE_PRESENT) )
*status |= APDU_CARD_PRESENT;
if ( !(rdrstates[0].event_state & PCSC_STATE_MUTE) )
*status |= APDU_CARD_ACTIVE;
#ifndef HAVE_W32_SYSTEM
/* We indicate a useful card if it is not in use by another
application. This is because we only use exclusive access
mode. */
if ( (*status & (APDU_CARD_PRESENT|APDU_CARD_ACTIVE))
== (APDU_CARD_PRESENT|APDU_CARD_ACTIVE)
&& !(rdrstates[0].event_state & PCSC_STATE_INUSE) )
*status |= APDU_CARD_USABLE;
#else
/* Some winscard drivers may set EXCLUSIVE and INUSE at the same
time when we are the only user (SCM SCR335) under Windows. */
if ((*status & (APDU_CARD_PRESENT|APDU_CARD_ACTIVE))
== (APDU_CARD_PRESENT|APDU_CARD_ACTIVE))
*status |= APDU_CARD_USABLE;
#endif
return 0;
}
#endif /*!NEED_PCSC_WRAPPER*/
#ifdef NEED_PCSC_WRAPPER
static int
pcsc_get_status_wrapped (int slot, unsigned int *status)
{
long err;
reader_table_t slotp;
size_t len, full_len;
int i, n;
unsigned char msgbuf[9];
unsigned char buffer[16];
int sw = SW_HOST_CARD_IO_ERROR;
slotp = reader_table + slot;
if (slotp->pcsc.req_fd == -1
|| slotp->pcsc.rsp_fd == -1
|| slotp->pcsc.pid == (pid_t)(-1) )
{
log_error ("pcsc_get_status: pcsc-wrapper not running\n");
return sw;
}
msgbuf[0] = 0x04; /* STATUS command. */
len = 0;
msgbuf[1] = (len >> 24);
msgbuf[2] = (len >> 16);
msgbuf[3] = (len >> 8);
msgbuf[4] = (len );
if ( writen (slotp->pcsc.req_fd, msgbuf, 5) )
{
log_error ("error sending PC/SC STATUS request: %s\n",
strerror (errno));
goto command_failed;
}
/* Read the response. */
if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
{
log_error ("error receiving PC/SC STATUS response: %s\n",
i? strerror (errno) : "premature EOF");
goto command_failed;
}
len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
if (msgbuf[0] != 0x81 || len < 4)
{
log_error ("invalid response header from PC/SC received\n");
goto command_failed;
}
len -= 4; /* Already read the error code. */
err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
| (msgbuf[7] << 8 ) | msgbuf[8]);
if (err)
{
log_error ("pcsc_status failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
/* This is a proper error code, so return immediately. */
return pcsc_error_to_sw (err);
}
full_len = len;
/* The current version returns 3 words but we allow also for old
versions returning only 2 words. */
n = 12 < len ? 12 : len;
if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len))
|| (len != 8 && len != 12))
{
log_error ("error receiving PC/SC STATUS response: %s\n",
i? strerror (errno) : "premature EOF");
goto command_failed;
}
slotp->is_t0 = (len == 12 && !!(buffer[11] & PCSC_PROTOCOL_T0));
full_len -= len;
/* Newer versions of the wrapper might send more status bytes.
Read them. */
while (full_len)
{
unsigned char dummybuf[128];
n = full_len < DIM (dummybuf) ? full_len : DIM (dummybuf);
if ((i=readn (slotp->pcsc.rsp_fd, dummybuf, n, &len)) || len != n)
{
log_error ("error receiving PC/SC TRANSMIT response: %s\n",
i? strerror (errno) : "premature EOF");
goto command_failed;
}
full_len -= n;
}
/* We are lucky: The wrapper already returns the data in the
required format. */
*status = buffer[3];
return 0;
command_failed:
close (slotp->pcsc.req_fd);
close (slotp->pcsc.rsp_fd);
slotp->pcsc.req_fd = -1;
slotp->pcsc.rsp_fd = -1;
kill (slotp->pcsc.pid, SIGTERM);
slotp->pcsc.pid = (pid_t)(-1);
slotp->used = 0;
return sw;
}
#endif /*NEED_PCSC_WRAPPER*/
static int
pcsc_get_status (int slot, unsigned int *status)
{
#ifdef NEED_PCSC_WRAPPER
return pcsc_get_status_wrapped (slot, status);
#else
return pcsc_get_status_direct (slot, status);
#endif
}
#ifndef NEED_PCSC_WRAPPER
static int
pcsc_send_apdu_direct (int slot, unsigned char *apdu, size_t apdulen,
unsigned char *buffer, size_t *buflen,
struct pininfo_s *pininfo)
{
long err;
struct pcsc_io_request_s send_pci;
unsigned long recv_len;
if (!reader_table[slot].atrlen
&& (err = reset_pcsc_reader (slot)))
return err;
if (DBG_CARD_IO)
log_printhex (" PCSC_data:", apdu, apdulen);
if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1))
send_pci.protocol = PCSC_PROTOCOL_T1;
else
send_pci.protocol = PCSC_PROTOCOL_T0;
send_pci.pci_len = sizeof send_pci;
recv_len = *buflen;
err = pcsc_transmit (reader_table[slot].pcsc.card,
&send_pci, apdu, apdulen,
NULL, buffer, &recv_len);
*buflen = recv_len;
if (err)
log_error ("pcsc_transmit failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
return pcsc_error_to_sw (err);
}
#endif /*!NEED_PCSC_WRAPPER*/
#ifdef NEED_PCSC_WRAPPER
static int
pcsc_send_apdu_wrapped (int slot, unsigned char *apdu, size_t apdulen,
unsigned char *buffer, size_t *buflen,
struct pininfo_s *pininfo)
{
long err;
reader_table_t slotp;
size_t len, full_len;
int i, n;
unsigned char msgbuf[9];
int sw = SW_HOST_CARD_IO_ERROR;
(void)pininfo;
if (!reader_table[slot].atrlen
&& (err = reset_pcsc_reader (slot)))
return err;
if (DBG_CARD_IO)
log_printhex (" PCSC_data:", apdu, apdulen);
slotp = reader_table + slot;
if (slotp->pcsc.req_fd == -1
|| slotp->pcsc.rsp_fd == -1
|| slotp->pcsc.pid == (pid_t)(-1) )
{
log_error ("pcsc_send_apdu: pcsc-wrapper not running\n");
return sw;
}
msgbuf[0] = 0x03; /* TRANSMIT command. */
len = apdulen;
msgbuf[1] = (len >> 24);
msgbuf[2] = (len >> 16);
msgbuf[3] = (len >> 8);
msgbuf[4] = (len );
if ( writen (slotp->pcsc.req_fd, msgbuf, 5)
|| writen (slotp->pcsc.req_fd, apdu, len))
{
log_error ("error sending PC/SC TRANSMIT request: %s\n",
strerror (errno));
goto command_failed;
}
/* Read the response. */
if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
{
log_error ("error receiving PC/SC TRANSMIT response: %s\n",
i? strerror (errno) : "premature EOF");
goto command_failed;
}
len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
if (msgbuf[0] != 0x81 || len < 4)
{
log_error ("invalid response header from PC/SC received\n");
goto command_failed;
}
len -= 4; /* Already read the error code. */
err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
| (msgbuf[7] << 8 ) | msgbuf[8]);
if (err)
{
log_error ("pcsc_transmit failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
return pcsc_error_to_sw (err);
}
full_len = len;
n = *buflen < len ? *buflen : len;
if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len)) || len != n)
{
log_error ("error receiving PC/SC TRANSMIT response: %s\n",
i? strerror (errno) : "premature EOF");
goto command_failed;
}
*buflen = n;
full_len -= len;
if (full_len)
{
log_error ("pcsc_send_apdu: provided buffer too short - truncated\n");
err = SW_HOST_INV_VALUE;
}
/* We need to read any rest of the response, to keep the
protocol running. */
while (full_len)
{
unsigned char dummybuf[128];
n = full_len < DIM (dummybuf) ? full_len : DIM (dummybuf);
if ((i=readn (slotp->pcsc.rsp_fd, dummybuf, n, &len)) || len != n)
{
log_error ("error receiving PC/SC TRANSMIT response: %s\n",
i? strerror (errno) : "premature EOF");
goto command_failed;
}
full_len -= n;
}
return err;
command_failed:
close (slotp->pcsc.req_fd);
close (slotp->pcsc.rsp_fd);
slotp->pcsc.req_fd = -1;
slotp->pcsc.rsp_fd = -1;
kill (slotp->pcsc.pid, SIGTERM);
slotp->pcsc.pid = (pid_t)(-1);
slotp->used = 0;
return sw;
}
#endif /*NEED_PCSC_WRAPPER*/
/* Send the APDU of length APDULEN to SLOT and return a maximum of
*BUFLEN data in BUFFER, the actual returned size will be stored at
BUFLEN. Returns: A status word. */
static int
pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
unsigned char *buffer, size_t *buflen,
struct pininfo_s *pininfo)
{
#ifdef NEED_PCSC_WRAPPER
return pcsc_send_apdu_wrapped (slot, apdu, apdulen, buffer, buflen, pininfo);
#else
return pcsc_send_apdu_direct (slot, apdu, apdulen, buffer, buflen, pininfo);
#endif
}
#ifndef NEED_PCSC_WRAPPER
static int
close_pcsc_reader_direct (int slot)
{
pcsc_release_context (reader_table[slot].pcsc.context);
xfree (reader_table[slot].rdrname);
reader_table[slot].rdrname = NULL;
reader_table[slot].used = 0;
return 0;
}
#endif /*!NEED_PCSC_WRAPPER*/
#ifdef NEED_PCSC_WRAPPER
static int
close_pcsc_reader_wrapped (int slot)
{
long err;
reader_table_t slotp;
size_t len;
int i;
unsigned char msgbuf[9];
slotp = reader_table + slot;
if (slotp->pcsc.req_fd == -1
|| slotp->pcsc.rsp_fd == -1
|| slotp->pcsc.pid == (pid_t)(-1) )
{
log_error ("close_pcsc_reader: pcsc-wrapper not running\n");
return 0;
}
msgbuf[0] = 0x02; /* CLOSE command. */
len = 0;
msgbuf[1] = (len >> 24);
msgbuf[2] = (len >> 16);
msgbuf[3] = (len >> 8);
msgbuf[4] = (len );
if ( writen (slotp->pcsc.req_fd, msgbuf, 5) )
{
log_error ("error sending PC/SC CLOSE request: %s\n",
strerror (errno));
goto command_failed;
}
/* Read the response. */
if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
{
log_error ("error receiving PC/SC CLOSE response: %s\n",
i? strerror (errno) : "premature EOF");
goto command_failed;
}
len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
if (msgbuf[0] != 0x81 || len < 4)
{
log_error ("invalid response header from PC/SC received\n");
goto command_failed;
}
len -= 4; /* Already read the error code. */
err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
| (msgbuf[7] << 8 ) | msgbuf[8]);
if (err)
log_error ("pcsc_close failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
/* We will close the wrapper in any case - errors are merely
informational. */
command_failed:
close (slotp->pcsc.req_fd);
close (slotp->pcsc.rsp_fd);
slotp->pcsc.req_fd = -1;
slotp->pcsc.rsp_fd = -1;
kill (slotp->pcsc.pid, SIGTERM);
slotp->pcsc.pid = (pid_t)(-1);
slotp->used = 0;
return 0;
}
#endif /*NEED_PCSC_WRAPPER*/
static int
close_pcsc_reader (int slot)
{
#ifdef NEED_PCSC_WRAPPER
return close_pcsc_reader_wrapped (slot);
#else
return close_pcsc_reader_direct (slot);
#endif
}
/* Connect a PC/SC card. */
#ifndef NEED_PCSC_WRAPPER
static int
connect_pcsc_card (int slot)
{
long err;
assert (slot >= 0 && slot < MAX_READER);
if (reader_table[slot].pcsc.card)
return SW_HOST_ALREADY_CONNECTED;
reader_table[slot].atrlen = 0;
reader_table[slot].last_status = 0;
reader_table[slot].is_t0 = 0;
err = pcsc_connect (reader_table[slot].pcsc.context,
reader_table[slot].rdrname,
PCSC_SHARE_EXCLUSIVE,
PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
&reader_table[slot].pcsc.card,
&reader_table[slot].pcsc.protocol);
if (err)
{
reader_table[slot].pcsc.card = 0;
if (err != PCSC_E_NO_SMARTCARD)
log_error ("pcsc_connect failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
}
else
{
char reader[250];
unsigned long readerlen, atrlen;
unsigned long card_state, card_protocol;
atrlen = DIM (reader_table[0].atr);
readerlen = sizeof reader -1 ;
err = pcsc_status (reader_table[slot].pcsc.card,
reader, &readerlen,
&card_state, &card_protocol,
reader_table[slot].atr, &atrlen);
if (err)
log_error ("pcsc_status failed: %s (0x%lx) %lu\n",
pcsc_error_string (err), err, readerlen);
else
{
if (atrlen > DIM (reader_table[0].atr))
log_bug ("ATR returned by pcsc_status is too large\n");
reader_table[slot].atrlen = atrlen;
/* If we got to here we know that a card is present
and usable. Remember this. */
reader_table[slot].last_status = ( APDU_CARD_USABLE
| APDU_CARD_PRESENT
| APDU_CARD_ACTIVE);
reader_table[slot].is_t0 = !!(card_protocol & PCSC_PROTOCOL_T0);
}
}
dump_reader_status (slot);
return pcsc_error_to_sw (err);
}
#endif /*!NEED_PCSC_WRAPPER*/
/* Disconnect a PC/SC card. Note that this succeeds even if the card
is not connected. */
#ifndef NEED_PCSC_WRAPPER
static int
disconnect_pcsc_card (int slot)
{
long err;
assert (slot >= 0 && slot < MAX_READER);
if (!reader_table[slot].pcsc.card)
return 0;
err = pcsc_disconnect (reader_table[slot].pcsc.card, PCSC_LEAVE_CARD);
if (err)
{
log_error ("pcsc_disconnect failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
return SW_HOST_CARD_IO_ERROR;
}
reader_table[slot].pcsc.card = 0;
return 0;
}
#endif /*!NEED_PCSC_WRAPPER*/
#ifndef NEED_PCSC_WRAPPER
static int
reset_pcsc_reader_direct (int slot)
{
int sw;
sw = disconnect_pcsc_card (slot);
if (!sw)
sw = connect_pcsc_card (slot);
return sw;
}
#endif /*NEED_PCSC_WRAPPER*/
#ifdef NEED_PCSC_WRAPPER
static int
reset_pcsc_reader_wrapped (int slot)
{
long err;
reader_table_t slotp;
size_t len;
int i, n;
unsigned char msgbuf[9];
unsigned int dummy_status;
int sw = SW_HOST_CARD_IO_ERROR;
slotp = reader_table + slot;
if (slotp->pcsc.req_fd == -1
|| slotp->pcsc.rsp_fd == -1
|| slotp->pcsc.pid == (pid_t)(-1) )
{
log_error ("pcsc_get_status: pcsc-wrapper not running\n");
return sw;
}
msgbuf[0] = 0x05; /* RESET command. */
len = 0;
msgbuf[1] = (len >> 24);
msgbuf[2] = (len >> 16);
msgbuf[3] = (len >> 8);
msgbuf[4] = (len );
if ( writen (slotp->pcsc.req_fd, msgbuf, 5) )
{
log_error ("error sending PC/SC RESET request: %s\n",
strerror (errno));
goto command_failed;
}
/* Read the response. */
if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
{
log_error ("error receiving PC/SC RESET response: %s\n",
i? strerror (errno) : "premature EOF");
goto command_failed;
}
len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
if (msgbuf[0] != 0x81 || len < 4)
{
log_error ("invalid response header from PC/SC received\n");
goto command_failed;
}
len -= 4; /* Already read the error code. */
if (len > DIM (slotp->atr))
{
log_error ("PC/SC returned a too large ATR (len=%lx)\n",
(unsigned long)len);
sw = SW_HOST_GENERAL_ERROR;
goto command_failed;
}
err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
| (msgbuf[7] << 8 ) | msgbuf[8]);
if (err)
{
log_error ("PC/SC RESET failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
/* If the error code is no smart card, we should not considere
this a major error and close the wrapper. */
sw = pcsc_error_to_sw (err);
if (err == PCSC_E_NO_SMARTCARD)
return sw;
goto command_failed;
}
/* The open function may return a zero for the ATR length to
indicate that no card is present. */
n = len;
if (n)
{
if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n)
{
log_error ("error receiving PC/SC RESET response: %s\n",
i? strerror (errno) : "premature EOF");
goto command_failed;
}
}
slotp->atrlen = len;
/* Read the status so that IS_T0 will be set. */
pcsc_get_status (slot, &dummy_status);
return 0;
command_failed:
close (slotp->pcsc.req_fd);
close (slotp->pcsc.rsp_fd);
slotp->pcsc.req_fd = -1;
slotp->pcsc.rsp_fd = -1;
kill (slotp->pcsc.pid, SIGTERM);
slotp->pcsc.pid = (pid_t)(-1);
slotp->used = 0;
return sw;
}
#endif /* !NEED_PCSC_WRAPPER */
/* Send an PC/SC reset command and return a status word on error or 0
on success. */
static int
reset_pcsc_reader (int slot)
{
#ifdef NEED_PCSC_WRAPPER
return reset_pcsc_reader_wrapped (slot);
#else
return reset_pcsc_reader_direct (slot);
#endif
}
/* Open the PC/SC reader without using the wrapper. Returns -1 on
error or a slot number for the reader. */
#ifndef NEED_PCSC_WRAPPER
static int
open_pcsc_reader_direct (const char *portstr)
{
long err;
int slot;
char *list = NULL;
unsigned long nreader, listlen;
char *p;
slot = new_reader_slot ();
if (slot == -1)
return -1;
/* Fixme: Allocating a context for each slot is not required. One
global context should be sufficient. */
err = pcsc_establish_context (PCSC_SCOPE_SYSTEM, NULL, NULL,
&reader_table[slot].pcsc.context);
if (err)
{
log_error ("pcsc_establish_context failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
reader_table[slot].used = 0;
return -1;
}
err = pcsc_list_readers (reader_table[slot].pcsc.context,
NULL, NULL, &nreader);
if (!err)
{
list = xtrymalloc (nreader+1); /* Better add 1 for safety reasons. */
if (!list)
{
log_error ("error allocating memory for reader list\n");
pcsc_release_context (reader_table[slot].pcsc.context);
reader_table[slot].used = 0;
return -1 /*SW_HOST_OUT_OF_CORE*/;
}
err = pcsc_list_readers (reader_table[slot].pcsc.context,
NULL, list, &nreader);
}
if (err)
{
log_error ("pcsc_list_readers failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
pcsc_release_context (reader_table[slot].pcsc.context);
reader_table[slot].used = 0;
xfree (list);
return -1;
}
listlen = nreader;
p = list;
while (nreader)
{
if (!*p && !p[1])
break;
if (*p)
log_info ("detected reader `%s'\n", p);
if (nreader < (strlen (p)+1))
{
log_error ("invalid response from pcsc_list_readers\n");
break;
}
nreader -= strlen (p)+1;
p += strlen (p) + 1;
}
reader_table[slot].rdrname = xtrymalloc (strlen (portstr? portstr : list)+1);
if (!reader_table[slot].rdrname)
{
log_error ("error allocating memory for reader name\n");
pcsc_release_context (reader_table[slot].pcsc.context);
reader_table[slot].used = 0;
return -1;
}
strcpy (reader_table[slot].rdrname, portstr? portstr : list);
xfree (list);
list = NULL;
reader_table[slot].pcsc.card = 0;
reader_table[slot].atrlen = 0;
reader_table[slot].last_status = 0;
reader_table[slot].connect_card = connect_pcsc_card;
reader_table[slot].disconnect_card = disconnect_pcsc_card;
reader_table[slot].close_reader = close_pcsc_reader;
reader_table[slot].reset_reader = reset_pcsc_reader;
reader_table[slot].get_status_reader = pcsc_get_status;
reader_table[slot].send_apdu_reader = pcsc_send_apdu;
reader_table[slot].dump_status_reader = dump_pcsc_reader_status;
dump_reader_status (slot);
return slot;
}
#endif /*!NEED_PCSC_WRAPPER */
/* Open the PC/SC reader using the pcsc_wrapper program. This is
needed to cope with different thread models and other peculiarities
of libpcsclite. */
#ifdef NEED_PCSC_WRAPPER
static int
open_pcsc_reader_wrapped (const char *portstr)
{
int slot;
reader_table_t slotp;
int fd, rp[2], wp[2];
int n, i;
pid_t pid;
size_t len;
unsigned char msgbuf[9];
int err;
unsigned int dummy_status;
int sw = SW_HOST_CARD_IO_ERROR;
/* Note that we use the constant and not the fucntion because this
code won't be be used under Windows. */
const char *wrapperpgm = GNUPG_LIBEXECDIR "/gnupg-pcsc-wrapper";
if (access (wrapperpgm, X_OK))
{
log_error ("can't run PC/SC access module `%s': %s\n",
wrapperpgm, strerror (errno));
return -1;
}
slot = new_reader_slot ();
if (slot == -1)
return -1;
slotp = reader_table + slot;
/* Fire up the PC/SCc wrapper. We don't use any fork/exec code from
the common directy but implement it directly so that this file
may still be source copied. */
if (pipe (rp) == -1)
{
log_error ("error creating a pipe: %s\n", strerror (errno));
slotp->used = 0;
return -1;
}
if (pipe (wp) == -1)
{
log_error ("error creating a pipe: %s\n", strerror (errno));
close (rp[0]);
close (rp[1]);
slotp->used = 0;
return -1;
}
pid = fork ();
if (pid == -1)
{
log_error ("error forking process: %s\n", strerror (errno));
close (rp[0]);
close (rp[1]);
close (wp[0]);
close (wp[1]);
slotp->used = 0;
return -1;
}
slotp->pcsc.pid = pid;
if (!pid)
{ /*
=== Child ===
*/
/* Double fork. */
pid = fork ();
if (pid == -1)
_exit (31);
if (pid)
_exit (0); /* Immediate exit this parent, so that the child
gets cleaned up by the init process. */
/* Connect our pipes. */
if (wp[0] != 0 && dup2 (wp[0], 0) == -1)
log_fatal ("dup2 stdin failed: %s\n", strerror (errno));
if (rp[1] != 1 && dup2 (rp[1], 1) == -1)
log_fatal ("dup2 stdout failed: %s\n", strerror (errno));
/* Send stderr to the bit bucket. */
fd = open ("/dev/null", O_WRONLY);
if (fd == -1)
log_fatal ("can't open `/dev/null': %s", strerror (errno));
if (fd != 2 && dup2 (fd, 2) == -1)
log_fatal ("dup2 stderr failed: %s\n", strerror (errno));
/* Close all other files. */
n = sysconf (_SC_OPEN_MAX);
if (n < 0)
n = MAX_OPEN_FDS;
for (i=3; i < n; i++)
close(i);
errno = 0;
execl (wrapperpgm,
"pcsc-wrapper",
"--",
"1", /* API version */
opt.pcsc_driver, /* Name of the PC/SC library. */
NULL);
_exit (31);
}
/*
=== Parent ===
*/
close (wp[0]);
close (rp[1]);
slotp->pcsc.req_fd = wp[1];
slotp->pcsc.rsp_fd = rp[0];
/* Wait for the intermediate child to terminate. */
#ifdef USE_GNU_PTH
#define WAIT pth_waitpid
#else
#define WAIT waitpid
#endif
while ( (i=WAIT (pid, NULL, 0)) == -1 && errno == EINTR)
;
#undef WAIT
/* Now send the open request. */
msgbuf[0] = 0x01; /* OPEN command. */
len = portstr? strlen (portstr):0;
msgbuf[1] = (len >> 24);
msgbuf[2] = (len >> 16);
msgbuf[3] = (len >> 8);
msgbuf[4] = (len );
if ( writen (slotp->pcsc.req_fd, msgbuf, 5)
|| (portstr && writen (slotp->pcsc.req_fd, portstr, len)))
{
log_error ("error sending PC/SC OPEN request: %s\n",
strerror (errno));
goto command_failed;
}
/* Read the response. */
if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
{
log_error ("error receiving PC/SC OPEN response: %s\n",
i? strerror (errno) : "premature EOF");
goto command_failed;
}
len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
if (msgbuf[0] != 0x81 || len < 4)
{
log_error ("invalid response header from PC/SC received\n");
goto command_failed;
}
len -= 4; /* Already read the error code. */
if (len > DIM (slotp->atr))
{
log_error ("PC/SC returned a too large ATR (len=%lx)\n",
(unsigned long)len);
goto command_failed;
}
err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
| (msgbuf[7] << 8 ) | msgbuf[8]);
if (err)
{
log_error ("PC/SC OPEN failed: %s\n", pcsc_error_string (err));
sw = pcsc_error_to_sw (err);
goto command_failed;
}
slotp->last_status = 0;
/* The open request may return a zero for the ATR length to
indicate that no card is present. */
n = len;
if (n)
{
if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n)
{
log_error ("error receiving PC/SC OPEN response: %s\n",
i? strerror (errno) : "premature EOF");
goto command_failed;
}
/* If we got to here we know that a card is present
and usable. Thus remember this. */
slotp->last_status = ( APDU_CARD_USABLE
| APDU_CARD_PRESENT
| APDU_CARD_ACTIVE);
}
slotp->atrlen = len;
reader_table[slot].close_reader = close_pcsc_reader;
reader_table[slot].reset_reader = reset_pcsc_reader;
reader_table[slot].get_status_reader = pcsc_get_status;
reader_table[slot].send_apdu_reader = pcsc_send_apdu;
reader_table[slot].dump_status_reader = dump_pcsc_reader_status;
/* Read the status so that IS_T0 will be set. */
pcsc_get_status (slot, &dummy_status);
dump_reader_status (slot);
return slot;
command_failed:
close (slotp->pcsc.req_fd);
close (slotp->pcsc.rsp_fd);
slotp->pcsc.req_fd = -1;
slotp->pcsc.rsp_fd = -1;
kill (slotp->pcsc.pid, SIGTERM);
slotp->pcsc.pid = (pid_t)(-1);
slotp->used = 0;
/* There is no way to return SW. */
return -1;
}
#endif /*NEED_PCSC_WRAPPER*/
static int
open_pcsc_reader (const char *portstr)
{
#ifdef NEED_PCSC_WRAPPER
return open_pcsc_reader_wrapped (portstr);
#else
return open_pcsc_reader_direct (portstr);
#endif
}
#ifdef HAVE_LIBUSB
/*
Internal CCID driver interface.
*/
static void
dump_ccid_reader_status (int slot)
{
log_info ("reader slot %d: using ccid driver\n", slot);
}
static int
close_ccid_reader (int slot)
{
ccid_close_reader (reader_table[slot].ccid.handle);
reader_table[slot].used = 0;
return 0;
}
static int
shutdown_ccid_reader (int slot)
{
ccid_shutdown_reader (reader_table[slot].ccid.handle);
return 0;
}
static int
reset_ccid_reader (int slot)
{
int err;
reader_table_t slotp = reader_table + slot;
unsigned char atr[33];
size_t atrlen;
err = ccid_get_atr (slotp->ccid.handle, atr, sizeof atr, &atrlen);
if (err)
return err;
/* If the reset was successful, update the ATR. */
assert (sizeof slotp->atr >= sizeof atr);
slotp->atrlen = atrlen;
memcpy (slotp->atr, atr, atrlen);
dump_reader_status (slot);
return 0;
}
static int
get_status_ccid (int slot, unsigned int *status)
{
int rc;
int bits;
rc = ccid_slot_status (reader_table[slot].ccid.handle, &bits);
if (rc)
return -1;
if (bits == 0)
*status = (APDU_CARD_USABLE|APDU_CARD_PRESENT|APDU_CARD_ACTIVE);
else if (bits == 1)
*status = APDU_CARD_PRESENT;
else
*status = 0;
return 0;
}
/* Actually send the APDU of length APDULEN to SLOT and return a
maximum of *BUFLEN data in BUFFER, the actual returned size will be
set to BUFLEN. Returns: Internal CCID driver error code. */
static int
send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen,
unsigned char *buffer, size_t *buflen,
struct pininfo_s *pininfo)
{
long err;
size_t maxbuflen;
/* If we don't have an ATR, we need to reset the reader first. */
if (!reader_table[slot].atrlen
&& (err = reset_ccid_reader (slot)))
return err;
if (DBG_CARD_IO)
log_printhex (" APDU_data:", apdu, apdulen);
maxbuflen = *buflen;
if (pininfo)
err = ccid_transceive_secure (reader_table[slot].ccid.handle,
apdu, apdulen,
pininfo->mode,
pininfo->minlen,
pininfo->maxlen,
pininfo->padlen,
buffer, maxbuflen, buflen);
else
err = ccid_transceive (reader_table[slot].ccid.handle,
apdu, apdulen,
buffer, maxbuflen, buflen);
if (err)
log_error ("ccid_transceive failed: (0x%lx)\n",
err);
return err;
}
/* Check whether the CCID reader supports the ISO command code COMMAND
on the keypad. Return 0 on success. For a description of the pin
parameters, see ccid-driver.c */
static int
check_ccid_keypad (int slot, int command, int pin_mode,
int pinlen_min, int pinlen_max, int pin_padlen)
{
unsigned char apdu[] = { 0, 0, 0, 0x81 };
apdu[1] = command;
return ccid_transceive_secure (reader_table[slot].ccid.handle,
apdu, sizeof apdu,
pin_mode, pinlen_min, pinlen_max, pin_padlen,
NULL, 0, NULL);
}
/* Open the reader and try to read an ATR. */
static int
open_ccid_reader (const char *portstr)
{
int err;
int slot;
reader_table_t slotp;
slot = new_reader_slot ();
if (slot == -1)
return -1;
slotp = reader_table + slot;
err = ccid_open_reader (&slotp->ccid.handle, portstr);
if (err)
{
slotp->used = 0;
return -1;
}
err = ccid_get_atr (slotp->ccid.handle,
slotp->atr, sizeof slotp->atr, &slotp->atrlen);
if (err)
{
slotp->atrlen = 0;
err = 0;
}
else
{
/* If we got to here we know that a card is present
and usable. Thus remember this. */
reader_table[slot].last_status = (APDU_CARD_USABLE
| APDU_CARD_PRESENT
| APDU_CARD_ACTIVE);
}
reader_table[slot].close_reader = close_ccid_reader;
reader_table[slot].shutdown_reader = shutdown_ccid_reader;
reader_table[slot].reset_reader = reset_ccid_reader;
reader_table[slot].get_status_reader = get_status_ccid;
reader_table[slot].send_apdu_reader = send_apdu_ccid;
reader_table[slot].check_keypad = check_ccid_keypad;
reader_table[slot].dump_status_reader = dump_ccid_reader_status;
dump_reader_status (slot);
return slot;
}
#endif /* HAVE_LIBUSB */
#ifdef USE_G10CODE_RAPDU
/*
The Remote APDU Interface.
This uses the Remote APDU protocol to contact a reader.
The port number is actually an index into the list of ports as
returned via the protocol.
*/
static int
rapdu_status_to_sw (int status)
{
int rc;
switch (status)
{
case RAPDU_STATUS_SUCCESS: rc = 0; break;
case RAPDU_STATUS_INVCMD:
case RAPDU_STATUS_INVPROT:
case RAPDU_STATUS_INVSEQ:
case RAPDU_STATUS_INVCOOKIE:
case RAPDU_STATUS_INVREADER: rc = SW_HOST_INV_VALUE; break;
case RAPDU_STATUS_TIMEOUT: rc = SW_HOST_CARD_IO_ERROR; break;
case RAPDU_STATUS_CARDIO: rc = SW_HOST_CARD_IO_ERROR; break;
case RAPDU_STATUS_NOCARD: rc = SW_HOST_NO_CARD; break;
case RAPDU_STATUS_CARDCHG: rc = SW_HOST_NO_CARD; break;
case RAPDU_STATUS_BUSY: rc = SW_HOST_BUSY; break;
case RAPDU_STATUS_NEEDRESET: rc = SW_HOST_CARD_INACTIVE; break;
default: rc = SW_HOST_GENERAL_ERROR; break;
}
return rc;
}
static int
close_rapdu_reader (int slot)
{
rapdu_release (reader_table[slot].rapdu.handle);
reader_table[slot].used = 0;
return 0;
}
static int
reset_rapdu_reader (int slot)
{
int err;
reader_table_t slotp;
rapdu_msg_t msg = NULL;
slotp = reader_table + slot;
err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_RESET);
if (err)
{
log_error ("sending rapdu command RESET failed: %s\n",
err < 0 ? strerror (errno): rapdu_strerror (err));
rapdu_msg_release (msg);
return rapdu_status_to_sw (err);
}
err = rapdu_read_msg (slotp->rapdu.handle, &msg);
if (err)
{
log_error ("receiving rapdu message failed: %s\n",
err < 0 ? strerror (errno): rapdu_strerror (err));
rapdu_msg_release (msg);
return rapdu_status_to_sw (err);
}
if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen)
{
int sw = rapdu_status_to_sw (msg->cmd);
log_error ("rapdu command RESET failed: %s\n",
rapdu_strerror (msg->cmd));
rapdu_msg_release (msg);
return sw;
}
if (msg->datalen > DIM (slotp->atr))
{
log_error ("ATR returned by the RAPDU layer is too large\n");
rapdu_msg_release (msg);
return SW_HOST_INV_VALUE;
}
slotp->atrlen = msg->datalen;
memcpy (slotp->atr, msg->data, msg->datalen);
rapdu_msg_release (msg);
return 0;
}
static int
my_rapdu_get_status (int slot, unsigned int *status)
{
int err;
reader_table_t slotp;
rapdu_msg_t msg = NULL;
int oldslot;
slotp = reader_table + slot;
oldslot = rapdu_set_reader (slotp->rapdu.handle, slot);
err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_GET_STATUS);
rapdu_set_reader (slotp->rapdu.handle, oldslot);
if (err)
{
log_error ("sending rapdu command GET_STATUS failed: %s\n",
err < 0 ? strerror (errno): rapdu_strerror (err));
return rapdu_status_to_sw (err);
}
err = rapdu_read_msg (slotp->rapdu.handle, &msg);
if (err)
{
log_error ("receiving rapdu message failed: %s\n",
err < 0 ? strerror (errno): rapdu_strerror (err));
rapdu_msg_release (msg);
return rapdu_status_to_sw (err);
}
if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen)
{
int sw = rapdu_status_to_sw (msg->cmd);
log_error ("rapdu command GET_STATUS failed: %s\n",
rapdu_strerror (msg->cmd));
rapdu_msg_release (msg);
return sw;
}
*status = msg->data[0];
rapdu_msg_release (msg);
return 0;
}
/* Actually send the APDU of length APDULEN to SLOT and return a
maximum of *BUFLEN data in BUFFER, the actual returned size will be
set to BUFLEN. Returns: APDU error code. */
static int
my_rapdu_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
unsigned char *buffer, size_t *buflen,
struct pininfo_s *pininfo)
{
int err;
reader_table_t slotp;
rapdu_msg_t msg = NULL;
size_t maxlen = *buflen;
slotp = reader_table + slot;
*buflen = 0;
if (DBG_CARD_IO)
log_printhex (" APDU_data:", apdu, apdulen);
if (apdulen < 4)
{
log_error ("rapdu_send_apdu: APDU is too short\n");
return SW_HOST_INV_VALUE;
}
err = rapdu_send_apdu (slotp->rapdu.handle, apdu, apdulen);
if (err)
{
log_error ("sending rapdu command APDU failed: %s\n",
err < 0 ? strerror (errno): rapdu_strerror (err));
rapdu_msg_release (msg);
return rapdu_status_to_sw (err);
}
err = rapdu_read_msg (slotp->rapdu.handle, &msg);
if (err)
{
log_error ("receiving rapdu message failed: %s\n",
err < 0 ? strerror (errno): rapdu_strerror (err));
rapdu_msg_release (msg);
return rapdu_status_to_sw (err);
}
if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen)
{
int sw = rapdu_status_to_sw (msg->cmd);
log_error ("rapdu command APDU failed: %s\n",
rapdu_strerror (msg->cmd));
rapdu_msg_release (msg);
return sw;
}
if (msg->datalen > maxlen)
{
log_error ("rapdu response apdu too large\n");
rapdu_msg_release (msg);
return SW_HOST_INV_VALUE;
}
*buflen = msg->datalen;
memcpy (buffer, msg->data, msg->datalen);
rapdu_msg_release (msg);
return 0;
}
static int
open_rapdu_reader (int portno,
const unsigned char *cookie, size_t length,
int (*readfnc) (void *opaque,
void *buffer, size_t size),
void *readfnc_value,
int (*writefnc) (void *opaque,
const void *buffer, size_t size),
void *writefnc_value,
void (*closefnc) (void *opaque),
void *closefnc_value)
{
int err;
int slot;
reader_table_t slotp;
rapdu_msg_t msg = NULL;
slot = new_reader_slot ();
if (slot == -1)
return -1;
slotp = reader_table + slot;
slotp->rapdu.handle = rapdu_new ();
if (!slotp->rapdu.handle)
{
slotp->used = 0;
return -1;
}
rapdu_set_reader (slotp->rapdu.handle, portno);
rapdu_set_iofunc (slotp->rapdu.handle,
readfnc, readfnc_value,
writefnc, writefnc_value,
closefnc, closefnc_value);
rapdu_set_cookie (slotp->rapdu.handle, cookie, length);
/* First try to get the current ATR, but if the card is inactive
issue a reset instead. */
err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_GET_ATR);
if (err == RAPDU_STATUS_NEEDRESET)
err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_RESET);
if (err)
{
log_info ("sending rapdu command GET_ATR/RESET failed: %s\n",
err < 0 ? strerror (errno): rapdu_strerror (err));
goto failure;
}
err = rapdu_read_msg (slotp->rapdu.handle, &msg);
if (err)
{
log_info ("receiving rapdu message failed: %s\n",
err < 0 ? strerror (errno): rapdu_strerror (err));
goto failure;
}
if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen)
{
log_info ("rapdu command GET ATR failed: %s\n",
rapdu_strerror (msg->cmd));
goto failure;
}
if (msg->datalen > DIM (slotp->atr))
{
log_error ("ATR returned by the RAPDU layer is too large\n");
goto failure;
}
slotp->atrlen = msg->datalen;
memcpy (slotp->atr, msg->data, msg->datalen);
reader_table[slot].close_reader = close_rapdu_reader;
reader_table[slot].reset_reader = reset_rapdu_reader;
reader_table[slot].get_status_reader = my_rapdu_get_status;
reader_table[slot].send_apdu_reader = my_rapdu_send_apdu;
reader_table[slot].check_keypad = NULL;
reader_table[slot].dump_status_reader = NULL;
dump_reader_status (slot);
rapdu_msg_release (msg);
return slot;
failure:
rapdu_msg_release (msg);
rapdu_release (slotp->rapdu.handle);
slotp->used = 0;
return -1;
}
#endif /*USE_G10CODE_RAPDU*/
/*
Driver Access
*/
static int
lock_slot (int slot)
{
#ifdef USE_GNU_PTH
if (!pth_mutex_acquire (&reader_table[slot].lock, 0, NULL))
{
log_error ("failed to acquire apdu lock: %s\n", strerror (errno));
return SW_HOST_LOCKING_FAILED;
}
#endif /*USE_GNU_PTH*/
return 0;
}
static int
trylock_slot (int slot)
{
#ifdef USE_GNU_PTH
if (!pth_mutex_acquire (&reader_table[slot].lock, TRUE, NULL))
{
if (errno == EBUSY)
return SW_HOST_BUSY;
log_error ("failed to acquire apdu lock: %s\n", strerror (errno));
return SW_HOST_LOCKING_FAILED;
}
#endif /*USE_GNU_PTH*/
return 0;
}
static void
unlock_slot (int slot)
{
#ifdef USE_GNU_PTH
if (!pth_mutex_release (&reader_table[slot].lock))
log_error ("failed to release apdu lock: %s\n", strerror (errno));
#endif /*USE_GNU_PTH*/
}
/* Open the reader and return an internal slot number or -1 on
error. If PORTSTR is NULL we default to a suitable port (for ctAPI:
the first USB reader. For PC/SC the first listed reader). */
int
apdu_open_reader (const char *portstr)
{
static int pcsc_api_loaded, ct_api_loaded;
#ifdef HAVE_LIBUSB
if (!opt.disable_ccid)
{
int slot, i;
const char *s;
slot = open_ccid_reader (portstr);
if (slot != -1)
return slot; /* got one */
/* If a CCID reader specification has been given, the user does
not want a fallback to other drivers. */
if (portstr)
for (s=portstr, i=0; *s; s++)
if (*s == ':' && (++i == 3))
return -1;
}
#endif /* HAVE_LIBUSB */
if (opt.ctapi_driver && *opt.ctapi_driver)
{
int port = portstr? atoi (portstr) : 32768;
if (!ct_api_loaded)
{
void *handle;
handle = dlopen (opt.ctapi_driver, RTLD_LAZY);
if (!handle)
{
log_error ("apdu_open_reader: failed to open driver: %s\n",
dlerror ());
return -1;
}
CT_init = dlsym (handle, "CT_init");
CT_data = dlsym (handle, "CT_data");
CT_close = dlsym (handle, "CT_close");
if (!CT_init || !CT_data || !CT_close)
{
log_error ("apdu_open_reader: invalid CT-API driver\n");
dlclose (handle);
return -1;
}
ct_api_loaded = 1;
}
return open_ct_reader (port);
}
/* No ctAPI configured, so lets try the PC/SC API */
if (!pcsc_api_loaded)
{
#ifndef NEED_PCSC_WRAPPER
void *handle;
handle = dlopen (opt.pcsc_driver, RTLD_LAZY);
if (!handle)
{
log_error ("apdu_open_reader: failed to open driver `%s': %s\n",
opt.pcsc_driver, dlerror ());
return -1;
}
pcsc_establish_context = dlsym (handle, "SCardEstablishContext");
pcsc_release_context = dlsym (handle, "SCardReleaseContext");
pcsc_list_readers = dlsym (handle, "SCardListReaders");
#if defined(_WIN32) || defined(__CYGWIN__)
if (!pcsc_list_readers)
pcsc_list_readers = dlsym (handle, "SCardListReadersA");
#endif
pcsc_get_status_change = dlsym (handle, "SCardGetStatusChange");
#if defined(_WIN32) || defined(__CYGWIN__)
if (!pcsc_get_status_change)
pcsc_get_status_change = dlsym (handle, "SCardGetStatusChangeA");
#endif
pcsc_connect = dlsym (handle, "SCardConnect");
#if defined(_WIN32) || defined(__CYGWIN__)
if (!pcsc_connect)
pcsc_connect = dlsym (handle, "SCardConnectA");
#endif
pcsc_reconnect = dlsym (handle, "SCardReconnect");
#if defined(_WIN32) || defined(__CYGWIN__)
if (!pcsc_reconnect)
pcsc_reconnect = dlsym (handle, "SCardReconnectA");
#endif
pcsc_disconnect = dlsym (handle, "SCardDisconnect");
pcsc_status = dlsym (handle, "SCardStatus");
#if defined(_WIN32) || defined(__CYGWIN__)
if (!pcsc_status)
pcsc_status = dlsym (handle, "SCardStatusA");
#endif
pcsc_begin_transaction = dlsym (handle, "SCardBeginTransaction");
pcsc_end_transaction = dlsym (handle, "SCardEndTransaction");
pcsc_transmit = dlsym (handle, "SCardTransmit");
pcsc_set_timeout = dlsym (handle, "SCardSetTimeout");
if (!pcsc_establish_context
|| !pcsc_release_context
|| !pcsc_list_readers
|| !pcsc_get_status_change
|| !pcsc_connect
|| !pcsc_reconnect
|| !pcsc_disconnect
|| !pcsc_status
|| !pcsc_begin_transaction
|| !pcsc_end_transaction
|| !pcsc_transmit
/* || !pcsc_set_timeout */)
{
/* Note that set_timeout is currently not used and also not
available under Windows. */
log_error ("apdu_open_reader: invalid PC/SC driver "
"(%d%d%d%d%d%d%d%d%d%d%d%d)\n",
!!pcsc_establish_context,
!!pcsc_release_context,
!!pcsc_list_readers,
!!pcsc_get_status_change,
!!pcsc_connect,
!!pcsc_reconnect,
!!pcsc_disconnect,
!!pcsc_status,
!!pcsc_begin_transaction,
!!pcsc_end_transaction,
!!pcsc_transmit,
!!pcsc_set_timeout );
dlclose (handle);
return -1;
}
#endif /*!NEED_PCSC_WRAPPER*/
pcsc_api_loaded = 1;
}
return open_pcsc_reader (portstr);
}
/* Open an remote reader and return an internal slot number or -1 on
error. This function is an alternative to apdu_open_reader and used
with remote readers only. Note that the supplied CLOSEFNC will
only be called once and the slot will not be valid afther this.
If PORTSTR is NULL we default to the first availabe port.
*/
int
apdu_open_remote_reader (const char *portstr,
const unsigned char *cookie, size_t length,
int (*readfnc) (void *opaque,
void *buffer, size_t size),
void *readfnc_value,
int (*writefnc) (void *opaque,
const void *buffer, size_t size),
void *writefnc_value,
void (*closefnc) (void *opaque),
void *closefnc_value)
{
#ifdef USE_G10CODE_RAPDU
return open_rapdu_reader (portstr? atoi (portstr) : 0,
cookie, length,
readfnc, readfnc_value,
writefnc, writefnc_value,
closefnc, closefnc_value);
#else
(void)portstr;
(void)cookie;
(void)length;
(void)readfnc;
(void)readfnc_value;
(void)writefnc;
(void)writefnc_value;
(void)closefnc;
(void)closefnc_value;
#ifdef _WIN32
errno = ENOENT;
#else
errno = ENOSYS;
#endif
return -1;
#endif
}
int
apdu_close_reader (int slot)
{
int sw;
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
sw = apdu_disconnect (slot);
if (sw)
return sw;
if (reader_table[slot].close_reader)
return reader_table[slot].close_reader (slot);
return SW_HOST_NOT_SUPPORTED;
}
/* Shutdown a reader; that is basically the same as a close but keeps
the handle ready for later use. A apdu_reset_reader or apdu_connect
should be used to get it active again. */
int
apdu_shutdown_reader (int slot)
{
int sw;
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
sw = apdu_disconnect (slot);
if (sw)
return sw;
if (reader_table[slot].shutdown_reader)
return reader_table[slot].shutdown_reader (slot);
return SW_HOST_NOT_SUPPORTED;
}
/* Enumerate all readers and return information on whether this reader
is in use. The caller should start with SLOT set to 0 and
increment it with each call until an error is returned. */
int
apdu_enum_reader (int slot, int *used)
{
if (slot < 0 || slot >= MAX_READER)
return SW_HOST_NO_DRIVER;
*used = reader_table[slot].used;
return 0;
}
/* Connect a card. This is used to power up the card and make sure
that an ATR is available. */
int
apdu_connect (int slot)
{
int sw;
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
/* Only if the access method provides a connect function we use it.
If not, we expect that the card has been implicitly connected by
apdu_open_reader. */
if (reader_table[slot].connect_card)
{
sw = lock_slot (slot);
if (!sw)
{
sw = reader_table[slot].connect_card (slot);
unlock_slot (slot);
}
}
else
sw = 0;
return sw;
}
int
apdu_disconnect (int slot)
{
int sw;
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
if (reader_table[slot].disconnect_card)
{
sw = lock_slot (slot);
if (!sw)
{
sw = reader_table[slot].disconnect_card (slot);
unlock_slot (slot);
}
}
else
sw = 0;
return sw;
}
/* Do a reset for the card in reader at SLOT. */
int
apdu_reset (int slot)
{
int sw;
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
if ((sw = lock_slot (slot)))
return sw;
reader_table[slot].last_status = 0;
if (reader_table[slot].reset_reader)
sw = reader_table[slot].reset_reader (slot);
if (!sw)
{
/* If we got to here we know that a card is present
and usable. Thus remember this. */
reader_table[slot].last_status = (APDU_CARD_USABLE
| APDU_CARD_PRESENT
| APDU_CARD_ACTIVE);
}
unlock_slot (slot);
return sw;
}
/* Activate a card if it has not yet been done. This is a kind of
reset-if-required. It is useful to test for presence of a card
before issuing a bunch of apdu commands. It does not wait on a
locked card. */
int
apdu_activate (int slot)
{
int sw;
unsigned int s;
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
if ((sw = trylock_slot (slot)))
return sw;
if (reader_table[slot].get_status_reader)
sw = reader_table[slot].get_status_reader (slot, &s);
if (!sw)
{
if (!(s & 2)) /* Card not present. */
sw = SW_HOST_NO_CARD;
else if ( ((s & 2) && !(s & 4))
|| !reader_table[slot].atrlen )
{
/* We don't have an ATR or a card is present though inactive:
do a reset now. */
if (reader_table[slot].reset_reader)
{
reader_table[slot].last_status = 0;
sw = reader_table[slot].reset_reader (slot);
if (!sw)
{
/* If we got to here we know that a card is present
and usable. Thus remember this. */
reader_table[slot].last_status = (APDU_CARD_USABLE
| APDU_CARD_PRESENT
| APDU_CARD_ACTIVE);
}
}
}
}
unlock_slot (slot);
return sw;
}
unsigned char *
apdu_get_atr (int slot, size_t *atrlen)
{
unsigned char *buf;
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return NULL;
if (!reader_table[slot].atrlen)
return NULL;
buf = xtrymalloc (reader_table[slot].atrlen);
if (!buf)
return NULL;
memcpy (buf, reader_table[slot].atr, reader_table[slot].atrlen);
*atrlen = reader_table[slot].atrlen;
return buf;
}
/* Retrieve the status for SLOT. The function does only wait for the
card to become available if HANG is set to true. On success the
bits in STATUS will be set to
APDU_CARD_USABLE (bit 0) = card present and usable
APDU_CARD_PRESENT (bit 1) = card present
APDU_CARD_ACTIVE (bit 2) = card active
(bit 3) = card access locked [not yet implemented]
For must applications, testing bit 0 is sufficient.
CHANGED will receive the value of the counter tracking the number
of card insertions. This value may be used to detect a card
change.
*/
int
apdu_get_status (int slot, int hang,
unsigned int *status, unsigned int *changed)
{
int sw;
unsigned int s;
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
if ((sw = hang? lock_slot (slot) : trylock_slot (slot)))
return sw;
if (reader_table[slot].get_status_reader)
sw = reader_table[slot].get_status_reader (slot, &s);
unlock_slot (slot);
if (sw)
{
reader_table[slot].last_status = 0;
return sw;
}
/* Keep track of changes. */
if (s != reader_table[slot].last_status
|| !reader_table[slot].any_status )
{
reader_table[slot].change_counter++;
- /* Make sure that the ATR is invalid so that a reset will be by
- activate. */
+ /* Make sure that the ATR is invalid so that a reset will be
+ triggered by activate. */
reader_table[slot].atrlen = 0;
}
reader_table[slot].any_status = 1;
+ reader_table[slot].last_status = s;
if (status)
*status = s;
if (changed)
*changed = reader_table[slot].change_counter;
return 0;
}
/* Check whether the reader supports the ISO command code COMMAND on
the keypad. Return 0 on success. For a description of the pin
parameters, see ccid-driver.c */
int
apdu_check_keypad (int slot, int command, int pin_mode,
int pinlen_min, int pinlen_max, int pin_padlen)
{
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
if (reader_table[slot].check_keypad)
return reader_table[slot].check_keypad (slot, command,
pin_mode, pinlen_min, pinlen_max,
pin_padlen);
else
return SW_HOST_NOT_SUPPORTED;
}
/* Dispatcher for the actual send_apdu function. Note, that this
function should be called in locked state. */
static int
send_apdu (int slot, unsigned char *apdu, size_t apdulen,
unsigned char *buffer, size_t *buflen, struct pininfo_s *pininfo)
{
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
if (reader_table[slot].send_apdu_reader)
return reader_table[slot].send_apdu_reader (slot,
apdu, apdulen,
buffer, buflen,
pininfo);
else
return SW_HOST_NOT_SUPPORTED;
}
/* Core APDU tranceiver function. Parameters are described at
apdu_send_le with the exception of PININFO which indicates keypad
related operations if not NULL. If EXTENDED_MODE is not NULL
command chaining or extended length will be used according to these
values:
n < 0 := Use command chaining without the data part limited to -n
in each chunk. If -1 is used a default value is used.
n == 1 := Use extended length for input and output with out a
length limit.
n > 1 := Use extended length with up to N bytes.
*/
static int
send_le (int slot, int class, int ins, int p0, int p1,
int lc, const char *data, int le,
unsigned char **retbuf, size_t *retbuflen,
struct pininfo_s *pininfo, int extended_mode)
{
#define RESULTLEN 258
unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in
the driver. */
size_t resultlen;
unsigned char apdu[5+256+1];
size_t apdulen;
int sw;
long rc; /* We need a long here due to PC/SC. */
int did_exact_length_hack = 0;
int use_chaining = 0;
int lc_chunk;
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
if (DBG_CARD_IO)
log_debug ("send apdu: c=%02X i=%02X p0=%02X p1=%02X lc=%d le=%d em=%d\n",
class, ins, p0, p1, lc, le, extended_mode);
if (lc != -1 && (lc > 255 || lc < 0))
{
/* Data does not fit into an APDU. What we do now dependes on
the EXTENDED_MODE parameter. */
if (!extended_mode)
return SW_WRONG_LENGTH; /* No way. to send such an APDU. */
else if (extended_mode > 0)
return SW_HOST_NOT_SUPPORTED; /* FIXME. */
else if (extended_mode < 0)
{
/* Send APDU using chaining mode. */
if (lc > 16384)
return SW_WRONG_LENGTH; /* Sanity check. */
if ((class&0xf0) != 0)
return SW_HOST_INV_VALUE; /* Upper 4 bits need to be 0. */
use_chaining = extended_mode == -1? 255 : -extended_mode;
use_chaining &= 0xff;
}
else
return SW_HOST_INV_VALUE;
}
if (le != -1 && (le > 256 || le < 0))
return SW_WRONG_LENGTH;
if ((!data && lc != -1) || (data && lc == -1))
return SW_HOST_INV_VALUE;
if ((sw = lock_slot (slot)))
return sw;
do
{
apdulen = 0;
apdu[apdulen] = class;
if (use_chaining && lc > 255)
{
apdu[apdulen] |= 0x10;
assert (use_chaining < 256);
lc_chunk = use_chaining;
lc -= use_chaining;
}
else
{
use_chaining = 0;
lc_chunk = lc;
}
apdulen++;
apdu[apdulen++] = ins;
apdu[apdulen++] = p0;
apdu[apdulen++] = p1;
if (lc_chunk != -1)
{
apdu[apdulen++] = lc_chunk;
memcpy (apdu+apdulen, data, lc_chunk);
data += lc_chunk;
apdulen += lc_chunk;
/* T=0 does not allow the use of Lc together with Le; thus
disable Le in this case. */
if (reader_table[slot].is_t0)
le = -1;
}
if (le != -1)
apdu[apdulen++] = le; /* Truncation is okay because 0 means 256. */
/* As safeguard don't pass any garbage from the stack to the driver. */
assert (sizeof (apdu) >= apdulen);
memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
exact_length_hack:
resultlen = RESULTLEN;
rc = send_apdu (slot, apdu, apdulen, result, &resultlen, pininfo);
if (rc || resultlen < 2)
{
log_error ("apdu_send_simple(%d) failed: %s\n",
slot, apdu_strerror (rc));
unlock_slot (slot);
return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
}
sw = (result[resultlen-2] << 8) | result[resultlen-1];
if (!did_exact_length_hack && SW_EXACT_LENGTH_P (sw))
{
apdu[apdulen-1] = (sw & 0x00ff);
did_exact_length_hack = 1;
goto exact_length_hack;
}
}
while (use_chaining && sw == SW_SUCCESS);
/* Store away the returned data but strip the statusword. */
resultlen -= 2;
if (DBG_CARD_IO)
{
log_debug (" response: sw=%04X datalen=%d\n",
sw, (unsigned int)resultlen);
if ( !retbuf && (sw == SW_SUCCESS || (sw & 0xff00) == SW_MORE_DATA))
log_printhex (" dump: ", result, resultlen);
}
if (sw == SW_SUCCESS || sw == SW_EOF_REACHED)
{
if (retbuf)
{
*retbuf = xtrymalloc (resultlen? resultlen : 1);
if (!*retbuf)
{
unlock_slot (slot);
return SW_HOST_OUT_OF_CORE;
}
*retbuflen = resultlen;
memcpy (*retbuf, result, resultlen);
}
}
else if ((sw & 0xff00) == SW_MORE_DATA)
{
unsigned char *p = NULL, *tmp;
size_t bufsize = 4096;
/* It is likely that we need to return much more data, so we
start off with a large buffer. */
if (retbuf)
{
*retbuf = p = xtrymalloc (bufsize);
if (!*retbuf)
{
unlock_slot (slot);
return SW_HOST_OUT_OF_CORE;
}
assert (resultlen < bufsize);
memcpy (p, result, resultlen);
p += resultlen;
}
do
{
int len = (sw & 0x00ff);
if (DBG_CARD_IO)
log_debug ("apdu_send_simple(%d): %d more bytes available\n",
slot, len);
apdulen = 0;
apdu[apdulen++] = class;
apdu[apdulen++] = 0xC0;
apdu[apdulen++] = 0;
apdu[apdulen++] = 0;
apdu[apdulen++] = len;
memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
resultlen = RESULTLEN;
rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
if (rc || resultlen < 2)
{
log_error ("apdu_send_simple(%d) for get response failed: %s\n",
slot, apdu_strerror (rc));
unlock_slot (slot);
return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
}
sw = (result[resultlen-2] << 8) | result[resultlen-1];
resultlen -= 2;
if (DBG_CARD_IO)
{
log_debug (" more: sw=%04X datalen=%d\n",
sw, (unsigned int)resultlen);
if (!retbuf && (sw==SW_SUCCESS || (sw&0xff00)==SW_MORE_DATA))
log_printhex (" dump: ", result, resultlen);
}
if ((sw & 0xff00) == SW_MORE_DATA
|| sw == SW_SUCCESS
|| sw == SW_EOF_REACHED )
{
if (retbuf && resultlen)
{
if (p - *retbuf + resultlen > bufsize)
{
bufsize += resultlen > 4096? resultlen: 4096;
tmp = xtryrealloc (*retbuf, bufsize);
if (!tmp)
{
unlock_slot (slot);
return SW_HOST_OUT_OF_CORE;
}
p = tmp + (p - *retbuf);
*retbuf = tmp;
}
memcpy (p, result, resultlen);
p += resultlen;
}
}
else
log_info ("apdu_send_simple(%d) "
"got unexpected status %04X from get response\n",
slot, sw);
}
while ((sw & 0xff00) == SW_MORE_DATA);
if (retbuf)
{
*retbuflen = p - *retbuf;
tmp = xtryrealloc (*retbuf, *retbuflen);
if (tmp)
*retbuf = tmp;
}
}
unlock_slot (slot);
if (DBG_CARD_IO && retbuf && sw == SW_SUCCESS)
log_printhex (" dump: ", *retbuf, *retbuflen);
return sw;
#undef RESULTLEN
}
/* Send an APDU to the card in SLOT. The APDU is created from all
given parameters: CLASS, INS, P0, P1, LC, DATA, LE. A value of -1
for LC won't sent this field and the data field; in this case DATA
must also be passed as NULL. The return value is the status word
or -1 for an invalid SLOT or other non card related error. If
RETBUF is not NULL, it will receive an allocated buffer with the
returned data. The length of that data will be put into
*RETBUFLEN. The caller is reponsible for releasing the buffer even
in case of errors. */
int
apdu_send_le(int slot, int class, int ins, int p0, int p1,
int lc, const char *data, int le,
unsigned char **retbuf, size_t *retbuflen)
{
return send_le (slot, class, ins, p0, p1,
lc, data, le,
retbuf, retbuflen,
NULL, 0);
}
/* Send an APDU to the card in SLOT. The APDU is created from all
given parameters: CLASS, INS, P0, P1, LC, DATA. A value of -1 for
LC won't sent this field and the data field; in this case DATA must
also be passed as NULL. The return value is the status word or -1
for an invalid SLOT or other non card related error. If RETBUF is
not NULL, it will receive an allocated buffer with the returned
data. The length of that data will be put into *RETBUFLEN. The
caller is reponsible for releasing the buffer even in case of
errors. */
int
apdu_send (int slot, int class, int ins, int p0, int p1,
int lc, const char *data, unsigned char **retbuf, size_t *retbuflen)
{
return send_le (slot, class, ins, p0, p1, lc, data, 256,
retbuf, retbuflen, NULL, 0);
}
/* Send an APDU to the card in SLOT. The APDU is created from all
given parameters: CLASS, INS, P0, P1, LC, DATA. A value of -1 for
LC won't sent this field and the data field; in this case DATA must
also be passed as NULL. The return value is the status word or -1
for an invalid SLOT or other non card related error. No data will be
returned. */
int
apdu_send_simple (int slot, int extended_mode,
int class, int ins, int p0, int p1,
int lc, const char *data)
{
return send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL, NULL,
extended_mode);
}
/* Same as apdu_send_simple but uses the keypad of the reader. */
int
apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1,
int lc, const char *data,
int pin_mode,
int pinlen_min, int pinlen_max, int pin_padlen)
{
struct pininfo_s pininfo;
pininfo.mode = pin_mode;
pininfo.minlen = pinlen_min;
pininfo.maxlen = pinlen_max;
pininfo.padlen = pin_padlen;
return send_le (slot, class, ins, p0, p1, lc, data, -1,
NULL, NULL, &pininfo, 0);
}
/* This is a more generic version of the apdu sending routine. It
takes an already formatted APDU in APDUDATA or length APDUDATALEN
and returns the with an APDU including the status word. With
HANDLE_MORE set to true this function will handle the MORE DATA
status and return all APDUs concatenated with one status word at
the end. The function does not return a regular status word but 0
on success. If the slot is locked, the function returns
immediately with an error. */
int
apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
int handle_more,
unsigned char **retbuf, size_t *retbuflen)
{
#define RESULTLEN 258
unsigned char apdu[5+256+1];
size_t apdulen;
unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in
the driver. */
size_t resultlen;
int sw;
long rc; /* we need a long here due to PC/SC. */
int class;
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
if ((sw = trylock_slot (slot)))
return sw;
/* We simply trunctate a too long APDU. */
if (apdudatalen > sizeof apdu)
apdudatalen = sizeof apdu;
apdulen = apdudatalen;
memcpy (apdu, apdudata, apdudatalen);
class = apdulen? *apdu : 0;
resultlen = RESULTLEN;
rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
if (rc || resultlen < 2)
{
log_error ("apdu_send_direct(%d) failed: %s\n",
slot, apdu_strerror (rc));
unlock_slot (slot);
return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
}
sw = (result[resultlen-2] << 8) | result[resultlen-1];
/* Store away the returned data but strip the statusword. */
resultlen -= 2;
if (DBG_CARD_IO)
{
log_debug (" response: sw=%04X datalen=%d\n",
sw, (unsigned int)resultlen);
if ( !retbuf && (sw == SW_SUCCESS || (sw & 0xff00) == SW_MORE_DATA))
log_printhex (" dump: ", result, resultlen);
}
if (handle_more && (sw & 0xff00) == SW_MORE_DATA)
{
unsigned char *p = NULL, *tmp;
size_t bufsize = 4096;
/* It is likely that we need to return much more data, so we
start off with a large buffer. */
if (retbuf)
{
*retbuf = p = xtrymalloc (bufsize + 2);
if (!*retbuf)
{
unlock_slot (slot);
return SW_HOST_OUT_OF_CORE;
}
assert (resultlen < bufsize);
memcpy (p, result, resultlen);
p += resultlen;
}
do
{
int len = (sw & 0x00ff);
if (DBG_CARD_IO)
log_debug ("apdu_send_direct(%d): %d more bytes available\n",
slot, len);
apdulen = 0;
apdu[apdulen++] = class;
apdu[apdulen++] = 0xC0;
apdu[apdulen++] = 0;
apdu[apdulen++] = 0;
apdu[apdulen++] = len;
memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
resultlen = RESULTLEN;
rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
if (rc || resultlen < 2)
{
log_error ("apdu_send_direct(%d) for get response failed: %s\n",
slot, apdu_strerror (rc));
unlock_slot (slot);
return rc ? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
}
sw = (result[resultlen-2] << 8) | result[resultlen-1];
resultlen -= 2;
if (DBG_CARD_IO)
{
log_debug (" more: sw=%04X datalen=%d\n",
sw, (unsigned int)resultlen);
if (!retbuf && (sw==SW_SUCCESS || (sw&0xff00)==SW_MORE_DATA))
log_printhex (" dump: ", result, resultlen);
}
if ((sw & 0xff00) == SW_MORE_DATA
|| sw == SW_SUCCESS
|| sw == SW_EOF_REACHED )
{
if (retbuf && resultlen)
{
if (p - *retbuf + resultlen > bufsize)
{
bufsize += resultlen > 4096? resultlen: 4096;
tmp = xtryrealloc (*retbuf, bufsize + 2);
if (!tmp)
{
unlock_slot (slot);
return SW_HOST_OUT_OF_CORE;
}
p = tmp + (p - *retbuf);
*retbuf = tmp;
}
memcpy (p, result, resultlen);
p += resultlen;
}
}
else
log_info ("apdu_send_sdirect(%d) "
"got unexpected status %04X from get response\n",
slot, sw);
}
while ((sw & 0xff00) == SW_MORE_DATA);
if (retbuf)
{
*retbuflen = p - *retbuf;
tmp = xtryrealloc (*retbuf, *retbuflen + 2);
if (tmp)
*retbuf = tmp;
}
}
else
{
if (retbuf)
{
*retbuf = xtrymalloc ((resultlen? resultlen : 1)+2);
if (!*retbuf)
{
unlock_slot (slot);
return SW_HOST_OUT_OF_CORE;
}
*retbuflen = resultlen;
memcpy (*retbuf, result, resultlen);
}
}
unlock_slot (slot);
/* Append the status word - we reseved the two extra bytes while
allocating the buffer. */
if (retbuf)
{
(*retbuf)[(*retbuflen)++] = (sw >> 8);
(*retbuf)[(*retbuflen)++] = sw;
}
if (DBG_CARD_IO && retbuf)
log_printhex (" dump: ", *retbuf, *retbuflen);
return 0;
#undef RESULTLEN
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Dec 14, 2:53 PM (2 h, 1 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
3c/21/b36a353dceae51f9bcc3a43a04ea

Event Timeline