libcacard: use the standalone project
libcacard is now a standalone project hosted with the Spice project (see the 2.5.0 release announcement), remove it from qemu tree. Use the library if found during configure or if --enable-smartcard. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Michael Tokarev <mjt@tls.msk.ru> Reviewed-by: Paolo Bonzini <pbonzini@redhat.com> Tested-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
684bb5770e
commit
7b02f5447c
1
.gitignore
vendored
1
.gitignore
vendored
@ -19,7 +19,6 @@
|
||||
/trace/generated-ust.c
|
||||
/ui/shader/texture-blit-frag.h
|
||||
/ui/shader/texture-blit-vert.h
|
||||
/libcacard/trace/generated-tracers.c
|
||||
*-timestamp
|
||||
/*-softmmu
|
||||
/*-darwin-user
|
||||
|
3
Makefile
3
Makefile
@ -163,9 +163,6 @@ dummy := $(call unnest-vars,, \
|
||||
ifneq ($(wildcard config-host.mak),)
|
||||
include $(SRC_PATH)/tests/Makefile
|
||||
endif
|
||||
ifeq ($(CONFIG_SMARTCARD_NSS),y)
|
||||
include $(SRC_PATH)/libcacard/Makefile
|
||||
endif
|
||||
|
||||
all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules
|
||||
|
||||
|
@ -32,18 +32,6 @@ crypto-aes-obj-y = crypto/
|
||||
|
||||
qom-obj-y = qom/
|
||||
|
||||
######################################################################
|
||||
# smartcard
|
||||
|
||||
libcacard-y += libcacard/cac.o libcacard/event.o
|
||||
libcacard-y += libcacard/vcard.o libcacard/vreader.o
|
||||
libcacard-y += libcacard/vcard_emul_nss.o
|
||||
libcacard-y += libcacard/vcard_emul_type.o
|
||||
libcacard-y += libcacard/card_7816.o
|
||||
libcacard-y += libcacard/vcardt.o
|
||||
libcacard/vcard_emul_nss.o-cflags := $(NSS_CFLAGS)
|
||||
libcacard/vcard_emul_nss.o-libs := $(NSS_LIBS)
|
||||
|
||||
######################################################################
|
||||
# Target independent part of system emulation. The long term path is to
|
||||
# suppress *all* target specific code in case of system emulation, i.e. a
|
||||
@ -85,8 +73,6 @@ common-obj-y += backends/
|
||||
|
||||
common-obj-$(CONFIG_SECCOMP) += qemu-seccomp.o
|
||||
|
||||
common-obj-$(CONFIG_SMARTCARD_NSS) += $(libcacard-y)
|
||||
|
||||
common-obj-$(CONFIG_FDT) += device_tree.o
|
||||
|
||||
######################################################################
|
||||
|
52
configure
vendored
52
configure
vendored
@ -302,7 +302,7 @@ trace_backends="nop"
|
||||
trace_file="trace"
|
||||
spice=""
|
||||
rbd=""
|
||||
smartcard_nss=""
|
||||
smartcard=""
|
||||
libusb=""
|
||||
usb_redir=""
|
||||
opengl=""
|
||||
@ -1039,9 +1039,9 @@ for opt do
|
||||
;;
|
||||
--enable-xfsctl) xfs="yes"
|
||||
;;
|
||||
--disable-smartcard-nss) smartcard_nss="no"
|
||||
--disable-smartcard) smartcard="no"
|
||||
;;
|
||||
--enable-smartcard-nss) smartcard_nss="yes"
|
||||
--enable-smartcard) smartcard="yes"
|
||||
;;
|
||||
--disable-libusb) libusb="no"
|
||||
;;
|
||||
@ -1354,7 +1354,7 @@ disabled with --disable-FEATURE, default is enabled if available:
|
||||
rbd rados block device (rbd)
|
||||
libiscsi iscsi support
|
||||
libnfs nfs support
|
||||
smartcard-nss smartcard nss support
|
||||
smartcard smartcard support (libcacard)
|
||||
libusb libusb (for usb passthrough)
|
||||
usb-redir usb network redirection support
|
||||
lzo support of lzo compression library
|
||||
@ -3810,34 +3810,20 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
# check for libcacard for smartcard support
|
||||
# check for smartcard support
|
||||
smartcard_cflags=""
|
||||
# TODO - what's the minimal nss version we support?
|
||||
if test "$smartcard_nss" != "no"; then
|
||||
cat > $TMPC << EOF
|
||||
#include <pk11pub.h>
|
||||
int main(void) { PK11_FreeSlot(0); return 0; }
|
||||
EOF
|
||||
# FIXME: do not include $glib_* in here
|
||||
nss_libs="$($pkg_config --libs nss 2>/dev/null) $glib_libs"
|
||||
nss_cflags="$($pkg_config --cflags nss 2>/dev/null) $glib_cflags"
|
||||
test_cflags="$nss_cflags"
|
||||
# The header files in nss < 3.13.3 have a bug which causes them to
|
||||
# emit a warning. If we're going to compile QEMU with -Werror, then
|
||||
# test that the headers don't have this bug. Otherwise we would pass
|
||||
# the configure test but fail to compile QEMU later.
|
||||
if test "$werror" = "yes"; then
|
||||
test_cflags="-Werror $test_cflags"
|
||||
fi
|
||||
if test -n "$libtool" &&
|
||||
$pkg_config --atleast-version=3.12.8 nss && \
|
||||
compile_prog "$test_cflags" "$nss_libs"; then
|
||||
smartcard_nss="yes"
|
||||
if test "$smartcard" != "no"; then
|
||||
if $pkg_config libcacard; then
|
||||
libcacard_cflags=$($pkg_config --cflags libcacard)
|
||||
libcacard_libs=$($pkg_config --libs libcacard)
|
||||
QEMU_CFLAGS="$QEMU_CFLAGS $libcacard_cflags"
|
||||
libs_softmmu="$libs_softmmu $libcacard_libs"
|
||||
smartcard="yes"
|
||||
else
|
||||
if test "$smartcard_nss" = "yes"; then
|
||||
feature_not_found "nss" "Install nss devel >= 3.12.8"
|
||||
if test "$smartcard" = "yes"; then
|
||||
feature_not_found "smartcard" "Install libcacard devel"
|
||||
fi
|
||||
smartcard_nss="no"
|
||||
smartcard="no"
|
||||
fi
|
||||
fi
|
||||
|
||||
@ -4618,7 +4604,7 @@ echo "spice support $spice"
|
||||
fi
|
||||
echo "rbd support $rbd"
|
||||
echo "xfsctl support $xfs"
|
||||
echo "nss used $smartcard_nss"
|
||||
echo "smartcard support $smartcard"
|
||||
echo "libusb $libusb"
|
||||
echo "usb net redir $usb_redir"
|
||||
echo "OpenGL support $opengl"
|
||||
@ -4995,10 +4981,8 @@ if test "$spice" = "yes" ; then
|
||||
echo "CONFIG_SPICE=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
if test "$smartcard_nss" = "yes" ; then
|
||||
echo "CONFIG_SMARTCARD_NSS=y" >> $config_host_mak
|
||||
echo "NSS_LIBS=$nss_libs" >> $config_host_mak
|
||||
echo "NSS_CFLAGS=$nss_cflags" >> $config_host_mak
|
||||
if test "$smartcard" = "yes" ; then
|
||||
echo "CONFIG_SMARTCARD=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
if test "$libusb" = "yes" ; then
|
||||
|
@ -1,483 +0,0 @@
|
||||
This file documents the CAC (Common Access Card) library in the libcacard
|
||||
subdirectory.
|
||||
|
||||
Virtual Smart Card Emulator
|
||||
|
||||
This emulator is designed to provide emulation of actual smart cards to a
|
||||
virtual card reader running in a guest virtual machine. The emulated smart
|
||||
cards can be representations of real smart cards, where the necessary functions
|
||||
such as signing, card removal/insertion, etc. are mapped to real, physical
|
||||
cards which are shared with the client machine the emulator is running on, or
|
||||
the cards could be pure software constructs.
|
||||
|
||||
The emulator is structured to allow multiple replaceable or additional pieces,
|
||||
so it can be easily modified for future requirements. The primary envisioned
|
||||
modifications are:
|
||||
|
||||
1) The socket connection to the virtual card reader (presumably a CCID reader,
|
||||
but other ISO-7816 compatible readers could be used). The code that handles
|
||||
this is in vscclient.c.
|
||||
|
||||
2) The virtual card low level emulation. This is currently supplied by using
|
||||
NSS. This emulation could be replaced by implementations based on other
|
||||
security libraries, including but not limitted to openssl+pkcs#11 library,
|
||||
raw pkcs#11, Microsoft CAPI, direct opensc calls, etc. The code that handles
|
||||
this is in vcard_emul_nss.c.
|
||||
|
||||
3) Emulation for new types of cards. The current implementation emulates the
|
||||
original DoD CAC standard with separate pki containers. This emulator lives in
|
||||
cac.c. More than one card type emulator could be included. Other cards could
|
||||
be emulated as well, including PIV, newer versions of CAC, PKCS #15, etc.
|
||||
|
||||
--------------------
|
||||
Replacing the Socket Based Virtual Reader Interface.
|
||||
|
||||
The current implementation contains a replaceable module vscclient.c. The
|
||||
current vscclient.c implements a sockets interface to the virtual ccid reader
|
||||
on the guest. CCID commands that are pertinent to emulation are passed
|
||||
across the socket, and their responses are passed back along that same socket.
|
||||
The protocol that vscclient uses is defined in vscard_common.h and connects
|
||||
to a qemu ccid usb device. Since this socket runs as a client, vscclient.c
|
||||
implements a program with a main entry. It also handles argument parsing for
|
||||
the emulator.
|
||||
|
||||
An application that wants to use the virtual reader can replace vscclient.c
|
||||
with its own implementation that connects to its own CCID reader. The calls
|
||||
that the CCID reader can call are:
|
||||
|
||||
VReaderList * vreader_get_reader_list();
|
||||
|
||||
This function returns a list of virtual readers. These readers may map to
|
||||
physical devices, or simulated devices depending on vcard the back end. Each
|
||||
reader in the list should represent a reader to the virtual machine. Virtual
|
||||
USB address mapping is left to the CCID reader front end. This call can be
|
||||
made any time to get an updated list. The returned list is a copy of the
|
||||
internal list that can be referenced by the caller without locking. This copy
|
||||
must be freed by the caller with vreader_list_delete when it is no longer
|
||||
needed.
|
||||
|
||||
VReaderListEntry *vreader_list_get_first(VReaderList *);
|
||||
|
||||
This function gets the first entry on the reader list. Along with
|
||||
vreader_list_get_next(), vreader_list_get_first() can be used to walk the
|
||||
reader list returned from vreader_get_reader_list(). VReaderListEntries are
|
||||
part of the list themselves and do not need to be freed separately from the
|
||||
list. If there are no entries on the list, it will return NULL.
|
||||
|
||||
VReaderListEntry *vreader_list_get_next(VReaderListEntry *);
|
||||
|
||||
This function gets the next entry in the list. If there are no more entries
|
||||
it will return NULL.
|
||||
|
||||
VReader * vreader_list_get_reader(VReaderListEntry *)
|
||||
|
||||
This function returns the reader stored in the reader List entry. Caller gets
|
||||
a new reference to a reader. The caller must free its reference when it is
|
||||
finished with vreader_free().
|
||||
|
||||
void vreader_free(VReader *reader);
|
||||
|
||||
This function frees a reference to a reader. Readers are reference counted
|
||||
and are automatically deleted when the last reference is freed.
|
||||
|
||||
void vreader_list_delete(VReaderList *list);
|
||||
|
||||
This function frees the list, all the elements on the list, and all the
|
||||
reader references held by the list.
|
||||
|
||||
VReaderStatus vreader_power_on(VReader *reader, char *atr, int *len);
|
||||
|
||||
This function simulates a card power on. A virtual card does not care about
|
||||
the actual voltage and other physical parameters, but it does care that the
|
||||
card is actually on or off. Cycling the card causes the card to reset. If
|
||||
the caller provides enough space, vreader_power_on will return the ATR of
|
||||
the virtual card. The amount of space provided in atr should be indicated
|
||||
in *len. The function modifies *len to be the actual length of of the
|
||||
returned ATR.
|
||||
|
||||
VReaderStatus vreader_power_off(VReader *reader);
|
||||
|
||||
This function simulates a power off of a virtual card.
|
||||
|
||||
VReaderStatus vreader_xfer_bytes(VReader *reader, unsigne char *send_buf,
|
||||
int send_buf_len,
|
||||
unsigned char *receive_buf,
|
||||
int receive_buf_len);
|
||||
|
||||
This function sends a raw apdu to a card and returns the card's response.
|
||||
The CCID front end should return the response back. Most of the emulation
|
||||
is driven from these APDUs.
|
||||
|
||||
VReaderStatus vreader_card_is_present(VReader *reader);
|
||||
|
||||
This function returns whether or not the reader has a card inserted. The
|
||||
vreader_power_on, vreader_power_off, and vreader_xfer_bytes will return
|
||||
VREADER_NO_CARD.
|
||||
|
||||
const char *vreader_get_name(VReader *reader);
|
||||
|
||||
This function returns the name of the reader. The name comes from the card
|
||||
emulator level and is usually related to the name of the physical reader.
|
||||
|
||||
VReaderID vreader_get_id(VReader *reader);
|
||||
|
||||
This function returns the id of a reader. All readers start out with an id
|
||||
of -1. The application can set the id with vreader_set_id.
|
||||
|
||||
VReaderStatus vreader_get_id(VReader *reader, VReaderID id);
|
||||
|
||||
This function sets the reader id. The application is responsible for making
|
||||
sure that the id is unique for all readers it is actively using.
|
||||
|
||||
VReader *vreader_find_reader_by_id(VReaderID id);
|
||||
|
||||
This function returns the reader which matches the id. If two readers match,
|
||||
only one is returned. The function returns NULL if the id is -1.
|
||||
|
||||
Event *vevent_wait_next_vevent();
|
||||
|
||||
This function blocks waiting for reader and card insertion events. There
|
||||
will be one event for each card insertion, each card removal, each reader
|
||||
insertion and each reader removal. At start up, events are created for all
|
||||
the initial readers found, as well as all the cards that are inserted.
|
||||
|
||||
Event *vevent_get_next_vevent();
|
||||
|
||||
This function returns a pending event if it exists, otherwise it returns
|
||||
NULL. It does not block.
|
||||
|
||||
----------------
|
||||
Card Type Emulator: Adding a New Virtual Card Type
|
||||
|
||||
The ISO 7816 card spec describes 2 types of cards:
|
||||
1) File system cards, where the smartcard is managed by reading and writing
|
||||
data to files in a file system. There is currently only boiler plate
|
||||
implemented for file system cards.
|
||||
2) VM cards, where the card has loadable applets which perform the card
|
||||
functions. The current implementation supports VM cards.
|
||||
|
||||
In the case of VM cards, the difference between various types of cards is
|
||||
really what applets have been installed in that card. This structure is
|
||||
mirrored in card type emulators. The 7816 emulator already handles the basic
|
||||
ISO 7186 commands. Card type emulators simply need to add the virtual applets
|
||||
which emulate the real card applets. Card type emulators have exactly one
|
||||
public entry point:
|
||||
|
||||
VCARDStatus xxx_card_init(VCard *card, const char *flags,
|
||||
const unsigned char *cert[],
|
||||
int cert_len[],
|
||||
VCardKey *key[],
|
||||
int cert_count);
|
||||
|
||||
The parameters for this are:
|
||||
card - the virtual card structure which will represent this card.
|
||||
flags - option flags that may be specific to this card type.
|
||||
cert - array of binary certificates.
|
||||
cert_len - array of lengths of each of the certificates specified in cert.
|
||||
key - array of opaque key structures representing the private keys on
|
||||
the card.
|
||||
cert_count - number of entries in cert, cert_len, and key arrays.
|
||||
|
||||
Any cert, cert_len, or key with the same index are matching sets. That is
|
||||
cert[0] is cert_len[0] long and has the corresponding private key of key[0].
|
||||
|
||||
The card type emulator is expected to own the VCardKeys, but it should copy
|
||||
any raw cert data it wants to save. It can create new applets and add them to
|
||||
the card using the following functions:
|
||||
|
||||
VCardApplet *vcard_new_applet(VCardProcessAPDU apdu_func,
|
||||
VCardResetApplet reset_func,
|
||||
const unsigned char *aid,
|
||||
int aid_len);
|
||||
|
||||
This function creates a new applet. Applet structures store the following
|
||||
information:
|
||||
1) the AID of the applet (set by aid and aid_len).
|
||||
2) a function to handle APDUs for this applet. (set by apdu_func, more on
|
||||
this below).
|
||||
3) a function to reset the applet state when the applet is selected.
|
||||
(set by reset_func, more on this below).
|
||||
3) applet private data, a data pointer used by the card type emulator to
|
||||
store any data or state it needs to complete requests. (set by a
|
||||
separate call).
|
||||
4) applet private data free, a function used to free the applet private
|
||||
data when the applet itself is destroyed.
|
||||
The created applet can be added to the card with vcard_add_applet below.
|
||||
|
||||
void vcard_set_applet_private(VCardApplet *applet,
|
||||
VCardAppletPrivate *private,
|
||||
VCardAppletPrivateFree private_free);
|
||||
This function sets the private data and the corresponding free function.
|
||||
VCardAppletPrivate is an opaque data structure to the rest of the emulator.
|
||||
The card type emulator can define it any way it wants by defining
|
||||
struct VCardAppletPrivateStruct {};. If there is already a private data
|
||||
structure on the applet, the old one is freed before the new one is set up.
|
||||
passing two NULL clear any existing private data.
|
||||
|
||||
VCardStatus vcard_add_applet(VCard *card, VCardApplet *applet);
|
||||
|
||||
Add an applet onto the list of applets attached to the card. Once an applet
|
||||
has been added, it can be selected by its AID, and then commands will be
|
||||
routed to it VCardProcessAPDU function. This function adopts the applet that
|
||||
is passed into it. Note: 2 applets with the same AID should not be added to
|
||||
the same card. It is permissible to add more than one applet. Multiple applets
|
||||
may have the same VCardPRocessAPDU entry point.
|
||||
|
||||
The certs and keys should be attached to private data associated with one or
|
||||
more appropriate applets for that card. Control will come to the card type
|
||||
emulators once one of its applets are selected through the VCardProcessAPDU
|
||||
function it specified when it created the applet.
|
||||
|
||||
The signature of VCardResetApplet is:
|
||||
VCardStatus (*VCardResetApplet) (VCard *card, int channel);
|
||||
This function will reset the any internal applet state that needs to be
|
||||
cleared after a select applet call. It should return VCARD_DONE;
|
||||
|
||||
The signature of VCardProcessAPDU is:
|
||||
VCardStatus (*VCardProcessAPDU)(VCard *card, VCardAPDU *apdu,
|
||||
VCardResponse **response);
|
||||
This function examines the APDU and determines whether it should process
|
||||
the apdu directly, reject the apdu as invalid, or pass the apdu on to
|
||||
the basic 7816 emulator for processing.
|
||||
If the 7816 emulator should process the apdu, then the VCardProcessAPDU
|
||||
should return VCARD_NEXT.
|
||||
If there is an error, then VCardProcessAPDU should return an error
|
||||
response using vcard_make_response and the appropriate 7816 error code
|
||||
(see card_7816t.h) or vcard_make_response with a card type specific error
|
||||
code. It should then return VCARD_DONE.
|
||||
If the apdu can be processed correctly, VCardProcessAPDU should do so,
|
||||
set the response value appropriately for that APDU, and return VCARD_DONE.
|
||||
VCardProcessAPDU should always set the response if it returns VCARD_DONE.
|
||||
It should always either return VCARD_DONE or VCARD_NEXT.
|
||||
|
||||
Parsing the APDU --
|
||||
|
||||
Prior to processing calling the card type emulator's VCardProcessAPDU function, the emulator has already decoded the APDU header and set several fields:
|
||||
|
||||
apdu->a_data - The raw apdu data bytes.
|
||||
apdu->a_len - The len of the raw apdu data.
|
||||
apdu->a_body - The start of any post header parameter data.
|
||||
apdu->a_Lc - The parameter length value.
|
||||
apdu->a_Le - The expected length of any returned data.
|
||||
apdu->a_cla - The raw apdu class.
|
||||
apdu->a_channel - The channel (decoded from the class).
|
||||
apdu->a_secure_messaging_type - The decoded secure messaging type
|
||||
(from class).
|
||||
apdu->a_type - The decode class type.
|
||||
apdu->a_gen_type - the generic class type (7816, PROPRIETARY, RFU, PTS).
|
||||
apdu->a_ins - The instruction byte.
|
||||
apdu->a_p1 - Parameter 1.
|
||||
apdu->a_p2 - Parameter 2.
|
||||
|
||||
Creating a Response --
|
||||
|
||||
The expected result of any APDU call is a response. The card type emulator must
|
||||
set *response with an appropriate VCardResponse value if it returns VCARD_DONE.
|
||||
Responses could be as simple as returning a 2 byte status word response, to as
|
||||
complex as returning a block of data along with a 2 byte response. Which is
|
||||
returned will depend on the semantics of the APDU. The following functions will
|
||||
create card responses.
|
||||
|
||||
VCardResponse *vcard_make_response(VCard7816Status status);
|
||||
|
||||
This is the most basic function to get a response. This function will
|
||||
return a response the consists solely one 2 byte status code. If that status
|
||||
code is defined in card_7816t.h, then this function is guaranteed to
|
||||
return a response with that status. If a cart type specific status code
|
||||
is passed and vcard_make_response fails to allocate the appropriate memory
|
||||
for that response, then vcard_make_response will return a VCardResponse
|
||||
of VCARD7816_STATUS_EXC_ERROR_MEMORY. In any case, this function is
|
||||
guaranteed to return a valid VCardResponse.
|
||||
|
||||
VCardResponse *vcard_response_new(unsigned char *buf, int len,
|
||||
VCard7816Status status);
|
||||
|
||||
This function is similar to vcard_make_response except it includes some
|
||||
returned data with the response. It could also fail to allocate enough
|
||||
memory, in which case it will return NULL.
|
||||
|
||||
VCardResponse *vcard_response_new_status_bytes(unsigned char sw1,
|
||||
unsigned char sw2);
|
||||
|
||||
Sometimes in 7816 the response bytes are treated as two separate bytes with
|
||||
split meanings. This function allows you to create a response based on
|
||||
two separate bytes. This function could fail, in which case it will return
|
||||
NULL.
|
||||
|
||||
VCardResponse *vcard_response_new_bytes(unsigned char *buf, int len,
|
||||
unsigned char sw1,
|
||||
unsigned char sw2);
|
||||
|
||||
This function is the same as vcard_response_new except you may specify
|
||||
the status as two separate bytes like vcard_response_new_status_bytes.
|
||||
|
||||
|
||||
Implementing functionality ---
|
||||
|
||||
The following helper functions access information about the current card
|
||||
and applet.
|
||||
|
||||
VCARDAppletPrivate *vcard_get_current_applet_private(VCard *card,
|
||||
int channel);
|
||||
|
||||
This function returns any private data set by the card type emulator on
|
||||
the currently selected applet. The card type emulator keeps track of the
|
||||
current applet state in this data structure. Any certs and keys associated
|
||||
with a particular applet is also stored here.
|
||||
|
||||
int vcard_emul_get_login_count(VCard *card);
|
||||
|
||||
This function returns the number of remaining login attempts for this
|
||||
card. If the card emulator does not know, or the card does not have a
|
||||
way of giving this information, this function returns -1.
|
||||
|
||||
|
||||
VCard7816Status vcard_emul_login(VCard *card, unsigned char *pin,
|
||||
int pin_len);
|
||||
|
||||
This function logs into the card and returns the standard 7816 status
|
||||
word depending on the success or failure of the call.
|
||||
|
||||
void vcard_emul_delete_key(VCardKey *key);
|
||||
|
||||
This function frees the VCardKey passed in to xxxx_card_init. The card
|
||||
type emulator is responsible for freeing this key when it no longer needs
|
||||
it.
|
||||
|
||||
VCard7816Status vcard_emul_rsa_op(VCard *card, VCardKey *key,
|
||||
unsigned char *buffer,
|
||||
int buffer_size);
|
||||
|
||||
This function does a raw rsa op on the buffer with the given key.
|
||||
|
||||
The sample card type emulator is found in cac.c. It implements the cac specific
|
||||
applets. Only those applets needed by the coolkey pkcs#11 driver on the guest
|
||||
have been implemented. To support the full range CAC middleware, a complete CAC
|
||||
card according to the CAC specs should be implemented here.
|
||||
|
||||
------------------------------
|
||||
Virtual Card Emulator
|
||||
|
||||
This code accesses both real smart cards and simulated smart cards through
|
||||
services provided on the client. The current implementation uses NSS, which
|
||||
already knows how to talk to various PKCS #11 modules on the client, and is
|
||||
portable to most operating systems. A particular emulator can have only one
|
||||
virtual card implementation at a time.
|
||||
|
||||
The virtual card emulator consists of a series of virtual card services. In
|
||||
addition to the services describe above (services starting with
|
||||
vcard_emul_xxxx), the virtual card emulator also provides the following
|
||||
functions:
|
||||
|
||||
VCardEmulError vcard_emul_init(cont VCardEmulOptions *options);
|
||||
|
||||
The options structure is built by another function in the virtual card
|
||||
interface where a string of virtual card emulator specific strings are
|
||||
mapped to the options. The actual structure is defined by the virtual card
|
||||
emulator and is used to determine the configuration of soft cards, or to
|
||||
determine which physical cards to present to the guest.
|
||||
|
||||
The vcard_emul_init function will build up sets of readers, create any
|
||||
threads that are needed to watch for changes in the reader state. If readers
|
||||
have cards present in them, they are also initialized.
|
||||
|
||||
Readers are created with the function.
|
||||
|
||||
VReader *vreader_new(VReaderEmul *reader_emul,
|
||||
VReaderEmulFree reader_emul_free);
|
||||
|
||||
The freeFunc is used to free the VReaderEmul * when the reader is
|
||||
destroyed. The VReaderEmul structure is an opaque structure to the
|
||||
rest of the code, but defined by the virtual card emulator, which can
|
||||
use it to store any reader specific state.
|
||||
|
||||
Once the reader has been created, it can be added to the front end with the
|
||||
call:
|
||||
|
||||
VReaderStatus vreader_add_reader(VReader *reader);
|
||||
|
||||
This function will automatically generate the appropriate new reader
|
||||
events and add the reader to the list.
|
||||
|
||||
To create a new card, the virtual card emulator will call a similar
|
||||
function.
|
||||
|
||||
VCard *vcard_new(VCardEmul *card_emul,
|
||||
VCardEmulFree card_emul_free);
|
||||
|
||||
Like vreader_new, this function takes a virtual card emulator specific
|
||||
structure which it uses to keep track of the card state.
|
||||
|
||||
Once the card is created, it is attached to a card type emulator with the
|
||||
following function:
|
||||
|
||||
VCardStatus vcard_init(VCard *vcard, VCardEmulType type,
|
||||
const char *flags,
|
||||
unsigned char *const *certs,
|
||||
int *cert_len,
|
||||
VCardKey *key[],
|
||||
int cert_count);
|
||||
|
||||
The vcard is the value returned from vcard_new. The type is the
|
||||
card type emulator that this card should presented to the guest as.
|
||||
The flags are card type emulator specific options. The certs,
|
||||
cert_len, and keys are all arrays of length cert_count. These are
|
||||
the same of the parameters xxxx_card_init() accepts.
|
||||
|
||||
Finally the card is associated with its reader by the call:
|
||||
|
||||
VReaderStatus vreader_insert_card(VReader *vreader, VCard *vcard);
|
||||
|
||||
This function, like vreader_add_reader, will take care of any event
|
||||
notification for the card insert.
|
||||
|
||||
|
||||
VCardEmulError vcard_emul_force_card_remove(VReader *vreader);
|
||||
|
||||
Force a card that is present to appear to be removed to the guest, even if
|
||||
that card is a physical card and is present.
|
||||
|
||||
|
||||
VCardEmulError vcard_emul_force_card_insert(VReader *reader);
|
||||
|
||||
Force a card that has been removed by vcard_emul_force_card_remove to be
|
||||
reinserted from the point of view of the guest. This will only work if the
|
||||
card is physically present (which is always true fro a soft card).
|
||||
|
||||
void vcard_emul_get_atr(Vcard *card, unsigned char *atr, int *atr_len);
|
||||
|
||||
Return the virtual ATR for the card. By convention this should be the value
|
||||
VCARD_ATR_PREFIX(size) followed by several ascii bytes related to this
|
||||
particular emulator. For instance the NSS emulator returns
|
||||
{VCARD_ATR_PREFIX(3), 'N', 'S', 'S' }. Do ot return more data then *atr_len;
|
||||
|
||||
void vcard_emul_reset(VCard *card, VCardPower power)
|
||||
|
||||
Set the state of 'card' to the current power level and reset its internal
|
||||
state (logout, etc).
|
||||
|
||||
-------------------------------------------------------
|
||||
List of files and their function:
|
||||
README - This file
|
||||
card_7816.c - emulate basic 7816 functionality. Parse APDUs.
|
||||
card_7816.h - apdu and response services definitions.
|
||||
card_7816t.h - 7816 specific structures, types and definitions.
|
||||
event.c - event handling code.
|
||||
event.h - event handling services definitions.
|
||||
eventt.h - event handling structures and types
|
||||
vcard.c - handle common virtual card services like creation, destruction, and
|
||||
applet management.
|
||||
vcard.h - common virtual card services function definitions.
|
||||
vcardt.h - comon virtual card types
|
||||
vreader.c - common virtual reader services.
|
||||
vreader.h - common virtual reader services definitions.
|
||||
vreadert.h - comon virtual reader types.
|
||||
vcard_emul_type.c - manage the card type emulators.
|
||||
vcard_emul_type.h - definitions for card type emulators.
|
||||
cac.c - card type emulator for CAC cards
|
||||
vcard_emul.h - virtual card emulator service definitions.
|
||||
vcard_emul_nss.c - virtual card emulator implementation for nss.
|
||||
vscclient.c - socket connection to guest qemu usb driver.
|
||||
vscard_common.h - common header with the guest qemu usb driver.
|
||||
mutex.h - header file for machine independent mutexes.
|
||||
link_test.c - static test to make sure all the symbols are properly defined.
|
@ -23,9 +23,8 @@ common-obj-$(CONFIG_USB_BLUETOOTH) += dev-bluetooth.o
|
||||
|
||||
ifeq ($(CONFIG_USB_SMARTCARD),y)
|
||||
common-obj-y += dev-smartcard-reader.o
|
||||
common-obj-y += ccid-card-passthru.o
|
||||
common-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o
|
||||
ccid-card-emulated.o-cflags := -I$(SRC_PATH)/libcacard
|
||||
common-obj-$(CONFIG_SMARTCARD) += ccid-card-passthru.o
|
||||
common-obj-$(CONFIG_SMARTCARD) += ccid-card-emulated.o
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_POSIX),y)
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "ccid.h"
|
||||
#include "libcacard/vscard_common.h"
|
||||
#include "cacard/vscard_common.h"
|
||||
|
||||
#define DPRINTF(card, lvl, fmt, ...) \
|
||||
do { \
|
||||
|
@ -1,45 +0,0 @@
|
||||
libcacard_includedir=$(includedir)/cacard
|
||||
|
||||
TOOLS += vscclient$(EXESUF)
|
||||
|
||||
# objects linked into a shared library, built with libtool with -fPIC if required
|
||||
libcacard-obj-y = $(libcacard-y)
|
||||
libcacard-lobj-y=$(patsubst %.o,%.lo,$(libcacard-obj-y))
|
||||
|
||||
# libtool will build the .o files, too
|
||||
$(libcacard-obj-y): | $(libcacard-lobj-y)
|
||||
|
||||
all: libcacard.la libcacard.pc
|
||||
|
||||
vscclient$(EXESUF): libcacard/vscclient.o libcacard.la
|
||||
$(call LINK,$^)
|
||||
|
||||
#########################################################################
|
||||
# Rules for building libcacard standalone library
|
||||
|
||||
libcacard.la: LDFLAGS += -rpath $(libdir) -no-undefined \
|
||||
-export-symbols $(SRC_PATH)/libcacard/libcacard.syms
|
||||
# Prevent libcacard.so linking against the entire world of 3rd party libs
|
||||
libcacard.la: LIBS =
|
||||
libcacard.la: $(libcacard-lobj-y)
|
||||
$(call LINK,$^)
|
||||
|
||||
libcacard.pc: $(SRC_PATH)/libcacard/libcacard.pc.in
|
||||
$(call quiet-command,sed -e 's|@LIBDIR@|$(libdir)|' \
|
||||
-e 's|@INCLUDEDIR@|$(libcacard_includedir)|' \
|
||||
-e 's|@VERSION@|$(shell cat $(SRC_PATH)/VERSION)|' \
|
||||
-e 's|@PREFIX@|$(prefix)|' $< > libcacard.pc,\
|
||||
" GEN $@")
|
||||
|
||||
.PHONY: install-libcacard
|
||||
|
||||
install: install-libcacard
|
||||
install-libcacard: libcacard.pc libcacard.la
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(libdir)"
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(libdir)/pkgconfig"
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(libcacard_includedir)"
|
||||
$(INSTALL_LIB) libcacard.la "$(DESTDIR)$(libdir)"
|
||||
$(INSTALL_DATA) libcacard.pc "$(DESTDIR)$(libdir)/pkgconfig"
|
||||
for inc in $(SRC_PATH)/libcacard/*.h; do \
|
||||
$(INSTALL_DATA) $$inc "$(DESTDIR)$(libcacard_includedir)"; \
|
||||
done
|
414
libcacard/cac.c
414
libcacard/cac.c
@ -1,414 +0,0 @@
|
||||
/*
|
||||
* implement the applets for the CAC card.
|
||||
*
|
||||
* This code is licensed under the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "glib-compat.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "cac.h"
|
||||
#include "vcard.h"
|
||||
#include "vcard_emul.h"
|
||||
#include "card_7816.h"
|
||||
|
||||
/* private data for PKI applets */
|
||||
typedef struct CACPKIAppletDataStruct {
|
||||
unsigned char *cert;
|
||||
int cert_len;
|
||||
unsigned char *cert_buffer;
|
||||
int cert_buffer_len;
|
||||
unsigned char *sign_buffer;
|
||||
int sign_buffer_len;
|
||||
VCardKey *key;
|
||||
} CACPKIAppletData;
|
||||
|
||||
/*
|
||||
* CAC applet private data
|
||||
*/
|
||||
struct VCardAppletPrivateStruct {
|
||||
union {
|
||||
CACPKIAppletData pki_data;
|
||||
void *reserved;
|
||||
} u;
|
||||
};
|
||||
|
||||
/*
|
||||
* handle all the APDU's that are common to all CAC applets
|
||||
*/
|
||||
static VCardStatus
|
||||
cac_common_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response)
|
||||
{
|
||||
int ef;
|
||||
VCardStatus ret = VCARD_FAIL;
|
||||
|
||||
switch (apdu->a_ins) {
|
||||
case VCARD7816_INS_SELECT_FILE:
|
||||
if (apdu->a_p1 != 0x02) {
|
||||
/* let the 7816 code handle applet switches */
|
||||
ret = VCARD_NEXT;
|
||||
break;
|
||||
}
|
||||
/* handle file id setting */
|
||||
if (apdu->a_Lc != 2) {
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_DATA_INVALID);
|
||||
ret = VCARD_DONE;
|
||||
break;
|
||||
}
|
||||
/* CAC 1.0 only supports ef = 0 */
|
||||
ef = apdu->a_body[0] | (apdu->a_body[1] << 8);
|
||||
if (ef != 0) {
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_FILE_NOT_FOUND);
|
||||
ret = VCARD_DONE;
|
||||
break;
|
||||
}
|
||||
*response = vcard_make_response(VCARD7816_STATUS_SUCCESS);
|
||||
ret = VCARD_DONE;
|
||||
break;
|
||||
case VCARD7816_INS_GET_RESPONSE:
|
||||
case VCARD7816_INS_VERIFY:
|
||||
/* let the 7816 code handle these */
|
||||
ret = VCARD_NEXT;
|
||||
break;
|
||||
case CAC_GET_PROPERTIES:
|
||||
case CAC_GET_ACR:
|
||||
/* skip these for now, this will probably be needed */
|
||||
*response = vcard_make_response(VCARD7816_STATUS_ERROR_P1_P2_INCORRECT);
|
||||
ret = VCARD_DONE;
|
||||
break;
|
||||
default:
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
|
||||
ret = VCARD_DONE;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* reset the inter call state between applet selects
|
||||
*/
|
||||
static VCardStatus
|
||||
cac_applet_pki_reset(VCard *card, int channel)
|
||||
{
|
||||
VCardAppletPrivate *applet_private;
|
||||
CACPKIAppletData *pki_applet;
|
||||
applet_private = vcard_get_current_applet_private(card, channel);
|
||||
assert(applet_private);
|
||||
pki_applet = &(applet_private->u.pki_data);
|
||||
|
||||
pki_applet->cert_buffer = NULL;
|
||||
g_free(pki_applet->sign_buffer);
|
||||
pki_applet->sign_buffer = NULL;
|
||||
pki_applet->cert_buffer_len = 0;
|
||||
pki_applet->sign_buffer_len = 0;
|
||||
return VCARD_DONE;
|
||||
}
|
||||
|
||||
static VCardStatus
|
||||
cac_applet_pki_process_apdu(VCard *card, VCardAPDU *apdu,
|
||||
VCardResponse **response)
|
||||
{
|
||||
CACPKIAppletData *pki_applet;
|
||||
VCardAppletPrivate *applet_private;
|
||||
int size, next;
|
||||
unsigned char *sign_buffer;
|
||||
bool retain_sign_buffer = FALSE;
|
||||
vcard_7816_status_t status;
|
||||
VCardStatus ret = VCARD_FAIL;
|
||||
|
||||
applet_private = vcard_get_current_applet_private(card, apdu->a_channel);
|
||||
assert(applet_private);
|
||||
pki_applet = &(applet_private->u.pki_data);
|
||||
|
||||
switch (apdu->a_ins) {
|
||||
case CAC_UPDATE_BUFFER:
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED);
|
||||
ret = VCARD_DONE;
|
||||
break;
|
||||
case CAC_GET_CERTIFICATE:
|
||||
if ((apdu->a_p2 != 0) || (apdu->a_p1 != 0)) {
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_P1_P2_INCORRECT);
|
||||
break;
|
||||
}
|
||||
assert(pki_applet->cert != NULL);
|
||||
size = apdu->a_Le;
|
||||
if (pki_applet->cert_buffer == NULL) {
|
||||
pki_applet->cert_buffer = pki_applet->cert;
|
||||
pki_applet->cert_buffer_len = pki_applet->cert_len;
|
||||
}
|
||||
size = MIN(size, pki_applet->cert_buffer_len);
|
||||
next = MIN(255, pki_applet->cert_buffer_len - size);
|
||||
*response = vcard_response_new_bytes(
|
||||
card, pki_applet->cert_buffer, size,
|
||||
apdu->a_Le, next ?
|
||||
VCARD7816_SW1_WARNING_CHANGE :
|
||||
VCARD7816_SW1_SUCCESS,
|
||||
next);
|
||||
pki_applet->cert_buffer += size;
|
||||
pki_applet->cert_buffer_len -= size;
|
||||
if ((*response == NULL) || (next == 0)) {
|
||||
pki_applet->cert_buffer = NULL;
|
||||
}
|
||||
if (*response == NULL) {
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
|
||||
}
|
||||
ret = VCARD_DONE;
|
||||
break;
|
||||
case CAC_SIGN_DECRYPT:
|
||||
if (apdu->a_p2 != 0) {
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_P1_P2_INCORRECT);
|
||||
break;
|
||||
}
|
||||
size = apdu->a_Lc;
|
||||
|
||||
sign_buffer = g_realloc(pki_applet->sign_buffer,
|
||||
pki_applet->sign_buffer_len + size);
|
||||
memcpy(sign_buffer+pki_applet->sign_buffer_len, apdu->a_body, size);
|
||||
size += pki_applet->sign_buffer_len;
|
||||
switch (apdu->a_p1) {
|
||||
case 0x80:
|
||||
/* p1 == 0x80 means we haven't yet sent the whole buffer, wait for
|
||||
* the rest */
|
||||
pki_applet->sign_buffer = sign_buffer;
|
||||
pki_applet->sign_buffer_len = size;
|
||||
*response = vcard_make_response(VCARD7816_STATUS_SUCCESS);
|
||||
retain_sign_buffer = TRUE;
|
||||
break;
|
||||
case 0x00:
|
||||
/* we now have the whole buffer, do the operation, result will be
|
||||
* in the sign_buffer */
|
||||
status = vcard_emul_rsa_op(card, pki_applet->key,
|
||||
sign_buffer, size);
|
||||
if (status != VCARD7816_STATUS_SUCCESS) {
|
||||
*response = vcard_make_response(status);
|
||||
break;
|
||||
}
|
||||
*response = vcard_response_new(card, sign_buffer, size, apdu->a_Le,
|
||||
VCARD7816_STATUS_SUCCESS);
|
||||
if (*response == NULL) {
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_P1_P2_INCORRECT);
|
||||
break;
|
||||
}
|
||||
if (!retain_sign_buffer) {
|
||||
g_free(sign_buffer);
|
||||
pki_applet->sign_buffer = NULL;
|
||||
pki_applet->sign_buffer_len = 0;
|
||||
}
|
||||
ret = VCARD_DONE;
|
||||
break;
|
||||
case CAC_READ_BUFFER:
|
||||
/* new CAC call, go ahead and use the old version for now */
|
||||
/* TODO: implement */
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
|
||||
ret = VCARD_DONE;
|
||||
break;
|
||||
default:
|
||||
ret = cac_common_process_apdu(card, apdu, response);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static VCardStatus
|
||||
cac_applet_id_process_apdu(VCard *card, VCardAPDU *apdu,
|
||||
VCardResponse **response)
|
||||
{
|
||||
VCardStatus ret = VCARD_FAIL;
|
||||
|
||||
switch (apdu->a_ins) {
|
||||
case CAC_UPDATE_BUFFER:
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED);
|
||||
ret = VCARD_DONE;
|
||||
break;
|
||||
case CAC_READ_BUFFER:
|
||||
/* new CAC call, go ahead and use the old version for now */
|
||||
/* TODO: implement */
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
|
||||
ret = VCARD_DONE;
|
||||
break;
|
||||
default:
|
||||
ret = cac_common_process_apdu(card, apdu, response);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* TODO: if we ever want to support general CAC middleware, we will need to
|
||||
* implement the various containers.
|
||||
*/
|
||||
static VCardStatus
|
||||
cac_applet_container_process_apdu(VCard *card, VCardAPDU *apdu,
|
||||
VCardResponse **response)
|
||||
{
|
||||
VCardStatus ret = VCARD_FAIL;
|
||||
|
||||
switch (apdu->a_ins) {
|
||||
case CAC_READ_BUFFER:
|
||||
case CAC_UPDATE_BUFFER:
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
|
||||
ret = VCARD_DONE;
|
||||
break;
|
||||
default:
|
||||
ret = cac_common_process_apdu(card, apdu, response);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* utilities for creating and destroying the private applet data
|
||||
*/
|
||||
static void
|
||||
cac_delete_pki_applet_private(VCardAppletPrivate *applet_private)
|
||||
{
|
||||
CACPKIAppletData *pki_applet_data;
|
||||
|
||||
if (applet_private == NULL) {
|
||||
return;
|
||||
}
|
||||
pki_applet_data = &(applet_private->u.pki_data);
|
||||
g_free(pki_applet_data->cert);
|
||||
g_free(pki_applet_data->sign_buffer);
|
||||
if (pki_applet_data->key != NULL) {
|
||||
vcard_emul_delete_key(pki_applet_data->key);
|
||||
}
|
||||
g_free(applet_private);
|
||||
}
|
||||
|
||||
static VCardAppletPrivate *
|
||||
cac_new_pki_applet_private(const unsigned char *cert,
|
||||
int cert_len, VCardKey *key)
|
||||
{
|
||||
CACPKIAppletData *pki_applet_data;
|
||||
VCardAppletPrivate *applet_private;
|
||||
|
||||
applet_private = g_new0(VCardAppletPrivate, 1);
|
||||
pki_applet_data = &(applet_private->u.pki_data);
|
||||
pki_applet_data->cert = (unsigned char *)g_malloc(cert_len+1);
|
||||
/*
|
||||
* if we want to support compression, then we simply change the 0 to a 1
|
||||
* and compress the cert data with libz
|
||||
*/
|
||||
pki_applet_data->cert[0] = 0; /* not compressed */
|
||||
memcpy(&pki_applet_data->cert[1], cert, cert_len);
|
||||
pki_applet_data->cert_len = cert_len+1;
|
||||
|
||||
pki_applet_data->key = key;
|
||||
return applet_private;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* create a new cac applet which links to a given cert
|
||||
*/
|
||||
static VCardApplet *
|
||||
cac_new_pki_applet(int i, const unsigned char *cert,
|
||||
int cert_len, VCardKey *key)
|
||||
{
|
||||
VCardAppletPrivate *applet_private;
|
||||
VCardApplet *applet;
|
||||
unsigned char pki_aid[] = { 0xa0, 0x00, 0x00, 0x00, 0x79, 0x01, 0x00 };
|
||||
int pki_aid_len = sizeof(pki_aid);
|
||||
|
||||
pki_aid[pki_aid_len-1] = i;
|
||||
|
||||
applet_private = cac_new_pki_applet_private(cert, cert_len, key);
|
||||
if (applet_private == NULL) {
|
||||
goto failure;
|
||||
}
|
||||
applet = vcard_new_applet(cac_applet_pki_process_apdu, cac_applet_pki_reset,
|
||||
pki_aid, pki_aid_len);
|
||||
if (applet == NULL) {
|
||||
goto failure;
|
||||
}
|
||||
vcard_set_applet_private(applet, applet_private,
|
||||
cac_delete_pki_applet_private);
|
||||
applet_private = NULL;
|
||||
|
||||
return applet;
|
||||
|
||||
failure:
|
||||
if (applet_private != NULL) {
|
||||
cac_delete_pki_applet_private(applet_private);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static unsigned char cac_default_container_aid[] = {
|
||||
0xa0, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00 };
|
||||
static unsigned char cac_id_aid[] = {
|
||||
0xa0, 0x00, 0x00, 0x00, 0x79, 0x03, 0x00 };
|
||||
/*
|
||||
* Initialize the cac card. This is the only public function in this file. All
|
||||
* the rest are connected through function pointers.
|
||||
*/
|
||||
VCardStatus
|
||||
cac_card_init(VReader *reader, VCard *card,
|
||||
const char *params,
|
||||
unsigned char * const *cert,
|
||||
int cert_len[],
|
||||
VCardKey *key[] /* adopt the keys*/,
|
||||
int cert_count)
|
||||
{
|
||||
int i;
|
||||
VCardApplet *applet;
|
||||
|
||||
/* CAC Cards are VM Cards */
|
||||
vcard_set_type(card, VCARD_VM);
|
||||
|
||||
/* create one PKI applet for each cert */
|
||||
for (i = 0; i < cert_count; i++) {
|
||||
applet = cac_new_pki_applet(i, cert[i], cert_len[i], key[i]);
|
||||
if (applet == NULL) {
|
||||
goto failure;
|
||||
}
|
||||
vcard_add_applet(card, applet);
|
||||
}
|
||||
|
||||
/* create a default blank container applet */
|
||||
applet = vcard_new_applet(cac_applet_container_process_apdu,
|
||||
NULL, cac_default_container_aid,
|
||||
sizeof(cac_default_container_aid));
|
||||
if (applet == NULL) {
|
||||
goto failure;
|
||||
}
|
||||
vcard_add_applet(card, applet);
|
||||
|
||||
/* create a default blank container applet */
|
||||
applet = vcard_new_applet(cac_applet_id_process_apdu,
|
||||
NULL, cac_id_aid,
|
||||
sizeof(cac_id_aid));
|
||||
if (applet == NULL) {
|
||||
goto failure;
|
||||
}
|
||||
vcard_add_applet(card, applet);
|
||||
return VCARD_DONE;
|
||||
|
||||
failure:
|
||||
return VCARD_FAIL;
|
||||
}
|
||||
|
@ -1,31 +0,0 @@
|
||||
/*
|
||||
* defines the entry point for the cac card. Only used by cac.c anc
|
||||
* vcard_emul_type.c
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
#ifndef CAC_H
|
||||
#define CAC_H 1
|
||||
#include "vcard.h"
|
||||
#include "vreader.h"
|
||||
|
||||
#define CAC_GET_PROPERTIES 0x56
|
||||
#define CAC_GET_ACR 0x4c
|
||||
#define CAC_READ_BUFFER 0x52
|
||||
#define CAC_UPDATE_BUFFER 0x58
|
||||
#define CAC_SIGN_DECRYPT 0x42
|
||||
#define CAC_GET_CERTIFICATE 0x36
|
||||
|
||||
/*
|
||||
* Initialize the cac card. This is the only public function in this file. All
|
||||
* the rest are connected through function pointers.
|
||||
*/
|
||||
VCardStatus cac_card_init(VReader *reader, VCard *card, const char *params,
|
||||
unsigned char * const *cert, int cert_len[],
|
||||
VCardKey *key[] /* adopt the keys*/,
|
||||
int cert_count);
|
||||
|
||||
/* not yet implemented */
|
||||
VCardStatus cac_is_cac_card(VReader *reader);
|
||||
#endif
|
@ -1,757 +0,0 @@
|
||||
/*
|
||||
* Implement the 7816 portion of the card spec
|
||||
*
|
||||
* This code is licensed under the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "glib-compat.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "vcard.h"
|
||||
#include "vcard_emul.h"
|
||||
#include "card_7816.h"
|
||||
|
||||
/*
|
||||
* set the status bytes based on the status word
|
||||
*/
|
||||
static void
|
||||
vcard_response_set_status(VCardResponse *response, vcard_7816_status_t status)
|
||||
{
|
||||
unsigned char sw1, sw2;
|
||||
response->b_status = status; /* make sure the status and swX representations
|
||||
* are consistent */
|
||||
sw1 = (status >> 8) & 0xff;
|
||||
sw2 = status & 0xff;
|
||||
response->b_sw1 = sw1;
|
||||
response->b_sw2 = sw2;
|
||||
response->b_data[response->b_len] = sw1;
|
||||
response->b_data[response->b_len+1] = sw2;
|
||||
}
|
||||
|
||||
/*
|
||||
* set the status bytes in a response buffer
|
||||
*/
|
||||
static void
|
||||
vcard_response_set_status_bytes(VCardResponse *response,
|
||||
unsigned char sw1, unsigned char sw2)
|
||||
{
|
||||
response->b_status = sw1 << 8 | sw2;
|
||||
response->b_sw1 = sw1;
|
||||
response->b_sw2 = sw2;
|
||||
response->b_data[response->b_len] = sw1;
|
||||
response->b_data[response->b_len+1] = sw2;
|
||||
}
|
||||
|
||||
/*
|
||||
* allocate a VCardResponse structure, plus space for the data buffer, and
|
||||
* set up everything but the resonse bytes.
|
||||
*/
|
||||
VCardResponse *
|
||||
vcard_response_new_data(unsigned char *buf, int len)
|
||||
{
|
||||
VCardResponse *new_response;
|
||||
|
||||
new_response = g_new(VCardResponse, 1);
|
||||
new_response->b_data = g_malloc(len + 2);
|
||||
memcpy(new_response->b_data, buf, len);
|
||||
new_response->b_total_len = len+2;
|
||||
new_response->b_len = len;
|
||||
new_response->b_type = VCARD_MALLOC;
|
||||
return new_response;
|
||||
}
|
||||
|
||||
static VCardResponse *
|
||||
vcard_init_buffer_response(VCard *card, unsigned char *buf, int len)
|
||||
{
|
||||
VCardResponse *response;
|
||||
VCardBufferResponse *buffer_response;
|
||||
|
||||
buffer_response = vcard_get_buffer_response(card);
|
||||
if (buffer_response) {
|
||||
vcard_set_buffer_response(card, NULL);
|
||||
vcard_buffer_response_delete(buffer_response);
|
||||
}
|
||||
buffer_response = vcard_buffer_response_new(buf, len);
|
||||
if (buffer_response == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
response = vcard_response_new_status_bytes(VCARD7816_SW1_RESPONSE_BYTES,
|
||||
len > 255 ? 0 : len);
|
||||
if (response == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
vcard_set_buffer_response(card, buffer_response);
|
||||
return response;
|
||||
}
|
||||
|
||||
/*
|
||||
* general buffer to hold results from APDU calls
|
||||
*/
|
||||
VCardResponse *
|
||||
vcard_response_new(VCard *card, unsigned char *buf,
|
||||
int len, int Le, vcard_7816_status_t status)
|
||||
{
|
||||
VCardResponse *new_response;
|
||||
|
||||
if (len > Le) {
|
||||
return vcard_init_buffer_response(card, buf, len);
|
||||
}
|
||||
new_response = vcard_response_new_data(buf, len);
|
||||
if (new_response == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
vcard_response_set_status(new_response, status);
|
||||
return new_response;
|
||||
}
|
||||
|
||||
/*
|
||||
* general buffer to hold results from APDU calls
|
||||
*/
|
||||
VCardResponse *
|
||||
vcard_response_new_bytes(VCard *card, unsigned char *buf, int len, int Le,
|
||||
unsigned char sw1, unsigned char sw2)
|
||||
{
|
||||
VCardResponse *new_response;
|
||||
|
||||
if (len > Le) {
|
||||
return vcard_init_buffer_response(card, buf, len);
|
||||
}
|
||||
new_response = vcard_response_new_data(buf, len);
|
||||
if (new_response == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
vcard_response_set_status_bytes(new_response, sw1, sw2);
|
||||
return new_response;
|
||||
}
|
||||
|
||||
/*
|
||||
* get a new Response buffer that only has a status.
|
||||
*/
|
||||
static VCardResponse *
|
||||
vcard_response_new_status(vcard_7816_status_t status)
|
||||
{
|
||||
VCardResponse *new_response;
|
||||
|
||||
new_response = g_new(VCardResponse, 1);
|
||||
new_response->b_data = &new_response->b_sw1;
|
||||
new_response->b_len = 0;
|
||||
new_response->b_total_len = 2;
|
||||
new_response->b_type = VCARD_MALLOC_STRUCT;
|
||||
vcard_response_set_status(new_response, status);
|
||||
return new_response;
|
||||
}
|
||||
|
||||
/*
|
||||
* same as above, but specify the status as separate bytes
|
||||
*/
|
||||
VCardResponse *
|
||||
vcard_response_new_status_bytes(unsigned char sw1, unsigned char sw2)
|
||||
{
|
||||
VCardResponse *new_response;
|
||||
|
||||
new_response = g_new(VCardResponse, 1);
|
||||
new_response->b_data = &new_response->b_sw1;
|
||||
new_response->b_len = 0;
|
||||
new_response->b_total_len = 2;
|
||||
new_response->b_type = VCARD_MALLOC_STRUCT;
|
||||
vcard_response_set_status_bytes(new_response, sw1, sw2);
|
||||
return new_response;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* free the response buffer. The Buffer has a type to handle the buffer
|
||||
* allocated in other ways than through malloc.
|
||||
*/
|
||||
void
|
||||
vcard_response_delete(VCardResponse *response)
|
||||
{
|
||||
if (response == NULL) {
|
||||
return;
|
||||
}
|
||||
switch (response->b_type) {
|
||||
case VCARD_MALLOC:
|
||||
/* everything was malloc'ed */
|
||||
g_free(response->b_data);
|
||||
g_free(response);
|
||||
break;
|
||||
case VCARD_MALLOC_DATA:
|
||||
/* only the data buffer was malloc'ed */
|
||||
g_free(response->b_data);
|
||||
break;
|
||||
case VCARD_MALLOC_STRUCT:
|
||||
/* only the structure was malloc'ed */
|
||||
g_free(response);
|
||||
break;
|
||||
case VCARD_STATIC:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* decode the class bit and set our generic type field, channel, and
|
||||
* secure messaging values.
|
||||
*/
|
||||
static vcard_7816_status_t
|
||||
vcard_apdu_set_class(VCardAPDU *apdu) {
|
||||
apdu->a_channel = 0;
|
||||
apdu->a_secure_messaging = 0;
|
||||
apdu->a_type = apdu->a_cla & 0xf0;
|
||||
apdu->a_gen_type = VCARD_7816_ISO;
|
||||
|
||||
/* parse the class tables 8 & 9 of the 7816-4 Part 4 spec */
|
||||
switch (apdu->a_type) {
|
||||
/* we only support the basic types */
|
||||
case 0x00:
|
||||
case 0x80:
|
||||
case 0x90:
|
||||
case 0xa0:
|
||||
apdu->a_channel = apdu->a_cla & 3;
|
||||
apdu->a_secure_messaging = apdu->a_cla & 0xe;
|
||||
break;
|
||||
case 0xb0:
|
||||
case 0xc0:
|
||||
break;
|
||||
|
||||
case 0x10:
|
||||
case 0x20:
|
||||
case 0x30:
|
||||
case 0x40:
|
||||
case 0x50:
|
||||
case 0x60:
|
||||
case 0x70:
|
||||
/* Reserved for future use */
|
||||
apdu->a_gen_type = VCARD_7816_RFU;
|
||||
break;
|
||||
case 0xd0:
|
||||
case 0xe0:
|
||||
case 0xf0:
|
||||
default:
|
||||
apdu->a_gen_type =
|
||||
(apdu->a_cla == 0xff) ? VCARD_7816_PTS : VCARD_7816_PROPRIETARY;
|
||||
break;
|
||||
}
|
||||
return VCARD7816_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* set the Le and Lc fields according to table 5 of the
|
||||
* 7816-4 part 4 spec
|
||||
*/
|
||||
static vcard_7816_status_t
|
||||
vcard_apdu_set_length(VCardAPDU *apdu)
|
||||
{
|
||||
int L, Le;
|
||||
|
||||
/* process according to table 5 of the 7816-4 Part 4 spec.
|
||||
* variable names match the variables in the spec */
|
||||
L = apdu->a_len-4; /* fixed APDU header */
|
||||
apdu->a_Lc = 0;
|
||||
apdu->a_Le = 0;
|
||||
apdu->a_body = NULL;
|
||||
switch (L) {
|
||||
case 0:
|
||||
/* 1 minimal apdu */
|
||||
return VCARD7816_STATUS_SUCCESS;
|
||||
case 1:
|
||||
/* 2S only return values apdu */
|
||||
/* zero maps to 256 here */
|
||||
apdu->a_Le = apdu->a_header->ah_Le ?
|
||||
apdu->a_header->ah_Le : 256;
|
||||
return VCARD7816_STATUS_SUCCESS;
|
||||
default:
|
||||
/* if the ah_Le byte is zero and we have more than
|
||||
* 1 byte in the header, then we must be using extended Le and Lc.
|
||||
* process the extended now. */
|
||||
if (apdu->a_header->ah_Le == 0) {
|
||||
if (L < 3) {
|
||||
/* coding error, need at least 3 bytes */
|
||||
return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
|
||||
}
|
||||
/* calculate the first extended value. Could be either Le or Lc */
|
||||
Le = (apdu->a_header->ah_body[0] << 8)
|
||||
|| apdu->a_header->ah_body[1];
|
||||
if (L == 3) {
|
||||
/* 2E extended, return data only */
|
||||
/* zero maps to 65536 */
|
||||
apdu->a_Le = Le ? Le : 65536;
|
||||
return VCARD7816_STATUS_SUCCESS;
|
||||
}
|
||||
if (Le == 0) {
|
||||
/* reserved for future use, probably for next time we need
|
||||
* to extend the lengths */
|
||||
return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
|
||||
}
|
||||
/* we know that the first extended value is Lc now */
|
||||
apdu->a_Lc = Le;
|
||||
apdu->a_body = &apdu->a_header->ah_body[2];
|
||||
if (L == Le+3) {
|
||||
/* 3E extended, only body parameters */
|
||||
return VCARD7816_STATUS_SUCCESS;
|
||||
}
|
||||
if (L == Le+5) {
|
||||
/* 4E extended, parameters and return data */
|
||||
Le = (apdu->a_data[apdu->a_len-2] << 8)
|
||||
|| apdu->a_data[apdu->a_len-1];
|
||||
apdu->a_Le = Le ? Le : 65536;
|
||||
return VCARD7816_STATUS_SUCCESS;
|
||||
}
|
||||
return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
|
||||
}
|
||||
/* not extended */
|
||||
apdu->a_Lc = apdu->a_header->ah_Le;
|
||||
apdu->a_body = &apdu->a_header->ah_body[0];
|
||||
if (L == apdu->a_Lc + 1) {
|
||||
/* 3S only body parameters */
|
||||
return VCARD7816_STATUS_SUCCESS;
|
||||
}
|
||||
if (L == apdu->a_Lc + 2) {
|
||||
/* 4S parameters and return data */
|
||||
Le = apdu->a_data[apdu->a_len-1];
|
||||
apdu->a_Le = Le ? Le : 256;
|
||||
return VCARD7816_STATUS_SUCCESS;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
|
||||
}
|
||||
|
||||
/*
|
||||
* create a new APDU from a raw set of bytes. This will decode all the
|
||||
* above fields. users of VCARDAPDU's can then depend on the already decoded
|
||||
* values.
|
||||
*/
|
||||
VCardAPDU *
|
||||
vcard_apdu_new(unsigned char *raw_apdu, int len, vcard_7816_status_t *status)
|
||||
{
|
||||
VCardAPDU *new_apdu;
|
||||
|
||||
*status = VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE;
|
||||
if (len < 4) {
|
||||
*status = VCARD7816_STATUS_ERROR_WRONG_LENGTH;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
new_apdu = g_new(VCardAPDU, 1);
|
||||
new_apdu->a_data = g_memdup(raw_apdu, len);
|
||||
new_apdu->a_len = len;
|
||||
*status = vcard_apdu_set_class(new_apdu);
|
||||
if (*status != VCARD7816_STATUS_SUCCESS) {
|
||||
g_free(new_apdu);
|
||||
return NULL;
|
||||
}
|
||||
*status = vcard_apdu_set_length(new_apdu);
|
||||
if (*status != VCARD7816_STATUS_SUCCESS) {
|
||||
g_free(new_apdu);
|
||||
new_apdu = NULL;
|
||||
}
|
||||
return new_apdu;
|
||||
}
|
||||
|
||||
void
|
||||
vcard_apdu_delete(VCardAPDU *apdu)
|
||||
{
|
||||
if (apdu == NULL) {
|
||||
return;
|
||||
}
|
||||
g_free(apdu->a_data);
|
||||
g_free(apdu);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* declare response buffers for all the 7816 defined error codes
|
||||
*/
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_SUCCESS)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_RET_CORUPT)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_CHANGE)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_FILE_FILLED)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR_CHANGE)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_LENGTH)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(
|
||||
VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_INVALID)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_NO_EF)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_PARAMETERS)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(
|
||||
VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_FILE_NOT_FOUND)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_P1_P2_INCORRECT)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_NOT_FOUND)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_INS_CODE_INVALID)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CLA_INVALID)
|
||||
VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_GENERAL)
|
||||
|
||||
/*
|
||||
* return a single response code. This function cannot fail. It will always
|
||||
* return a response.
|
||||
*/
|
||||
VCardResponse *
|
||||
vcard_make_response(vcard_7816_status_t status)
|
||||
{
|
||||
VCardResponse *response;
|
||||
|
||||
switch (status) {
|
||||
/* known 7816 response codes */
|
||||
case VCARD7816_STATUS_SUCCESS:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_SUCCESS);
|
||||
case VCARD7816_STATUS_WARNING:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_WARNING);
|
||||
case VCARD7816_STATUS_WARNING_RET_CORUPT:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_WARNING_RET_CORUPT);
|
||||
case VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE);
|
||||
case VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED);
|
||||
case VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID);
|
||||
case VCARD7816_STATUS_WARNING_CHANGE:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_WARNING_CHANGE);
|
||||
case VCARD7816_STATUS_WARNING_FILE_FILLED:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_WARNING_FILE_FILLED);
|
||||
case VCARD7816_STATUS_EXC_ERROR:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_EXC_ERROR);
|
||||
case VCARD7816_STATUS_EXC_ERROR_CHANGE:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_EXC_ERROR_CHANGE);
|
||||
case VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
|
||||
case VCARD7816_STATUS_ERROR_WRONG_LENGTH:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_WRONG_LENGTH);
|
||||
case VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED);
|
||||
case VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED);
|
||||
case VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED);
|
||||
case VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
|
||||
case VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE);
|
||||
case VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED);
|
||||
case VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED);
|
||||
case VCARD7816_STATUS_ERROR_DATA_INVALID:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_DATA_INVALID);
|
||||
case VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED);
|
||||
case VCARD7816_STATUS_ERROR_DATA_NO_EF:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_DATA_NO_EF);
|
||||
case VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING);
|
||||
case VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT);
|
||||
case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_WRONG_PARAMETERS);
|
||||
case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA);
|
||||
case VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED);
|
||||
case VCARD7816_STATUS_ERROR_FILE_NOT_FOUND:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_FILE_NOT_FOUND);
|
||||
case VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND);
|
||||
case VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE);
|
||||
case VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT);
|
||||
case VCARD7816_STATUS_ERROR_P1_P2_INCORRECT:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_P1_P2_INCORRECT);
|
||||
case VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT);
|
||||
case VCARD7816_STATUS_ERROR_DATA_NOT_FOUND:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_DATA_NOT_FOUND);
|
||||
case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2);
|
||||
case VCARD7816_STATUS_ERROR_INS_CODE_INVALID:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_INS_CODE_INVALID);
|
||||
case VCARD7816_STATUS_ERROR_CLA_INVALID:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_CLA_INVALID);
|
||||
case VCARD7816_STATUS_ERROR_GENERAL:
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_ERROR_GENERAL);
|
||||
default:
|
||||
/* we don't know this status code, create a response buffer to
|
||||
* hold it */
|
||||
response = vcard_response_new_status(status);
|
||||
if (response == NULL) {
|
||||
/* couldn't allocate the buffer, return memmory error */
|
||||
return VCARD_RESPONSE_GET_STATIC(
|
||||
VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Add File card support here if you need it.
|
||||
*/
|
||||
static VCardStatus
|
||||
vcard7816_file_system_process_apdu(VCard *card, VCardAPDU *apdu,
|
||||
VCardResponse **response)
|
||||
{
|
||||
/* TODO: if we want to support a virtual file system card, we do it here.
|
||||
* It would probably be a pkcs #15 card type */
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
|
||||
return VCARD_DONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* VM card (including java cards)
|
||||
*/
|
||||
static VCardStatus
|
||||
vcard7816_vm_process_apdu(VCard *card, VCardAPDU *apdu,
|
||||
VCardResponse **response)
|
||||
{
|
||||
int bytes_to_copy, next_byte_count, count;
|
||||
VCardApplet *current_applet;
|
||||
VCardBufferResponse *buffer_response;
|
||||
vcard_7816_status_t status;
|
||||
|
||||
/* parse the class first */
|
||||
if (apdu->a_gen_type != VCARD_7816_ISO) {
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
|
||||
return VCARD_DONE;
|
||||
}
|
||||
|
||||
/* use a switch so that if we need to support secure channel stuff later,
|
||||
* we know where to put it */
|
||||
switch (apdu->a_secure_messaging) {
|
||||
case 0x0: /* no SM */
|
||||
break;
|
||||
case 0x4: /* proprietary SM */
|
||||
case 0x8: /* header not authenticated */
|
||||
case 0xc: /* header authenticated */
|
||||
default:
|
||||
/* for now, don't try to support secure channel stuff in the
|
||||
* virtual card. */
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED);
|
||||
return VCARD_DONE;
|
||||
}
|
||||
|
||||
/* now parse the instruction */
|
||||
switch (apdu->a_ins) {
|
||||
case VCARD7816_INS_MANAGE_CHANNEL: /* secure channel op */
|
||||
case VCARD7816_INS_EXTERNAL_AUTHENTICATE: /* secure channel op */
|
||||
case VCARD7816_INS_GET_CHALLENGE: /* secure channel op */
|
||||
case VCARD7816_INS_INTERNAL_AUTHENTICATE: /* secure channel op */
|
||||
case VCARD7816_INS_ERASE_BINARY: /* applet control op */
|
||||
case VCARD7816_INS_READ_BINARY: /* applet control op */
|
||||
case VCARD7816_INS_WRITE_BINARY: /* applet control op */
|
||||
case VCARD7816_INS_UPDATE_BINARY: /* applet control op */
|
||||
case VCARD7816_INS_READ_RECORD: /* file op */
|
||||
case VCARD7816_INS_WRITE_RECORD: /* file op */
|
||||
case VCARD7816_INS_UPDATE_RECORD: /* file op */
|
||||
case VCARD7816_INS_APPEND_RECORD: /* file op */
|
||||
case VCARD7816_INS_ENVELOPE:
|
||||
case VCARD7816_INS_PUT_DATA:
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
|
||||
break;
|
||||
|
||||
case VCARD7816_INS_SELECT_FILE:
|
||||
if (apdu->a_p1 != 0x04) {
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED);
|
||||
break;
|
||||
}
|
||||
|
||||
/* side effect, deselect the current applet if no applet has been found
|
||||
* */
|
||||
current_applet = vcard_find_applet(card, apdu->a_body, apdu->a_Lc);
|
||||
vcard_select_applet(card, apdu->a_channel, current_applet);
|
||||
if (current_applet) {
|
||||
unsigned char *aid;
|
||||
int aid_len;
|
||||
aid = vcard_applet_get_aid(current_applet, &aid_len);
|
||||
*response = vcard_response_new(card, aid, aid_len, apdu->a_Le,
|
||||
VCARD7816_STATUS_SUCCESS);
|
||||
} else {
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_FILE_NOT_FOUND);
|
||||
}
|
||||
break;
|
||||
|
||||
case VCARD7816_INS_VERIFY:
|
||||
if ((apdu->a_p1 != 0x00) || (apdu->a_p2 != 0x00)) {
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_WRONG_PARAMETERS);
|
||||
} else {
|
||||
if (apdu->a_Lc == 0) {
|
||||
/* handle pin count if possible */
|
||||
count = vcard_emul_get_login_count(card);
|
||||
if (count < 0) {
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_DATA_NOT_FOUND);
|
||||
} else {
|
||||
if (count > 0xf) {
|
||||
count = 0xf;
|
||||
}
|
||||
*response = vcard_response_new_status_bytes(
|
||||
VCARD7816_SW1_WARNING_CHANGE,
|
||||
0xc0 | count);
|
||||
if (*response == NULL) {
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
status = vcard_emul_login(card, apdu->a_body, apdu->a_Lc);
|
||||
*response = vcard_make_response(status);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case VCARD7816_INS_GET_RESPONSE:
|
||||
buffer_response = vcard_get_buffer_response(card);
|
||||
if (!buffer_response) {
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_DATA_NOT_FOUND);
|
||||
/* handle error */
|
||||
break;
|
||||
}
|
||||
bytes_to_copy = MIN(buffer_response->len, apdu->a_Le);
|
||||
next_byte_count = MIN(256, buffer_response->len - bytes_to_copy);
|
||||
*response = vcard_response_new_bytes(
|
||||
card, buffer_response->current, bytes_to_copy,
|
||||
apdu->a_Le,
|
||||
next_byte_count ?
|
||||
VCARD7816_SW1_RESPONSE_BYTES : VCARD7816_SW1_SUCCESS,
|
||||
next_byte_count);
|
||||
buffer_response->current += bytes_to_copy;
|
||||
buffer_response->len -= bytes_to_copy;
|
||||
if (*response == NULL || (next_byte_count == 0)) {
|
||||
vcard_set_buffer_response(card, NULL);
|
||||
vcard_buffer_response_delete(buffer_response);
|
||||
}
|
||||
if (*response == NULL) {
|
||||
*response =
|
||||
vcard_make_response(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
|
||||
}
|
||||
break;
|
||||
|
||||
case VCARD7816_INS_GET_DATA:
|
||||
*response =
|
||||
vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
|
||||
break;
|
||||
|
||||
default:
|
||||
*response =
|
||||
vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
|
||||
break;
|
||||
}
|
||||
|
||||
/* response should have been set somewhere */
|
||||
assert(*response != NULL);
|
||||
return VCARD_DONE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* APDU processing starts here. This routes the card processing stuff to the
|
||||
* right location.
|
||||
*/
|
||||
VCardStatus
|
||||
vcard_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response)
|
||||
{
|
||||
VCardStatus status;
|
||||
VCardBufferResponse *buffer_response;
|
||||
|
||||
/* first handle any PTS commands, which aren't really APDU's */
|
||||
if (apdu->a_type == VCARD_7816_PTS) {
|
||||
/* the PTS responses aren't really responses either */
|
||||
*response = vcard_response_new_data(apdu->a_data, apdu->a_len);
|
||||
/* PTS responses have no status bytes */
|
||||
(*response)->b_total_len = (*response)->b_len;
|
||||
return VCARD_DONE;
|
||||
}
|
||||
buffer_response = vcard_get_buffer_response(card);
|
||||
if (buffer_response && apdu->a_ins != VCARD7816_INS_GET_RESPONSE) {
|
||||
/* clear out buffer_response, return an error */
|
||||
vcard_set_buffer_response(card, NULL);
|
||||
vcard_buffer_response_delete(buffer_response);
|
||||
*response = vcard_make_response(VCARD7816_STATUS_EXC_ERROR);
|
||||
return VCARD_DONE;
|
||||
}
|
||||
|
||||
status = vcard_process_applet_apdu(card, apdu, response);
|
||||
if (status != VCARD_NEXT) {
|
||||
return status;
|
||||
}
|
||||
switch (vcard_get_type(card)) {
|
||||
case VCARD_FILE_SYSTEM:
|
||||
return vcard7816_file_system_process_apdu(card, apdu, response);
|
||||
case VCARD_VM:
|
||||
return vcard7816_vm_process_apdu(card, apdu, response);
|
||||
case VCARD_DIRECT:
|
||||
/* if we are type direct, then the applet should handle everything */
|
||||
assert(!"VCARD_DIRECT: applet failure");
|
||||
break;
|
||||
}
|
||||
*response =
|
||||
vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
|
||||
return VCARD_DONE;
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Implement the 7816 portion of the card spec
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
#ifndef CARD_7816_H
|
||||
#define CARD_7816_H 1
|
||||
|
||||
#include "card_7816t.h"
|
||||
#include "vcardt.h"
|
||||
|
||||
/*
|
||||
* constructors for VCardResponse's
|
||||
*/
|
||||
/* response from a return buffer and a status */
|
||||
VCardResponse *vcard_response_new(VCard *card, unsigned char *buf, int len,
|
||||
int Le, vcard_7816_status_t status);
|
||||
/* response from a return buffer and status bytes */
|
||||
VCardResponse *vcard_response_new_bytes(VCard *card, unsigned char *buf,
|
||||
int len, int Le,
|
||||
unsigned char sw1, unsigned char sw2);
|
||||
/* response from just status bytes */
|
||||
VCardResponse *vcard_response_new_status_bytes(unsigned char sw1,
|
||||
unsigned char sw2);
|
||||
/* response from just status: NOTE this cannot fail, it will always return a
|
||||
* valid response, if it can't allocate memory, the response will be
|
||||
* VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE */
|
||||
VCardResponse *vcard_make_response(vcard_7816_status_t status);
|
||||
|
||||
/* create a raw response (status has already been encoded */
|
||||
VCardResponse *vcard_response_new_data(unsigned char *buf, int len);
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* destructor for VCardResponse.
|
||||
* Can be called with a NULL response
|
||||
*/
|
||||
void vcard_response_delete(VCardResponse *response);
|
||||
|
||||
/*
|
||||
* constructor for VCardAPDU
|
||||
*/
|
||||
VCardAPDU *vcard_apdu_new(unsigned char *raw_apdu, int len,
|
||||
unsigned short *status);
|
||||
|
||||
/*
|
||||
* destructor for VCardAPDU
|
||||
* Can be called with a NULL apdu
|
||||
*/
|
||||
void vcard_apdu_delete(VCardAPDU *apdu);
|
||||
|
||||
/*
|
||||
* APDU processing starts here. This routes the card processing stuff to the
|
||||
* right location. Always returns a valid response.
|
||||
*/
|
||||
VCardStatus vcard_process_apdu(VCard *card, VCardAPDU *apdu,
|
||||
VCardResponse **response);
|
||||
|
||||
#endif
|
@ -1,165 +0,0 @@
|
||||
/*
|
||||
* Implement the 7816 portion of the card spec
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
#ifndef CARD_7816T_H
|
||||
#define CARD_7816T_H 1
|
||||
|
||||
typedef unsigned short vcard_7816_status_t;
|
||||
|
||||
struct VCardResponseStruct {
|
||||
unsigned char *b_data;
|
||||
vcard_7816_status_t b_status;
|
||||
unsigned char b_sw1;
|
||||
unsigned char b_sw2;
|
||||
int b_len;
|
||||
int b_total_len;
|
||||
enum VCardResponseBufferType {
|
||||
VCARD_MALLOC,
|
||||
VCARD_MALLOC_DATA,
|
||||
VCARD_MALLOC_STRUCT,
|
||||
VCARD_STATIC
|
||||
} b_type;
|
||||
};
|
||||
|
||||
#define VCARD_RESPONSE_NEW_STATIC_STATUS(stat) \
|
||||
static const VCardResponse VCardResponse##stat = \
|
||||
{(unsigned char *)&VCardResponse##stat.b_sw1, (stat), ((stat) >> 8), \
|
||||
((stat) & 0xff), 0, 2, VCARD_STATIC};
|
||||
|
||||
#define VCARD_RESPONSE_NEW_STATIC_STATUS_BYTES(sw1, sw2) \
|
||||
static const VCardResponse VCARDResponse##sw1 = \
|
||||
{(unsigned char *)&VCardResponse##name.b_sw1, ((sw1) << 8 | (sw2)), \
|
||||
(sw1), (sw2), 0, 2, VCARD_STATIC};
|
||||
|
||||
/* cast away the const, callers need may need to 'free' the
|
||||
* result, and const implies that they don't */
|
||||
#define VCARD_RESPONSE_GET_STATIC(name) \
|
||||
((VCardResponse *)(&VCardResponse##name))
|
||||
|
||||
typedef enum {
|
||||
VCARD_7816_ISO,
|
||||
VCARD_7816_RFU,
|
||||
VCARD_7816_PTS,
|
||||
VCARD_7816_PROPRIETARY
|
||||
} VCardAPDUType;
|
||||
|
||||
|
||||
/*
|
||||
* 7816 header. All APDU's have this header.
|
||||
* They must be laid out in this order.
|
||||
*/
|
||||
struct VCardAPDUHeader {
|
||||
unsigned char ah_cla;
|
||||
unsigned char ah_ins;
|
||||
unsigned char ah_p1;
|
||||
unsigned char ah_p2;
|
||||
unsigned char ah_Le;
|
||||
unsigned char ah_body[1]; /* indefinite length */
|
||||
};
|
||||
|
||||
/*
|
||||
* 7816 APDU structure. The raw bytes are stored in the union and can be
|
||||
* accessed directly through u.data (which is aliased as a_data).
|
||||
*
|
||||
* Names of the fields match the 7816 documentation.
|
||||
*/
|
||||
struct VCardAPDUStruct {
|
||||
int a_len; /* length of the whole buffer, including header */
|
||||
int a_Lc; /* 7816 Lc (parameter length) value */
|
||||
int a_Le; /* 7816 Le (expected result length) value */
|
||||
unsigned char *a_body; /* pointer to the parameter */
|
||||
int a_channel; /* decoded channel */
|
||||
int a_secure_messaging; /* decoded secure messaging type */
|
||||
int a_type; /* decoded type from cla (top nibble of class) */
|
||||
VCardAPDUType a_gen_type; /* generic type (7816, PROPRIETARY, RFU, etc) */
|
||||
union {
|
||||
struct VCardAPDUHeader *header;
|
||||
unsigned char *data;
|
||||
} u;
|
||||
/* give the subfields a unified look */
|
||||
#define a_header u.header
|
||||
#define a_data u.data
|
||||
#define a_cla a_header->ah_cla /* class */
|
||||
#define a_ins a_header->ah_ins /* instruction */
|
||||
#define a_p1 a_header->ah_p1 /* parameter 1 */
|
||||
#define a_p2 a_header->ah_p2 /* parameter 2 */
|
||||
};
|
||||
|
||||
/* 7816 status codes */
|
||||
#define VCARD7816_STATUS_SUCCESS 0x9000
|
||||
#define VCARD7816_STATUS_WARNING 0x6200
|
||||
#define VCARD7816_STATUS_WARNING_RET_CORUPT 0x6281
|
||||
#define VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE 0x6282
|
||||
#define VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED 0x6283
|
||||
#define VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID 0x6284
|
||||
#define VCARD7816_STATUS_WARNING_CHANGE 0x6300
|
||||
#define VCARD7816_STATUS_WARNING_FILE_FILLED 0x6381
|
||||
#define VCARD7816_STATUS_EXC_ERROR 0x6400
|
||||
#define VCARD7816_STATUS_EXC_ERROR_CHANGE 0x6500
|
||||
#define VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE 0x6581
|
||||
#define VCARD7816_STATUS_ERROR_WRONG_LENGTH 0x6700
|
||||
#define VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED 0x6800
|
||||
#define VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED 0x6881
|
||||
#define VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED 0x6882
|
||||
#define VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED 0x6900
|
||||
#define VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE 0x6981
|
||||
#define VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED 0x6982
|
||||
#define VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED 0x6983
|
||||
#define VCARD7816_STATUS_ERROR_DATA_INVALID 0x6984
|
||||
#define VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED 0x6985
|
||||
#define VCARD7816_STATUS_ERROR_DATA_NO_EF 0x6986
|
||||
#define VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING 0x6987
|
||||
#define VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT 0x6988
|
||||
#define VCARD7816_STATUS_ERROR_WRONG_PARAMETERS 0x6a00
|
||||
#define VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA 0x6a80
|
||||
#define VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED 0x6a81
|
||||
#define VCARD7816_STATUS_ERROR_FILE_NOT_FOUND 0x6a82
|
||||
#define VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND 0x6a83
|
||||
#define VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE 0x6a84
|
||||
#define VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT 0x6a85
|
||||
#define VCARD7816_STATUS_ERROR_P1_P2_INCORRECT 0x6a86
|
||||
#define VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT 0x6a87
|
||||
#define VCARD7816_STATUS_ERROR_DATA_NOT_FOUND 0x6a88
|
||||
#define VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2 0x6b00
|
||||
#define VCARD7816_STATUS_ERROR_INS_CODE_INVALID 0x6d00
|
||||
#define VCARD7816_STATUS_ERROR_CLA_INVALID 0x6e00
|
||||
#define VCARD7816_STATUS_ERROR_GENERAL 0x6f00
|
||||
/* 7816 sw1 codes */
|
||||
#define VCARD7816_SW1_SUCCESS 0x90
|
||||
#define VCARD7816_SW1_RESPONSE_BYTES 0x61
|
||||
#define VCARD7816_SW1_WARNING 0x62
|
||||
#define VCARD7816_SW1_WARNING_CHANGE 0x63
|
||||
#define VCARD7816_SW1_EXC_ERROR 0x64
|
||||
#define VCARD7816_SW1_EXC_ERROR_CHANGE 0x65
|
||||
#define VCARD7816_SW1_ERROR_WRONG_LENGTH 0x67
|
||||
#define VCARD7816_SW1_CLA_ERROR 0x68
|
||||
#define VCARD7816_SW1_COMMAND_ERROR 0x69
|
||||
#define VCARD7816_SW1_P1_P2_ERROR 0x6a
|
||||
#define VCARD7816_SW1_LE_ERROR 0x6c
|
||||
#define VCARD7816_SW1_INS_ERROR 0x6d
|
||||
#define VCARD7816_SW1_CLA_NOT_SUPPORTED 0x6e
|
||||
|
||||
/* 7816 Instructions */
|
||||
#define VCARD7816_INS_MANAGE_CHANNEL 0x70
|
||||
#define VCARD7816_INS_EXTERNAL_AUTHENTICATE 0x82
|
||||
#define VCARD7816_INS_GET_CHALLENGE 0x84
|
||||
#define VCARD7816_INS_INTERNAL_AUTHENTICATE 0x88
|
||||
#define VCARD7816_INS_ERASE_BINARY 0x0e
|
||||
#define VCARD7816_INS_READ_BINARY 0xb0
|
||||
#define VCARD7816_INS_WRITE_BINARY 0xd0
|
||||
#define VCARD7816_INS_UPDATE_BINARY 0xd6
|
||||
#define VCARD7816_INS_READ_RECORD 0xb2
|
||||
#define VCARD7816_INS_WRITE_RECORD 0xd2
|
||||
#define VCARD7816_INS_UPDATE_RECORD 0xdc
|
||||
#define VCARD7816_INS_APPEND_RECORD 0xe2
|
||||
#define VCARD7816_INS_ENVELOPE 0xc2
|
||||
#define VCARD7816_INS_PUT_DATA 0xda
|
||||
#define VCARD7816_INS_GET_DATA 0xca
|
||||
#define VCARD7816_INS_SELECT_FILE 0xa4
|
||||
#define VCARD7816_INS_VERIFY 0x20
|
||||
#define VCARD7816_INS_GET_RESPONSE 0xc0
|
||||
|
||||
#endif
|
@ -1,103 +0,0 @@
|
||||
/*
|
||||
* event queue implementation.
|
||||
*
|
||||
* This code is licensed under the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "glib-compat.h"
|
||||
|
||||
#include "vcard.h"
|
||||
#include "vreader.h"
|
||||
#include "vevent.h"
|
||||
|
||||
VEvent *
|
||||
vevent_new(VEventType type, VReader *reader, VCard *card)
|
||||
{
|
||||
VEvent *new_vevent;
|
||||
|
||||
new_vevent = g_new(VEvent, 1);
|
||||
new_vevent->next = NULL;
|
||||
new_vevent->type = type;
|
||||
new_vevent->reader = vreader_reference(reader);
|
||||
new_vevent->card = vcard_reference(card);
|
||||
|
||||
return new_vevent;
|
||||
}
|
||||
|
||||
void
|
||||
vevent_delete(VEvent *vevent)
|
||||
{
|
||||
if (vevent == NULL) {
|
||||
return;
|
||||
}
|
||||
vreader_free(vevent->reader);
|
||||
vcard_free(vevent->card);
|
||||
g_free(vevent);
|
||||
}
|
||||
|
||||
/*
|
||||
* VEvent queue management
|
||||
*/
|
||||
|
||||
static VEvent *vevent_queue_head;
|
||||
static VEvent *vevent_queue_tail;
|
||||
static CompatGMutex vevent_queue_lock;
|
||||
static CompatGCond vevent_queue_condition;
|
||||
|
||||
void vevent_queue_init(void)
|
||||
{
|
||||
vevent_queue_head = vevent_queue_tail = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
vevent_queue_vevent(VEvent *vevent)
|
||||
{
|
||||
vevent->next = NULL;
|
||||
g_mutex_lock(&vevent_queue_lock);
|
||||
if (vevent_queue_head) {
|
||||
assert(vevent_queue_tail);
|
||||
vevent_queue_tail->next = vevent;
|
||||
} else {
|
||||
vevent_queue_head = vevent;
|
||||
}
|
||||
vevent_queue_tail = vevent;
|
||||
g_cond_signal(&vevent_queue_condition);
|
||||
g_mutex_unlock(&vevent_queue_lock);
|
||||
}
|
||||
|
||||
/* must have lock */
|
||||
static VEvent *
|
||||
vevent_dequeue_vevent(void)
|
||||
{
|
||||
VEvent *vevent = NULL;
|
||||
if (vevent_queue_head) {
|
||||
vevent = vevent_queue_head;
|
||||
vevent_queue_head = vevent->next;
|
||||
vevent->next = NULL;
|
||||
}
|
||||
return vevent;
|
||||
}
|
||||
|
||||
VEvent *vevent_wait_next_vevent(void)
|
||||
{
|
||||
VEvent *vevent;
|
||||
|
||||
g_mutex_lock(&vevent_queue_lock);
|
||||
while ((vevent = vevent_dequeue_vevent()) == NULL) {
|
||||
g_cond_wait(&vevent_queue_condition, &vevent_queue_lock);
|
||||
}
|
||||
g_mutex_unlock(&vevent_queue_lock);
|
||||
return vevent;
|
||||
}
|
||||
|
||||
VEvent *vevent_get_next_vevent(void)
|
||||
{
|
||||
VEvent *vevent;
|
||||
|
||||
g_mutex_lock(&vevent_queue_lock);
|
||||
vevent = vevent_dequeue_vevent();
|
||||
g_mutex_unlock(&vevent_queue_lock);
|
||||
return vevent;
|
||||
}
|
||||
|
@ -1,29 +0,0 @@
|
||||
/*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef EVENTT_H
|
||||
#define EVENTT_H 1
|
||||
#include "vreadert.h"
|
||||
#include "vcardt.h"
|
||||
|
||||
typedef struct VEventStruct VEvent;
|
||||
|
||||
typedef enum {
|
||||
VEVENT_READER_INSERT,
|
||||
VEVENT_READER_REMOVE,
|
||||
VEVENT_CARD_INSERT,
|
||||
VEVENT_CARD_REMOVE,
|
||||
VEVENT_LAST,
|
||||
} VEventType;
|
||||
|
||||
struct VEventStruct {
|
||||
VEvent *next;
|
||||
VEventType type;
|
||||
VReader *reader;
|
||||
VCard *card;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
@ -1,13 +0,0 @@
|
||||
prefix=@PREFIX@
|
||||
exec_prefix=${prefix}
|
||||
libdir=@LIBDIR@
|
||||
includedir=@INCLUDEDIR@
|
||||
|
||||
Name: cacard
|
||||
Description: CA Card library
|
||||
Version: @VERSION@
|
||||
|
||||
Requires.private: nss glib-2.0
|
||||
Libs: -L${libdir} -lcacard
|
||||
Libs.private:
|
||||
Cflags: -I${includedir}
|
@ -1,77 +0,0 @@
|
||||
cac_card_init
|
||||
cac_is_cac_card
|
||||
vcard_add_applet
|
||||
vcard_apdu_delete
|
||||
vcard_apdu_new
|
||||
vcard_applet_get_aid
|
||||
vcard_buffer_response_delete
|
||||
vcard_buffer_response_new
|
||||
vcard_delete_applet
|
||||
vcard_emul_delete_key
|
||||
vcard_emul_force_card_insert
|
||||
vcard_emul_force_card_remove
|
||||
vcard_emul_get_atr
|
||||
vcard_emul_get_login_count
|
||||
vcard_emul_init
|
||||
vcard_emul_login
|
||||
vcard_emul_options
|
||||
vcard_emul_replay_insertion_events
|
||||
vcard_emul_reset
|
||||
vcard_emul_rsa_op
|
||||
vcard_emul_type_from_string
|
||||
vcard_emul_type_select
|
||||
vcard_emul_usage
|
||||
vcard_find_applet
|
||||
vcard_free
|
||||
vcard_get_atr
|
||||
vcard_get_buffer_response
|
||||
vcard_get_current_applet_private
|
||||
vcard_get_private
|
||||
vcard_get_type
|
||||
vcard_init
|
||||
vcard_make_response
|
||||
vcard_new
|
||||
vcard_new_applet
|
||||
vcard_process_apdu
|
||||
vcard_process_applet_apdu
|
||||
vcard_reference
|
||||
vcard_reset
|
||||
vcard_response_delete
|
||||
vcard_response_new
|
||||
vcard_response_new_bytes
|
||||
vcard_response_new_data
|
||||
vcard_response_new_status_bytes
|
||||
vcard_select_applet
|
||||
vcard_set_applet_private
|
||||
vcard_set_atr_func
|
||||
vcard_set_buffer_response
|
||||
vcard_set_type
|
||||
vevent_delete
|
||||
vevent_get_next_vevent
|
||||
vevent_new
|
||||
vevent_queue_init
|
||||
vevent_queue_vevent
|
||||
vevent_wait_next_vevent
|
||||
vreader_add_reader
|
||||
vreader_card_is_present
|
||||
vreader_free
|
||||
vreader_get_id
|
||||
vreader_get_name
|
||||
vreader_get_private
|
||||
vreader_get_reader_by_id
|
||||
vreader_get_reader_by_name
|
||||
vreader_get_reader_list
|
||||
vreader_init
|
||||
vreader_insert_card
|
||||
vreader_list_delete
|
||||
vreader_list_get_first
|
||||
vreader_list_get_next
|
||||
vreader_list_get_reader
|
||||
vreader_new
|
||||
vreader_power_off
|
||||
vreader_power_on
|
||||
vreader_queue_card_event
|
||||
vreader_reference
|
||||
vreader_remove_reader
|
||||
vreader_set_id
|
||||
vreader_xfr_bytes
|
@ -1,22 +0,0 @@
|
||||
/*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "vcard.h"
|
||||
|
||||
VCardStatus cac_card_init(const char *flags, VCard *card,
|
||||
const unsigned char *cert[],
|
||||
int cert_len[], VCardKey *key[] /* adopt the keys*/,
|
||||
int cert_count);
|
||||
/*
|
||||
* this will crash... just test the linkage right now
|
||||
*/
|
||||
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
VCard *card; /* no constructor yet */
|
||||
cac_card_init("", card, NULL, 0, NULL, 0);
|
||||
}
|
||||
|
@ -1,325 +0,0 @@
|
||||
/*
|
||||
* implement the Java card standard.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "glib-compat.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "vcard.h"
|
||||
#include "vcard_emul.h"
|
||||
#include "card_7816t.h"
|
||||
|
||||
struct VCardAppletStruct {
|
||||
VCardApplet *next;
|
||||
VCardProcessAPDU process_apdu;
|
||||
VCardResetApplet reset_applet;
|
||||
unsigned char *aid;
|
||||
int aid_len;
|
||||
void *applet_private;
|
||||
VCardAppletPrivateFree applet_private_free;
|
||||
};
|
||||
|
||||
struct VCardStruct {
|
||||
int reference_count;
|
||||
VCardApplet *applet_list;
|
||||
VCardApplet *current_applet[MAX_CHANNEL];
|
||||
VCardBufferResponse *vcard_buffer_response;
|
||||
VCardType type;
|
||||
VCardEmul *vcard_private;
|
||||
VCardEmulFree vcard_private_free;
|
||||
VCardGetAtr vcard_get_atr;
|
||||
};
|
||||
|
||||
VCardBufferResponse *
|
||||
vcard_buffer_response_new(unsigned char *buffer, int size)
|
||||
{
|
||||
VCardBufferResponse *new_buffer;
|
||||
|
||||
new_buffer = g_new(VCardBufferResponse, 1);
|
||||
new_buffer->buffer = (unsigned char *)g_memdup(buffer, size);
|
||||
new_buffer->buffer_len = size;
|
||||
new_buffer->current = new_buffer->buffer;
|
||||
new_buffer->len = size;
|
||||
return new_buffer;
|
||||
}
|
||||
|
||||
void
|
||||
vcard_buffer_response_delete(VCardBufferResponse *buffer_response)
|
||||
{
|
||||
if (buffer_response == NULL) {
|
||||
return;
|
||||
}
|
||||
g_free(buffer_response->buffer);
|
||||
g_free(buffer_response);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* clean up state after a reset
|
||||
*/
|
||||
void
|
||||
vcard_reset(VCard *card, VCardPower power)
|
||||
{
|
||||
int i;
|
||||
VCardApplet *applet = NULL;
|
||||
|
||||
if (card->type == VCARD_DIRECT) {
|
||||
/* select the last applet */
|
||||
VCardApplet *current_applet = NULL;
|
||||
for (current_applet = card->applet_list; current_applet;
|
||||
current_applet = current_applet->next) {
|
||||
applet = current_applet;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < MAX_CHANNEL; i++) {
|
||||
card->current_applet[i] = applet;
|
||||
}
|
||||
if (card->vcard_buffer_response) {
|
||||
vcard_buffer_response_delete(card->vcard_buffer_response);
|
||||
card->vcard_buffer_response = NULL;
|
||||
}
|
||||
vcard_emul_reset(card, power);
|
||||
if (applet) {
|
||||
applet->reset_applet(card, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* applet utilities */
|
||||
|
||||
/*
|
||||
* applet utilities
|
||||
*/
|
||||
/* constructor */
|
||||
VCardApplet *
|
||||
vcard_new_applet(VCardProcessAPDU applet_process_function,
|
||||
VCardResetApplet applet_reset_function,
|
||||
unsigned char *aid, int aid_len)
|
||||
{
|
||||
VCardApplet *applet;
|
||||
|
||||
applet = g_new0(VCardApplet, 1);
|
||||
applet->process_apdu = applet_process_function;
|
||||
applet->reset_applet = applet_reset_function;
|
||||
|
||||
applet->aid = g_memdup(aid, aid_len);
|
||||
applet->aid_len = aid_len;
|
||||
return applet;
|
||||
}
|
||||
|
||||
/* destructor */
|
||||
void
|
||||
vcard_delete_applet(VCardApplet *applet)
|
||||
{
|
||||
if (applet == NULL) {
|
||||
return;
|
||||
}
|
||||
if (applet->applet_private_free) {
|
||||
applet->applet_private_free(applet->applet_private);
|
||||
}
|
||||
g_free(applet->aid);
|
||||
g_free(applet);
|
||||
}
|
||||
|
||||
/* accessor */
|
||||
void
|
||||
vcard_set_applet_private(VCardApplet *applet, VCardAppletPrivate *private,
|
||||
VCardAppletPrivateFree private_free)
|
||||
{
|
||||
if (applet->applet_private_free) {
|
||||
applet->applet_private_free(applet->applet_private);
|
||||
}
|
||||
applet->applet_private = private;
|
||||
applet->applet_private_free = private_free;
|
||||
}
|
||||
|
||||
VCard *
|
||||
vcard_new(VCardEmul *private, VCardEmulFree private_free)
|
||||
{
|
||||
VCard *new_card;
|
||||
|
||||
new_card = g_new0(VCard, 1);
|
||||
new_card->type = VCARD_VM;
|
||||
new_card->vcard_private = private;
|
||||
new_card->vcard_private_free = private_free;
|
||||
new_card->reference_count = 1;
|
||||
return new_card;
|
||||
}
|
||||
|
||||
VCard *
|
||||
vcard_reference(VCard *vcard)
|
||||
{
|
||||
if (vcard == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
vcard->reference_count++;
|
||||
return vcard;
|
||||
}
|
||||
|
||||
void
|
||||
vcard_free(VCard *vcard)
|
||||
{
|
||||
VCardApplet *current_applet;
|
||||
VCardApplet *next_applet;
|
||||
|
||||
if (vcard == NULL) {
|
||||
return;
|
||||
}
|
||||
vcard->reference_count--;
|
||||
if (vcard->reference_count != 0) {
|
||||
return;
|
||||
}
|
||||
if (vcard->vcard_private_free) {
|
||||
(*vcard->vcard_private_free)(vcard->vcard_private);
|
||||
}
|
||||
for (current_applet = vcard->applet_list; current_applet;
|
||||
current_applet = next_applet) {
|
||||
next_applet = current_applet->next;
|
||||
vcard_delete_applet(current_applet);
|
||||
}
|
||||
vcard_buffer_response_delete(vcard->vcard_buffer_response);
|
||||
g_free(vcard);
|
||||
}
|
||||
|
||||
void
|
||||
vcard_get_atr(VCard *vcard, unsigned char *atr, int *atr_len)
|
||||
{
|
||||
if (vcard->vcard_get_atr) {
|
||||
(*vcard->vcard_get_atr)(vcard, atr, atr_len);
|
||||
return;
|
||||
}
|
||||
vcard_emul_get_atr(vcard, atr, atr_len);
|
||||
}
|
||||
|
||||
void
|
||||
vcard_set_atr_func(VCard *card, VCardGetAtr vcard_get_atr)
|
||||
{
|
||||
card->vcard_get_atr = vcard_get_atr;
|
||||
}
|
||||
|
||||
|
||||
VCardStatus
|
||||
vcard_add_applet(VCard *card, VCardApplet *applet)
|
||||
{
|
||||
applet->next = card->applet_list;
|
||||
card->applet_list = applet;
|
||||
/* if our card-type is direct, always call the applet */
|
||||
if (card->type == VCARD_DIRECT) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_CHANNEL; i++) {
|
||||
card->current_applet[i] = applet;
|
||||
}
|
||||
}
|
||||
return VCARD_DONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* manage applets
|
||||
*/
|
||||
VCardApplet *
|
||||
vcard_find_applet(VCard *card, unsigned char *aid, int aid_len)
|
||||
{
|
||||
VCardApplet *current_applet;
|
||||
|
||||
for (current_applet = card->applet_list; current_applet;
|
||||
current_applet = current_applet->next) {
|
||||
if (current_applet->aid_len != aid_len) {
|
||||
continue;
|
||||
}
|
||||
if (memcmp(current_applet->aid, aid, aid_len) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return current_applet;
|
||||
}
|
||||
|
||||
unsigned char *
|
||||
vcard_applet_get_aid(VCardApplet *applet, int *aid_len)
|
||||
{
|
||||
if (applet == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
*aid_len = applet->aid_len;
|
||||
return applet->aid;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
vcard_select_applet(VCard *card, int channel, VCardApplet *applet)
|
||||
{
|
||||
assert(channel < MAX_CHANNEL);
|
||||
|
||||
/* If using an emulated card, make sure to log out of any already logged in
|
||||
* session. */
|
||||
vcard_emul_logout(card);
|
||||
|
||||
card->current_applet[channel] = applet;
|
||||
/* reset the applet */
|
||||
if (applet && applet->reset_applet) {
|
||||
applet->reset_applet(card, channel);
|
||||
}
|
||||
}
|
||||
|
||||
VCardAppletPrivate *
|
||||
vcard_get_current_applet_private(VCard *card, int channel)
|
||||
{
|
||||
VCardApplet *applet = card->current_applet[channel];
|
||||
|
||||
if (applet == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return applet->applet_private;
|
||||
}
|
||||
|
||||
VCardStatus
|
||||
vcard_process_applet_apdu(VCard *card, VCardAPDU *apdu,
|
||||
VCardResponse **response)
|
||||
{
|
||||
if (card->current_applet[apdu->a_channel]) {
|
||||
return card->current_applet[apdu->a_channel]->process_apdu(
|
||||
card, apdu, response);
|
||||
}
|
||||
return VCARD_NEXT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Accessor functions
|
||||
*/
|
||||
/* accessor functions for the response buffer */
|
||||
VCardBufferResponse *
|
||||
vcard_get_buffer_response(VCard *card)
|
||||
{
|
||||
return card->vcard_buffer_response;
|
||||
}
|
||||
|
||||
void
|
||||
vcard_set_buffer_response(VCard *card, VCardBufferResponse *buffer)
|
||||
{
|
||||
card->vcard_buffer_response = buffer;
|
||||
}
|
||||
|
||||
|
||||
/* accessor functions for the type */
|
||||
VCardType
|
||||
vcard_get_type(VCard *card)
|
||||
{
|
||||
return card->type;
|
||||
}
|
||||
|
||||
void
|
||||
vcard_set_type(VCard *card, VCardType type)
|
||||
{
|
||||
card->type = type;
|
||||
}
|
||||
|
||||
/* accessor for private data */
|
||||
VCardEmul *
|
||||
vcard_get_private(VCard *vcard)
|
||||
{
|
||||
return vcard->vcard_private;
|
||||
}
|
||||
|
@ -1,86 +0,0 @@
|
||||
/*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
#ifndef VCARD_H
|
||||
#define VCARD_H 1
|
||||
|
||||
#include "vcardt.h"
|
||||
|
||||
/*
|
||||
* response buffer constructors and destructors.
|
||||
*
|
||||
* response buffers are used when we need to return more data than will fit in
|
||||
* a normal APDU response (nominally 254 bytes).
|
||||
*/
|
||||
VCardBufferResponse *vcard_buffer_response_new(unsigned char *buffer, int size);
|
||||
void vcard_buffer_response_delete(VCardBufferResponse *buffer_response);
|
||||
|
||||
|
||||
/*
|
||||
* clean up state on reset
|
||||
*/
|
||||
void vcard_reset(VCard *card, VCardPower power);
|
||||
|
||||
/*
|
||||
* applet utilities
|
||||
*/
|
||||
/*
|
||||
* Constructor for a VCardApplet
|
||||
*/
|
||||
VCardApplet *vcard_new_applet(VCardProcessAPDU applet_process_function,
|
||||
VCardResetApplet applet_reset_function,
|
||||
unsigned char *aid, int aid_len);
|
||||
|
||||
/*
|
||||
* destructor for a VCardApplet
|
||||
* Can be called with a NULL applet
|
||||
*/
|
||||
void vcard_delete_applet(VCardApplet *applet);
|
||||
|
||||
/* accessor - set the card type specific private data */
|
||||
void vcard_set_applet_private(VCardApplet *applet, VCardAppletPrivate *_private,
|
||||
VCardAppletPrivateFree private_free);
|
||||
|
||||
/* set type of vcard */
|
||||
void vcard_set_type(VCard *card, VCardType type);
|
||||
|
||||
/*
|
||||
* utilities interacting with the current applet
|
||||
*/
|
||||
/* add a new applet to a card */
|
||||
VCardStatus vcard_add_applet(VCard *card, VCardApplet *applet);
|
||||
/* find the applet on the card with the given aid */
|
||||
VCardApplet *vcard_find_applet(VCard *card, unsigned char *aid, int aid_len);
|
||||
/* set the following applet to be current on the given channel */
|
||||
void vcard_select_applet(VCard *card, int channel, VCardApplet *applet);
|
||||
/* get the card type specific private data on the given channel */
|
||||
VCardAppletPrivate *vcard_get_current_applet_private(VCard *card, int channel);
|
||||
/* fetch the applet's id */
|
||||
unsigned char *vcard_applet_get_aid(VCardApplet *applet, int *aid_len);
|
||||
|
||||
/* process the apdu for the current selected applet/file */
|
||||
VCardStatus vcard_process_applet_apdu(VCard *card, VCardAPDU *apdu,
|
||||
VCardResponse **response);
|
||||
/*
|
||||
* VCard utilities
|
||||
*/
|
||||
/* constructor */
|
||||
VCard *vcard_new(VCardEmul *_private, VCardEmulFree private_free);
|
||||
/* get a reference */
|
||||
VCard *vcard_reference(VCard *);
|
||||
/* destructor (reference counted) */
|
||||
void vcard_free(VCard *);
|
||||
/* get the atr from the card */
|
||||
void vcard_get_atr(VCard *card, unsigned char *atr, int *atr_len);
|
||||
void vcard_set_atr_func(VCard *card, VCardGetAtr vcard_get_atr);
|
||||
|
||||
/* accessor functions for the response buffer */
|
||||
VCardBufferResponse *vcard_get_buffer_response(VCard *card);
|
||||
void vcard_set_buffer_response(VCard *card, VCardBufferResponse *buffer);
|
||||
/* accessor functions for the type */
|
||||
VCardType vcard_get_type(VCard *card);
|
||||
/* get the private data */
|
||||
VCardEmul *vcard_get_private(VCard *card);
|
||||
|
||||
#endif
|
@ -1,66 +0,0 @@
|
||||
/*
|
||||
* This is the actual card emulator.
|
||||
*
|
||||
* These functions can be implemented in different ways on different platforms
|
||||
* using the underlying system primitives. For Linux it uses NSS, though direct
|
||||
* to PKCS #11, openssl+pkcs11, or even gnu crypto libraries+pkcs #11 could be
|
||||
* used. On Windows CAPI could be used.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef VCARD_EMUL_H
|
||||
#define VCARD_EMUL_H 1
|
||||
|
||||
#include "card_7816t.h"
|
||||
#include "vcard.h"
|
||||
#include "vcard_emul_type.h"
|
||||
|
||||
/*
|
||||
* types
|
||||
*/
|
||||
typedef enum {
|
||||
VCARD_EMUL_OK = 0,
|
||||
VCARD_EMUL_FAIL,
|
||||
/* return values by vcard_emul_init */
|
||||
VCARD_EMUL_INIT_ALREADY_INITED,
|
||||
} VCardEmulError;
|
||||
|
||||
/* options are emul specific. call card_emul_parse_args to change a string
|
||||
* To an options struct */
|
||||
typedef struct VCardEmulOptionsStruct VCardEmulOptions;
|
||||
|
||||
/*
|
||||
* Login functions
|
||||
*/
|
||||
/* return the number of login attempts still possible on the card. if unknown,
|
||||
* return -1 */
|
||||
int vcard_emul_get_login_count(VCard *card);
|
||||
/* login into the card, return the 7816 status word (sw2 || sw1) */
|
||||
vcard_7816_status_t vcard_emul_login(VCard *card, unsigned char *pin,
|
||||
int pin_len);
|
||||
void vcard_emul_logout(VCard *card);
|
||||
|
||||
/*
|
||||
* key functions
|
||||
*/
|
||||
/* delete a key */
|
||||
void vcard_emul_delete_key(VCardKey *key);
|
||||
/* RSA sign/decrypt with the key, signature happens 'in place' */
|
||||
vcard_7816_status_t vcard_emul_rsa_op(VCard *card, VCardKey *key,
|
||||
unsigned char *buffer, int buffer_size);
|
||||
|
||||
void vcard_emul_reset(VCard *card, VCardPower power);
|
||||
void vcard_emul_get_atr(VCard *card, unsigned char *atr, int *atr_len);
|
||||
|
||||
/* Re-insert of a card that has been removed by force removal */
|
||||
VCardEmulError vcard_emul_force_card_insert(VReader *vreader);
|
||||
/* Force a card removal even if the card is not physically removed */
|
||||
VCardEmulError vcard_emul_force_card_remove(VReader *vreader);
|
||||
|
||||
VCardEmulOptions *vcard_emul_options(const char *args);
|
||||
VCardEmulError vcard_emul_init(const VCardEmulOptions *options);
|
||||
void vcard_emul_replay_insertion_events(void);
|
||||
void vcard_emul_usage(void);
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -1,57 +0,0 @@
|
||||
/*
|
||||
* This file contains utility functions which abstract the different card
|
||||
* types. The goal is that new card types can easily be added by simply
|
||||
* changing this file and vcard_emul_type.h. It is currently not a requirement
|
||||
* to dynamically add new card types.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include <strings.h>
|
||||
#include "vcardt.h"
|
||||
#include "vcard_emul_type.h"
|
||||
#include "cac.h"
|
||||
|
||||
VCardStatus vcard_init(VReader *vreader, VCard *vcard,
|
||||
VCardEmulType type, const char *params,
|
||||
unsigned char *const *cert, int cert_len[],
|
||||
VCardKey *key[], int cert_count)
|
||||
{
|
||||
switch (type) {
|
||||
case VCARD_EMUL_NONE:
|
||||
break;
|
||||
case VCARD_EMUL_CAC:
|
||||
return cac_card_init(vreader, vcard, params,
|
||||
cert, cert_len, key, cert_count);
|
||||
/* add new ones here */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return VCARD_FAIL;
|
||||
}
|
||||
|
||||
VCardEmulType vcard_emul_type_select(VReader *vreader)
|
||||
{
|
||||
#ifdef notdef
|
||||
/* since there is only one emulator no need to call this function */
|
||||
if (cac_is_cac_card(vreader) == VCARD_DONE) {
|
||||
return VCARD_EMUL_CAC;
|
||||
}
|
||||
#endif
|
||||
/* return the default */
|
||||
return VCARD_EMUL_CAC;
|
||||
}
|
||||
|
||||
VCardEmulType vcard_emul_type_from_string(const char *type_string)
|
||||
{
|
||||
if (strcasecmp(type_string, "CAC") == 0) {
|
||||
return VCARD_EMUL_CAC;
|
||||
}
|
||||
#ifdef USE_PASSTHRU
|
||||
if (strcasecmp(type_string, "PASSTHRU") == 0) {
|
||||
return VCARD_EMUL_PASSTHRU;
|
||||
}
|
||||
#endif
|
||||
return VCARD_EMUL_NONE;
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
/*
|
||||
* This header file abstracts the different card types. The goal is new card
|
||||
* types can easily be added by simply changing this file and
|
||||
* vcard_emul_type.c. It is currently not a requirement to dynamically add new
|
||||
* card types.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef VCARD_EMUL_TYPE_H
|
||||
#define VCARD_EMUL_TYPE_H 1
|
||||
#include "vcardt.h"
|
||||
#include "vreadert.h"
|
||||
|
||||
/*
|
||||
* types
|
||||
*/
|
||||
typedef enum {
|
||||
VCARD_EMUL_NONE = 0,
|
||||
VCARD_EMUL_CAC,
|
||||
VCARD_EMUL_PASSTHRU
|
||||
} VCardEmulType;
|
||||
|
||||
/* functions used by the rest of the emulator */
|
||||
VCardStatus vcard_init(VReader *vreader, VCard *vcard, VCardEmulType type,
|
||||
const char *params, unsigned char * const *cert,
|
||||
int cert_len[], VCardKey *key[], int cert_count);
|
||||
VCardEmulType vcard_emul_type_select(VReader *vreader);
|
||||
VCardEmulType vcard_emul_type_from_string(const char *type_string);
|
||||
|
||||
#endif
|
@ -1,40 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include "vcardt.h"
|
||||
|
||||
#include "vcardt_internal.h"
|
||||
|
||||
/* create an ATR with appropriate historical bytes */
|
||||
#define ATR_TS_DIRECT_CONVENTION 0x3b
|
||||
#define ATR_TA_PRESENT 0x10
|
||||
#define ATR_TB_PRESENT 0x20
|
||||
#define ATR_TC_PRESENT 0x40
|
||||
#define ATR_TD_PRESENT 0x80
|
||||
|
||||
unsigned char *vcard_alloc_atr(const char *postfix, int *atr_len)
|
||||
{
|
||||
int postfix_len;
|
||||
const char prefix[] = "VCARD_";
|
||||
const char default_postfix[] = "DEFAULT";
|
||||
const int prefix_len = sizeof(prefix) - 1;
|
||||
int total_len;
|
||||
unsigned char *atr;
|
||||
|
||||
if (postfix == NULL) {
|
||||
postfix = default_postfix;
|
||||
}
|
||||
postfix_len = strlen(postfix);
|
||||
total_len = 3 + prefix_len + postfix_len;
|
||||
atr = g_malloc(total_len);
|
||||
atr[0] = ATR_TS_DIRECT_CONVENTION;
|
||||
atr[1] = ATR_TD_PRESENT + prefix_len + postfix_len;
|
||||
atr[2] = 0x00;
|
||||
memcpy(&atr[3], prefix, prefix_len);
|
||||
memcpy(&atr[3 + prefix_len], postfix, postfix_len);
|
||||
if (atr_len) {
|
||||
*atr_len = total_len;
|
||||
}
|
||||
return atr;
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
/*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
#ifndef VCARDT_H
|
||||
#define VCARDT_H 1
|
||||
|
||||
/*
|
||||
* these should come from some common spice header file
|
||||
*/
|
||||
#include <assert.h>
|
||||
#ifndef MIN
|
||||
#define MIN(x, y) ((x) > (y) ? (y) : (x))
|
||||
#define MAX(x, y) ((x) > (y) ? (x) : (y))
|
||||
#endif
|
||||
|
||||
typedef struct VCardStruct VCard;
|
||||
typedef struct VCardAPDUStruct VCardAPDU;
|
||||
typedef struct VCardResponseStruct VCardResponse;
|
||||
typedef struct VCardBufferResponseStruct VCardBufferResponse;
|
||||
typedef struct VCardAppletStruct VCardApplet;
|
||||
typedef struct VCardAppletPrivateStruct VCardAppletPrivate;
|
||||
typedef struct VCardKeyStruct VCardKey; /* opaque */
|
||||
typedef struct VCardEmulStruct VCardEmul;
|
||||
|
||||
#define MAX_CHANNEL 4
|
||||
|
||||
typedef enum {
|
||||
VCARD_DONE,
|
||||
VCARD_NEXT,
|
||||
VCARD_FAIL
|
||||
} VCardStatus;
|
||||
|
||||
typedef enum {
|
||||
VCARD_FILE_SYSTEM,
|
||||
VCARD_VM,
|
||||
VCARD_DIRECT
|
||||
} VCardType;
|
||||
|
||||
typedef enum {
|
||||
VCARD_POWER_ON,
|
||||
VCARD_POWER_OFF
|
||||
} VCardPower;
|
||||
|
||||
typedef VCardStatus (*VCardProcessAPDU)(VCard *card, VCardAPDU *apdu,
|
||||
VCardResponse **response);
|
||||
typedef VCardStatus (*VCardResetApplet)(VCard *card, int channel);
|
||||
typedef void (*VCardAppletPrivateFree) (VCardAppletPrivate *);
|
||||
typedef void (*VCardEmulFree) (VCardEmul *);
|
||||
typedef void (*VCardGetAtr) (VCard *, unsigned char *atr, int *atr_len);
|
||||
|
||||
struct VCardBufferResponseStruct {
|
||||
unsigned char *buffer;
|
||||
int buffer_len;
|
||||
unsigned char *current;
|
||||
int len;
|
||||
};
|
||||
|
||||
#endif
|
@ -1,6 +0,0 @@
|
||||
#ifndef VCARDT_INTERNAL_H
|
||||
#define VCARDT_INTERNAL_H
|
||||
|
||||
unsigned char *vcard_alloc_atr(const char *postfix, int *atr_len);
|
||||
|
||||
#endif
|
@ -1,27 +0,0 @@
|
||||
/*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
#ifndef EVENT_H
|
||||
#define EVENT_H 1
|
||||
#include "eventt.h"
|
||||
#include "vreadert.h"
|
||||
#include "vcardt.h"
|
||||
|
||||
VEvent *vevent_new(VEventType type, VReader *reader, VCard *card);
|
||||
void vevent_delete(VEvent *);
|
||||
|
||||
/*
|
||||
* VEvent queueing services
|
||||
*/
|
||||
void vevent_queue_vevent(VEvent *);
|
||||
void vevent_queue_init(void);
|
||||
|
||||
/*
|
||||
* VEvent dequeing services
|
||||
*/
|
||||
VEvent *vevent_wait_next_vevent(void);
|
||||
VEvent *vevent_get_next_vevent(void);
|
||||
|
||||
|
||||
#endif
|
@ -1,578 +0,0 @@
|
||||
/*
|
||||
* emulate the reader
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifdef G_LOG_DOMAIN
|
||||
#undef G_LOG_DOMAIN
|
||||
#endif
|
||||
#define G_LOG_DOMAIN "libcacard"
|
||||
|
||||
#include "glib-compat.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "vcard.h"
|
||||
#include "vcard_emul.h"
|
||||
#include "card_7816.h"
|
||||
#include "vreader.h"
|
||||
#include "vevent.h"
|
||||
#include "cac.h" /* just for debugging defines */
|
||||
|
||||
#define LIBCACARD_LOG_DOMAIN "libcacard"
|
||||
|
||||
struct VReaderStruct {
|
||||
int reference_count;
|
||||
VCard *card;
|
||||
char *name;
|
||||
vreader_id_t id;
|
||||
CompatGMutex lock;
|
||||
VReaderEmul *reader_private;
|
||||
VReaderEmulFree reader_private_free;
|
||||
};
|
||||
|
||||
/*
|
||||
* Debug helpers
|
||||
*/
|
||||
|
||||
static const char *
|
||||
apdu_ins_to_string(int ins)
|
||||
{
|
||||
switch (ins) {
|
||||
case VCARD7816_INS_MANAGE_CHANNEL:
|
||||
return "manage channel";
|
||||
case VCARD7816_INS_EXTERNAL_AUTHENTICATE:
|
||||
return "external authenticate";
|
||||
case VCARD7816_INS_GET_CHALLENGE:
|
||||
return "get challenge";
|
||||
case VCARD7816_INS_INTERNAL_AUTHENTICATE:
|
||||
return "internal authenticate";
|
||||
case VCARD7816_INS_ERASE_BINARY:
|
||||
return "erase binary";
|
||||
case VCARD7816_INS_READ_BINARY:
|
||||
return "read binary";
|
||||
case VCARD7816_INS_WRITE_BINARY:
|
||||
return "write binary";
|
||||
case VCARD7816_INS_UPDATE_BINARY:
|
||||
return "update binary";
|
||||
case VCARD7816_INS_READ_RECORD:
|
||||
return "read record";
|
||||
case VCARD7816_INS_WRITE_RECORD:
|
||||
return "write record";
|
||||
case VCARD7816_INS_UPDATE_RECORD:
|
||||
return "update record";
|
||||
case VCARD7816_INS_APPEND_RECORD:
|
||||
return "append record";
|
||||
case VCARD7816_INS_ENVELOPE:
|
||||
return "envelope";
|
||||
case VCARD7816_INS_PUT_DATA:
|
||||
return "put data";
|
||||
case VCARD7816_INS_GET_DATA:
|
||||
return "get data";
|
||||
case VCARD7816_INS_SELECT_FILE:
|
||||
return "select file";
|
||||
case VCARD7816_INS_VERIFY:
|
||||
return "verify";
|
||||
case VCARD7816_INS_GET_RESPONSE:
|
||||
return "get response";
|
||||
case CAC_GET_PROPERTIES:
|
||||
return "get properties";
|
||||
case CAC_GET_ACR:
|
||||
return "get acr";
|
||||
case CAC_READ_BUFFER:
|
||||
return "read buffer";
|
||||
case CAC_UPDATE_BUFFER:
|
||||
return "update buffer";
|
||||
case CAC_SIGN_DECRYPT:
|
||||
return "sign decrypt";
|
||||
case CAC_GET_CERTIFICATE:
|
||||
return "get certificate";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
/* manage locking */
|
||||
static inline void
|
||||
vreader_lock(VReader *reader)
|
||||
{
|
||||
g_mutex_lock(&reader->lock);
|
||||
}
|
||||
|
||||
static inline void
|
||||
vreader_unlock(VReader *reader)
|
||||
{
|
||||
g_mutex_unlock(&reader->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* vreader constructor
|
||||
*/
|
||||
VReader *
|
||||
vreader_new(const char *name, VReaderEmul *private,
|
||||
VReaderEmulFree private_free)
|
||||
{
|
||||
VReader *reader;
|
||||
|
||||
reader = g_new(VReader, 1);
|
||||
g_mutex_init(&reader->lock);
|
||||
reader->reference_count = 1;
|
||||
reader->name = g_strdup(name);
|
||||
reader->card = NULL;
|
||||
reader->id = (vreader_id_t)-1;
|
||||
reader->reader_private = private;
|
||||
reader->reader_private_free = private_free;
|
||||
return reader;
|
||||
}
|
||||
|
||||
/* get a reference */
|
||||
VReader*
|
||||
vreader_reference(VReader *reader)
|
||||
{
|
||||
if (reader == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
vreader_lock(reader);
|
||||
reader->reference_count++;
|
||||
vreader_unlock(reader);
|
||||
return reader;
|
||||
}
|
||||
|
||||
/* free a reference */
|
||||
void
|
||||
vreader_free(VReader *reader)
|
||||
{
|
||||
if (reader == NULL) {
|
||||
return;
|
||||
}
|
||||
vreader_lock(reader);
|
||||
if (reader->reference_count-- > 1) {
|
||||
vreader_unlock(reader);
|
||||
return;
|
||||
}
|
||||
vreader_unlock(reader);
|
||||
g_mutex_clear(&reader->lock);
|
||||
if (reader->card) {
|
||||
vcard_free(reader->card);
|
||||
}
|
||||
g_free(reader->name);
|
||||
if (reader->reader_private_free) {
|
||||
reader->reader_private_free(reader->reader_private);
|
||||
}
|
||||
g_free(reader);
|
||||
}
|
||||
|
||||
static VCard *
|
||||
vreader_get_card(VReader *reader)
|
||||
{
|
||||
VCard *card;
|
||||
|
||||
vreader_lock(reader);
|
||||
card = vcard_reference(reader->card);
|
||||
vreader_unlock(reader);
|
||||
return card;
|
||||
}
|
||||
|
||||
VReaderStatus
|
||||
vreader_card_is_present(VReader *reader)
|
||||
{
|
||||
VCard *card = vreader_get_card(reader);
|
||||
|
||||
if (card == NULL) {
|
||||
return VREADER_NO_CARD;
|
||||
}
|
||||
vcard_free(card);
|
||||
return VREADER_OK;
|
||||
}
|
||||
|
||||
vreader_id_t
|
||||
vreader_get_id(VReader *reader)
|
||||
{
|
||||
if (reader == NULL) {
|
||||
return (vreader_id_t)-1;
|
||||
}
|
||||
return reader->id;
|
||||
}
|
||||
|
||||
VReaderStatus
|
||||
vreader_set_id(VReader *reader, vreader_id_t id)
|
||||
{
|
||||
if (reader == NULL) {
|
||||
return VREADER_NO_CARD;
|
||||
}
|
||||
reader->id = id;
|
||||
return VREADER_OK;
|
||||
}
|
||||
|
||||
const char *
|
||||
vreader_get_name(VReader *reader)
|
||||
{
|
||||
if (reader == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return reader->name;
|
||||
}
|
||||
|
||||
VReaderEmul *
|
||||
vreader_get_private(VReader *reader)
|
||||
{
|
||||
return reader->reader_private;
|
||||
}
|
||||
|
||||
static VReaderStatus
|
||||
vreader_reset(VReader *reader, VCardPower power, unsigned char *atr, int *len)
|
||||
{
|
||||
VCard *card = vreader_get_card(reader);
|
||||
|
||||
if (card == NULL) {
|
||||
return VREADER_NO_CARD;
|
||||
}
|
||||
/*
|
||||
* clean up our state
|
||||
*/
|
||||
vcard_reset(card, power);
|
||||
if (atr) {
|
||||
vcard_get_atr(card, atr, len);
|
||||
}
|
||||
vcard_free(card); /* free our reference */
|
||||
return VREADER_OK;
|
||||
}
|
||||
|
||||
VReaderStatus
|
||||
vreader_power_on(VReader *reader, unsigned char *atr, int *len)
|
||||
{
|
||||
return vreader_reset(reader, VCARD_POWER_ON, atr, len);
|
||||
}
|
||||
|
||||
VReaderStatus
|
||||
vreader_power_off(VReader *reader)
|
||||
{
|
||||
return vreader_reset(reader, VCARD_POWER_OFF, NULL, 0);
|
||||
}
|
||||
|
||||
|
||||
VReaderStatus
|
||||
vreader_xfr_bytes(VReader *reader,
|
||||
unsigned char *send_buf, int send_buf_len,
|
||||
unsigned char *receive_buf, int *receive_buf_len)
|
||||
{
|
||||
VCardAPDU *apdu;
|
||||
VCardResponse *response = NULL;
|
||||
VCardStatus card_status;
|
||||
unsigned short status;
|
||||
VCard *card = vreader_get_card(reader);
|
||||
|
||||
if (card == NULL) {
|
||||
return VREADER_NO_CARD;
|
||||
}
|
||||
|
||||
apdu = vcard_apdu_new(send_buf, send_buf_len, &status);
|
||||
if (apdu == NULL) {
|
||||
response = vcard_make_response(status);
|
||||
card_status = VCARD_DONE;
|
||||
} else {
|
||||
g_debug("%s: CLS=0x%x,INS=0x%x,P1=0x%x,P2=0x%x,Lc=%d,Le=%d %s",
|
||||
__func__, apdu->a_cla, apdu->a_ins, apdu->a_p1, apdu->a_p2,
|
||||
apdu->a_Lc, apdu->a_Le, apdu_ins_to_string(apdu->a_ins));
|
||||
card_status = vcard_process_apdu(card, apdu, &response);
|
||||
if (response) {
|
||||
g_debug("%s: status=%d sw1=0x%x sw2=0x%x len=%d (total=%d)",
|
||||
__func__, response->b_status, response->b_sw1,
|
||||
response->b_sw2, response->b_len, response->b_total_len);
|
||||
}
|
||||
}
|
||||
assert(card_status == VCARD_DONE && response);
|
||||
int size = MIN(*receive_buf_len, response->b_total_len);
|
||||
memcpy(receive_buf, response->b_data, size);
|
||||
*receive_buf_len = size;
|
||||
vcard_response_delete(response);
|
||||
vcard_apdu_delete(apdu);
|
||||
vcard_free(card); /* free our reference */
|
||||
return VREADER_OK;
|
||||
}
|
||||
|
||||
struct VReaderListStruct {
|
||||
VReaderListEntry *head;
|
||||
VReaderListEntry *tail;
|
||||
};
|
||||
|
||||
struct VReaderListEntryStruct {
|
||||
VReaderListEntry *next;
|
||||
VReaderListEntry *prev;
|
||||
VReader *reader;
|
||||
};
|
||||
|
||||
|
||||
static VReaderListEntry *
|
||||
vreader_list_entry_new(VReader *reader)
|
||||
{
|
||||
VReaderListEntry *new_reader_list_entry;
|
||||
|
||||
new_reader_list_entry = g_new0(VReaderListEntry, 1);
|
||||
new_reader_list_entry->reader = vreader_reference(reader);
|
||||
return new_reader_list_entry;
|
||||
}
|
||||
|
||||
static void
|
||||
vreader_list_entry_delete(VReaderListEntry *entry)
|
||||
{
|
||||
if (entry == NULL) {
|
||||
return;
|
||||
}
|
||||
vreader_free(entry->reader);
|
||||
g_free(entry);
|
||||
}
|
||||
|
||||
|
||||
static VReaderList *
|
||||
vreader_list_new(void)
|
||||
{
|
||||
VReaderList *new_reader_list;
|
||||
|
||||
new_reader_list = g_new0(VReaderList, 1);
|
||||
return new_reader_list;
|
||||
}
|
||||
|
||||
void
|
||||
vreader_list_delete(VReaderList *list)
|
||||
{
|
||||
VReaderListEntry *current_entry;
|
||||
VReaderListEntry *next_entry;
|
||||
for (current_entry = vreader_list_get_first(list); current_entry;
|
||||
current_entry = next_entry) {
|
||||
next_entry = vreader_list_get_next(current_entry);
|
||||
vreader_list_entry_delete(current_entry);
|
||||
}
|
||||
g_free(list);
|
||||
}
|
||||
|
||||
|
||||
VReaderListEntry *
|
||||
vreader_list_get_first(VReaderList *list)
|
||||
{
|
||||
return list ? list->head : NULL;
|
||||
}
|
||||
|
||||
VReaderListEntry *
|
||||
vreader_list_get_next(VReaderListEntry *current)
|
||||
{
|
||||
return current ? current->next : NULL;
|
||||
}
|
||||
|
||||
VReader *
|
||||
vreader_list_get_reader(VReaderListEntry *entry)
|
||||
{
|
||||
return entry ? vreader_reference(entry->reader) : NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
vreader_queue(VReaderList *list, VReaderListEntry *entry)
|
||||
{
|
||||
if (entry == NULL) {
|
||||
return;
|
||||
}
|
||||
entry->next = NULL;
|
||||
entry->prev = list->tail;
|
||||
if (list->head) {
|
||||
list->tail->next = entry;
|
||||
} else {
|
||||
list->head = entry;
|
||||
}
|
||||
list->tail = entry;
|
||||
}
|
||||
|
||||
static void
|
||||
vreader_dequeue(VReaderList *list, VReaderListEntry *entry)
|
||||
{
|
||||
if (entry == NULL) {
|
||||
return;
|
||||
}
|
||||
if (entry->next == NULL) {
|
||||
list->tail = entry->prev;
|
||||
} else if (entry->prev == NULL) {
|
||||
list->head = entry->next;
|
||||
} else {
|
||||
entry->prev->next = entry->next;
|
||||
entry->next->prev = entry->prev;
|
||||
}
|
||||
if ((list->tail == NULL) || (list->head == NULL)) {
|
||||
list->head = list->tail = NULL;
|
||||
}
|
||||
entry->next = entry->prev = NULL;
|
||||
}
|
||||
|
||||
static VReaderList *vreader_list;
|
||||
static CompatGMutex vreader_list_mutex;
|
||||
|
||||
static void
|
||||
vreader_list_init(void)
|
||||
{
|
||||
vreader_list = vreader_list_new();
|
||||
}
|
||||
|
||||
static void
|
||||
vreader_list_lock(void)
|
||||
{
|
||||
g_mutex_lock(&vreader_list_mutex);
|
||||
}
|
||||
|
||||
static void
|
||||
vreader_list_unlock(void)
|
||||
{
|
||||
g_mutex_unlock(&vreader_list_mutex);
|
||||
}
|
||||
|
||||
static VReaderList *
|
||||
vreader_copy_list(VReaderList *list)
|
||||
{
|
||||
VReaderList *new_list;
|
||||
VReaderListEntry *current_entry;
|
||||
|
||||
new_list = vreader_list_new();
|
||||
if (new_list == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
for (current_entry = vreader_list_get_first(list); current_entry;
|
||||
current_entry = vreader_list_get_next(current_entry)) {
|
||||
VReader *reader = vreader_list_get_reader(current_entry);
|
||||
VReaderListEntry *new_entry = vreader_list_entry_new(reader);
|
||||
|
||||
vreader_free(reader);
|
||||
vreader_queue(new_list, new_entry);
|
||||
}
|
||||
return new_list;
|
||||
}
|
||||
|
||||
VReaderList *
|
||||
vreader_get_reader_list(void)
|
||||
{
|
||||
VReaderList *new_reader_list;
|
||||
|
||||
vreader_list_lock();
|
||||
new_reader_list = vreader_copy_list(vreader_list);
|
||||
vreader_list_unlock();
|
||||
return new_reader_list;
|
||||
}
|
||||
|
||||
VReader *
|
||||
vreader_get_reader_by_id(vreader_id_t id)
|
||||
{
|
||||
VReader *reader = NULL;
|
||||
VReaderListEntry *current_entry;
|
||||
|
||||
if (id == (vreader_id_t) -1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
vreader_list_lock();
|
||||
for (current_entry = vreader_list_get_first(vreader_list); current_entry;
|
||||
current_entry = vreader_list_get_next(current_entry)) {
|
||||
VReader *creader = vreader_list_get_reader(current_entry);
|
||||
if (creader->id == id) {
|
||||
reader = creader;
|
||||
break;
|
||||
}
|
||||
vreader_free(creader);
|
||||
}
|
||||
vreader_list_unlock();
|
||||
return reader;
|
||||
}
|
||||
|
||||
VReader *
|
||||
vreader_get_reader_by_name(const char *name)
|
||||
{
|
||||
VReader *reader = NULL;
|
||||
VReaderListEntry *current_entry;
|
||||
|
||||
vreader_list_lock();
|
||||
for (current_entry = vreader_list_get_first(vreader_list); current_entry;
|
||||
current_entry = vreader_list_get_next(current_entry)) {
|
||||
VReader *creader = vreader_list_get_reader(current_entry);
|
||||
if (strcmp(creader->name, name) == 0) {
|
||||
reader = creader;
|
||||
break;
|
||||
}
|
||||
vreader_free(creader);
|
||||
}
|
||||
vreader_list_unlock();
|
||||
return reader;
|
||||
}
|
||||
|
||||
/* called from card_emul to initialize the readers */
|
||||
VReaderStatus
|
||||
vreader_add_reader(VReader *reader)
|
||||
{
|
||||
VReaderListEntry *reader_entry;
|
||||
|
||||
reader_entry = vreader_list_entry_new(reader);
|
||||
if (reader_entry == NULL) {
|
||||
return VREADER_OUT_OF_MEMORY;
|
||||
}
|
||||
vreader_list_lock();
|
||||
vreader_queue(vreader_list, reader_entry);
|
||||
vreader_list_unlock();
|
||||
vevent_queue_vevent(vevent_new(VEVENT_READER_INSERT, reader, NULL));
|
||||
return VREADER_OK;
|
||||
}
|
||||
|
||||
|
||||
VReaderStatus
|
||||
vreader_remove_reader(VReader *reader)
|
||||
{
|
||||
VReaderListEntry *current_entry;
|
||||
|
||||
vreader_list_lock();
|
||||
for (current_entry = vreader_list_get_first(vreader_list); current_entry;
|
||||
current_entry = vreader_list_get_next(current_entry)) {
|
||||
if (current_entry->reader == reader) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
vreader_dequeue(vreader_list, current_entry);
|
||||
vreader_list_unlock();
|
||||
vreader_list_entry_delete(current_entry);
|
||||
vevent_queue_vevent(vevent_new(VEVENT_READER_REMOVE, reader, NULL));
|
||||
return VREADER_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate VEVENT_CARD_INSERT or VEVENT_CARD_REMOVE based on vreader
|
||||
* state. Separated from vreader_insert_card to allow replaying events
|
||||
* for a given state.
|
||||
*/
|
||||
void
|
||||
vreader_queue_card_event(VReader *reader)
|
||||
{
|
||||
vevent_queue_vevent(vevent_new(
|
||||
reader->card ? VEVENT_CARD_INSERT : VEVENT_CARD_REMOVE, reader,
|
||||
reader->card));
|
||||
}
|
||||
|
||||
/*
|
||||
* insert/remove a new card. for removal, card == NULL
|
||||
*/
|
||||
VReaderStatus
|
||||
vreader_insert_card(VReader *reader, VCard *card)
|
||||
{
|
||||
vreader_lock(reader);
|
||||
if (reader->card) {
|
||||
/* decrement reference count */
|
||||
vcard_free(reader->card);
|
||||
reader->card = NULL;
|
||||
}
|
||||
reader->card = vcard_reference(card);
|
||||
vreader_unlock(reader);
|
||||
vreader_queue_card_event(reader);
|
||||
return VREADER_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* initialize all the static reader structures
|
||||
*/
|
||||
void
|
||||
vreader_init(void)
|
||||
{
|
||||
vreader_list_init();
|
||||
}
|
||||
|
@ -1,55 +0,0 @@
|
||||
/*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef VREADER_H
|
||||
#define VREADER_H 1
|
||||
|
||||
#include "eventt.h"
|
||||
#include "vreadert.h"
|
||||
#include "vcardt.h"
|
||||
|
||||
/*
|
||||
* calls for reader front end
|
||||
*/
|
||||
VReaderStatus vreader_power_on(VReader *reader, unsigned char *atr, int *len);
|
||||
VReaderStatus vreader_power_off(VReader *reader);
|
||||
VReaderStatus vreader_xfr_bytes(VReader *reader, unsigned char *send_buf,
|
||||
int send_buf_len, unsigned char *receive_buf,
|
||||
int *receive_buf_len);
|
||||
|
||||
/* constructor */
|
||||
VReader *vreader_new(const char *readerName, VReaderEmul *emul_private,
|
||||
VReaderEmulFree private_free);
|
||||
/* get a new reference to a reader */
|
||||
VReader *vreader_reference(VReader *reader);
|
||||
/* "destructor" (readers are reference counted) */
|
||||
void vreader_free(VReader *reader);
|
||||
|
||||
/* accessors */
|
||||
VReaderEmul *vreader_get_private(VReader *);
|
||||
VReaderStatus vreader_card_is_present(VReader *reader);
|
||||
void vreader_queue_card_event(VReader *reader);
|
||||
const char *vreader_get_name(VReader *reader);
|
||||
vreader_id_t vreader_get_id(VReader *reader);
|
||||
VReaderStatus vreader_set_id(VReader *reader, vreader_id_t id);
|
||||
|
||||
/* list operations */
|
||||
VReaderList *vreader_get_reader_list(void);
|
||||
void vreader_list_delete(VReaderList *list);
|
||||
VReader *vreader_list_get_reader(VReaderListEntry *entry);
|
||||
VReaderListEntry *vreader_list_get_first(VReaderList *list);
|
||||
VReaderListEntry *vreader_list_get_next(VReaderListEntry *list);
|
||||
VReader *vreader_get_reader_by_id(vreader_id_t id);
|
||||
VReader *vreader_get_reader_by_name(const char *name);
|
||||
|
||||
/*
|
||||
* list tools for vcard_emul
|
||||
*/
|
||||
void vreader_init(void);
|
||||
VReaderStatus vreader_add_reader(VReader *reader);
|
||||
VReaderStatus vreader_remove_reader(VReader *reader);
|
||||
VReaderStatus vreader_insert_card(VReader *reader, VCard *card);
|
||||
|
||||
#endif
|
@ -1,24 +0,0 @@
|
||||
/*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef VREADERT_H
|
||||
#define VREADERT_H 1
|
||||
|
||||
typedef enum {
|
||||
VREADER_OK = 0,
|
||||
VREADER_NO_CARD,
|
||||
VREADER_OUT_OF_MEMORY
|
||||
} VReaderStatus;
|
||||
|
||||
typedef unsigned int vreader_id_t;
|
||||
typedef struct VReaderStruct VReader;
|
||||
typedef struct VReaderListStruct VReaderList;
|
||||
typedef struct VReaderListEntryStruct VReaderListEntry;
|
||||
|
||||
typedef struct VReaderEmulStruct VReaderEmul;
|
||||
typedef void (*VReaderEmulFree)(VReaderEmul *);
|
||||
|
||||
#endif
|
||||
|
@ -1,178 +0,0 @@
|
||||
/* Virtual Smart Card protocol definition
|
||||
*
|
||||
* This protocol is between a host using virtual smart card readers,
|
||||
* and a client providing the smart cards, perhaps by emulating them or by
|
||||
* access to real cards.
|
||||
*
|
||||
* Definitions for this protocol:
|
||||
* Host - user of the card
|
||||
* Client - owner of the card
|
||||
*
|
||||
* The current implementation passes the raw APDU's from 7816 and additionally
|
||||
* contains messages to setup and teardown readers, handle insertion and
|
||||
* removal of cards, negotiate the protocol via capabilities and provide
|
||||
* for error responses.
|
||||
*
|
||||
* Copyright (c) 2011 Red Hat.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef VSCARD_COMMON_H
|
||||
#define VSCARD_COMMON_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define VERSION_MAJOR_BITS 11
|
||||
#define VERSION_MIDDLE_BITS 11
|
||||
#define VERSION_MINOR_BITS 10
|
||||
|
||||
#define MAKE_VERSION(major, middle, minor) \
|
||||
((major << (VERSION_MINOR_BITS + VERSION_MIDDLE_BITS)) \
|
||||
| (middle << VERSION_MINOR_BITS) \
|
||||
| (minor))
|
||||
|
||||
/*
|
||||
* IMPORTANT NOTE on VERSION
|
||||
*
|
||||
* The version below MUST be changed whenever a change in this file is made.
|
||||
*
|
||||
* The last digit, the minor, is for bug fix changes only.
|
||||
*
|
||||
* The middle digit is for backward / forward compatible changes, updates
|
||||
* to the existing messages, addition of fields.
|
||||
*
|
||||
* The major digit is for a breaking change of protocol, presumably
|
||||
* something that cannot be accommodated with the existing protocol.
|
||||
*/
|
||||
|
||||
#define VSCARD_VERSION MAKE_VERSION(0, 0, 2)
|
||||
|
||||
typedef enum VSCMsgType {
|
||||
VSC_Init = 1,
|
||||
VSC_Error,
|
||||
VSC_ReaderAdd,
|
||||
VSC_ReaderRemove,
|
||||
VSC_ATR,
|
||||
VSC_CardRemove,
|
||||
VSC_APDU,
|
||||
VSC_Flush,
|
||||
VSC_FlushComplete
|
||||
} VSCMsgType;
|
||||
|
||||
typedef enum VSCErrorCode {
|
||||
VSC_SUCCESS = 0,
|
||||
VSC_GENERAL_ERROR = 1,
|
||||
VSC_CANNOT_ADD_MORE_READERS,
|
||||
VSC_CARD_ALREAY_INSERTED,
|
||||
} VSCErrorCode;
|
||||
|
||||
#define VSCARD_UNDEFINED_READER_ID 0xffffffff
|
||||
#define VSCARD_MINIMAL_READER_ID 0
|
||||
|
||||
#define VSCARD_MAGIC (*(uint32_t *)"VSCD")
|
||||
|
||||
/*
|
||||
* Header
|
||||
* Each message starts with the header.
|
||||
* type - message type
|
||||
* reader_id - used by messages that are reader specific
|
||||
* length - length of payload (not including header, i.e. zero for
|
||||
* messages containing empty payloads)
|
||||
*/
|
||||
typedef struct VSCMsgHeader {
|
||||
uint32_t type;
|
||||
uint32_t reader_id;
|
||||
uint32_t length;
|
||||
uint8_t data[0];
|
||||
} VSCMsgHeader;
|
||||
|
||||
/*
|
||||
* VSCMsgInit Client <-> Host
|
||||
* Client sends it on connection, with its own capabilities.
|
||||
* Host replies with VSCMsgInit filling in its capabilities.
|
||||
*
|
||||
* It is not meant to be used for negotiation, i.e. sending more then
|
||||
* once from any side, but could be used for that in the future.
|
||||
*/
|
||||
typedef struct VSCMsgInit {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
uint32_t capabilities[1]; /* receiver must check length,
|
||||
array may grow in the future*/
|
||||
} VSCMsgInit;
|
||||
|
||||
/*
|
||||
* VSCMsgError Client <-> Host
|
||||
* This message is a response to any of:
|
||||
* Reader Add
|
||||
* Reader Remove
|
||||
* Card Remove
|
||||
* If the operation was successful then VSC_SUCCESS
|
||||
* is returned, other wise a specific error code.
|
||||
*/
|
||||
typedef struct VSCMsgError {
|
||||
uint32_t code;
|
||||
} VSCMsgError;
|
||||
|
||||
/*
|
||||
* VSCMsgReaderAdd Client -> Host
|
||||
* Host replies with allocated reader id in VSCMsgError with code==SUCCESS.
|
||||
*
|
||||
* name - name of the reader on client side, UTF-8 encoded. Only used
|
||||
* for client presentation (may be translated to the device presented to the
|
||||
* guest), protocol wise only reader_id is important.
|
||||
*/
|
||||
typedef struct VSCMsgReaderAdd {
|
||||
uint8_t name[0];
|
||||
} VSCMsgReaderAdd;
|
||||
|
||||
/*
|
||||
* VSCMsgReaderRemove Client -> Host
|
||||
* The client's reader has been removed.
|
||||
*/
|
||||
typedef struct VSCMsgReaderRemove {
|
||||
} VSCMsgReaderRemove;
|
||||
|
||||
/*
|
||||
* VSCMsgATR Client -> Host
|
||||
* Answer to reset. Sent for card insertion or card reset. The reset/insertion
|
||||
* happens on the client side, they do not require any action from the host.
|
||||
*/
|
||||
typedef struct VSCMsgATR {
|
||||
uint8_t atr[0];
|
||||
} VSCMsgATR;
|
||||
|
||||
/*
|
||||
* VSCMsgCardRemove Client -> Host
|
||||
* The client card has been removed.
|
||||
*/
|
||||
typedef struct VSCMsgCardRemove {
|
||||
} VSCMsgCardRemove;
|
||||
|
||||
/*
|
||||
* VSCMsgAPDU Client <-> Host
|
||||
* Main reason of existence. Transfer a single APDU in either direction.
|
||||
*/
|
||||
typedef struct VSCMsgAPDU {
|
||||
uint8_t data[0];
|
||||
} VSCMsgAPDU;
|
||||
|
||||
/*
|
||||
* VSCMsgFlush Host -> Client
|
||||
* Request client to send a FlushComplete message when it is done
|
||||
* servicing all outstanding APDUs
|
||||
*/
|
||||
typedef struct VSCMsgFlush {
|
||||
} VSCMsgFlush;
|
||||
|
||||
/*
|
||||
* VSCMsgFlush Client -> Host
|
||||
* Client response to Flush after all APDUs have been processed and
|
||||
* responses sent.
|
||||
*/
|
||||
typedef struct VSCMsgFlushComplete {
|
||||
} VSCMsgFlushComplete;
|
||||
|
||||
#endif /* VSCARD_COMMON_H */
|
@ -1,785 +0,0 @@
|
||||
/*
|
||||
* Tester for VSCARD protocol, client side.
|
||||
*
|
||||
* Can be used with ccid-card-passthru.
|
||||
*
|
||||
* Copyright (c) 2011 Red Hat.
|
||||
* Written by Alon Levy.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef _WIN32
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#define closesocket(x) close(x)
|
||||
#else
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
#include "glib-compat.h"
|
||||
|
||||
#include "vscard_common.h"
|
||||
|
||||
#include "vreader.h"
|
||||
#include "vcard_emul.h"
|
||||
#include "vevent.h"
|
||||
|
||||
static int verbose;
|
||||
|
||||
static void
|
||||
print_byte_array(
|
||||
uint8_t *arrBytes,
|
||||
unsigned int nSize
|
||||
) {
|
||||
int i;
|
||||
for (i = 0; i < nSize; i++) {
|
||||
printf("%02X ", arrBytes[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void
|
||||
print_usage(void) {
|
||||
printf("vscclient [-c <certname> .. -e <emul_args> -d <level>%s] "
|
||||
"<host> <port>\n",
|
||||
#ifdef USE_PASSTHRU
|
||||
" -p");
|
||||
printf(" -p use passthrough mode\n");
|
||||
#else
|
||||
"");
|
||||
#endif
|
||||
vcard_emul_usage();
|
||||
}
|
||||
|
||||
static GIOChannel *channel_socket;
|
||||
static GByteArray *socket_to_send;
|
||||
static CompatGMutex socket_to_send_lock;
|
||||
static guint socket_tag;
|
||||
|
||||
static void
|
||||
update_socket_watch(void);
|
||||
|
||||
static gboolean
|
||||
do_socket_send(GIOChannel *source,
|
||||
GIOCondition condition,
|
||||
gpointer data)
|
||||
{
|
||||
gsize bw;
|
||||
GError *err = NULL;
|
||||
|
||||
g_return_val_if_fail(socket_to_send->len != 0, FALSE);
|
||||
g_return_val_if_fail(condition & G_IO_OUT, FALSE);
|
||||
|
||||
g_io_channel_write_chars(channel_socket,
|
||||
(gchar *)socket_to_send->data, socket_to_send->len, &bw, &err);
|
||||
if (err != NULL) {
|
||||
g_error("Error while sending socket %s", err->message);
|
||||
return FALSE;
|
||||
}
|
||||
g_byte_array_remove_range(socket_to_send, 0, bw);
|
||||
|
||||
if (socket_to_send->len == 0) {
|
||||
update_socket_watch();
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
socket_prepare_sending(gpointer user_data)
|
||||
{
|
||||
update_socket_watch();
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int
|
||||
send_msg(
|
||||
VSCMsgType type,
|
||||
uint32_t reader_id,
|
||||
const void *msg,
|
||||
unsigned int length
|
||||
) {
|
||||
VSCMsgHeader mhHeader;
|
||||
|
||||
g_mutex_lock(&socket_to_send_lock);
|
||||
|
||||
if (verbose > 10) {
|
||||
printf("sending type=%d id=%u, len =%u (0x%x)\n",
|
||||
type, reader_id, length, length);
|
||||
}
|
||||
|
||||
mhHeader.type = htonl(type);
|
||||
mhHeader.reader_id = 0;
|
||||
mhHeader.length = htonl(length);
|
||||
g_byte_array_append(socket_to_send, (guint8 *)&mhHeader, sizeof(mhHeader));
|
||||
g_byte_array_append(socket_to_send, (guint8 *)msg, length);
|
||||
g_idle_add(socket_prepare_sending, NULL);
|
||||
|
||||
g_mutex_unlock(&socket_to_send_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static VReader *pending_reader;
|
||||
static CompatGMutex pending_reader_lock;
|
||||
static CompatGCond pending_reader_condition;
|
||||
|
||||
#define MAX_ATR_LEN 40
|
||||
static gpointer
|
||||
event_thread(gpointer arg)
|
||||
{
|
||||
unsigned char atr[MAX_ATR_LEN];
|
||||
int atr_len;
|
||||
VEvent *event;
|
||||
unsigned int reader_id;
|
||||
|
||||
|
||||
while (1) {
|
||||
const char *reader_name;
|
||||
|
||||
event = vevent_wait_next_vevent();
|
||||
if (event == NULL) {
|
||||
break;
|
||||
}
|
||||
reader_id = vreader_get_id(event->reader);
|
||||
if (reader_id == VSCARD_UNDEFINED_READER_ID &&
|
||||
event->type != VEVENT_READER_INSERT) {
|
||||
/* ignore events from readers qemu has rejected */
|
||||
/* if qemu is still deciding on this reader, wait to see if need to
|
||||
* forward this event */
|
||||
g_mutex_lock(&pending_reader_lock);
|
||||
if (!pending_reader || (pending_reader != event->reader)) {
|
||||
/* wasn't for a pending reader, this reader has already been
|
||||
* rejected by qemu */
|
||||
g_mutex_unlock(&pending_reader_lock);
|
||||
vevent_delete(event);
|
||||
continue;
|
||||
}
|
||||
/* this reader hasn't been told its status from qemu yet, wait for
|
||||
* that status */
|
||||
while (pending_reader != NULL) {
|
||||
g_cond_wait(&pending_reader_condition, &pending_reader_lock);
|
||||
}
|
||||
g_mutex_unlock(&pending_reader_lock);
|
||||
/* now recheck the id */
|
||||
reader_id = vreader_get_id(event->reader);
|
||||
if (reader_id == VSCARD_UNDEFINED_READER_ID) {
|
||||
/* this reader was rejected */
|
||||
vevent_delete(event);
|
||||
continue;
|
||||
}
|
||||
/* reader was accepted, now forward the event */
|
||||
}
|
||||
switch (event->type) {
|
||||
case VEVENT_READER_INSERT:
|
||||
/* tell qemu to insert a new CCID reader */
|
||||
/* wait until qemu has responded to our first reader insert
|
||||
* before we send a second. That way we won't confuse the responses
|
||||
* */
|
||||
g_mutex_lock(&pending_reader_lock);
|
||||
while (pending_reader != NULL) {
|
||||
g_cond_wait(&pending_reader_condition, &pending_reader_lock);
|
||||
}
|
||||
pending_reader = vreader_reference(event->reader);
|
||||
g_mutex_unlock(&pending_reader_lock);
|
||||
reader_name = vreader_get_name(event->reader);
|
||||
if (verbose > 10) {
|
||||
printf(" READER INSERT: %s\n", reader_name);
|
||||
}
|
||||
send_msg(VSC_ReaderAdd,
|
||||
reader_id, /* currerntly VSCARD_UNDEFINED_READER_ID */
|
||||
NULL, 0 /* TODO reader_name, strlen(reader_name) */);
|
||||
break;
|
||||
case VEVENT_READER_REMOVE:
|
||||
/* future, tell qemu that an old CCID reader has been removed */
|
||||
if (verbose > 10) {
|
||||
printf(" READER REMOVE: %u\n", reader_id);
|
||||
}
|
||||
send_msg(VSC_ReaderRemove, reader_id, NULL, 0);
|
||||
break;
|
||||
case VEVENT_CARD_INSERT:
|
||||
/* get the ATR (intended as a response to a power on from the
|
||||
* reader */
|
||||
atr_len = MAX_ATR_LEN;
|
||||
vreader_power_on(event->reader, atr, &atr_len);
|
||||
/* ATR call functions as a Card Insert event */
|
||||
if (verbose > 10) {
|
||||
printf(" CARD INSERT %u: ", reader_id);
|
||||
print_byte_array(atr, atr_len);
|
||||
}
|
||||
send_msg(VSC_ATR, reader_id, atr, atr_len);
|
||||
break;
|
||||
case VEVENT_CARD_REMOVE:
|
||||
/* Card removed */
|
||||
if (verbose > 10) {
|
||||
printf(" CARD REMOVE %u:\n", reader_id);
|
||||
}
|
||||
send_msg(VSC_CardRemove, reader_id, NULL, 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
vevent_delete(event);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static unsigned int
|
||||
get_id_from_string(char *string, unsigned int default_id)
|
||||
{
|
||||
unsigned int id = atoi(string);
|
||||
|
||||
/* don't accidentally swith to zero because no numbers have been supplied */
|
||||
if ((id == 0) && *string != '0') {
|
||||
return default_id;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
static int
|
||||
on_host_init(VSCMsgHeader *mhHeader, VSCMsgInit *incoming)
|
||||
{
|
||||
uint32_t *capabilities = (incoming->capabilities);
|
||||
int num_capabilities =
|
||||
1 + ((mhHeader->length - sizeof(VSCMsgInit)) / sizeof(uint32_t));
|
||||
int i;
|
||||
|
||||
incoming->version = ntohl(incoming->version);
|
||||
if (incoming->version != VSCARD_VERSION) {
|
||||
if (verbose > 0) {
|
||||
printf("warning: host has version %d, we have %d\n",
|
||||
verbose, VSCARD_VERSION);
|
||||
}
|
||||
}
|
||||
if (incoming->magic != VSCARD_MAGIC) {
|
||||
printf("unexpected magic: got %d, expected %d\n",
|
||||
incoming->magic, VSCARD_MAGIC);
|
||||
return -1;
|
||||
}
|
||||
for (i = 0 ; i < num_capabilities; ++i) {
|
||||
capabilities[i] = ntohl(capabilities[i]);
|
||||
}
|
||||
/* Future: check capabilities */
|
||||
/* remove whatever reader might be left in qemu,
|
||||
* in case of an unclean previous exit. */
|
||||
send_msg(VSC_ReaderRemove, VSCARD_MINIMAL_READER_ID, NULL, 0);
|
||||
/* launch the event_thread. This will trigger reader adds for all the
|
||||
* existing readers */
|
||||
g_thread_new("vsc/event", event_thread, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
enum {
|
||||
STATE_HEADER,
|
||||
STATE_MESSAGE,
|
||||
};
|
||||
|
||||
#define APDUBufSize 270
|
||||
|
||||
static gboolean
|
||||
do_socket_read(GIOChannel *source,
|
||||
GIOCondition condition,
|
||||
gpointer data)
|
||||
{
|
||||
int rv;
|
||||
int dwSendLength;
|
||||
int dwRecvLength;
|
||||
uint8_t pbRecvBuffer[APDUBufSize];
|
||||
static uint8_t pbSendBuffer[APDUBufSize];
|
||||
VReaderStatus reader_status;
|
||||
VReader *reader = NULL;
|
||||
static VSCMsgHeader mhHeader;
|
||||
VSCMsgError *error_msg;
|
||||
GError *err = NULL;
|
||||
|
||||
static gchar *buf;
|
||||
static gsize br, to_read;
|
||||
static int state = STATE_HEADER;
|
||||
|
||||
if (state == STATE_HEADER && to_read == 0) {
|
||||
buf = (gchar *)&mhHeader;
|
||||
to_read = sizeof(mhHeader);
|
||||
}
|
||||
|
||||
if (to_read > 0) {
|
||||
g_io_channel_read_chars(source, (gchar *)buf, to_read, &br, &err);
|
||||
if (err != NULL) {
|
||||
g_error("error while reading: %s", err->message);
|
||||
}
|
||||
buf += br;
|
||||
to_read -= br;
|
||||
if (to_read != 0) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (state == STATE_HEADER) {
|
||||
mhHeader.type = ntohl(mhHeader.type);
|
||||
mhHeader.reader_id = ntohl(mhHeader.reader_id);
|
||||
mhHeader.length = ntohl(mhHeader.length);
|
||||
if (verbose) {
|
||||
printf("Header: type=%d, reader_id=%u length=%d (0x%x)\n",
|
||||
mhHeader.type, mhHeader.reader_id, mhHeader.length,
|
||||
mhHeader.length);
|
||||
}
|
||||
switch (mhHeader.type) {
|
||||
case VSC_APDU:
|
||||
case VSC_Flush:
|
||||
case VSC_Error:
|
||||
case VSC_Init:
|
||||
buf = (gchar *)pbSendBuffer;
|
||||
to_read = mhHeader.length;
|
||||
state = STATE_MESSAGE;
|
||||
return TRUE;
|
||||
default:
|
||||
fprintf(stderr, "Unexpected message of type 0x%X\n", mhHeader.type);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (state == STATE_MESSAGE) {
|
||||
switch (mhHeader.type) {
|
||||
case VSC_APDU:
|
||||
if (verbose) {
|
||||
printf(" recv APDU: ");
|
||||
print_byte_array(pbSendBuffer, mhHeader.length);
|
||||
}
|
||||
/* Transmit received APDU */
|
||||
dwSendLength = mhHeader.length;
|
||||
dwRecvLength = sizeof(pbRecvBuffer);
|
||||
reader = vreader_get_reader_by_id(mhHeader.reader_id);
|
||||
reader_status = vreader_xfr_bytes(reader,
|
||||
pbSendBuffer, dwSendLength,
|
||||
pbRecvBuffer, &dwRecvLength);
|
||||
if (reader_status == VREADER_OK) {
|
||||
mhHeader.length = dwRecvLength;
|
||||
if (verbose) {
|
||||
printf(" send response: ");
|
||||
print_byte_array(pbRecvBuffer, mhHeader.length);
|
||||
}
|
||||
send_msg(VSC_APDU, mhHeader.reader_id,
|
||||
pbRecvBuffer, dwRecvLength);
|
||||
} else {
|
||||
rv = reader_status; /* warning: not meaningful */
|
||||
send_msg(VSC_Error, mhHeader.reader_id, &rv, sizeof(uint32_t));
|
||||
}
|
||||
vreader_free(reader);
|
||||
reader = NULL; /* we've freed it, don't use it by accident
|
||||
again */
|
||||
break;
|
||||
case VSC_Flush:
|
||||
/* TODO: actually flush */
|
||||
send_msg(VSC_FlushComplete, mhHeader.reader_id, NULL, 0);
|
||||
break;
|
||||
case VSC_Error:
|
||||
error_msg = (VSCMsgError *) pbSendBuffer;
|
||||
if (error_msg->code == VSC_SUCCESS) {
|
||||
g_mutex_lock(&pending_reader_lock);
|
||||
if (pending_reader) {
|
||||
vreader_set_id(pending_reader, mhHeader.reader_id);
|
||||
vreader_free(pending_reader);
|
||||
pending_reader = NULL;
|
||||
g_cond_signal(&pending_reader_condition);
|
||||
}
|
||||
g_mutex_unlock(&pending_reader_lock);
|
||||
break;
|
||||
}
|
||||
printf("warning: qemu refused to add reader\n");
|
||||
if (error_msg->code == VSC_CANNOT_ADD_MORE_READERS) {
|
||||
/* clear pending reader, qemu can't handle any more */
|
||||
g_mutex_lock(&pending_reader_lock);
|
||||
if (pending_reader) {
|
||||
pending_reader = NULL;
|
||||
/* make sure the event loop doesn't hang */
|
||||
g_cond_signal(&pending_reader_condition);
|
||||
}
|
||||
g_mutex_unlock(&pending_reader_lock);
|
||||
}
|
||||
break;
|
||||
case VSC_Init:
|
||||
if (on_host_init(&mhHeader, (VSCMsgInit *)pbSendBuffer) < 0) {
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
state = STATE_HEADER;
|
||||
}
|
||||
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
do_socket(GIOChannel *source,
|
||||
GIOCondition condition,
|
||||
gpointer data)
|
||||
{
|
||||
/* not sure if two watches work well with a single win32 sources */
|
||||
if (condition & G_IO_OUT) {
|
||||
if (!do_socket_send(source, condition, data)) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (condition & G_IO_IN) {
|
||||
if (!do_socket_read(source, condition, data)) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
update_socket_watch(void)
|
||||
{
|
||||
gboolean out = socket_to_send->len > 0;
|
||||
|
||||
if (socket_tag != 0) {
|
||||
g_source_remove(socket_tag);
|
||||
}
|
||||
|
||||
socket_tag = g_io_add_watch(channel_socket,
|
||||
G_IO_IN | (out ? G_IO_OUT : 0), do_socket, NULL);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
do_command(GIOChannel *source,
|
||||
GIOCondition condition,
|
||||
gpointer data)
|
||||
{
|
||||
char *string;
|
||||
VCardEmulError error;
|
||||
static unsigned int default_reader_id;
|
||||
unsigned int reader_id;
|
||||
VReader *reader = NULL;
|
||||
GError *err = NULL;
|
||||
|
||||
g_assert(condition & G_IO_IN);
|
||||
|
||||
reader_id = default_reader_id;
|
||||
g_io_channel_read_line(source, &string, NULL, NULL, &err);
|
||||
if (err != NULL) {
|
||||
g_error("Error while reading command: %s", err->message);
|
||||
}
|
||||
|
||||
if (string != NULL) {
|
||||
if (strncmp(string, "exit", 4) == 0) {
|
||||
/* remove all the readers */
|
||||
VReaderList *list = vreader_get_reader_list();
|
||||
VReaderListEntry *reader_entry;
|
||||
printf("Active Readers:\n");
|
||||
for (reader_entry = vreader_list_get_first(list); reader_entry;
|
||||
reader_entry = vreader_list_get_next(reader_entry)) {
|
||||
VReader *reader = vreader_list_get_reader(reader_entry);
|
||||
vreader_id_t reader_id;
|
||||
reader_id = vreader_get_id(reader);
|
||||
if (reader_id == -1) {
|
||||
continue;
|
||||
}
|
||||
/* be nice and signal card removal first (qemu probably should
|
||||
* do this itself) */
|
||||
if (vreader_card_is_present(reader) == VREADER_OK) {
|
||||
send_msg(VSC_CardRemove, reader_id, NULL, 0);
|
||||
}
|
||||
send_msg(VSC_ReaderRemove, reader_id, NULL, 0);
|
||||
}
|
||||
exit(0);
|
||||
} else if (strncmp(string, "insert", 6) == 0) {
|
||||
if (string[6] == ' ') {
|
||||
reader_id = get_id_from_string(&string[7], reader_id);
|
||||
}
|
||||
reader = vreader_get_reader_by_id(reader_id);
|
||||
if (reader != NULL) {
|
||||
error = vcard_emul_force_card_insert(reader);
|
||||
printf("insert %s, returned %d\n",
|
||||
vreader_get_name(reader), error);
|
||||
} else {
|
||||
printf("no reader by id %u found\n", reader_id);
|
||||
}
|
||||
} else if (strncmp(string, "remove", 6) == 0) {
|
||||
if (string[6] == ' ') {
|
||||
reader_id = get_id_from_string(&string[7], reader_id);
|
||||
}
|
||||
reader = vreader_get_reader_by_id(reader_id);
|
||||
if (reader != NULL) {
|
||||
error = vcard_emul_force_card_remove(reader);
|
||||
printf("remove %s, returned %d\n",
|
||||
vreader_get_name(reader), error);
|
||||
} else {
|
||||
printf("no reader by id %u found\n", reader_id);
|
||||
}
|
||||
} else if (strncmp(string, "select", 6) == 0) {
|
||||
if (string[6] == ' ') {
|
||||
reader_id = get_id_from_string(&string[7],
|
||||
VSCARD_UNDEFINED_READER_ID);
|
||||
}
|
||||
if (reader_id != VSCARD_UNDEFINED_READER_ID) {
|
||||
reader = vreader_get_reader_by_id(reader_id);
|
||||
}
|
||||
if (reader) {
|
||||
printf("Selecting reader %u, %s\n", reader_id,
|
||||
vreader_get_name(reader));
|
||||
default_reader_id = reader_id;
|
||||
} else {
|
||||
printf("Reader with id %u not found\n", reader_id);
|
||||
}
|
||||
} else if (strncmp(string, "debug", 5) == 0) {
|
||||
if (string[5] == ' ') {
|
||||
verbose = get_id_from_string(&string[6], 0);
|
||||
}
|
||||
printf("debug level = %d\n", verbose);
|
||||
} else if (strncmp(string, "list", 4) == 0) {
|
||||
VReaderList *list = vreader_get_reader_list();
|
||||
VReaderListEntry *reader_entry;
|
||||
printf("Active Readers:\n");
|
||||
for (reader_entry = vreader_list_get_first(list); reader_entry;
|
||||
reader_entry = vreader_list_get_next(reader_entry)) {
|
||||
VReader *reader = vreader_list_get_reader(reader_entry);
|
||||
vreader_id_t reader_id;
|
||||
reader_id = vreader_get_id(reader);
|
||||
if (reader_id == -1) {
|
||||
continue;
|
||||
}
|
||||
printf("%3u %s %s\n", reader_id,
|
||||
vreader_card_is_present(reader) == VREADER_OK ?
|
||||
"CARD_PRESENT" : " ",
|
||||
vreader_get_name(reader));
|
||||
}
|
||||
printf("Inactive Readers:\n");
|
||||
for (reader_entry = vreader_list_get_first(list); reader_entry;
|
||||
reader_entry = vreader_list_get_next(reader_entry)) {
|
||||
VReader *reader = vreader_list_get_reader(reader_entry);
|
||||
vreader_id_t reader_id;
|
||||
reader_id = vreader_get_id(reader);
|
||||
if (reader_id != -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("INA %s %s\n",
|
||||
vreader_card_is_present(reader) == VREADER_OK ?
|
||||
"CARD_PRESENT" : " ",
|
||||
vreader_get_name(reader));
|
||||
}
|
||||
vreader_list_delete(list);
|
||||
} else if (*string != 0) {
|
||||
printf("valid commands:\n");
|
||||
printf("insert [reader_id]\n");
|
||||
printf("remove [reader_id]\n");
|
||||
printf("select reader_id\n");
|
||||
printf("list\n");
|
||||
printf("debug [level]\n");
|
||||
printf("exit\n");
|
||||
}
|
||||
}
|
||||
vreader_free(reader);
|
||||
printf("> ");
|
||||
fflush(stdout);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* just for ease of parsing command line arguments. */
|
||||
#define MAX_CERTS 100
|
||||
|
||||
static int
|
||||
connect_to_qemu(
|
||||
const char *host,
|
||||
const char *port
|
||||
) {
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *server = NULL;
|
||||
int ret, sock;
|
||||
|
||||
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock < 0) {
|
||||
/* Error */
|
||||
fprintf(stderr, "Error opening socket!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = 0;
|
||||
hints.ai_protocol = 0; /* Any protocol */
|
||||
|
||||
ret = getaddrinfo(host, port, &hints, &server);
|
||||
|
||||
if (ret != 0) {
|
||||
/* Error */
|
||||
fprintf(stderr, "getaddrinfo failed\n");
|
||||
goto cleanup_socket;
|
||||
}
|
||||
|
||||
if (connect(sock, server->ai_addr, server->ai_addrlen) < 0) {
|
||||
/* Error */
|
||||
fprintf(stderr, "Could not connect\n");
|
||||
goto cleanup_socket;
|
||||
}
|
||||
if (verbose) {
|
||||
printf("Connected (sizeof Header=%zd)!\n", sizeof(VSCMsgHeader));
|
||||
}
|
||||
|
||||
freeaddrinfo(server);
|
||||
return sock;
|
||||
|
||||
cleanup_socket:
|
||||
if (server) {
|
||||
freeaddrinfo(server);
|
||||
}
|
||||
closesocket(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
main(
|
||||
int argc,
|
||||
char *argv[]
|
||||
) {
|
||||
GMainLoop *loop;
|
||||
GIOChannel *channel_stdin;
|
||||
char *qemu_host;
|
||||
char *qemu_port;
|
||||
|
||||
VCardEmulOptions *command_line_options = NULL;
|
||||
|
||||
char *cert_names[MAX_CERTS];
|
||||
char *emul_args = NULL;
|
||||
int cert_count = 0;
|
||||
int c, sock;
|
||||
|
||||
#ifdef _WIN32
|
||||
WSADATA Data;
|
||||
|
||||
if (WSAStartup(MAKEWORD(2, 2), &Data) != 0) {
|
||||
c = WSAGetLastError();
|
||||
fprintf(stderr, "WSAStartup: %d\n", c);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
#if !GLIB_CHECK_VERSION(2, 31, 0)
|
||||
if (!g_thread_supported()) {
|
||||
g_thread_init(NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
while ((c = getopt(argc, argv, "c:e:pd:")) != -1) {
|
||||
switch (c) {
|
||||
case 'c':
|
||||
if (cert_count >= MAX_CERTS) {
|
||||
printf("too many certificates (max = %d)\n", MAX_CERTS);
|
||||
exit(5);
|
||||
}
|
||||
cert_names[cert_count++] = optarg;
|
||||
break;
|
||||
case 'e':
|
||||
emul_args = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
print_usage();
|
||||
exit(4);
|
||||
break;
|
||||
case 'd':
|
||||
verbose = get_id_from_string(optarg, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (argc - optind != 2) {
|
||||
print_usage();
|
||||
exit(4);
|
||||
}
|
||||
|
||||
if (cert_count > 0) {
|
||||
char *new_args;
|
||||
int len, i;
|
||||
/* if we've given some -c options, we clearly we want do so some
|
||||
* software emulation. add that emulation now. this is NSS Emulator
|
||||
* specific */
|
||||
if (emul_args == NULL) {
|
||||
emul_args = (char *)"db=\"/etc/pki/nssdb\"";
|
||||
}
|
||||
#define SOFT_STRING ",soft=(,Virtual Reader,CAC,,"
|
||||
/* 2 == close paren & null */
|
||||
len = strlen(emul_args) + strlen(SOFT_STRING) + 2;
|
||||
for (i = 0; i < cert_count; i++) {
|
||||
len += strlen(cert_names[i])+1; /* 1 == comma */
|
||||
}
|
||||
new_args = g_malloc(len);
|
||||
strcpy(new_args, emul_args);
|
||||
strcat(new_args, SOFT_STRING);
|
||||
for (i = 0; i < cert_count; i++) {
|
||||
strcat(new_args, cert_names[i]);
|
||||
strcat(new_args, ",");
|
||||
}
|
||||
strcat(new_args, ")");
|
||||
emul_args = new_args;
|
||||
}
|
||||
if (emul_args) {
|
||||
command_line_options = vcard_emul_options(emul_args);
|
||||
}
|
||||
|
||||
qemu_host = g_strdup(argv[argc - 2]);
|
||||
qemu_port = g_strdup(argv[argc - 1]);
|
||||
sock = connect_to_qemu(qemu_host, qemu_port);
|
||||
if (sock == -1) {
|
||||
fprintf(stderr, "error opening socket, exiting.\n");
|
||||
exit(5);
|
||||
}
|
||||
|
||||
socket_to_send = g_byte_array_new();
|
||||
vcard_emul_init(command_line_options);
|
||||
loop = g_main_loop_new(NULL, TRUE);
|
||||
|
||||
printf("> ");
|
||||
fflush(stdout);
|
||||
|
||||
#ifdef _WIN32
|
||||
channel_stdin = g_io_channel_win32_new_fd(STDIN_FILENO);
|
||||
#else
|
||||
channel_stdin = g_io_channel_unix_new(STDIN_FILENO);
|
||||
#endif
|
||||
g_io_add_watch(channel_stdin, G_IO_IN, do_command, NULL);
|
||||
#ifdef _WIN32
|
||||
channel_socket = g_io_channel_win32_new_socket(sock);
|
||||
#else
|
||||
channel_socket = g_io_channel_unix_new(sock);
|
||||
#endif
|
||||
g_io_channel_set_encoding(channel_socket, NULL, NULL);
|
||||
/* we buffer ourself for thread safety reasons */
|
||||
g_io_channel_set_buffered(channel_socket, FALSE);
|
||||
|
||||
/* Send init message, Host responds (and then we send reader attachments) */
|
||||
VSCMsgInit init = {
|
||||
.version = htonl(VSCARD_VERSION),
|
||||
.magic = VSCARD_MAGIC,
|
||||
.capabilities = {0}
|
||||
};
|
||||
send_msg(VSC_Init, 0, &init, sizeof(init));
|
||||
|
||||
g_main_loop_run(loop);
|
||||
g_main_loop_unref(loop);
|
||||
|
||||
g_io_channel_unref(channel_stdin);
|
||||
g_io_channel_unref(channel_socket);
|
||||
g_byte_array_free(socket_to_send, TRUE);
|
||||
|
||||
closesocket(sock);
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user