[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 09/17] libcacard: initial commit
From: |
Alon Levy |
Subject: |
[Qemu-devel] [PATCH 09/17] libcacard: initial commit |
Date: |
Thu, 3 Feb 2011 23:07:06 +0200 |
From: Robert Relyea <address@hidden>
libcacard emulates a Common Access Card (CAC) which is a standard
for smartcards. It is used by the emulated ccid card introduced in
a following patch. Docs are available in docs/libcacard.txt
Signed-off-by: Alon Levy <address@hidden>
---
changes from v15->v16:
Build:
* don't erase self with distclean
* fix make clean after make distclean
* Makefile: make vscclient link quiet
Behavioral:
* vcard_emul_nss: load coolkey in more situations
* vscclient:
* use hton,ntoh
* send init on connect, only start vevent thread on response
* read payload after header check, before type switch
* remove Reconnect
* update for vscard_common changes, empty Flush implementation
Style/Whitespace:
* fix wrong variable usage
* remove unused variable
* use only C style comments
* add copyright header
* fix tabulation
---
Makefile | 6 +-
Makefile.objs | 5 +
Makefile.target | 2 +
configure | 25 +
docs/libcacard.txt | 483 +++++++++++++++++
libcacard/Makefile | 14 +
libcacard/cac.c | 411 +++++++++++++++
libcacard/cac.h | 20 +
libcacard/card_7816.c | 780 ++++++++++++++++++++++++++++
libcacard/card_7816.h | 60 +++
libcacard/card_7816t.h | 163 ++++++
libcacard/event.c | 112 ++++
libcacard/eventt.h | 28 +
libcacard/link_test.c | 20 +
libcacard/mutex.h | 59 +++
libcacard/passthru.c | 612 ++++++++++++++++++++++
libcacard/passthru.h | 50 ++
libcacard/vcard.c | 350 +++++++++++++
libcacard/vcard.h | 85 +++
libcacard/vcard_emul.h | 62 +++
libcacard/vcard_emul_nss.c | 1192 +++++++++++++++++++++++++++++++++++++++++++
libcacard/vcard_emul_type.c | 60 +++
libcacard/vcard_emul_type.h | 29 +
libcacard/vcardt.h | 66 +++
libcacard/vevent.h | 26 +
libcacard/vreader.c | 526 +++++++++++++++++++
libcacard/vreader.h | 54 ++
libcacard/vreadert.h | 23 +
libcacard/vscclient.c | 743 +++++++++++++++++++++++++++
29 files changed, 6064 insertions(+), 2 deletions(-)
create mode 100644 docs/libcacard.txt
create mode 100644 libcacard/Makefile
create mode 100644 libcacard/cac.c
create mode 100644 libcacard/cac.h
create mode 100644 libcacard/card_7816.c
create mode 100644 libcacard/card_7816.h
create mode 100644 libcacard/card_7816t.h
create mode 100644 libcacard/event.c
create mode 100644 libcacard/eventt.h
create mode 100644 libcacard/link_test.c
create mode 100644 libcacard/mutex.h
create mode 100644 libcacard/passthru.c
create mode 100644 libcacard/passthru.h
create mode 100644 libcacard/vcard.c
create mode 100644 libcacard/vcard.h
create mode 100644 libcacard/vcard_emul.h
create mode 100644 libcacard/vcard_emul_nss.c
create mode 100644 libcacard/vcard_emul_type.c
create mode 100644 libcacard/vcard_emul_type.h
create mode 100644 libcacard/vcardt.h
create mode 100644 libcacard/vevent.h
create mode 100644 libcacard/vreader.c
create mode 100644 libcacard/vreader.h
create mode 100644 libcacard/vreadert.h
create mode 100644 libcacard/vscclient.c
diff --git a/Makefile b/Makefile
index eca4c76..9286eb1 100644
--- a/Makefile
+++ b/Makefile
@@ -173,6 +173,8 @@ check-qlist: check-qlist.o qlist.o qint.o $(CHECK_PROG_DEPS)
check-qfloat: check-qfloat.o qfloat.o $(CHECK_PROG_DEPS)
check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o
qjson.o json-streamer.o json-lexer.o json-parser.o $(CHECK_PROG_DEPS)
+QEMULIBS=libhw32 libhw64 libuser libdis libdis-user
+
clean:
# avoid old build problems by removing potentially incorrect old files
rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h
gen-op-arm.h
@@ -184,7 +186,7 @@ clean:
rm -f trace-dtrace.dtrace trace-dtrace.dtrace-timestamp
rm -f trace-dtrace.h trace-dtrace.h-timestamp
$(MAKE) -C tests clean
- for d in $(ALL_SUBDIRS) libhw32 libhw64 libuser libdis libdis-user; do \
+ for d in $(ALL_SUBDIRS) $(QEMULIBS) libcacard; do \
if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \
rm -f $$d/qemu-options.def; \
done
@@ -195,7 +197,7 @@ distclean: clean
rm -f roms/seabios/config.mak roms/vgabios/config.mak
rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.dvi qemu-doc.fn
qemu-doc.info qemu-doc.ky qemu-doc.log qemu-doc.pdf qemu-doc.pg qemu-doc.toc
qemu-doc.tp qemu-doc.vr
rm -f qemu-tech.info qemu-tech.aux qemu-tech.cp qemu-tech.dvi
qemu-tech.fn qemu-tech.info qemu-tech.ky qemu-tech.log qemu-tech.pdf
qemu-tech.pg qemu-tech.toc qemu-tech.tp qemu-tech.vr
- for d in $(TARGET_DIRS) libhw32 libhw64 libuser libdis libdis-user; do \
+ for d in $(TARGET_DIRS) $(QEMULIBS); do \
rm -rf $$d || exit 1 ; \
done
diff --git a/Makefile.objs b/Makefile.objs
index 94c36a2..3fba145 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -313,6 +313,11 @@ user-obj-y += qemu-timer-common.o
endif
endif
+######################################################################
+# smartcard
+
+libcacard-y = cac.o event.o passthru.o vcard.o vreader.o vcard_emul_nss.o
vcard_emul_type.o card_7816.o
+
vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
diff --git a/Makefile.target b/Makefile.target
index b0ba95f..902e15d 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -340,6 +340,8 @@ obj-y += $(addprefix $(HWDIR)/, $(hw-obj-y))
endif # CONFIG_SOFTMMU
+obj-y += $(addprefix ../libcacard/, $(libcacard-$(CONFIG_SMARTCARD)))
+
obj-y += $(addprefix ../, $(trace-obj-y))
obj-$(CONFIG_GDBSTUB_XML) += gdbstub-xml.o
diff --git a/configure b/configure
index 14a035a..9926ed4 100755
--- a/configure
+++ b/configure
@@ -2240,6 +2240,19 @@ EOF
fi
fi
+# check for libcacard for smartcard support
+smartcard_cflags="-I\$(SRC_PATH)/libcacard"
+libcacard_libs=$($pkgconfig --libs nss 2>/dev/null)
+libcacard_cflags=$($pkgconfig --cflags nss)
+# TODO - what's the minimal nss version we support?
+if $pkgconfig --atleast-version=3.12.8 nss; then
+ smartcard="yes"
+ QEMU_CFLAGS="$QEMU_CFLAGS $smartcard_cflags $libcacard_cflags"
+ LIBS="$libcacard_libs $LIBS"
+else
+ smartcard="no"
+fi
+
##########################################
##########################################
@@ -3075,6 +3088,11 @@ fi
if test "$target_darwin_user" = "yes" ; then
echo "CONFIG_DARWIN_USER=y" >> $config_target_mak
fi
+if test "$smartcard" = "yes" ; then
+ echo "subdir-$target: subdir-libcacard" >> $config_host_mak
+ echo "libcacard_libs=$libcacard_libs" >> $config_host_mak
+ echo "libcacard_cflags=$libcacard_cflags" >> $config_host_mak
+fi
list=""
if test ! -z "$gdb_xml_files" ; then
for x in $gdb_xml_files; do
@@ -3288,6 +3306,13 @@ for hwlib in 32 64; do
echo "QEMU_CFLAGS+=-DTARGET_PHYS_ADDR_BITS=$hwlib" > $d/config.mak
done
+if [ $source_path != $workdir ]; then
+ # out of tree build
+ mkdir -p libcacard
+ rm -f libcacard/Makefile
+ ln -s $source_path/libcacard/Makefile libcacard/Makefile
+fi
+
d=libuser
mkdir -p $d
symlink $source_path/Makefile.user $d/Makefile
diff --git a/docs/libcacard.txt b/docs/libcacard.txt
new file mode 100644
index 0000000..5dee6fa
--- /dev/null
+++ b/docs/libcacard.txt
@@ -0,0 +1,483 @@
+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 replacable 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 replacable 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 it's own implementation that connects to it's 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 it's reference when it is
+ finished with vreader_free().
+
+ void vreader_free(VReader *reader);
+
+ This function frees a reference to a reader. Reader's 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 functions simulates a card power on. Virtual cards do 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 functions send 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 prepresent 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 corresponsing 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 it's aid, and then commands will be
+ routed to it VCardProcessAPDU function. This function adopts the applet the
+ passed int applet. Note: 2 applets with the same AID should not be added to
+ the same card. It's 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 messagin 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.
+Reponses 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 soley one 2 byte status code. If that status
+ code is defined in card_7816t.h, then this function is guarrenteed 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
+ guarrenteed 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 the number of remaing 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 logins into the card and return 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 virutal 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 similiar
+ 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
+ the same of the parameters xxxx_card_init() accepts.
+
+ Finally the card is associated with it's 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.
diff --git a/libcacard/Makefile b/libcacard/Makefile
new file mode 100644
index 0000000..554c6ea
--- /dev/null
+++ b/libcacard/Makefile
@@ -0,0 +1,14 @@
+-include ../config-host.mak
+-include $(SRC_PATH)/Makefile.objs
+-include $(SRC_PATH)/rules.mak
+
+$(call set-vpath, $(SRC_PATH):$(SRC_PATH)/libcacard)
+
+vscclient: $(libcacard-y) vscclient.o
+ $(call quiet-command,$(CC) $(libcacard_libs) -o $@ $^," LINK
$(TARGET_DIR)$@")
+
+all: vscclient
+
+clean:
+ rm -f *.o */*.o *.d */*.d *.a */*.a *~ */*~ vscclient
+
diff --git a/libcacard/cac.c b/libcacard/cac.c
new file mode 100644
index 0000000..e51caec
--- /dev/null
+++ b/libcacard/cac.c
@@ -0,0 +1,411 @@
+/*
+ * implement the applets for the CAC card.
+ */
+#include "cac.h"
+#include "vcard.h"
+#include "vcard_emul.h"
+#include "card_7816.h"
+#include <stdlib.h>
+#include <string.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
+
+/* 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;
+
+ switch (apdu->a_ins) {
+ case VCARD7816_INS_SELECT_FILE:
+ if (apdu->a_p1 != 0x02) {
+ /* let the 7816 code handle applet switches */
+ return VCARD_NEXT;
+ }
+ /* handle file id setting */
+ if (apdu->a_Lc != 2) {
+ *response = vcard_make_response(
+ VCARD7816_STATUS_ERROR_DATA_INVALID);
+ return VCARD_DONE;
+ }
+ /* 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);
+ return VCARD_DONE;
+ }
+ *response = vcard_make_response(VCARD7816_STATUS_SUCCESS);
+ return VCARD_DONE;
+ case VCARD7816_INS_GET_RESPONSE:
+ case VCARD7816_INS_VERIFY:
+ /* let the 7816 code handle these */
+ return VCARD_NEXT;
+ 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);
+ return VCARD_DONE;
+ }
+ *response = vcard_make_response(
+ VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+ return VCARD_DONE;
+}
+
+/*
+ * resest the inter call state between applet selects
+ */
+static VCardStatus
+cac_applet_pki_reset(VCard *card, int channel)
+{
+ VCardAppletPrivate *applet_private = NULL;
+ CACPKIAppletData *pki_applet = NULL;
+ applet_private = vcard_get_current_applet_private(card, channel);
+ ASSERT(applet_private);
+ pki_applet = &(applet_private->u.pki_data);
+
+ pki_applet->cert_buffer = NULL;
+ if (pki_applet->sign_buffer) {
+ 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 = NULL;
+ VCardAppletPrivate *applet_private = NULL;
+ int size, next;
+ unsigned char *sign_buffer;
+ vcard_7816_status_t status;
+
+ 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);
+ return VCARD_DONE;
+ 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);
+ }
+ return VCARD_DONE;
+ 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 = realloc(pki_applet->sign_buffer,
+ pki_applet->sign_buffer_len+size);
+ if (sign_buffer == NULL) {
+ free(pki_applet->sign_buffer);
+ pki_applet->sign_buffer = NULL;
+ pki_applet->sign_buffer_len = 0;
+ *response = vcard_make_response(
+ VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
+ return VCARD_DONE;
+ }
+ 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);
+ return VCARD_DONE;
+ 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;
+ }
+ free(sign_buffer);
+ pki_applet->sign_buffer = NULL;
+ pki_applet->sign_buffer_len = 0;
+ return VCARD_DONE;
+ 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);
+ return VCARD_DONE;
+ }
+ return cac_common_process_apdu(card, apdu, response);
+}
+
+
+static VCardStatus
+cac_applet_id_process_apdu(VCard *card, VCardAPDU *apdu,
+ VCardResponse **response)
+{
+ switch (apdu->a_ins) {
+ case CAC_UPDATE_BUFFER:
+ *response = vcard_make_response(
+ VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED);
+ return VCARD_DONE;
+ 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);
+ return VCARD_DONE;
+ }
+ return cac_common_process_apdu(card, apdu, response);
+}
+
+
+/*
+ * 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)
+{
+ switch (apdu->a_ins) {
+ case CAC_READ_BUFFER:
+ case CAC_UPDATE_BUFFER:
+ *response = vcard_make_response(
+ VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+ return VCARD_DONE;
+ default:
+ break;
+ }
+ return cac_common_process_apdu(card, apdu, response);
+}
+
+/*
+ * utilities for creating and destroying the private applet data
+ */
+static void
+cac_delete_pki_applet_private(VCardAppletPrivate *applet_private)
+{
+ CACPKIAppletData *pki_applet_data = NULL;
+ if (pki_applet_data == NULL) {
+ return;
+ }
+ pki_applet_data = &(applet_private->u.pki_data);
+ if (pki_applet_data->cert != NULL) {
+ free(pki_applet_data->cert);
+ }
+ if (pki_applet_data->sign_buffer != NULL) {
+ free(pki_applet_data->sign_buffer);
+ }
+ if (pki_applet_data->key != NULL) {
+ vcard_emul_delete_key(pki_applet_data->key);
+ }
+ free(applet_private);
+}
+
+static VCardAppletPrivate *
+cac_new_pki_applet_private(const unsigned char *cert,
+ int cert_len, VCardKey *key)
+{
+ CACPKIAppletData *pki_applet_data = NULL;
+ VCardAppletPrivate *applet_private = NULL;
+ applet_private = (VCardAppletPrivate *)malloc(sizeof(VCardAppletPrivate));
+
+ if (applet_private == NULL) {
+ goto fail;
+ }
+ pki_applet_data= &(applet_private->u.pki_data);
+ pki_applet_data->cert_buffer = NULL;
+ pki_applet_data->cert_buffer_len = 0;
+ pki_applet_data->sign_buffer = NULL;
+ pki_applet_data->sign_buffer_len = 0;
+ pki_applet_data->key = NULL;
+ pki_applet_data->cert = (unsigned char *)malloc(cert_len+1);
+ if (pki_applet_data->cert == NULL) {
+ goto fail;
+ }
+ /*
+ * 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;
+
+fail:
+ if (applet_private) {
+ cac_delete_pki_applet_private(applet_private);
+ }
+ return NULL;
+}
+
+
+/*
+ * 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 = NULL;
+ VCardApplet *applet = NULL;
+ 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;
+}
+
diff --git a/libcacard/cac.h b/libcacard/cac.h
new file mode 100644
index 0000000..bb2a9f0
--- /dev/null
+++ b/libcacard/cac.h
@@ -0,0 +1,20 @@
+/*
+ * defines the entry point for the cac card. Only used by cac.c anc
+ * vcard_emul_type.c
+ */
+#ifndef CAC_H
+#define CAC_H 1
+#include "vcard.h"
+#include "vreader.h"
+/*
+ * 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
diff --git a/libcacard/card_7816.c b/libcacard/card_7816.c
new file mode 100644
index 0000000..e2d8466
--- /dev/null
+++ b/libcacard/card_7816.c
@@ -0,0 +1,780 @@
+/*
+ * Implement the 7816 portion of the card spec
+ *
+ */
+
+#include "vcard.h"
+#include "vcard_emul.h"
+#include "card_7816.h"
+#include <stdlib.h>
+#include <string.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 = (VCardResponse *)malloc(sizeof(VCardResponse));
+ if (!new_response) {
+ return NULL;
+ }
+ new_response->b_data = malloc(len+2);
+ if (!new_response->b_data) {
+ free(new_response);
+ return NULL;
+ }
+ 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 Reponse buffer that only has a status.
+ */
+static VCardResponse *
+vcard_response_new_status(vcard_7816_status_t status)
+{
+ VCardResponse *new_response;
+
+ new_response = (VCardResponse *)malloc(sizeof(VCardResponse));
+ if (!new_response) {
+ return NULL;
+ }
+ 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 = (VCardResponse *)malloc(sizeof(VCardResponse));
+ if (!new_response) {
+ return NULL;
+ }
+ 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 */
+ if (response->b_data) {
+ free(response->b_data);
+ }
+ free(response);
+ break;
+ case VCARD_MALLOC_DATA:
+ /* only the data buffer was malloc'ed */
+ if (response->b_data) {
+ free(response->b_data);
+ }
+ break;
+ case VCARD_MALLOC_STRUCT:
+ /* only the structure was malloc'ed */
+ 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_PROPIETARY;
+ break;
+ }
+ return VCARD7816_STATUS_SUCCESS;
+}
+
+/*
+ * set the Le and Lc fiels 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 = (VCardAPDU *)malloc(sizeof(VCardAPDU));
+ if (!new_apdu) {
+ return NULL;
+ }
+ new_apdu->a_data = malloc(len);
+ if (!new_apdu->a_data) {
+ free(new_apdu);
+ return NULL;
+ }
+ memcpy(new_apdu->a_data, raw_apdu, len);
+ new_apdu->a_len = len;
+ *status = vcard_apdu_set_class(new_apdu);
+ if (*status != VCARD7816_STATUS_SUCCESS) {
+ free(new_apdu);
+ return NULL;
+ }
+ *status = vcard_apdu_set_length(new_apdu);
+ if (*status != VCARD7816_STATUS_SUCCESS) {
+ free(new_apdu);
+ new_apdu = NULL;
+ }
+ return new_apdu;
+}
+
+void
+vcard_apdu_delete(VCardAPDU *apdu)
+{
+ if (apdu == NULL) {
+ return;
+ }
+ if (apdu->a_data) {
+ free(apdu->a_data);
+ }
+ 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 = NULL;
+
+ 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);
+ }
+ }
+ ASSERT(response);
+ 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 */
+ }
+ 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;
+}
diff --git a/libcacard/card_7816.h b/libcacard/card_7816.h
new file mode 100644
index 0000000..4351b1c
--- /dev/null
+++ b/libcacard/card_7816.h
@@ -0,0 +1,60 @@
+/*
+ * Implement the 7816 portion of the card spec
+ *
+ */
+#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 alwyas 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
diff --git a/libcacard/card_7816t.h b/libcacard/card_7816t.h
new file mode 100644
index 0000000..531455c
--- /dev/null
+++ b/libcacard/card_7816t.h
@@ -0,0 +1,163 @@
+/*
+ * Implement the 7816 portion of the card spec
+ *
+ */
+#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_PROPIETARY
+} 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]; /* indefinate 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
diff --git a/libcacard/event.c b/libcacard/event.c
new file mode 100644
index 0000000..25d99ae
--- /dev/null
+++ b/libcacard/event.c
@@ -0,0 +1,112 @@
+/*
+ *
+ */
+#include "vcard.h"
+#include "vreader.h"
+#include "vevent.h"
+
+/*
+ * OS includes
+ */
+#include <stdlib.h>
+
+/*
+ * from spice
+ */
+#include "mutex.h"
+
+VEvent *
+vevent_new(VEventType type, VReader *reader, VCard *card)
+{
+ VEvent *new_vevent;
+
+ new_vevent = (VEvent *)malloc(sizeof(VEvent));
+ if (new_vevent == NULL) {
+ return NULL;
+ }
+ 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);
+ free(vevent);
+}
+
+/*
+ * VEvent queue management
+ */
+
+static VEvent *vevent_queue_head = NULL;
+static VEvent *vevent_queue_tail = NULL;
+static mutex_t vevent_queue_lock;
+static condition_t vevent_queue_condition;
+
+void vevent_queue_init(void)
+{
+ MUTEX_INIT(vevent_queue_lock);
+ CONDITION_INIT(vevent_queue_condition);
+ vevent_queue_head = vevent_queue_tail = NULL;
+}
+
+void
+vevent_queue_vevent(VEvent *vevent)
+{
+ vevent->next = NULL;
+ 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;
+ CONDITION_NOTIFY(vevent_queue_condition);
+ 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;
+
+ MUTEX_LOCK(vevent_queue_lock);
+ while ((vevent = vevent_dequeue_vevent()) == NULL) {
+ CONDITION_WAIT(vevent_queue_condition, vevent_queue_lock);
+ }
+ MUTEX_UNLOCK(vevent_queue_lock);
+ return vevent;
+}
+
+VEvent * vevent_get_next_vevent(void)
+{
+ VEvent *vevent;
+
+ MUTEX_LOCK(vevent_queue_lock);
+ vevent = vevent_dequeue_vevent();
+ MUTEX_UNLOCK(vevent_queue_lock);
+ return vevent;
+}
+
diff --git a/libcacard/eventt.h b/libcacard/eventt.h
new file mode 100644
index 0000000..4c3df4f
--- /dev/null
+++ b/libcacard/eventt.h
@@ -0,0 +1,28 @@
+/*
+ *
+ */
+
+#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
+
+
diff --git a/libcacard/link_test.c b/libcacard/link_test.c
new file mode 100644
index 0000000..40d2c6d
--- /dev/null
+++ b/libcacard/link_test.c
@@ -0,0 +1,20 @@
+/*
+ *
+ */
+#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);
+}
+
diff --git a/libcacard/mutex.h b/libcacard/mutex.h
new file mode 100644
index 0000000..ffbdb31
--- /dev/null
+++ b/libcacard/mutex.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2009 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * This header file provides a way of mapping windows and linux thread calls
+ * to a set of macros. Ideally this would be shared by whatever subsystem we
+ * link with.
+ */
+
+#ifndef _H_MUTEX
+#define _H_MUTEX
+#ifdef _WIN32
+#include <windows.h>
+typedef CRITICAL_SECTION mutex_t;
+#define MUTEX_INIT(mutex) InitializeCriticalSection(&mutex)
+#define MUTEX_LOCK(mutex) EnterCriticalSection(&mutex)
+#define MUTEX_UNLOCK(mutex) LeaveCriticalSection(&mutex)
+typedef CONDITION_VARIABLE condition_t;
+#define CONDITION_INIT(cond) InitializeConditionVariable(&cond)
+#define CONDITION_WAIT(cond,mutex) \
+ SleepConditionVariableCS(&cond,&mutex,INFINTE)
+#define CONDITION_NOTIFY(cond) WakeConditionVariable(&cond)
+typedef uint32_t thread_t;
+typedef HANDLE thread_status_t;
+#define THREAD_CREATE(tid, func, arg) \
+ CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func, arg, 0, &tid)
+#define THREAD_SUCCESS(status) ((status) != NULL)
+#else
+#include <pthread.h>
+typedef pthread_mutex_t mutex_t;
+#define MUTEX_INIT(mutex) pthread_mutex_init(&mutex, NULL)
+#define MUTEX_LOCK(mutex) pthread_mutex_lock(&mutex)
+#define MUTEX_UNLOCK(mutex) pthread_mutex_unlock(&mutex)
+typedef pthread_cond_t condition_t;
+#define CONDITION_INIT(cond) pthread_cond_init(&cond, NULL)
+#define CONDITION_WAIT(cond,mutex) pthread_cond_wait(&cond,&mutex)
+#define CONDITION_NOTIFY(cond) pthread_cond_signal(&cond)
+typedef pthread_t thread_t;
+typedef int thread_status_t;
+#define THREAD_CREATE(tid, func, arg) pthread_create(&tid, NULL, func, arg)
+#define THREAD_SUCCESS(status) ((status) == 0)
+#endif
+
+#endif /* _H_MUTEX */
diff --git a/libcacard/passthru.c b/libcacard/passthru.c
new file mode 100644
index 0000000..09471c7
--- /dev/null
+++ b/libcacard/passthru.c
@@ -0,0 +1,612 @@
+/*
+ * implement the applets for the CAC card.
+ */
+#ifdef USE_PASSTHRU
+#include "vcard.h"
+#include "vcard_emul.h"
+#include "card_7816.h"
+#include "vreader.h"
+#include "mutex.h"
+#include "vcard_emul.h"
+#include "passthru.h"
+#include <stdlib.h>
+#include <string.h>
+#include <pcsclite.h>
+
+/*
+ * Passthru applet private data
+ */
+struct VCardAppletPrivateStruct {
+ char *reader_name;
+ /* pcsc-lite parameters */
+ SCARDHANDLE hCard;
+ uint32_t hProtocol;
+ SCARD_IO_REQUEST *send_io;
+ unsigned char atr[MAX_ATR_SIZE];
+ int atr_len;
+};
+
+static SCARDCONTEXT global_context = 0;
+
+#define MAX_RESPONSE_LENGTH 261 /*65537 */
+/*
+ * handle all the APDU's that are common to all CAC applets
+ */
+static VCardStatus
+passthru_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response)
+{
+ LONG rv;
+ unsigned char buf[MAX_RESPONSE_LENGTH];
+ uint32_t len = MAX_RESPONSE_LENGTH;
+ VCardAppletPrivate *applet_private = NULL;
+ SCARD_IO_REQUEST receive_io;
+
+ applet_private = vcard_get_current_applet_private(card, 0);
+ if (applet_private == NULL) {
+ *response = vcard_make_response(VCARD7816_STATUS_EXC_ERROR);
+ return VCARD_DONE;
+ }
+
+ rv = SCardTransmit(applet_private->hCard, applet_private->send_io,
+ apdu->a_data, apdu->a_len, &receive_io, buf, &len);
+ if (rv != SCARD_S_SUCCESS) {
+ *response = vcard_make_response(VCARD7816_STATUS_EXC_ERROR);
+ return VCARD_DONE;
+ }
+
+ *response = vcard_response_new_data(buf,len);
+ if (*response == NULL) {
+ *response =
+ vcard_make_response(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
+ } else {
+ (*response)->b_total_len = (*response)->b_len;
+ }
+ return VCARD_DONE;
+}
+
+static void
+passthru_card_set_atr(VCard *card, unsigned char *atr, int atr_len)
+{
+ VCardAppletPrivate *applet_private = NULL;
+ applet_private = vcard_get_current_applet_private(card, 0);
+ if (applet_private == NULL) {
+ return;
+ }
+ applet_private->atr_len = MIN(atr_len, sizeof(applet_private->atr));
+ memcpy(applet_private->atr, atr, applet_private->atr_len);
+}
+
+static void passthru_card_get_atr(VCard *card, unsigned char *atr, int
*atr_len)
+{
+ VCardAppletPrivate *applet_private = NULL;
+ SCARD_READERSTATE *state;
+
+ applet_private = vcard_get_current_applet_private(card, 0);
+ if ((applet_private == NULL) || (applet_private->atr_len == 0)) {
+ vcard_emul_get_atr(card, atr, atr_len);
+ return;
+ }
+ *atr_len = MIN(applet_private->atr_len, *atr_len);
+ memcpy(atr,applet_private->atr,*atr_len);
+ return;
+}
+
+/*
+ * resest the inter call state between applet selects
+ */
+static VCardStatus
+passthru_reset(VCard *card, int channel)
+{
+ return VCARD_DONE;
+}
+
+static VCardStatus
+passthru_pcsc_lite_init()
+{
+ LONG rv;
+ if (global_context != 0) {
+ return VCARD_DONE;
+ }
+ rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL,
&global_context);
+ if (rv != SCARD_S_SUCCESS) {
+ return VCARD_FAIL;
+ }
+ return VCARD_DONE;
+}
+
+/*
+ * match if s1 is completely contained in s2
+ */
+static int
+string_match(const char *s1, const char *s2)
+{
+ int len = strlen(s1);
+ const char *start;
+
+ for (start = strchr(s2, *s1); start; start = strchr(start+1, *s1)) {
+ if (strncmp(start, s1, len) == 0) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+/*
+ * Look for the reader that best matches the name for VReader
+ */
+static char *
+passthru_get_reader_name(VReader *reader)
+{
+ const char *reader_name = vreader_get_name(reader);
+ char *reader_list = NULL;
+ char *reader_entry = NULL;
+ char *reader_match = NULL;
+ uint32_t reader_string_length;
+ VCardStatus status;
+ LONG rv;
+
+ if (reader_name == NULL) {
+ return NULL;
+ }
+
+ status = passthru_pcsc_lite_init();
+ if (status != VCARD_DONE) {
+ return NULL;
+ }
+
+
+ /* find the existing reader names */
+ rv = SCardListReaders(global_context, NULL, NULL, &reader_string_length);
+ if (rv != SCARD_S_SUCCESS) {
+ return NULL;
+ }
+ reader_list = (char *)malloc(reader_string_length);
+ rv = SCardListReaders(global_context, NULL, reader_list,
+ &reader_string_length);
+ if (rv != SCARD_S_SUCCESS) {
+ goto cleanup;
+ }
+
+ /* match that name */
+ for (reader_entry= reader_list;*reader_entry;
+ reader_entry += strlen(reader_entry)+1) {
+ if (string_match(reader_entry, reader_name)) {
+ reader_match = strdup(reader_entry);
+ break;
+ }
+ }
+cleanup:
+ if (reader_list) {
+ free(reader_list);
+ }
+ return reader_match;
+}
+
+
+/*
+ * utilities for creating and destroying the private applet data
+ */
+static void
+passthru_delete_applet_private(VCardAppletPrivate *applet_private)
+{
+ if (applet_private == NULL) {
+ return;
+ }
+ if (applet_private->hCard) {
+ SCardDisconnect(applet_private->hCard,SCARD_LEAVE_CARD);
+ }
+ if (applet_private->reader_name != NULL) {
+ free(applet_private->reader_name);
+ }
+ free(applet_private);
+}
+
+static VCardAppletPrivate *
+passthru_new_applet_private(VReader *reader)
+{
+ VCardAppletPrivate *applet_private = NULL;
+ LONG rv;
+
+ applet_private = (VCardAppletPrivate *)malloc(sizeof(VCardAppletPrivate));
+
+ if (applet_private == NULL) {
+ goto fail;
+ }
+ applet_private->hCard = 0;
+ applet_private->reader_name = NULL;
+
+ applet_private->reader_name = passthru_get_reader_name(reader);
+ if (applet_private->reader_name == NULL) {
+ goto fail;
+ }
+
+ rv = SCardConnect( global_context, applet_private->reader_name,
+ SCARD_SHARE_DIRECT, SCARD_PROTOCOL_T0|SCARD_PROTOCOL_T1,
+ &applet_private->hCard,
+ &applet_private->hProtocol);
+ if (rv != SCARD_S_SUCCESS) {
+ goto fail;
+ }
+
+ if (applet_private->hProtocol == SCARD_PROTOCOL_T0) {
+ applet_private->send_io = SCARD_PCI_T0;
+ } else {
+ applet_private->send_io = SCARD_PCI_T1;
+ }
+ applet_private->atr_len = 0;
+ return applet_private;
+
+fail:
+ if (applet_private) {
+ passthru_delete_applet_private(applet_private);
+ }
+ return NULL;
+}
+
+
+/*
+ * create a new applet which links to our override function.
+ */
+static VCardApplet *
+passthru_new_applet(VReader *reader)
+{
+ VCardAppletPrivate *applet_private = NULL;
+ VCardApplet *applet = NULL;
+ unsigned char passthru_aid[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
+ int passthru_aid_len = sizeof (passthru_aid);
+
+ applet_private = passthru_new_applet_private(reader);
+ if (applet_private == NULL) {
+ goto failure;
+ }
+ applet = vcard_new_applet(passthru_process_apdu, passthru_reset,
+ passthru_aid, passthru_aid_len);
+ if (applet == NULL) {
+ goto failure;
+ }
+ vcard_set_applet_private(applet, applet_private,
+ passthru_delete_applet_private);
+ applet_private = NULL;
+
+ return applet;
+
+failure:
+ if (applet_private != NULL) {
+ passthru_delete_applet_private(applet_private);
+ }
+ return NULL;
+}
+
+
+
+/*
+ * Initialize the card. This is the only 'card type emulator' portion of this
+ * the rest are connected through function pointers.
+ */
+VCardStatus
+passthru_card_init(VReader *vreader, VCard *card,
+ const char *flags,
+ unsigned char * const *cert,
+ int cert_len[],
+ VCardKey *key[] /* adopt the keys*/,
+ int cert_count)
+{
+ int i;
+ VCardApplet *applet;
+
+ /* Don't do soft emulation of the 7816, pass everything to the card */
+ vcard_set_type(card,VCARD_DIRECT);
+
+ applet = passthru_new_applet(vreader);
+ if (applet == NULL) {
+ goto failure;
+ }
+
+ vcard_add_applet(card, applet);
+
+ /* we are adopting the keys, so free them now (since we don't use them) */
+ for (i=0; i < cert_count; i++) {
+ vcard_emul_delete_key(key[i]);
+ }
+
+ return VCARD_DONE;
+
+failure:
+ return VCARD_FAIL;
+}
+
+/*
+ * Begin passthru_emul code. This emulator only works with the passthru card
+ * type.
+ *
+ */
+
+/*
+ * Get the state entry that matches this reader. If none found, return NULL
+ */
+static SCARD_READERSTATE_A *
+passthru_get_reader_state(SCARD_READERSTATE_A *reader_states,
+ int reader_count, char *name)
+{
+ int i;
+
+ for (i=0; i < reader_count; i++) {
+ if (name == NULL && reader_states[i].szReader == NULL) {
+ // looking for a blank slot to return
+ return &reader_states[i];
+ }
+ if (name == NULL || reader_states[i].szReader == NULL) {
+ continue;
+ }
+ if (strcmp(name, reader_states[i].szReader) == 0) {
+ return &reader_states[i];
+ }
+ }
+ return NULL;
+}
+
+/*
+ * find a card slot that has been cleared out
+ */
+static SCARD_READERSTATE_A *
+passthru_get_blank_reader(SCARD_READERSTATE_A *reader_states, int reader_count)
+{
+ return passthru_get_reader_state(reader_states, reader_count, NULL);
+}
+
+
+/*
+ * This is the main work of the emulator, handling the thread that looks for
+ * changes in the readers and the cards.
+ */
+static void *
+passthru_emul_event_thread(void *args)
+{
+ char *reader_list = NULL;
+ int reader_list_len = 0;
+ SCARD_READERSTATE_A *reader_states = NULL;
+ int reader_count = 0; /* number of active readers */
+ int max_reader_count = 0; /* size of the reader_state array (including
+ inactive readers) */
+ LONG rv;
+ int timeout=1000;
+ int i;
+
+ do {
+ /* variables to hold on to our new values until we are ready to replace
+ * our old values */
+ char *new_reader_list = NULL;
+ int new_reader_list_len = 0;
+ int new_reader_count = 0;
+
+ /* other temps */
+ char * reader_entry;
+ VReader *reader;
+
+ /*
+ * First check to see if the reader list has changed
+ */
+ rv = SCardListReaders(global_context, NULL, NULL,
&new_reader_list_len);
+ if (rv != SCARD_S_SUCCESS) {
+ goto next;
+ }
+ /*
+ * If the names have changed, we need to update our list and states.
+ * This is where we detect reader insertions and removals.
+ */
+ if (new_reader_list_len != reader_list_len) {
+ /* update the list */
+ new_reader_list = (char *)malloc(new_reader_list_len);
+ if (new_reader_list == NULL) {
+ goto next;
+ }
+ rv = SCardListReaders(global_context, NULL, new_reader_list,
+ &new_reader_list_len);
+ if (rv != SCARD_S_SUCCESS) {
+ free(new_reader_list);
+ goto next;
+ }
+ /* clear out our event state */
+ for (i=0; i < reader_count; i++) {
+ reader_states[i].dwEventState = 0;
+ }
+ /* count the readers and mark the ones that are still with us */
+ for (reader_entry = new_reader_list; *reader_entry;
+ reader_entry += strlen(reader_entry)+1) {
+ SCARD_READERSTATE_A *this_state;
+ new_reader_count++;
+ /* if the reader is still on the list, mark it present */
+ this_state = passthru_get_reader_state(reader_states,
+ reader_count,
+ reader_entry);
+ if (this_state) {
+ this_state->dwEventState = SCARD_STATE_PRESENT;
+ }
+ }
+ /* eject any removed readers */
+ for (i=0; i < reader_count; i++) {
+ if (reader_states[i].dwEventState == SCARD_STATE_PRESENT) {
+ reader_states[i].dwEventState = 0;
+ continue;
+ }
+ reader = vreader_get_reader_by_name(reader_states[i].szReader);
+ vreader_remove_reader(reader);
+ vreader_free(reader);
+ reader_states[i].szReader = NULL;
+ }
+ /* handle the shrinking list */
+ if (new_reader_count < reader_count) {
+ /* fold all the valid entries at the end of our reader_states
+ * array up into those locations vacated by ejected readers. */
+ for (i=reader_count-1; i < (new_reader_count -1); i--) {
+ if (reader_states[i].szReader) {
+ SCARD_READERSTATE_A *blank_reader;
+ blank_reader =
+ passthru_get_blank_reader(reader_states,
+ new_reader_count);
+ assert(blank_reader);
+ *blank_reader = reader_states[i];
+ reader_states[i].szReader = NULL;
+ }
+ }
+ }
+ /* handle the growing list */
+ if (new_reader_count > max_reader_count) {
+ SCARD_READERSTATE_A *new_reader_states;
+
+ /* grow the list */
+ new_reader_states =
+ (SCARD_READERSTATE_A *)realloc(reader_states,
+ sizeof(SCARD_READERSTATE_A)*new_reader_count);
+ if (new_reader_states) {
+ /* successful, update our current state */
+ reader_states = new_reader_states;
+ max_reader_count = new_reader_count;
+ } else {
+ new_reader_count = max_reader_count; /* couldn't get enough
+ * space to handle
+ * all the new readers
+ * */
+ }
+ /* mark our new entries as empty */
+ for (i=reader_count; i > new_reader_count; i++) {
+ reader_states[i].szReader = NULL;
+ }
+ }
+ /* now walk the reader list, updating the state */
+ for (reader_entry = new_reader_list; *reader_entry;
+ reader_entry += strlen(reader_entry)+1) {
+ SCARD_READERSTATE_A *this_state;
+ this_state = passthru_get_reader_state(reader_states,
+ new_reader_count,
+ reader_entry);
+ if (this_state) {
+ /* replace the old copy of the string with the new copy.
+ * This will allow us to free reader_list at the end */
+ reader_states->szReader = reader_entry;
+ continue;
+ }
+ /* this is a new reader, add it to the list */
+ this_state =
+ passthru_get_blank_reader(reader_states, new_reader_count);
+ if (!this_state) {
+ continue; /* this can happen of we couldn't get enough
+ slots in the grow list */
+ }
+ this_state->szReader = reader_entry;
+ this_state->dwCurrentState = SCARD_STATE_UNAWARE;
+ reader = vreader_new(reader_entry, NULL, NULL);
+ if (reader) {
+ vreader_add_reader(reader);
+ }
+ vreader_free(reader);
+ }
+ /* finally update our current variables */
+ free(reader_list);
+ reader_list = new_reader_list;
+ reader_list_len = new_reader_list_len;
+ reader_count = new_reader_count;
+ }
+next:
+ rv = SCardGetStatusChange(global_context, timeout,
+ reader_states, reader_count);
+ if (rv == SCARD_E_TIMEOUT) {
+ continue; /* check for new readers */
+ }
+ if (rv != SCARD_S_SUCCESS) {
+ static int restarts = 0;
+ VCardStatus status;
+
+ /* try resetting the pcsc_lite subsystem */
+ SCardReleaseContext(global_context);
+ global_context = 0; /* should close it */
+ printf("***** SCard failure %x\n", rv);
+ restarts++;
+ if (restarts >= 3) {
+ printf("***** SCard failed %d times\n", restarts);
+ return; /* exit thread */
+ }
+ status = passthru_pcsc_lite_init();
+ assert(status == CARD_DONE);
+ sleep(1);
+ continue;
+ }
+ /* deal with card insertion/removal */
+ for (i=0; i < reader_count ; i++) {
+ if ((reader_states[i].dwEventState & SCARD_STATE_CHANGED) == 0) {
+ continue;
+ }
+ reader_states[i].dwCurrentState = reader_states[i].dwEventState;
+ reader = vreader_get_reader_by_name(reader_states[i].szReader);
+ if (reader == NULL) {
+ continue;
+ }
+ if (reader_states[i].dwEventState & SCARD_STATE_EMPTY) {
+ if (vreader_card_is_present(reader) == VREADER_OK) {
+ vreader_insert_card(reader, NULL);
+ }
+ }
+ if (reader_states[i].dwEventState & SCARD_STATE_PRESENT) {
+ VCard *card;
+ VCardStatus status = VCARD_FAIL;
+ /* if there already was a card present, eject it before we
+ * insert the new one */
+ if (vreader_card_is_present(reader) == VREADER_OK) {
+ vreader_insert_card(reader, NULL);
+ }
+
+ card = vcard_new(NULL, NULL);
+ if (card != NULL) {
+ status = passthru_card_init(reader, card, "",
+ NULL, NULL, NULL, 0);
+ passthru_card_set_atr(card, reader_states[i].rgbAtr,
+ reader_states[i].cbAtr);
+ vcard_set_atr_func(card, passthru_card_get_atr);
+ }
+ if (status == VCARD_DONE) {
+ vreader_insert_card(reader, card);
+ }
+ vcard_free(card);
+ }
+ vreader_free(reader);
+ }
+
+ } while (1);
+ return NULL;
+}
+
+/*
+ * Initializing the passthru emul is simply initializing pcsc-lite and
+ * launching the event thread.
+ */
+VCardStatus
+passthru_emul_init(VCardEmulOptions *options)
+{
+ thread_t tid;
+ thread_status_t tstatus;
+ VCardStatus status;
+
+ vreader_init();
+ vevent_queue_init();
+
+ status = passthru_pcsc_lite_init();
+ if (status != VCARD_DONE) {
+ return status;
+ }
+
+ /* launch reader thread */
+ tstatus = THREAD_CREATE(tid, passthru_emul_event_thread, NULL);
+ if (!THREAD_SUCCESS(tstatus)) {
+ return VCARD_FAIL;
+ }
+ return VCARD_DONE;
+}
+
+
+VCardEmulOptions *
+passthru_emul_options(const char *args)
+{
+ return NULL;
+}
+#endif
diff --git a/libcacard/passthru.h b/libcacard/passthru.h
new file mode 100644
index 0000000..3589d12
--- /dev/null
+++ b/libcacard/passthru.h
@@ -0,0 +1,50 @@
+/*
+ * passthru card type emulator and passhtru emulator.
+ *
+ * passhtru card type emulator can be used with other low level card emulators,
+ * as long as they can recognize card insertion and removals.
+ *
+ * the passthru vcard_emulator, can only use passthru card types.
+ *
+ * Be careful using passthru. 1) passthru does not know the locking state of
+ * the card from the guest side, and thus does not try to get locks. This means
+ * client access can interfere with the guest use of the card. 2) passthru does
+ * not provide the guest and client unique login states for the card. That
+ * means that it is possible for the guest to access private data on the
+ * card without authenticating. You have been warned.
+ *
+ * Passthru is most useful in the following cases: 1) provisioning. Card type
+ * emulators cannot emulate the open platform secure connections because the
+ * client software does not have access to the global platform keys on the
+ * card. Passthru drives these apdu's directly to the card. 2) odd cards. If
+ * you have guest software the knows how to access the card, but no client
+ * side PKCS #11 module, then passthru can provide access to those cards.
+ */
+
+#ifndef PASSTHRU_H
+#define PASSTHRU_H 1
+
+#include "vcard.h"
+#include "vcard_emul.h"
+#include "vreader.h"
+
+/*
+ * Initialize the card. This is the only 'card type emulator' portion of this
+ * the rest are connected through function pointers. NOTE: certs are ignored,
+ * keys are freed.
+ */
+VCardStatus passthru_card_init(VReader *vreader, VCard *card,
+ const char *flags, unsigned char * const *cert, int cert_len[],
+ VCardKey *key[], int cert_count);
+
+/*
+ * Use this instead of vcard_emul_init to initialize passthru.
+ * passthru is the exception to the rule that only one emul can be compiled
+ * at once. NOTE: you can still have only one emul active at once. The
+ * passhtru card type emul, however can be used with other emuls.
+ *
+ * passthru does not support other card type emuls.
+ */
+VCardStatus passthru_emul_init(VCardEmulOptions *options);
+VCardEmulOptions *passthru_emul_options(const char *args);
+#endif
diff --git a/libcacard/vcard.c b/libcacard/vcard.c
new file mode 100644
index 0000000..9672001
--- /dev/null
+++ b/libcacard/vcard.c
@@ -0,0 +1,350 @@
+/*
+ * implement the Java card standard.
+ *
+ */
+#include <stdlib.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 = (VCardBufferResponse *)malloc(sizeof(VCardBufferResponse));
+ if (new_buffer == NULL) {
+ return NULL;
+ }
+ new_buffer->buffer = (unsigned char *)malloc(size);
+ if (new_buffer->buffer == NULL) {
+ free(new_buffer);
+ return NULL;
+ }
+ memcpy(new_buffer->buffer, 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;
+ }
+ if (buffer_response->buffer) {
+ free(buffer_response->buffer);
+ }
+ 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 = (VCardApplet *)malloc(sizeof(VCardApplet));
+ if (applet == NULL) {
+ return NULL;
+ }
+ applet->next = NULL;
+ applet->applet_private = NULL;
+ applet->applet_private_free = NULL;
+ applet->process_apdu = applet_process_function;
+ applet->reset_applet = applet_reset_function;
+
+ applet->aid = malloc(aid_len);
+ if (applet->aid == NULL) {
+ free(applet);
+ return NULL;
+ }
+ memcpy(applet->aid, 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);
+ applet->applet_private = NULL;
+ }
+ if (applet->aid) {
+ free(applet->aid);
+ applet->aid = NULL;
+ }
+ 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;
+ int i;
+
+ new_card = (VCard *)malloc(sizeof(VCard));
+ new_card->applet_list = NULL;
+ for (i=0; i < MAX_CHANNEL; i++) {
+ new_card->current_applet[i] = NULL;
+ }
+ new_card->vcard_buffer_response = NULL;
+ new_card->type = VCARD_VM;
+ new_card->vcard_private = private;
+ new_card->vcard_private_free = private_free;
+ new_card->vcard_get_atr = NULL;
+ 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 = NULL;
+ VCardApplet *next_applet = NULL;
+
+ 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);
+ vcard->vcard_private_free = 0;
+ vcard->vcard_private = 0;
+ }
+ 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);
+ free(vcard);
+ return;
+}
+
+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);
+ 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;
+}
+
diff --git a/libcacard/vcard.h b/libcacard/vcard.h
new file mode 100644
index 0000000..8dbd761
--- /dev/null
+++ b/libcacard/vcard.h
@@ -0,0 +1,85 @@
+/*
+ *
+ */
+#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
diff --git a/libcacard/vcard_emul.h b/libcacard/vcard_emul.h
new file mode 100644
index 0000000..05fb57e
--- /dev/null
+++ b/libcacard/vcard_emul.h
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+#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);
+
+/*
+ * 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
diff --git a/libcacard/vcard_emul_nss.c b/libcacard/vcard_emul_nss.c
new file mode 100644
index 0000000..6887cf6
--- /dev/null
+++ b/libcacard/vcard_emul_nss.c
@@ -0,0 +1,1192 @@
+/*
+ * 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.
+ */
+#include "vcard.h"
+#include "card_7816t.h"
+#include "vcard_emul.h"
+#include "vreader.h"
+#include "vevent.h"
+
+/*
+ * NSS headers
+ */
+#include <nss.h>
+#include <pk11pub.h>
+#include <cert.h>
+#include <key.h>
+#include <secmod.h>
+#include <prthread.h>
+#include <secerr.h>
+
+/*
+ * system headers
+ */
+#include <stdlib.h>
+#include <string.h>
+
+struct VCardKeyStruct {
+ CERTCertificate *cert;
+ PK11SlotInfo *slot;
+ SECKEYPrivateKey *key;
+};
+
+
+typedef struct VirtualReaderOptionsStruct VirtualReaderOptions;
+
+struct VReaderEmulStruct {
+ PK11SlotInfo *slot;
+ VCardEmulType default_type;
+ char *type_params;
+ PRBool present;
+ int series;
+ VCard *saved_vcard;
+};
+
+/*
+ * NSS Specific options
+ */
+struct VirtualReaderOptionsStruct {
+ char *name;
+ char *vname;
+ VCardEmulType card_type;
+ char *type_params;
+ char **cert_name;
+ int cert_count;
+};
+
+struct VCardEmulOptionsStruct {
+ void *nss_db;
+ VirtualReaderOptions *vreader;
+ int vreader_count;
+ VCardEmulType hw_card_type;
+ const char *hw_type_params;
+ PRBool use_hw;
+};
+
+static int nss_emul_init = 0;
+
+/* if we have more that just the slot, define
+ * VCardEmulStruct here */
+
+/*
+ * allocate the set of arrays for certs, cert_len, key
+ */
+static PRBool
+vcard_emul_alloc_arrays(unsigned char ***certsp, int **cert_lenp,
+ VCardKey ***keysp, int cert_count)
+{
+ *certsp = NULL;
+ *cert_lenp = NULL;
+ *keysp = NULL;
+ *certsp = (unsigned char **)malloc(sizeof(unsigned char *)*cert_count);
+ if (*certsp == NULL) {
+ return PR_FALSE;
+ }
+ *cert_lenp = (int *)malloc(sizeof(int)*cert_count);
+ if (*cert_lenp == NULL) {
+ free(*certsp);
+ *certsp = NULL;
+ return PR_FALSE;
+ }
+ *keysp = (VCardKey **)malloc(sizeof(VCardKey *)*cert_count);
+ if (*keysp != NULL) {
+ return PR_TRUE;
+ }
+ free(*cert_lenp);
+ free(*certsp);
+ *cert_lenp = NULL;
+ *certsp = NULL;
+ return PR_FALSE;
+}
+
+/*
+ * Emulator specific card information
+ */
+typedef struct CardEmulCardStruct CardEmulPrivate;
+
+static VCardEmul *
+vcard_emul_new_card(PK11SlotInfo *slot)
+{
+ PK11_ReferenceSlot(slot);
+ /* currently we don't need anything other than the slot */
+ return (VCardEmul *)slot;
+}
+
+static void
+vcard_emul_delete_card(VCardEmul *vcard_emul)
+{
+ PK11SlotInfo *slot = (PK11SlotInfo *)vcard_emul;
+ if (slot == NULL) {
+ return;
+ }
+ PK11_FreeSlot(slot);
+}
+
+static PK11SlotInfo *
+vcard_emul_card_get_slot(VCard *card)
+{
+ /* note, the card is holding the reference, no need to get another one */
+ return (PK11SlotInfo *)vcard_get_private(card);
+}
+
+
+/*
+ * key functions
+ */
+/* private constructure */
+static VCardKey *
+vcard_emul_make_key(PK11SlotInfo *slot, CERTCertificate *cert)
+{
+ VCardKey * key;
+
+ key = (VCardKey *)malloc(sizeof(VCardKey));
+ if (key == NULL) {
+ return NULL;
+ }
+ key->slot = PK11_ReferenceSlot(slot);
+ key->cert = CERT_DupCertificate(cert);
+ /* NOTE: if we aren't logged into the token, this could return NULL */
+ /* NOTE: the cert is a temp cert, not necessarily the cert in the token,
+ * use the DER version of this function */
+ key->key = PK11_FindKeyByDERCert(slot,cert, NULL);
+ return key;
+}
+
+/* destructor */
+void
+vcard_emul_delete_key(VCardKey *key)
+{
+ if (!nss_emul_init || (key == NULL)) {
+ return;
+ }
+ if (key->key) {
+ SECKEY_DestroyPrivateKey(key->key);
+ key->key = NULL;
+ }
+ if (key->cert) {
+ CERT_DestroyCertificate(key->cert);
+ }
+ if (key->slot) {
+ PK11_FreeSlot(key->slot);
+ }
+ return;
+}
+
+/*
+ * grab the nss key from a VCardKey. If it doesn't exist, try to look it up
+ */
+static SECKEYPrivateKey *
+vcard_emul_get_nss_key(VCardKey *key)
+{
+ if (key->key) {
+ return key->key;
+ }
+ /* NOTE: if we aren't logged into the token, this could return NULL */
+ key->key = PK11_FindPrivateKeyFromCert(key->slot,key->cert, NULL);
+ return key->key;
+}
+
+/*
+ * Map NSS errors to 7816 errors
+ */
+static vcard_7816_status_t
+vcard_emul_map_error(int error)
+{
+ switch (error) {
+ case SEC_ERROR_TOKEN_NOT_LOGGED_IN:
+ return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED;
+ case SEC_ERROR_BAD_DATA:
+ case SEC_ERROR_OUTPUT_LEN:
+ case SEC_ERROR_INPUT_LEN:
+ case SEC_ERROR_INVALID_ARGS:
+ case SEC_ERROR_INVALID_ALGORITHM:
+ case SEC_ERROR_NO_KEY:
+ case SEC_ERROR_INVALID_KEY:
+ case SEC_ERROR_DECRYPTION_DISALLOWED:
+ return VCARD7816_STATUS_ERROR_DATA_INVALID;
+ case SEC_ERROR_NO_MEMORY:
+ return VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE;
+ }
+ return VCARD7816_STATUS_EXC_ERROR_CHANGE;
+}
+
+/* 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)
+{
+ SECKEYPrivateKey *priv_key;
+ unsigned signature_len;
+ SECStatus rv;
+
+ if ((!nss_emul_init) || (key == NULL)) {
+ /* couldn't get the key, indicate that we aren't logged in */
+ return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED;
+ }
+ priv_key = vcard_emul_get_nss_key(key);
+
+ /*
+ * this is only true of the rsa signature
+ */
+ signature_len = PK11_SignatureLen(priv_key);
+ if (buffer_size != signature_len) {
+ return VCARD7816_STATUS_ERROR_DATA_INVALID;
+ }
+ rv = PK11_PrivDecryptRaw(priv_key, buffer, &signature_len, signature_len,
+ buffer, buffer_size);
+ if (rv != SECSuccess) {
+ return vcard_emul_map_error(PORT_GetError());
+ }
+ ASSERT(buffer_size == signature_len);
+ return VCARD7816_STATUS_SUCCESS;
+}
+
+/*
+ * 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)
+{
+ return -1;
+}
+
+/* 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)
+{
+ PK11SlotInfo *slot;
+ unsigned char *pin_string = NULL;
+ int i;
+ SECStatus rv;
+
+ if (!nss_emul_init) {
+ return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED;
+ }
+ slot = vcard_emul_card_get_slot(card);
+ /* We depend on the PKCS #11 module internal login state here because we
+ * create a separate process to handle each guest instance. If we needed
+ * to handle multiple guests from one process, then we would need to keep
+ * a lot of extra state in our card structure
+ * */
+ pin_string = malloc(pin_len+1);
+ if (pin_string == NULL) {
+ return VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE;
+ }
+ memcpy(pin_string,pin,pin_len);
+ pin_string[pin_len] = 0;
+
+ /* handle CAC expanded pins correctly */
+ for (i = pin_len-1; i >= 0 && (pin_string[i] == 0xff); i--) {
+ pin_string[i] = 0;
+ }
+
+ rv = PK11_Authenticate(slot, PR_FALSE, pin_string);
+ memset(pin_string, 0, pin_len); /* don't let the pin hang around in memory
+ to be snooped */
+ free(pin_string);
+ if (rv == SECSuccess) {
+ return VCARD7816_STATUS_SUCCESS;
+ }
+ /* map the error from port get error */
+ return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED;
+}
+
+void
+vcard_emul_reset(VCard *card, VCardPower power)
+{
+ PK11SlotInfo *slot;
+
+ if (!nss_emul_init) {
+ return;
+ }
+
+ /* if we reset the card (either power on or power off), we loose our login
+ * state */
+ /* TODO: we may also need to send insertion/removal events? */
+ slot = vcard_emul_card_get_slot(card);
+ (void)PK11_Logout(slot);
+ return;
+}
+
+
+static VReader *
+vcard_emul_find_vreader_from_slot(PK11SlotInfo *slot)
+{
+ VReaderList *reader_list = vreader_get_reader_list();
+ VReaderListEntry *current_entry = NULL;
+
+ if (reader_list == NULL) {
+ return NULL;
+ }
+ for (current_entry= vreader_list_get_first(reader_list); current_entry;
+ current_entry=vreader_list_get_next(current_entry)) {
+ VReader *reader = vreader_list_get_reader(current_entry);
+ VReaderEmul *reader_emul = vreader_get_private(reader);
+ if (reader_emul->slot == slot) {
+ return reader;
+ }
+ vreader_free(reader);
+ }
+
+ return NULL;
+}
+
+/*
+ * create a new reader emul
+ */
+static VReaderEmul *
+vreader_emul_new(PK11SlotInfo *slot, VCardEmulType type, const char *params)
+{
+ VReaderEmul *new_reader_emul;
+
+ new_reader_emul = (VReaderEmul *)malloc(sizeof(VReaderEmul));
+ if (new_reader_emul == NULL) {
+ return NULL;
+ }
+
+ new_reader_emul->slot = PK11_ReferenceSlot(slot);
+ new_reader_emul->default_type = type;
+ new_reader_emul->type_params = strdup(params);
+ new_reader_emul->present = PR_FALSE;
+ new_reader_emul->series = 0;
+ new_reader_emul->saved_vcard = NULL;
+ return new_reader_emul;
+}
+
+static void
+vreader_emul_delete(VReaderEmul *vreader_emul)
+{
+ if (vreader_emul == NULL) {
+ return;
+ }
+ if (vreader_emul->slot) {
+ PK11_FreeSlot(vreader_emul->slot);
+ }
+ if (vreader_emul->type_params) {
+ free(vreader_emul->type_params);
+ }
+ free(vreader_emul);
+}
+
+/*
+ * TODO: move this to emulater non-specific file
+ */
+static VCardEmulType
+vcard_emul_get_type(VReader *vreader)
+{
+ VReaderEmul *vreader_emul;
+
+ vreader_emul = vreader_get_private(vreader);
+ if (vreader_emul && vreader_emul->default_type != VCARD_EMUL_NONE) {
+ return vreader_emul->default_type;
+ }
+
+ return vcard_emul_type_select(vreader);
+}
+/*
+ * TODO: move this to emulater non-specific file
+ */
+static const char *
+vcard_emul_get_type_params(VReader *vreader)
+{
+ VReaderEmul *vreader_emul;
+
+ vreader_emul = vreader_get_private(vreader);
+ if (vreader_emul && vreader_emul->type_params) {
+ return vreader_emul->type_params;
+ }
+
+ return "";
+}
+
+/* pull the slot out of the reader private data */
+static PK11SlotInfo *
+vcard_emul_reader_get_slot(VReader *vreader)
+{
+ VReaderEmul *vreader_emul = vreader_get_private(vreader);
+ if (vreader_emul == NULL) {
+ return NULL;
+ }
+ return vreader_emul->slot;
+}
+
+/*
+ * Card ATR's map to physical cards. VCARD_ATR_PREFIX will set appropriate
+ * historical bytes for any software emulated card. The remaining bytes can be
+ * used to indicate the actual emulator
+ */
+static const unsigned char nss_atr[] = { VCARD_ATR_PREFIX(3), 'N', 'S', 'S' };
+
+void
+vcard_emul_get_atr(VCard *card, unsigned char *atr, int *atr_len)
+{
+ int len = MIN(sizeof(nss_atr), *atr_len);
+ ASSERT(atr != NULL);
+
+ memcpy(atr, nss_atr, len);
+ *atr_len = len;
+ return;
+}
+
+/*
+ * create a new card from certs and keys
+ */
+static VCard *
+vcard_emul_make_card(VReader *reader,
+ unsigned char * const *certs, int *cert_len,
+ VCardKey *keys[], int cert_count)
+{
+ VCardEmul *vcard_emul;
+ VCard *vcard;
+ PK11SlotInfo *slot;
+ VCardEmulType type;
+ const char *params;
+
+ type = vcard_emul_get_type(reader);
+
+ /* ignore the inserted card */
+ if (type == VCARD_EMUL_NONE) {
+ return NULL;
+ }
+ slot = vcard_emul_reader_get_slot(reader);
+ if (slot == NULL) {
+ return NULL;
+ }
+
+ params = vcard_emul_get_type_params(reader);
+ /* params these can be NULL */
+
+ vcard_emul = vcard_emul_new_card(slot);
+ if (vcard_emul == NULL) {
+ return NULL;
+ }
+ vcard = vcard_new(vcard_emul, vcard_emul_delete_card);
+ if (vcard == NULL) {
+ vcard_emul_delete_card(vcard_emul);
+ return NULL;
+ }
+ vcard_init(reader, vcard, type, params, certs, cert_len, keys, cert_count);
+ return vcard;
+}
+
+
+/*
+ * 'clone' a physical card as a virtual card
+ */
+static VCard *
+vcard_emul_mirror_card(VReader *vreader)
+{
+ /*
+ * lookup certs using the C_FindObjects. The Stan Cert handle won't give
+ * us the real certs until we log in.
+ */
+ PK11GenericObject *firstObj, *thisObj;
+ int cert_count;
+ unsigned char **certs;
+ int *cert_len;
+ VCardKey **keys;
+ PK11SlotInfo *slot;
+ PRBool ret;
+
+ slot = vcard_emul_reader_get_slot(vreader);
+ if (slot == NULL) {
+ return NULL;
+ }
+
+ firstObj = PK11_FindGenericObjects(slot, CKO_CERTIFICATE);
+ if (firstObj == NULL) {
+ return NULL;
+ }
+
+ /* count the certs */
+ cert_count=0;
+ for (thisObj = firstObj; thisObj;
+ thisObj = PK11_GetNextGenericObject(thisObj)) {
+ cert_count++;
+ }
+
+ if (cert_count == 0) {
+ PK11_DestroyGenericObjects(firstObj);
+ return NULL;
+ }
+
+ /* allocate the arrays */
+ ret = vcard_emul_alloc_arrays(&certs,&cert_len, &keys, cert_count);
+ if (ret == PR_FALSE) {
+ return NULL;
+ }
+
+ /* fill in the arrays */
+ cert_count = 0;
+ for (thisObj = firstObj; thisObj;
+ thisObj = PK11_GetNextGenericObject(thisObj)) {
+ SECItem derCert;
+ CERTCertificate *cert;
+ SECStatus rv;
+
+ rv = PK11_ReadRawAttribute(PK11_TypeGeneric, thisObj,
+ CKA_VALUE, &derCert);
+ if (rv != SECSuccess) {
+ continue;
+ }
+ /* create floating temp cert. This gives us a cert structure even if
+ * the token isn't logged in */
+ cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &derCert,
+ NULL, PR_FALSE, PR_TRUE);
+ SECITEM_FreeItem(&derCert, PR_FALSE);
+ if (cert == NULL) {
+ continue;
+ }
+
+ certs[cert_count] = cert->derCert.data;
+ cert_len[cert_count] = cert->derCert.len;
+ keys[cert_count] = vcard_emul_make_key(slot, cert);
+ cert_count++;
+ CERT_DestroyCertificate(cert); /* key obj still has a reference */
+ }
+
+ /* now create the card */
+ return vcard_emul_make_card(vreader, certs, cert_len, keys, cert_count);
+}
+
+static VCardEmulType default_card_type = VCARD_EMUL_NONE;
+static const char *default_type_params = "";
+
+/*
+ * This thread looks for card and reader insertions and puts events on the
+ * event queue
+ */
+static void
+vcard_emul_event_thread(void *arg)
+{
+ PK11SlotInfo *slot;
+ VReader *vreader;
+ VReaderEmul *vreader_emul;
+ VCard *vcard;
+ SECMODModule *module = (SECMODModule *)arg;
+
+ do {
+ slot = SECMOD_WaitForAnyTokenEvent(module, 0, 500);
+ if (slot == NULL) {
+ break;
+ }
+ vreader = vcard_emul_find_vreader_from_slot(slot);
+ if (vreader == NULL) {
+ /* new vreader */
+ vreader_emul = vreader_emul_new(slot, default_card_type,
+ default_type_params);
+ vreader = vreader_new(PK11_GetSlotName(slot), vreader_emul,
+ vreader_emul_delete);
+ PK11_FreeSlot(slot);
+ slot = NULL;
+ vreader_add_reader(vreader);
+ vreader_free(vreader);
+ continue;
+ }
+ /* card remove/insert */
+ vreader_emul = vreader_get_private(vreader);
+ if (PK11_IsPresent(slot)) {
+ int series = PK11_GetSlotSeries(slot);
+ if (series != vreader_emul->series) {
+ if (vreader_emul->present) {
+ vreader_insert_card(vreader, NULL);
+ }
+ vcard = vcard_emul_mirror_card(vreader);
+ vreader_insert_card(vreader, vcard);
+ vcard_free(vcard);
+ }
+ vreader_emul->series = series;
+ vreader_emul->present = 1;
+ vreader_free(vreader);
+ PK11_FreeSlot(slot);
+ continue;
+ }
+ if (vreader_emul->present) {
+ vreader_insert_card(vreader, NULL);
+ }
+ vreader_emul->series = 0;
+ vreader_emul->present = 0;
+ PK11_FreeSlot(slot);
+ vreader_free(vreader);
+ } while(1);
+}
+
+/* if the card is inserted when we start up, make sure our state is correct */
+static void
+vcard_emul_init_series(VReader *vreader, VCard *vcard)
+{
+ VReaderEmul *vreader_emul = vreader_get_private(vreader);
+ PK11SlotInfo *slot = vreader_emul->slot;
+
+ vreader_emul->present = PK11_IsPresent(slot);
+ vreader_emul->series = PK11_GetSlotSeries(slot);
+ if (vreader_emul->present == 0) {
+ vreader_insert_card(vreader, NULL);
+ }
+}
+
+/*
+ * each module has a separate wait call, create a thread for each module that
+ * we are using.
+ */
+static void
+vcard_emul_new_event_thread(SECMODModule *module)
+{
+ PR_CreateThread(PR_SYSTEM_THREAD, vcard_emul_event_thread,
+ module, PR_PRIORITY_HIGH, PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD, 0);
+}
+
+static const VCardEmulOptions default_options = {
+ .nss_db = NULL,
+ .vreader = NULL,
+ .vreader_count = 0,
+ .hw_card_type = VCARD_EMUL_CAC,
+ .hw_type_params = "",
+ .use_hw = PR_TRUE
+};
+
+
+/*
+ * NSS needs the app to supply a password prompt. In our case the only time
+ * the password is supplied is as part of the Login APDU. The actual password
+ * is passed in the pw_arg in that case. In all other cases pw_arg should be
+ * NULL.
+ */
+static char *
+vcard_emul_get_password(PK11SlotInfo *slot, PRBool retries, void *pw_arg)
+{
+ /* if it didn't work the first time, don't keep trying */
+ if (retries) {
+ return NULL;
+ }
+ /* we are looking up a password when we don't have one in hand */
+ if (pw_arg == NULL) {
+ return NULL;
+ }
+ /* TODO: we really should verify that were are using the right slot */
+ return PORT_Strdup(pw_arg);
+}
+
+/* Force a card removal even if the card is not physically removed */
+VCardEmulError
+vcard_emul_force_card_remove(VReader *vreader)
+{
+ if (!nss_emul_init || (vreader_card_is_present(vreader) != VREADER_OK)) {
+ return VCARD_EMUL_FAIL; /* card is already removed */
+ }
+
+ /* OK, remove it */
+ vreader_insert_card(vreader, NULL);
+ return VCARD_EMUL_OK;
+}
+
+/* Re-insert of a card that has been removed by force removal */
+VCardEmulError
+vcard_emul_force_card_insert(VReader *vreader)
+{
+ VReaderEmul *vreader_emul;
+ VCard *vcard;
+
+ if (!nss_emul_init || (vreader_card_is_present(vreader) == VREADER_OK)) {
+ return VCARD_EMUL_FAIL; /* card is already removed */
+ }
+ vreader_emul = vreader_get_private(vreader);
+
+ /* if it's a softcard, get the saved vcard from the reader emul structure
*/
+ if (vreader_emul->saved_vcard) {
+ vcard = vcard_reference(vreader_emul->saved_vcard);
+ } else {
+ /* it must be a physical card, rebuild it */
+ if (!PK11_IsPresent(vreader_emul->slot)) {
+ /* physical card has been removed, not way to reinsert it */
+ return VCARD_EMUL_FAIL;
+ }
+ vcard = vcard_emul_mirror_card(vreader);
+ }
+ vreader_insert_card(vreader, vcard);
+ vcard_free(vcard);
+
+ return VCARD_EMUL_OK;
+}
+
+
+static PRBool
+module_has_removable_hw_slots(SECMODModule *mod)
+{
+ int i;
+ PRBool ret = PR_FALSE;
+ SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock();
+
+ if (!moduleLock) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return ret;
+ }
+ SECMOD_GetReadLock(moduleLock);
+ for (i=0; i < mod->slotCount; i++) {
+ PK11SlotInfo *slot = mod->slots[i];
+ if (PK11_IsRemovable(slot) && PK11_IsHW(slot)) {
+ ret = PR_TRUE;
+ break;
+ }
+ }
+ SECMOD_ReleaseReadLock(moduleLock);
+ return ret;
+}
+
+/* Previously we returned FAIL if no readers found. This makes
+ * no sense when using hardware, since there may be no readers connected
+ * at the time vcard_emul_init is called, but they will be properly
+ * recognized later. So Instead return FAIL only if no_hw==1 and no
+ * vcards can be created (indicates error with certificates provided
+ * or db), or if any other higher level error (NSS error, missing coolkey). */
+static int vcard_emul_init_called = 0;
+
+VCardEmulError
+vcard_emul_init(const VCardEmulOptions *options)
+{
+ SECStatus rv;
+ PRBool ret, has_readers=PR_FALSE, need_coolkey_module;
+ VReader *vreader;
+ VReaderEmul *vreader_emul;
+ SECMODListLock *module_lock;
+ SECMODModuleList *module_list;
+ SECMODModuleList *mlp;
+ int i;
+
+ if (vcard_emul_init_called) {
+ return VCARD_EMUL_INIT_ALREADY_INITED;
+ }
+ vcard_emul_init_called = 1;
+ vreader_init();
+ vevent_queue_init();
+
+ if (options == NULL) {
+ options = &default_options;
+ }
+
+ /* first initialize NSS */
+ if (options->nss_db) {
+ rv = NSS_Init(options->nss_db);
+ } else {
+ rv = NSS_Init("sql:/etc/pki/nssdb");
+ }
+ if (rv != SECSuccess) {
+ return VCARD_EMUL_FAIL;
+ }
+ /* Set password callback function */
+ PK11_SetPasswordFunc(vcard_emul_get_password);
+
+ /* set up soft cards emulated by software certs rather than physical cards
+ * */
+ for (i = 0; i < options->vreader_count; i++) {
+ int j;
+ int cert_count;
+ unsigned char **certs;
+ int *cert_len;
+ VCardKey **keys;
+ PK11SlotInfo *slot;
+
+ slot = PK11_FindSlotByName(options->vreader[i].name);
+ if (slot == NULL) {
+ continue;
+ }
+ vreader_emul = vreader_emul_new(slot, options->vreader[i].card_type,
+ options->vreader[i].type_params);
+ vreader = vreader_new(options->vreader[i].vname, vreader_emul,
+ vreader_emul_delete);
+ vreader_add_reader(vreader);
+ cert_count = options->vreader[i].cert_count;
+
+ ret = vcard_emul_alloc_arrays(&certs, &cert_len, &keys,
+ options->vreader[i].cert_count);
+ if (ret == PR_FALSE) {
+ continue;
+ }
+ cert_count = 0;
+ for (j=0; j < options->vreader[i].cert_count; j++) {
+ /* we should have a better way of identifying certs than by
+ * nickname here */
+ CERTCertificate *cert = PK11_FindCertFromNickname(
+ options->vreader[i].cert_name[j],
+ NULL);
+ if (cert == NULL) {
+ continue;
+ }
+ certs[cert_count] = cert->derCert.data;
+ cert_len[cert_count] = cert->derCert.len;
+ keys[cert_count] = vcard_emul_make_key(slot, cert);
+ /* this is safe because the key is still holding a cert reference
*/
+ CERT_DestroyCertificate(cert);
+ cert_count++;
+ }
+ if (cert_count) {
+ VCard *vcard = vcard_emul_make_card(vreader, certs, cert_len,
+ keys, cert_count);
+ vreader_insert_card(vreader, vcard);
+ vcard_emul_init_series(vreader, vcard);
+ /* allow insertion and removal of soft cards */
+ vreader_emul->saved_vcard = vcard_reference(vcard);
+ vcard_free(vcard);
+ vreader_free(vreader);
+ has_readers = PR_TRUE;
+ }
+ }
+
+ /* if we aren't suppose to use hw, skip looking up hardware tokens */
+ if (!options->use_hw) {
+ nss_emul_init = has_readers;
+ return has_readers ? VCARD_EMUL_OK : VCARD_EMUL_FAIL;
+ }
+
+ /* make sure we have some PKCS #11 module loaded */
+ module_lock = SECMOD_GetDefaultModuleListLock();
+ module_list = SECMOD_GetDefaultModuleList();
+ need_coolkey_module = !has_readers;
+ SECMOD_GetReadLock(module_lock);
+ for (mlp = module_list; mlp; mlp = mlp->next) {
+ SECMODModule * module = mlp->module;
+ if (module_has_removable_hw_slots(module)) {
+ need_coolkey_module = PR_FALSE;
+ break;
+ }
+ }
+ SECMOD_ReleaseReadLock(module_lock);
+
+ if (need_coolkey_module) {
+ SECMODModule *module;
+ module = SECMOD_LoadUserModule(
+ (char*)"library=libcoolkeypk11.so name=Coolkey",
+ NULL, PR_FALSE);
+ if (module == NULL) {
+ return VCARD_EMUL_FAIL;
+ }
+ SECMOD_DestroyModule(module); /* free our reference, Module will still
+ * be on the list.
+ * until we destroy it */
+ }
+
+ /* now examine all the slots, finding which should be readers */
+ /* We should control this with options. For now we mirror out any
+ * removable hardware slot */
+ default_card_type = options->hw_card_type;
+ default_type_params = strdup(options->hw_type_params);
+
+ SECMOD_GetReadLock(module_lock);
+ for (mlp = module_list; mlp; mlp = mlp->next) {
+ SECMODModule * module = mlp->module;
+ PRBool has_emul_slots = PR_FALSE;
+
+ if (module == NULL) {
+ continue;
+ }
+
+ for (i=0; i < module->slotCount; i++) {
+ PK11SlotInfo *slot = module->slots[i];
+
+ /* only map removable HW slots */
+ if (slot == NULL || !PK11_IsRemovable(slot) || !PK11_IsHW(slot)) {
+ continue;
+ }
+ vreader_emul = vreader_emul_new(slot, options->hw_card_type,
+ options->hw_type_params);
+ vreader = vreader_new(PK11_GetSlotName(slot), vreader_emul,
+ vreader_emul_delete);
+ vreader_add_reader(vreader);
+
+ has_readers = PR_TRUE;
+ has_emul_slots = PR_TRUE;
+
+ if (PK11_IsPresent(slot)) {
+ VCard *vcard;
+ vcard = vcard_emul_mirror_card(vreader);
+ vreader_insert_card(vreader, vcard);
+ vcard_emul_init_series(vreader, vcard);
+ vcard_free(vcard);
+ }
+ }
+ if (has_emul_slots) {
+ vcard_emul_new_event_thread(module);
+ }
+ }
+ SECMOD_ReleaseReadLock(module_lock);
+ nss_emul_init = has_readers;
+
+ return VCARD_EMUL_OK;
+}
+
+/* Recreate card insert events for all readers (user should
+ * deduce implied reader insert. perhaps do a reader insert as well?)
+ */
+void
+vcard_emul_replay_insertion_events(void)
+{
+ VReaderListEntry *current_entry;
+ VReaderListEntry *next_entry = NULL;
+ VReaderList *list = vreader_get_reader_list();
+
+ for (current_entry= vreader_list_get_first(list); current_entry;
+ current_entry=next_entry) {
+ VReader *vreader = vreader_list_get_reader(current_entry);
+ next_entry = vreader_list_get_next(current_entry);
+ vreader_queue_card_event(vreader);
+ }
+}
+
+/*
+ * Silly little functions to help parsing our argument string
+ */
+static char *
+copy_string(const char *str, int str_len)
+{
+ char *new_str;
+
+ new_str = malloc(str_len+1);
+ memcpy(new_str, str, str_len);
+ new_str[str_len] = 0;
+ return new_str;
+}
+
+static int
+count_tokens(const char *str, char token, char token_end)
+{
+ int count = 0;
+
+ for (;*str;str++) {
+ if (*str == token) {
+ count++;
+ }
+ if (*str == token_end) {
+ break;
+ }
+ }
+ return count;
+}
+
+static const char *
+find_token(const char *str, char token, char token_end)
+{
+ /* just do the blind simple thing */
+ for (;*str;str++) {
+ if ((*str == token) || (*str == token_end)) {
+ break;
+ }
+ }
+ return str;
+}
+
+static const char *
+strip(const char *str)
+{
+ for(;*str; str++) {
+ if ((*str != ' ') && (*str != '\n') &&
+ (*str != '\t') && (*str != '\r')) {
+ break;
+ }
+ }
+ return str;
+}
+
+static const char *
+find_blank(const char *str)
+{
+ for(;*str; str++) {
+ if ((*str == ' ') || (*str == '\n') ||
+ (*str == '\t') || (*str == '\r')) {
+ break;
+ }
+ }
+ return str;
+}
+
+
+/*
+ * We really want to use some existing argument parsing library here. That
+ * would give us a consistant look */
+static VCardEmulOptions options;
+#define READER_STEP 4
+
+VCardEmulOptions *
+vcard_emul_options(const char *args)
+{
+ int reader_count = 0;
+ VCardEmulOptions *opts;
+ char type_str[100];
+ int type_len;
+
+ /* Allow the future use of allocating the options structure on the fly */
+ memcpy(&options, &default_options, sizeof(options));
+ opts = &options;
+
+ do {
+ args = strip(args); /* strip off the leading spaces */
+ if (*args == ',') {
+ continue;
+ }
+ /* soft=(slot_name,virt_name,emul_type,emul_flags,cert_1, (no eol)
+ * cert_2,cert_3...) */
+ if (strncmp(args,"soft=",5) == 0) {
+ const char *name;
+ const char *vname;
+ const char *type_params;
+ VCardEmulType type;
+ int name_length, vname_length, type_params_length, count, i;
+ VirtualReaderOptions *vreaderOpt = NULL;
+
+ args = strip(args+5);
+ if (*args != '(') {
+ continue;
+ }
+ name = args;
+ args = find_token(args+1,',',')');
+ if (*args == 0) {
+ break;
+ }
+ if (*args == ')') {
+ args++;
+ continue;
+ }
+ args = strip(args+1);
+ name_length = args - name - 2;
+ vname = args;
+ args = find_token(args+1,',',')');
+ if (*args == 0) {
+ break;
+ }
+ if (*args == ')') {
+ args++;
+ continue;
+ }
+ vname_length = args - name - 2;
+ args = strip(args+1);
+ type_len = find_token(args,',',')') - args;
+ assert(sizeof(type_str) > type_len);
+ strncpy(type_str, args, type_len);
+ type_str[type_len] = 0;
+ type = vcard_emul_type_from_string(type_str);
+ args = find_token(args,',',')');
+ if (*args == 0) {
+ break;
+ }
+ if (*args == ')') {
+ args++;
+ continue;
+ }
+ args = strip(args++);
+ type_params=args;
+ args = find_token(args+1,',',')');
+ if (*args == 0) {
+ break;
+ }
+ if (*args == ')') {
+ args++;
+ continue;
+ }
+ type_params_length = args - name;
+ args = strip(args++);
+ if (*args == 0) {
+ break;
+ }
+
+ if (opts->vreader_count >= reader_count) {
+ reader_count += READER_STEP;
+ vreaderOpt = realloc(opts->vreader,
+ reader_count*sizeof(*vreaderOpt));
+ if (vreaderOpt == NULL) {
+ return opts; /* we're done */
+ }
+ }
+ opts->vreader = vreaderOpt;
+ vreaderOpt = &vreaderOpt[opts->vreader_count];
+ vreaderOpt->name = copy_string(name, name_length);
+ vreaderOpt->vname = copy_string(vname, vname_length);
+ vreaderOpt->card_type = type;
+ vreaderOpt->type_params = copy_string(type_params,
type_params_length);
+ count = count_tokens(args,',',')');
+ vreaderOpt->cert_count = count;
+ vreaderOpt->cert_name = (char **)malloc(count*sizeof(char *));
+ for (i=0; i < count; i++) {
+ const char *cert = args + 1;
+ args = find_token(args + 1, ',', ')');
+ vreaderOpt->cert_name[i] = copy_string(cert, args - cert);
+ }
+ if (*args == ')') {
+ args++;
+ }
+ opts->vreader_count++;
+ /* use_hw= */
+ } else if (strncmp(args,"use_hw=",7) == 0) {
+ args = strip(args+7);
+ if (*args == '0' || *args == 'N' || *args == 'n' || *args == 'F') {
+ opts->use_hw = PR_FALSE;
+ } else {
+ opts->use_hw = PR_TRUE;
+ }
+ args = find_blank(args);
+ /* hw_type= */
+ } else if (strncmp(args,"hw_type=",8) == 0) {
+ args = strip(args+8);
+ opts->hw_card_type = vcard_emul_type_from_string(args);
+ args = find_blank(args);
+ /* hw_params= */
+ } else if (strncmp(args,"hw_params=",10) == 0) {
+ const char *params;
+ args = strip(args+10);
+ params= args;
+ args = find_blank(args);
+ opts->hw_type_params = copy_string(params, args-params);
+ /* db="/data/base/path" */
+ } else if (strncmp(args,"db=",3) == 0) {
+ const char *db;
+ args = strip(args+3);
+ if (*args != '"') {
+ continue;
+ }
+ args++;
+ db = args;
+ args = find_token(args, '"', '\n');
+ opts->nss_db = copy_string(db,args-db);
+ if (*args != 0) {
+ args++;
+ }
+ } else args = find_blank(args);
+ } while (*args != 0);
+
+ return opts;
+}
+
+void
+vcard_emul_usage(void)
+{
+ fprintf(stderr,
+"emul args: comma separated list of the following arguments\n"
+" db={nss_database} (default sql:/etc/pki/nssdb)\n"
+" use_hw=[yes|no] (default yes)\n"
+" hw_type={card_type_to_emulate} (default CAC)\n"
+" hw_param={param_for_card} (default \"\")\n"
+" soft=({slot_name},{vreader_name},{card_type_to_emulate},{params_for_card},\n"
+" {cert1},{cert2},{cert3} (default none)\n"
+"\n"
+" {nss_database} The location of the NSS cert & key database\n"
+" {card_type_to_emulate} What card interface to present to the guest\n"
+" {param_for_card} Card interface specific parameters\n"
+" {slot_name} NSS slot that contains the certs\n"
+" {vreader_name} Virutal reader name to present to the guest\n"
+" {certN} Nickname of the certificate n on the virtual card\n"
+"\n"
+"These parameters come as a single string separated by blanks or newlines."
+"\n"
+"Unless use_hw is set to no, all tokens that look like removable hardware\n"
+"tokens will be presented to the guest using the emulator specified by \n"
+"hw_type, and parameters of hw_param.\n"
+"\n"
+"If more one or more soft= parameters are specified, these readers will be\n"
+"presented to the guest\n");
+}
diff --git a/libcacard/vcard_emul_type.c b/libcacard/vcard_emul_type.c
new file mode 100644
index 0000000..adbc54b
--- /dev/null
+++ b/libcacard/vcard_emul_type.c
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#include <strings.h>
+#include "vcardt.h"
+#include "vcard_emul_type.h"
+#include "cac.h"
+#include "passthru.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);
+#ifdef USE_PASSTHRU
+ case VCARD_EMUL_PASSTHRU:
+ return passthru_card_init(vreader, vcard, params,
+ cert, cert_len, key, cert_count);
+#endif
+ /* 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;
+}
diff --git a/libcacard/vcard_emul_type.h b/libcacard/vcard_emul_type.h
new file mode 100644
index 0000000..da15528
--- /dev/null
+++ b/libcacard/vcard_emul_type.h
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+#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
diff --git a/libcacard/vcardt.h b/libcacard/vcardt.h
new file mode 100644
index 0000000..e371451
--- /dev/null
+++ b/libcacard/vcardt.h
@@ -0,0 +1,66 @@
+/*
+ *
+ */
+#ifndef VCARDT_H
+#define VCARDT_H 1
+
+/*
+ * these should come from some common spice header file
+ */
+#include <assert.h>
+#ifndef ASSERT
+#define ASSERT assert
+#endif
+#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
+
+/* create an ATR with appropriate historical bytes */
+#define VCARD_ATR_PREFIX(size) 0x3b, 0x66+(size), 0x00, 0xff, \
+ 'V', 'C', 'A', 'R', 'D', '_'
+
+
+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
diff --git a/libcacard/vevent.h b/libcacard/vevent.h
new file mode 100644
index 0000000..f202ea8
--- /dev/null
+++ b/libcacard/vevent.h
@@ -0,0 +1,26 @@
+/*
+ *
+ */
+#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
diff --git a/libcacard/vreader.c b/libcacard/vreader.c
new file mode 100644
index 0000000..5e46019
--- /dev/null
+++ b/libcacard/vreader.c
@@ -0,0 +1,526 @@
+/*
+ * emulate the reader
+ */
+#include "vcard.h"
+#include "vcard_emul.h"
+#include "card_7816.h"
+#include "vreader.h"
+#include "vevent.h"
+
+/*
+ * System includes
+ */
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * spice includes
+ */
+#include "mutex.h"
+
+struct VReaderStruct {
+ int reference_count;
+ VCard *card;
+ char *name;
+ vreader_id_t id;
+ mutex_t lock;
+ VReaderEmul *reader_private;
+ VReaderEmulFree reader_private_free;
+};
+
+/* manage locking */
+static inline void
+vreader_lock(VReader *reader)
+{
+ MUTEX_LOCK(reader->lock);
+}
+
+static inline void
+vreader_unlock(VReader *reader)
+{
+ MUTEX_UNLOCK(reader->lock);
+}
+
+/*
+ * vreader constructor
+ */
+VReader *
+vreader_new(const char *name, VReaderEmul *private,
+ VReaderEmulFree private_free)
+{
+ VReader *reader;
+
+ reader = (VReader *)malloc(sizeof(VReader));
+ if (reader == NULL) {
+ return NULL;
+ }
+ MUTEX_INIT(reader->lock);
+ reader->reference_count = 1;
+ reader->name = name ? strdup(name) : NULL;
+ 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);
+ if (reader->card) {
+ vcard_free(reader->card);
+ }
+ if (reader->name) {
+ free(reader->name);
+ }
+ if (reader->reader_private_free) {
+ reader->reader_private_free(reader->reader_private);
+ }
+ free(reader);
+ return;
+}
+
+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 {
+ card_status = vcard_process_apdu(card, apdu, &response);
+ }
+ ASSERT(card_status == VCARD_DONE);
+ if (card_status == VCARD_DONE) {
+ 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 = (VReaderListEntry *)
+ malloc(sizeof(VReaderListEntry));
+ if (new_reader_list_entry == NULL) {
+ return NULL;
+ }
+ new_reader_list_entry->next = NULL;
+ new_reader_list_entry->prev = NULL;
+ 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);
+ free(entry);
+}
+
+
+static VReaderList *
+vreader_list_new(void)
+{
+ VReaderList *new_reader_list;
+
+ new_reader_list = (VReaderList *)malloc(sizeof(VReaderList));
+ if (new_reader_list == NULL) {
+ return NULL;
+ }
+ new_reader_list->head = NULL;
+ new_reader_list->tail = NULL;
+ return new_reader_list;
+}
+
+void
+vreader_list_delete(VReaderList *list)
+{
+ VReaderListEntry *current_entry;
+ VReaderListEntry *next_entry = NULL;
+ 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);
+ }
+ list->head = NULL;
+ list->tail = NULL;
+ 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 = NULL;
+static mutex_t vreader_list_mutex;
+
+static void
+vreader_list_init(void)
+{
+ vreader_list = vreader_list_new();
+ MUTEX_INIT(vreader_list_mutex);
+}
+
+static void
+vreader_list_lock(void)
+{
+ MUTEX_LOCK(vreader_list_mutex);
+}
+
+static void
+vreader_list_unlock(void)
+{
+ MUTEX_UNLOCK(vreader_list_mutex);
+}
+
+static VReaderList *
+vreader_copy_list(VReaderList *list)
+{
+ VReaderList *new_list = NULL;
+ VReaderListEntry *current_entry = NULL;
+
+ 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 = NULL;
+
+ 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 = 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 (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();
+}
+
diff --git a/libcacard/vreader.h b/libcacard/vreader.h
new file mode 100644
index 0000000..c7054da
--- /dev/null
+++ b/libcacard/vreader.h
@@ -0,0 +1,54 @@
+/*
+ *
+ */
+
+#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
diff --git a/libcacard/vreadert.h b/libcacard/vreadert.h
new file mode 100644
index 0000000..4c01259
--- /dev/null
+++ b/libcacard/vreadert.h
@@ -0,0 +1,23 @@
+/*
+ *
+ */
+
+#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
+
diff --git a/libcacard/vscclient.c b/libcacard/vscclient.c
new file mode 100644
index 0000000..f29fd6b
--- /dev/null
+++ b/libcacard/vscclient.c
@@ -0,0 +1,743 @@
+/*
+ * Tester for VSCARD protocol, client side.
+ *
+ * Can be used with ccid-card-passthru.
+ *
+ * Copyright (c) 2011 Red Hat.
+ * Written by Alon Levy.
+ *
+ * This code is licenced under the GNU LGPL, version 2 or later.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "vscard_common.h"
+
+#include "vreader.h"
+#include "vcard_emul.h"
+#include "vevent.h"
+#include "passthru.h"
+
+#include "mutex.h"
+
+int verbose = 0;
+
+int sock;
+
+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 mutex_t write_lock;
+
+static int
+send_msg (
+ VSCMsgType type,
+ uint32_t reader_id,
+ const void *msg,
+ unsigned int length
+) {
+ int rv;
+ VSCMsgHeader mhHeader;
+
+ MUTEX_LOCK(write_lock);
+
+ if (verbose > 10) {
+ printf("sending type=%d id=%d, len =%d (0x%x)\n",
+ type, reader_id, length, length);
+ }
+
+ mhHeader.type = htonl(type);
+ mhHeader.reader_id = 0;
+ mhHeader.length = htonl(length);
+ rv = write (
+ sock,
+ &mhHeader,
+ sizeof (mhHeader)
+ );
+ if (rv < 0) {
+ /* Error */
+ printf ("write header error\n");
+ close (sock);
+ MUTEX_UNLOCK(write_lock);
+ return (16);
+ }
+ rv = write (
+ sock,
+ msg,
+ length
+ );
+ if (rv < 0) {
+ /* Error */
+ printf ("write error\n");
+ close (sock);
+ MUTEX_UNLOCK(write_lock);
+ return (16);
+ }
+ MUTEX_UNLOCK(write_lock);
+
+ return (0);
+}
+
+static VReader *pending_reader = NULL;
+static mutex_t pending_reader_lock;
+static condition_t pending_reader_condition;
+
+#define MAX_ATR_LEN 40
+static void *
+event_thread(void *arg)
+{
+ unsigned char atr[ MAX_ATR_LEN];
+ int atr_len = MAX_ATR_LEN;
+ VEvent *event = NULL;
+ 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 */
+ 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 */
+ MUTEX_UNLOCK(pending_reader_lock);
+ vevent_delete(event);
+ continue;
+ }
+ /* this reader hasn't been told it's status from qemu yet, wait for
+ * that status */
+ while (pending_reader != NULL) {
+ CONDITION_WAIT(pending_reader_condition,pending_reader_lock);
+ }
+ 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
+ * */
+ MUTEX_LOCK(pending_reader_lock);
+ while (pending_reader != NULL) {
+ CONDITION_WAIT(pending_reader_condition,pending_reader_lock);
+ }
+ pending_reader = vreader_reference(event->reader);
+ 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
+ /*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: %d \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 %d: ", 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 %d: \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 void
+do_command(void)
+{
+ char inbuf[255];
+ char *string;
+ VCardEmulError error;
+ static unsigned int default_reader_id = 0;
+ unsigned int reader_id;
+ VReader *reader = NULL;
+
+ reader_id = default_reader_id;
+ string = fgets(inbuf, sizeof(inbuf), stdin);
+ 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",
+ reader ? vreader_get_name(reader)
+ : "invalid reader", error);
+ } else {
+ printf("no reader by id %d 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",
+ reader ? vreader_get_name(reader)
+ : "invalid reader", error);
+ } else {
+ printf("no reader by id %d 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 %d, %s\n", reader_id,
+ vreader_get_name(reader));
+ default_reader_id = reader_id;
+ } else {
+ printf("Reader with id %d 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("%3d %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));
+ }
+ } 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);
+}
+
+
+#define APDUBufSize 270
+
+// 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;
+ int ret;
+
+ sock = socket (
+ AF_INET,
+ SOCK_STREAM,
+ 0
+ );
+ if (sock < 0) {
+ // Error
+ printf ("Error opening socket!\n");
+ }
+
+ 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) {
+ printf ("getaddrinfo failed\n");
+ return (5);
+ }
+
+ if (connect (
+ sock,
+ server->ai_addr,
+ server->ai_addrlen
+ ) < 0
+ ) {
+ // Error
+ printf ("Could not connect\n");
+ return (5);
+ }
+ if (verbose) {
+ printf ("Connected (sizeof Header=%zd)!\n", sizeof (VSCMsgHeader));
+ }
+ return sock;
+}
+
+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;
+ int rv;
+ pthread_t thread_id;
+
+ 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 */
+ rv = pthread_create(&thread_id, NULL, event_thread, NULL);
+ if (rv < 0) {
+ perror("pthread_create");
+ return rv;
+ }
+ return 0;
+}
+
+int
+main (
+ int argc,
+ char *argv[]
+) {
+ char* qemu_host;
+ char* qemu_port;
+ VSCMsgHeader mhHeader;
+ VSCMsgError *error_msg;
+
+ int rv;
+ int dwSendLength;
+ int dwRecvLength;
+ uint8_t pbRecvBuffer[APDUBufSize];
+ uint8_t pbSendBuffer[APDUBufSize];
+ VReaderStatus reader_status;
+ VReader *reader = NULL;
+ VCardEmulOptions *command_line_options = NULL;
+ int passthru = 0;
+
+ char* cert_names[MAX_CERTS];
+ char* emul_args = NULL;
+ int cert_count = 0;
+ int c;
+
+ 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':
+#ifdef USE_PASSTHRU
+ passthru = 1;
+#else
+ print_usage();
+ exit(4);
+#endif
+ break;
+ case 'd':
+ verbose = get_id_from_string(optarg,1);
+ break;
+ }
+ }
+
+ if (argc - optind != 2) {
+ print_usage();
+ exit (4);
+ }
+
+ if (!passthru && 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 = 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) {
+#ifdef USE_PASSTHRU
+ command_line_options = passthru ? passthru_emul_options(emul_args) :
+#else
+ command_line_options =
+#endif
+ vcard_emul_options(emul_args);
+ }
+
+ qemu_host = strdup(argv[argc - 2]);
+ qemu_port = strdup(argv[argc -1]);
+ sock = connect_to_qemu(qemu_host, qemu_port);
+
+ MUTEX_INIT(write_lock);
+ MUTEX_INIT(pending_reader_lock);
+ CONDITION_INIT(pending_reader_condition);
+
+#ifdef USE_PASSTHRU
+ if (passthru) {
+ passthru_emul_init(command_line_options);
+ } else
+#endif
+ vcard_emul_init(command_line_options);
+
+ printf("> ");
+ fflush(stdout);
+
+ /* 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, mhHeader.reader_id, &init, sizeof(init));
+
+ do {
+ fd_set fds;
+
+ FD_ZERO(&fds);
+ FD_SET(1,&fds);
+ FD_SET(sock,&fds);
+
+ /* waiting on input from the socket */
+ rv = select(sock+1, &fds, NULL, NULL, NULL);
+ if (rv < 0) {
+ /* handle error */
+ perror("select");
+ return (7);
+ }
+ if (FD_ISSET(1,&fds)) {
+ do_command();
+ }
+ if (!FD_ISSET(sock,&fds)) {
+ continue;
+ }
+
+ rv = read (
+ sock,
+ &mhHeader,
+ sizeof (mhHeader)
+ );
+ if (rv < sizeof(mhHeader)) {
+ /* Error */
+ if (rv < 0) {
+ perror("header read error\n");
+ } else {
+ printf ("header short read %d\n", rv);
+ }
+ return (8);
+ }
+ 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=%d 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:
+ rv = read (
+ sock,
+ pbSendBuffer,
+ mhHeader.length
+ );
+ break;
+ default:
+ printf ("Unexpected message of type 0x%X\n", mhHeader.type);
+ return 0;
+ }
+ switch (mhHeader.type) {
+ case VSC_APDU:
+ if (rv < 0) {
+ /* Error */
+ printf ("read error\n");
+ close (sock);
+ return (8);
+ }
+ if (verbose) {
+ printf (" recv APDU: ");
+ print_byte_array (pbSendBuffer, mhHeader.length);
+ }
+ /* Transmit recieved 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) {
+ MUTEX_LOCK(pending_reader_lock);
+ if (pending_reader) {
+ vreader_set_id(pending_reader, mhHeader.reader_id);
+ vreader_free(pending_reader);
+ pending_reader = NULL;
+ CONDITION_NOTIFY(pending_reader_condition);
+ }
+ MUTEX_UNLOCK(pending_reader_lock);
+ break;
+ }
+ printf("error: qemu refused to add reader\n");
+ if (error_msg->code == VSC_CANNOT_ADD_MORE_READERS) {
+ /* clear pending reader, qemu can't handle any more */
+ MUTEX_LOCK(pending_reader_lock);
+ if (pending_reader) {
+ pending_reader = NULL;
+ /* make sure the event loop doesn't hang */
+ CONDITION_NOTIFY(pending_reader_condition);
+ }
+ MUTEX_UNLOCK(pending_reader_lock);
+ }
+ break;
+ case VSC_Init:
+ if (on_host_init(&mhHeader, (VSCMsgInit*)pbSendBuffer) < 0) {
+ return -1;
+ }
+ break;
+ default:
+ printf ("Default\n");
+ return 0;
+ }
+ } while (rv >= 0);
+
+
+ return (0);
+}
--
1.7.4
- [Qemu-devel] [PATCH 08/17] ccid: add passthru card device, (continued)
- [Qemu-devel] [PATCH 08/17] ccid: add passthru card device, Alon Levy, 2011/02/03
- [Qemu-devel] [PATCH 05/17] usb-ccid: add CCID bus, Alon Levy, 2011/02/03
- [Qemu-devel] [PATCH 13/17] ccid: add qdev description strings, Alon Levy, 2011/02/03
- [Qemu-devel] [PATCH 12/17] ccid: configure: add --enable/disable and nss only disable, Alon Levy, 2011/02/03
- [Qemu-devel] [PATCH 10/17] ccid: add ccid-card-emulated device (v2), Alon Levy, 2011/02/03
- [Qemu-devel] [PATCH 11/17] ccid: add docs, Alon Levy, 2011/02/03
- [Qemu-devel] [PATCH 14/17] smartcard, configure: add --enable-smartcard-nss, report only nss, Alon Levy, 2011/02/03
- [Qemu-devel] [PATCH 15/17] smartcard,configure: report only nss, Alon Levy, 2011/02/03
- [Qemu-devel] [PATCH 16/17] ccid-card-emulated: don't link with NSS if --disable-smartcard-nss, Alon Levy, 2011/02/03
- [Qemu-devel] [PATCH 17/17] ccid.h: add copyright, fix define and remove non C89 comments, Alon Levy, 2011/02/03
- [Qemu-devel] [PATCH 09/17] libcacard: initial commit,
Alon Levy <=
- Re: [Qemu-devel] [PATCH 00/20] usb-ccid (v17), Alon Levy, 2011/02/03
- Re: [Qemu-devel] [PATCH 00/20] usb-ccid (v17), Alon Levy, 2011/02/03