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:
Marc-André Lureau 2015-08-30 11:48:40 +02:00
parent 684bb5770e
commit 7b02f5447c
33 changed files with 21 additions and 5849 deletions

1
.gitignore vendored
View File

@ -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

View File

@ -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

View File

@ -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
View File

@ -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

View File

@ -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.

View File

@ -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)

View File

@ -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 { \

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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}

View File

@ -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

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -1,6 +0,0 @@
#ifndef VCARDT_INTERNAL_H
#define VCARDT_INTERNAL_H
unsigned char *vcard_alloc_atr(const char *postfix, int *atr_len);
#endif

View File

@ -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

View File

@ -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();
}

View File

@ -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

View File

@ -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

View File

@ -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 */

View File

@ -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;
}