mirror of
https://gitlab.com/qemu-project/qemu
synced 2024-11-05 20:35:44 +00:00
Merge remote-tracking branch 'alon/libcacard_ccid.1' into staging
# By Alon Levy (15) and others # Via Alon Levy * alon/libcacard_ccid.1: (28 commits) libcacard/cac: change big switch functions to single return point dev-smartcard-reader: empty implementation for Mechanical (fail correctly) libcacard: move atr setting from macro to function libcacard/vreader: add debugging messages for apdu dev-smartcard-reader: copy atr protocol to ccid parameters dev-smartcard-reader: change default protocol to T=0 dev-smartcard-reader: define structs for CCID_Parameter internals ccid-card-passthru, dev-smartcard-reader: add debug environment variables ccid-card-passthru: add atr check libcacard: change default ATR dev-smartcard-reader: reuse usb.h definitions dev-smartcard-reader: support windows guest dev-smartcard-reader: remove aborts (never triggered, but just in case) dev-smartcard-reader: nicer debug messages dev-smartcard-reader: white space fixes libcacard: remove default libcoolkey loading libcacard: remove sql: prefix libcacard: teach vscclient to use GMainLoop for portability libcacard: vscclient to use QemuThread for portability libcacard: split vscclient main() from socket reading ... Message-id: 921423767.1475937.1366790789930.JavaMail.root@redhat.com Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
commit
1bfffe21a6
19 changed files with 839 additions and 343 deletions
8
Makefile
8
Makefile
|
@ -166,11 +166,15 @@ recurse-all: $(SUBDIR_RULES) $(ROMSUBDIR_RULES)
|
|||
|
||||
bt-host.o: QEMU_CFLAGS += $(BLUEZ_CFLAGS)
|
||||
|
||||
version.o: $(SRC_PATH)/version.rc config-host.h
|
||||
version.o: $(SRC_PATH)/version.rc config-host.h | version.lo
|
||||
$(call quiet-command,$(WINDRES) -I. -o $@ $<," RC $(TARGET_DIR)$@")
|
||||
version.lo: $(SRC_PATH)/version.rc config-host.h
|
||||
$(call quiet-command,$(LIBTOOL) --mode=compile --tag=RC $(WINDRES) -I. -o $@ $<,"lt RC $(TARGET_DIR)$@")
|
||||
|
||||
version-obj-$(CONFIG_WIN32) += version.o
|
||||
Makefile: $(version-obj-y)
|
||||
version-lobj-$(CONFIG_WIN32) += $(if $(LIBTOOL),version.lo)
|
||||
Makefile: $(version-obj-y) $(version-lobj-y)
|
||||
|
||||
|
||||
######################################################################
|
||||
# Build libraries
|
||||
|
|
|
@ -32,6 +32,7 @@ libcacard-y += libcacard/vcard.o libcacard/vreader.o
|
|||
libcacard-y += libcacard/vcard_emul_nss.o
|
||||
libcacard-y += libcacard/vcard_emul_type.o
|
||||
libcacard-y += libcacard/card_7816.o
|
||||
libcacard-y += libcacard/vcardt.o
|
||||
|
||||
######################################################################
|
||||
# Target independent part of system emulation. The long term path is to
|
||||
|
|
8
configure
vendored
8
configure
vendored
|
@ -1244,7 +1244,7 @@ fi
|
|||
gcc_flags="-Wold-style-declaration -Wold-style-definition -Wtype-limits"
|
||||
gcc_flags="-Wformat-security -Wformat-y2k -Winit-self -Wignored-qualifiers $gcc_flags"
|
||||
gcc_flags="-Wmissing-include-dirs -Wempty-body -Wnested-externs $gcc_flags"
|
||||
gcc_flags="-fstack-protector-all -Wendif-labels $gcc_flags"
|
||||
gcc_flags="-Wendif-labels $gcc_flags"
|
||||
gcc_flags="-Wno-initializer-overrides $gcc_flags"
|
||||
# Note that we do not add -Werror to gcc_flags here, because that would
|
||||
# enable it for all configure tests. If a configure test failed due
|
||||
|
@ -1263,6 +1263,11 @@ for flag in $gcc_flags; do
|
|||
fi
|
||||
done
|
||||
|
||||
if compile_prog "-Werror -fstack-protector-all" "" ; then
|
||||
QEMU_CFLAGS="$QEMU_CFLAGS -fstack-protector-all"
|
||||
LIBTOOLFLAGS="$LIBTOOLFLAGS -Wc,-fstack-protector-all"
|
||||
fi
|
||||
|
||||
# Workaround for http://gcc.gnu.org/PR55489. Happens with -fPIE/-fPIC and
|
||||
# large functions that use global variables. The bug is in all releases of
|
||||
# GCC, but it became particularly acute in 4.6.x and 4.7.x. It is fixed in
|
||||
|
@ -4078,6 +4083,7 @@ else
|
|||
echo "AUTOCONF_HOST := " >> $config_host_mak
|
||||
fi
|
||||
echo "LDFLAGS=$LDFLAGS" >> $config_host_mak
|
||||
echo "LIBTOOLFLAGS=$LIBTOOLFLAGS" >> $config_host_mak
|
||||
echo "LIBS+=$LIBS" >> $config_host_mak
|
||||
echo "LIBS_TOOLS+=$libs_tools" >> $config_host_mak
|
||||
echo "EXESUF=$EXESUF" >> $config_host_mak
|
||||
|
|
|
@ -462,17 +462,20 @@ typedef struct EnumTable {
|
|||
uint32_t value;
|
||||
} EnumTable;
|
||||
|
||||
EnumTable backend_enum_table[] = {
|
||||
static const EnumTable backend_enum_table[] = {
|
||||
{BACKEND_NSS_EMULATED_NAME, BACKEND_NSS_EMULATED},
|
||||
{BACKEND_CERTIFICATES_NAME, BACKEND_CERTIFICATES},
|
||||
{NULL, 0},
|
||||
};
|
||||
|
||||
static uint32_t parse_enumeration(char *str,
|
||||
EnumTable *table, uint32_t not_found_value)
|
||||
const EnumTable *table, uint32_t not_found_value)
|
||||
{
|
||||
uint32_t ret = not_found_value;
|
||||
|
||||
if (str == NULL)
|
||||
return 0;
|
||||
|
||||
while (table->name != NULL) {
|
||||
if (strcmp(table->name, str) == 0) {
|
||||
ret = table->value;
|
||||
|
@ -487,7 +490,7 @@ static int emulated_initfn(CCIDCardState *base)
|
|||
{
|
||||
EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
|
||||
VCardEmulError ret;
|
||||
EnumTable *ptable;
|
||||
const EnumTable *ptable;
|
||||
|
||||
QSIMPLEQ_INIT(&card->event_list);
|
||||
QSIMPLEQ_INIT(&card->guest_apdu_list);
|
||||
|
|
|
@ -27,7 +27,7 @@ do { \
|
|||
#define D_VERBOSE 4
|
||||
|
||||
/* TODO: do we still need this? */
|
||||
uint8_t DEFAULT_ATR[] = {
|
||||
static const uint8_t DEFAULT_ATR[] = {
|
||||
/*
|
||||
* From some example somewhere
|
||||
* 0x3B, 0xB0, 0x18, 0x00, 0xD1, 0x81, 0x05, 0xB1, 0x40, 0x38, 0x1F, 0x03, 0x28
|
||||
|
@ -138,6 +138,59 @@ static void ccid_card_vscard_handle_init(
|
|||
ccid_card_vscard_send_init(card);
|
||||
}
|
||||
|
||||
static int check_atr(PassthruState *card, uint8_t *data, int len)
|
||||
{
|
||||
int historical_length, opt_bytes;
|
||||
int td_count = 0;
|
||||
int td;
|
||||
|
||||
if (len < 2) {
|
||||
return 0;
|
||||
}
|
||||
historical_length = data[1] & 0xf;
|
||||
opt_bytes = 0;
|
||||
if (data[0] != 0x3b && data[0] != 0x3f) {
|
||||
DPRINTF(card, D_WARN, "atr's T0 is 0x%X, not in {0x3b, 0x3f}\n",
|
||||
data[0]);
|
||||
return 0;
|
||||
}
|
||||
td_count = 0;
|
||||
td = data[1] >> 4;
|
||||
while (td && td_count < 2 && opt_bytes + historical_length + 2 < len) {
|
||||
td_count++;
|
||||
if (td & 0x1) {
|
||||
opt_bytes++;
|
||||
}
|
||||
if (td & 0x2) {
|
||||
opt_bytes++;
|
||||
}
|
||||
if (td & 0x4) {
|
||||
opt_bytes++;
|
||||
}
|
||||
if (td & 0x8) {
|
||||
opt_bytes++;
|
||||
td = data[opt_bytes + 2] >> 4;
|
||||
}
|
||||
}
|
||||
if (len < 2 + historical_length + opt_bytes) {
|
||||
DPRINTF(card, D_WARN,
|
||||
"atr too short: len %d, but historical_len %d, T1 0x%X\n",
|
||||
len, historical_length, data[1]);
|
||||
return 0;
|
||||
}
|
||||
if (len > 2 + historical_length + opt_bytes) {
|
||||
DPRINTF(card, D_WARN,
|
||||
"atr too long: len %d, but hist/opt %d/%d, T1 0x%X\n",
|
||||
len, historical_length, opt_bytes, data[1]);
|
||||
/* let it through */
|
||||
}
|
||||
DPRINTF(card, D_VERBOSE,
|
||||
"atr passes check: %d total length, %d historical, %d optional\n",
|
||||
len, historical_length, opt_bytes);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void ccid_card_vscard_handle_message(PassthruState *card,
|
||||
VSCMsgHeader *scr_msg_header)
|
||||
{
|
||||
|
@ -152,6 +205,12 @@ static void ccid_card_vscard_handle_message(PassthruState *card,
|
|||
VSC_GENERAL_ERROR);
|
||||
break;
|
||||
}
|
||||
if (!check_atr(card, data, scr_msg_header->length)) {
|
||||
error_report("ATR is inconsistent, ignoring");
|
||||
ccid_card_vscard_send_error(card, scr_msg_header->reader_id,
|
||||
VSC_GENERAL_ERROR);
|
||||
break;
|
||||
}
|
||||
memcpy(card->atr, data, scr_msg_header->length);
|
||||
card->atr_length = scr_msg_header->length;
|
||||
ccid_card_card_inserted(&card->base);
|
||||
|
@ -291,6 +350,8 @@ static int passthru_initfn(CCIDCardState *base)
|
|||
error_report("missing chardev");
|
||||
return -1;
|
||||
}
|
||||
card->debug = parse_debug_env("QEMU_CCID_PASSTHRU_DEBUG", D_VERBOSE,
|
||||
card->debug);
|
||||
assert(sizeof(DEFAULT_ATR) <= MAX_ATR_SIZE);
|
||||
memcpy(card->atr, DEFAULT_ATR, sizeof(DEFAULT_ATR));
|
||||
card->atr_length = sizeof(DEFAULT_ATR);
|
||||
|
|
|
@ -68,12 +68,6 @@ do { \
|
|||
#define BULK_IN_BUF_SIZE 384
|
||||
#define BULK_IN_PENDING_NUM 8
|
||||
|
||||
#define InterfaceOutClass \
|
||||
((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)<<8)
|
||||
|
||||
#define InterfaceInClass \
|
||||
((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE)<<8)
|
||||
|
||||
#define CCID_MAX_PACKET_SIZE 64
|
||||
|
||||
#define CCID_CONTROL_ABORT 0x1
|
||||
|
@ -195,10 +189,34 @@ typedef struct QEMU_PACKED CCID_SlotStatus {
|
|||
uint8_t bClockStatus;
|
||||
} CCID_SlotStatus;
|
||||
|
||||
typedef struct QEMU_PACKED CCID_T0ProtocolDataStructure {
|
||||
uint8_t bmFindexDindex;
|
||||
uint8_t bmTCCKST0;
|
||||
uint8_t bGuardTimeT0;
|
||||
uint8_t bWaitingIntegerT0;
|
||||
uint8_t bClockStop;
|
||||
} CCID_T0ProtocolDataStructure;
|
||||
|
||||
typedef struct QEMU_PACKED CCID_T1ProtocolDataStructure {
|
||||
uint8_t bmFindexDindex;
|
||||
uint8_t bmTCCKST1;
|
||||
uint8_t bGuardTimeT1;
|
||||
uint8_t bWaitingIntegerT1;
|
||||
uint8_t bClockStop;
|
||||
uint8_t bIFSC;
|
||||
uint8_t bNadValue;
|
||||
} CCID_T1ProtocolDataStructure;
|
||||
|
||||
typedef union CCID_ProtocolDataStructure {
|
||||
CCID_T0ProtocolDataStructure t0;
|
||||
CCID_T1ProtocolDataStructure t1;
|
||||
uint8_t data[7]; /* must be = max(sizeof(t0), sizeof(t1)) */
|
||||
} CCID_ProtocolDataStructure;
|
||||
|
||||
typedef struct QEMU_PACKED CCID_Parameter {
|
||||
CCID_BULK_IN b;
|
||||
uint8_t bProtocolNum;
|
||||
uint8_t abProtocolDataStructure[0];
|
||||
CCID_ProtocolDataStructure abProtocolDataStructure;
|
||||
} CCID_Parameter;
|
||||
|
||||
typedef struct QEMU_PACKED CCID_DataBlock {
|
||||
|
@ -230,7 +248,7 @@ typedef struct QEMU_PACKED CCID_SetParameters {
|
|||
CCID_Header hdr;
|
||||
uint8_t bProtocolNum;
|
||||
uint16_t abRFU;
|
||||
uint8_t abProtocolDataStructure[0];
|
||||
CCID_ProtocolDataStructure abProtocolDataStructure;
|
||||
} CCID_SetParameters;
|
||||
|
||||
typedef struct CCID_Notify_Slot_Change {
|
||||
|
@ -260,8 +278,6 @@ typedef struct CCIDBus {
|
|||
BusState qbus;
|
||||
} CCIDBus;
|
||||
|
||||
#define MAX_PROTOCOL_SIZE 7
|
||||
|
||||
/*
|
||||
* powered - defaults to true, changed by PowerOn/PowerOff messages
|
||||
*/
|
||||
|
@ -285,7 +301,7 @@ typedef struct USBCCIDState {
|
|||
uint8_t bError;
|
||||
uint8_t bmCommandStatus;
|
||||
uint8_t bProtocolNum;
|
||||
uint8_t abProtocolDataStructure[MAX_PROTOCOL_SIZE];
|
||||
CCID_ProtocolDataStructure abProtocolDataStructure;
|
||||
uint32_t ulProtocolDataStructureSize;
|
||||
uint32_t state_vmstate;
|
||||
uint32_t migration_target_ip;
|
||||
|
@ -319,8 +335,8 @@ static const uint8_t qemu_ccid_descriptor[] = {
|
|||
*/
|
||||
0x07, /* u8 bVoltageSupport; 01h - 5.0v, 02h - 3.0, 03 - 1.8 */
|
||||
|
||||
0x03, 0x00, /* u32 dwProtocols; RRRR PPPP. RRRR = 0000h.*/
|
||||
0x00, 0x00, /* PPPP: 0001h = Protocol T=0, 0002h = Protocol T=1 */
|
||||
0x00, 0x00, /* u32 dwProtocols; RRRR PPPP. RRRR = 0000h.*/
|
||||
0x01, 0x00, /* PPPP: 0001h = Protocol T=0, 0002h = Protocol T=1 */
|
||||
/* u32 dwDefaultClock; in kHZ (0x0fa0 is 4 MHz) */
|
||||
0xa0, 0x0f, 0x00, 0x00,
|
||||
/* u32 dwMaximumClock; */
|
||||
|
@ -359,11 +375,11 @@ static const uint8_t qemu_ccid_descriptor[] = {
|
|||
* 20000 Short APDU level exchange with CCID
|
||||
* 40000 Short and Extended APDU level exchange with CCID
|
||||
*
|
||||
* + 100000 USB Wake up signaling supported on card
|
||||
* 100000 USB Wake up signaling supported on card
|
||||
* insertion and removal. Must set bit 5 in bmAttributes
|
||||
* in Configuration descriptor if 100000 is set.
|
||||
*/
|
||||
0xfe, 0x04, 0x11, 0x00,
|
||||
0xfe, 0x04, 0x01, 0x00,
|
||||
/*
|
||||
* u32 dwMaxCCIDMessageLength; For extended APDU in
|
||||
* [261 + 10 , 65544 + 10]. Otherwise the minimum is
|
||||
|
@ -410,8 +426,8 @@ static const USBDescStrings desc_strings = {
|
|||
static const USBDescIface desc_iface0 = {
|
||||
.bInterfaceNumber = 0,
|
||||
.bNumEndpoints = 3,
|
||||
.bInterfaceClass = 0x0b,
|
||||
.bInterfaceSubClass = 0x00,
|
||||
.bInterfaceClass = USB_CLASS_CSCID,
|
||||
.bInterfaceSubClass = USB_SUBCLASS_UNDEFINED,
|
||||
.bInterfaceProtocol = 0x00,
|
||||
.iInterface = STR_INTERFACE,
|
||||
.ndesc = 1,
|
||||
|
@ -471,6 +487,7 @@ static const USBDesc desc_ccid = {
|
|||
static const uint8_t *ccid_card_get_atr(CCIDCardState *card, uint32_t *len)
|
||||
{
|
||||
CCIDCardClass *cc = CCID_CARD_GET_CLASS(card);
|
||||
|
||||
if (cc->get_atr) {
|
||||
return cc->get_atr(card, len);
|
||||
}
|
||||
|
@ -482,6 +499,7 @@ static void ccid_card_apdu_from_guest(CCIDCardState *card,
|
|||
uint32_t len)
|
||||
{
|
||||
CCIDCardClass *cc = CCID_CARD_GET_CLASS(card);
|
||||
|
||||
if (cc->apdu_from_guest) {
|
||||
cc->apdu_from_guest(card, apdu, len);
|
||||
}
|
||||
|
@ -490,6 +508,7 @@ static void ccid_card_apdu_from_guest(CCIDCardState *card,
|
|||
static int ccid_card_exitfn(CCIDCardState *card)
|
||||
{
|
||||
CCIDCardClass *cc = CCID_CARD_GET_CLASS(card);
|
||||
|
||||
if (cc->exitfn) {
|
||||
return cc->exitfn(card);
|
||||
}
|
||||
|
@ -499,6 +518,7 @@ static int ccid_card_exitfn(CCIDCardState *card)
|
|||
static int ccid_card_initfn(CCIDCardState *card)
|
||||
{
|
||||
CCIDCardClass *cc = CCID_CARD_GET_CLASS(card);
|
||||
|
||||
if (cc->initfn) {
|
||||
return cc->initfn(card);
|
||||
}
|
||||
|
@ -635,13 +655,47 @@ static void ccid_handle_reset(USBDevice *dev)
|
|||
ccid_reset(s);
|
||||
}
|
||||
|
||||
static const char *ccid_control_to_str(USBCCIDState *s, int request)
|
||||
{
|
||||
switch (request) {
|
||||
/* generic - should be factored out if there are other debugees */
|
||||
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
|
||||
return "(generic) set address";
|
||||
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
|
||||
return "(generic) get descriptor";
|
||||
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
|
||||
return "(generic) get configuration";
|
||||
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
|
||||
return "(generic) set configuration";
|
||||
case DeviceRequest | USB_REQ_GET_STATUS:
|
||||
return "(generic) get status";
|
||||
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
|
||||
return "(generic) clear feature";
|
||||
case DeviceOutRequest | USB_REQ_SET_FEATURE:
|
||||
return "(generic) set_feature";
|
||||
case InterfaceRequest | USB_REQ_GET_INTERFACE:
|
||||
return "(generic) get interface";
|
||||
case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
|
||||
return "(generic) set interface";
|
||||
/* class requests */
|
||||
case ClassInterfaceOutRequest | CCID_CONTROL_ABORT:
|
||||
return "ABORT";
|
||||
case ClassInterfaceRequest | CCID_CONTROL_GET_CLOCK_FREQUENCIES:
|
||||
return "GET_CLOCK_FREQUENCIES";
|
||||
case ClassInterfaceRequest | CCID_CONTROL_GET_DATA_RATES:
|
||||
return "GET_DATA_RATES";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
static void ccid_handle_control(USBDevice *dev, USBPacket *p, int request,
|
||||
int value, int index, int length, uint8_t *data)
|
||||
{
|
||||
USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
|
||||
int ret;
|
||||
|
||||
DPRINTF(s, 1, "got control %x, value %x\n", request, value);
|
||||
DPRINTF(s, 1, "%s: got control %s (%x), value %x\n", __func__,
|
||||
ccid_control_to_str(s, request), request, value);
|
||||
ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
|
||||
if (ret >= 0) {
|
||||
return;
|
||||
|
@ -649,15 +703,15 @@ static void ccid_handle_control(USBDevice *dev, USBPacket *p, int request,
|
|||
|
||||
switch (request) {
|
||||
/* Class specific requests. */
|
||||
case InterfaceOutClass | CCID_CONTROL_ABORT:
|
||||
case ClassInterfaceOutRequest | CCID_CONTROL_ABORT:
|
||||
DPRINTF(s, 1, "ccid_control abort UNIMPLEMENTED\n");
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
case InterfaceInClass | CCID_CONTROL_GET_CLOCK_FREQUENCIES:
|
||||
case ClassInterfaceRequest | CCID_CONTROL_GET_CLOCK_FREQUENCIES:
|
||||
DPRINTF(s, 1, "ccid_control get clock frequencies UNIMPLEMENTED\n");
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
case InterfaceInClass | CCID_CONTROL_GET_DATA_RATES:
|
||||
case ClassInterfaceRequest | CCID_CONTROL_GET_DATA_RATES:
|
||||
DPRINTF(s, 1, "ccid_control get data rates UNIMPLEMENTED\n");
|
||||
p->status = USB_RET_STALL;
|
||||
break;
|
||||
|
@ -691,7 +745,7 @@ static uint8_t ccid_calc_status(USBCCIDState *s)
|
|||
* bmCommandStatus
|
||||
*/
|
||||
uint8_t ret = ccid_card_status(s) | (s->bmCommandStatus << 6);
|
||||
DPRINTF(s, D_VERBOSE, "status = %d\n", ret);
|
||||
DPRINTF(s, D_VERBOSE, "%s: status = %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -733,7 +787,7 @@ static void ccid_write_parameters(USBCCIDState *s, CCID_Header *recv)
|
|||
h->b.bStatus = ccid_calc_status(s);
|
||||
h->b.bError = s->bError;
|
||||
h->bProtocolNum = s->bProtocolNum;
|
||||
memcpy(h->abProtocolDataStructure, s->abProtocolDataStructure, len);
|
||||
h->abProtocolDataStructure = s->abProtocolDataStructure;
|
||||
ccid_reset_error_status(s);
|
||||
}
|
||||
|
||||
|
@ -752,12 +806,18 @@ static void ccid_write_data_block(USBCCIDState *s, uint8_t slot, uint8_t seq,
|
|||
p->b.bStatus = ccid_calc_status(s);
|
||||
p->b.bError = s->bError;
|
||||
if (p->b.bError) {
|
||||
DPRINTF(s, D_VERBOSE, "error %d", p->b.bError);
|
||||
DPRINTF(s, D_VERBOSE, "error %d\n", p->b.bError);
|
||||
}
|
||||
memcpy(p->abData, data, len);
|
||||
ccid_reset_error_status(s);
|
||||
}
|
||||
|
||||
static void ccid_report_error_failed(USBCCIDState *s, uint8_t error)
|
||||
{
|
||||
s->bmCommandStatus = COMMAND_STATUS_FAILED;
|
||||
s->bError = error;
|
||||
}
|
||||
|
||||
static void ccid_write_data_block_answer(USBCCIDState *s,
|
||||
const uint8_t *data, uint32_t len)
|
||||
{
|
||||
|
@ -765,64 +825,103 @@ static void ccid_write_data_block_answer(USBCCIDState *s,
|
|||
uint8_t slot;
|
||||
|
||||
if (!ccid_has_pending_answers(s)) {
|
||||
abort();
|
||||
DPRINTF(s, D_WARN, "error: no pending answer to return to guest\n");
|
||||
ccid_report_error_failed(s, ERROR_ICC_MUTE);
|
||||
return;
|
||||
}
|
||||
ccid_remove_pending_answer(s, &slot, &seq);
|
||||
ccid_write_data_block(s, slot, seq, data, len);
|
||||
}
|
||||
|
||||
static uint8_t atr_get_protocol_num(const uint8_t *atr, uint32_t len)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (len < 2 || !(atr[1] & 0x80)) {
|
||||
/* too short or TD1 not included */
|
||||
return 0; /* T=0, default */
|
||||
}
|
||||
i = 1 + !!(atr[1] & 0x10) + !!(atr[1] & 0x20) + !!(atr[1] & 0x40);
|
||||
i += !!(atr[1] & 0x80);
|
||||
return atr[i] & 0x0f;
|
||||
}
|
||||
|
||||
static void ccid_write_data_block_atr(USBCCIDState *s, CCID_Header *recv)
|
||||
{
|
||||
const uint8_t *atr = NULL;
|
||||
uint32_t len = 0;
|
||||
uint8_t atr_protocol_num;
|
||||
CCID_T0ProtocolDataStructure *t0 = &s->abProtocolDataStructure.t0;
|
||||
CCID_T1ProtocolDataStructure *t1 = &s->abProtocolDataStructure.t1;
|
||||
|
||||
if (s->card) {
|
||||
atr = ccid_card_get_atr(s->card, &len);
|
||||
}
|
||||
atr_protocol_num = atr_get_protocol_num(atr, len);
|
||||
DPRINTF(s, D_VERBOSE, "%s: atr contains protocol=%d\n", __func__,
|
||||
atr_protocol_num);
|
||||
/* set parameters from ATR - see spec page 109 */
|
||||
s->bProtocolNum = (atr_protocol_num <= 1 ? atr_protocol_num
|
||||
: s->bProtocolNum);
|
||||
switch (atr_protocol_num) {
|
||||
case 0:
|
||||
/* TODO: unimplemented ATR T0 parameters */
|
||||
t0->bmFindexDindex = 0;
|
||||
t0->bmTCCKST0 = 0;
|
||||
t0->bGuardTimeT0 = 0;
|
||||
t0->bWaitingIntegerT0 = 0;
|
||||
t0->bClockStop = 0;
|
||||
break;
|
||||
case 1:
|
||||
/* TODO: unimplemented ATR T1 parameters */
|
||||
t1->bmFindexDindex = 0;
|
||||
t1->bmTCCKST1 = 0;
|
||||
t1->bGuardTimeT1 = 0;
|
||||
t1->bWaitingIntegerT1 = 0;
|
||||
t1->bClockStop = 0;
|
||||
t1->bIFSC = 0;
|
||||
t1->bNadValue = 0;
|
||||
break;
|
||||
default:
|
||||
DPRINTF(s, D_WARN, "%s: error: unsupported ATR protocol %d\n",
|
||||
__func__, atr_protocol_num);
|
||||
}
|
||||
ccid_write_data_block(s, recv->bSlot, recv->bSeq, atr, len);
|
||||
}
|
||||
|
||||
static void ccid_set_parameters(USBCCIDState *s, CCID_Header *recv)
|
||||
{
|
||||
CCID_SetParameters *ph = (CCID_SetParameters *) recv;
|
||||
uint32_t len = 0;
|
||||
if ((ph->bProtocolNum & 3) == 0) {
|
||||
len = 5;
|
||||
}
|
||||
if ((ph->bProtocolNum & 3) == 1) {
|
||||
len = 7;
|
||||
}
|
||||
if (len == 0) {
|
||||
s->bmCommandStatus = COMMAND_STATUS_FAILED;
|
||||
s->bError = 7; /* Protocol invalid or not supported */
|
||||
uint32_t protocol_num = ph->bProtocolNum & 3;
|
||||
|
||||
if (protocol_num != 0 && protocol_num != 1) {
|
||||
ccid_report_error_failed(s, ERROR_CMD_NOT_SUPPORTED);
|
||||
return;
|
||||
}
|
||||
s->bProtocolNum = ph->bProtocolNum;
|
||||
memcpy(s->abProtocolDataStructure, ph->abProtocolDataStructure, len);
|
||||
s->ulProtocolDataStructureSize = len;
|
||||
DPRINTF(s, 1, "%s: using len %d\n", __func__, len);
|
||||
s->bProtocolNum = protocol_num;
|
||||
s->abProtocolDataStructure = ph->abProtocolDataStructure;
|
||||
}
|
||||
|
||||
/*
|
||||
* must be 5 bytes for T=0, 7 bytes for T=1
|
||||
* See page 52
|
||||
*/
|
||||
static const uint8_t abDefaultProtocolDataStructure[7] = {
|
||||
0x77, 0x00, 0x00, 0x00, 0x00, 0xfe /*IFSC*/, 0x00 /*NAD*/ };
|
||||
static const CCID_ProtocolDataStructure defaultProtocolDataStructure = {
|
||||
.t1 = {
|
||||
.bmFindexDindex = 0x77,
|
||||
.bmTCCKST1 = 0x00,
|
||||
.bGuardTimeT1 = 0x00,
|
||||
.bWaitingIntegerT1 = 0x00,
|
||||
.bClockStop = 0x00,
|
||||
.bIFSC = 0xfe,
|
||||
.bNadValue = 0x00,
|
||||
}
|
||||
};
|
||||
|
||||
static void ccid_reset_parameters(USBCCIDState *s)
|
||||
{
|
||||
uint32_t len = sizeof(abDefaultProtocolDataStructure);
|
||||
|
||||
s->bProtocolNum = 1; /* T=1 */
|
||||
s->ulProtocolDataStructureSize = len;
|
||||
memcpy(s->abProtocolDataStructure, abDefaultProtocolDataStructure, len);
|
||||
}
|
||||
|
||||
static void ccid_report_error_failed(USBCCIDState *s, uint8_t error)
|
||||
{
|
||||
s->bmCommandStatus = COMMAND_STATUS_FAILED;
|
||||
s->bError = error;
|
||||
s->bProtocolNum = 0; /* T=0 */
|
||||
s->abProtocolDataStructure = defaultProtocolDataStructure;
|
||||
}
|
||||
|
||||
/* NOTE: only a single slot is supported (SLOT_0) */
|
||||
|
@ -869,6 +968,28 @@ static void ccid_on_apdu_from_guest(USBCCIDState *s, CCID_XferBlock *recv)
|
|||
}
|
||||
}
|
||||
|
||||
static const char *ccid_message_type_to_str(uint8_t type)
|
||||
{
|
||||
switch (type) {
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOn: return "IccPowerOn";
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOff: return "IccPowerOff";
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus: return "GetSlotStatus";
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_XfrBlock: return "XfrBlock";
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_GetParameters: return "GetParameters";
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_ResetParameters: return "ResetParameters";
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_SetParameters: return "SetParameters";
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_Escape: return "Escape";
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_IccClock: return "IccClock";
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_T0APDU: return "T0APDU";
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_Secure: return "Secure";
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_Mechanical: return "Mechanical";
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_Abort: return "Abort";
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_SetDataRateAndClockFrequency:
|
||||
return "SetDataRateAndClockFrequency";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
static void ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p)
|
||||
{
|
||||
CCID_Header *ccid_header;
|
||||
|
@ -891,13 +1012,15 @@ static void ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p)
|
|||
"%s: bad USB_TOKEN_OUT length, should be at least 10 bytes\n",
|
||||
__func__);
|
||||
} else {
|
||||
DPRINTF(s, D_MORE_INFO, "%s %x\n", __func__, ccid_header->bMessageType);
|
||||
DPRINTF(s, D_MORE_INFO, "%s %x %s\n", __func__,
|
||||
ccid_header->bMessageType,
|
||||
ccid_message_type_to_str(ccid_header->bMessageType));
|
||||
switch (ccid_header->bMessageType) {
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus:
|
||||
ccid_write_slot_status(s, ccid_header);
|
||||
break;
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOn:
|
||||
DPRINTF(s, 1, "PowerOn: %d\n",
|
||||
DPRINTF(s, 1, "%s: PowerOn: %d\n", __func__,
|
||||
((CCID_IccPowerOn *)(ccid_header))->bPowerSelect);
|
||||
s->powered = true;
|
||||
if (!ccid_card_inserted(s)) {
|
||||
|
@ -907,7 +1030,6 @@ static void ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p)
|
|||
ccid_write_data_block_atr(s, ccid_header);
|
||||
break;
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOff:
|
||||
DPRINTF(s, 1, "PowerOff\n");
|
||||
ccid_reset_error_status(s);
|
||||
s->powered = false;
|
||||
ccid_write_slot_status(s, ccid_header);
|
||||
|
@ -929,6 +1051,10 @@ static void ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p)
|
|||
ccid_reset_error_status(s);
|
||||
ccid_write_parameters(s, ccid_header);
|
||||
break;
|
||||
case CCID_MESSAGE_TYPE_PC_to_RDR_Mechanical:
|
||||
ccid_report_error_failed(s, 0);
|
||||
ccid_write_slot_status(s, ccid_header);
|
||||
break;
|
||||
default:
|
||||
DPRINTF(s, 1,
|
||||
"handle_data: ERROR: unhandled message type %Xh\n",
|
||||
|
@ -1068,7 +1194,9 @@ void ccid_card_send_apdu_to_guest(CCIDCardState *card,
|
|||
s->bmCommandStatus = COMMAND_STATUS_NO_ERROR;
|
||||
answer = ccid_peek_next_answer(s);
|
||||
if (answer == NULL) {
|
||||
abort();
|
||||
DPRINTF(s, D_WARN, "%s: error: unexpected lack of answer\n", __func__);
|
||||
ccid_report_error_failed(s, ERROR_HW_ERROR);
|
||||
return;
|
||||
}
|
||||
DPRINTF(s, 1, "APDU returned to guest %d (answer seq %d, slot %d)\n",
|
||||
len, answer->seq, answer->slot);
|
||||
|
@ -1201,6 +1329,7 @@ static int ccid_initfn(USBDevice *dev)
|
|||
s->bulk_out_pos = 0;
|
||||
ccid_reset_parameters(s);
|
||||
ccid_reset(s);
|
||||
s->debug = parse_debug_env("QEMU_CCID_DEBUG", D_VERBOSE, s->debug);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1285,7 +1414,7 @@ static VMStateDescription ccid_vmstate = {
|
|||
VMSTATE_UINT8(bError, USBCCIDState),
|
||||
VMSTATE_UINT8(bmCommandStatus, USBCCIDState),
|
||||
VMSTATE_UINT8(bProtocolNum, USBCCIDState),
|
||||
VMSTATE_BUFFER(abProtocolDataStructure, USBCCIDState),
|
||||
VMSTATE_BUFFER(abProtocolDataStructure.data, USBCCIDState),
|
||||
VMSTATE_UINT32(ulProtocolDataStructureSize, USBCCIDState),
|
||||
VMSTATE_STRUCT_ARRAY(bulk_in_pending, USBCCIDState,
|
||||
BULK_IN_PENDING_NUM, 1, bulk_in_vmstate, BulkIn),
|
||||
|
|
|
@ -482,4 +482,9 @@ can_use_buffer_find_nonzero_offset(const void *buf, size_t len)
|
|||
}
|
||||
size_t buffer_find_nonzero_offset(const void *buf, size_t len);
|
||||
|
||||
/*
|
||||
* helper to parse debug environment variables
|
||||
*/
|
||||
int parse_debug_env(const char *name, int max, int initial);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -12,13 +12,6 @@
|
|||
#include "vcard_emul.h"
|
||||
#include "card_7816.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;
|
||||
|
@ -47,41 +40,51 @@ static VCardStatus
|
|||
cac_common_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response)
|
||||
{
|
||||
int ef;
|
||||
VCardStatus ret = VCARD_FAIL;
|
||||
|
||||
switch (apdu->a_ins) {
|
||||
case VCARD7816_INS_SELECT_FILE:
|
||||
if (apdu->a_p1 != 0x02) {
|
||||
/* let the 7816 code handle applet switches */
|
||||
return VCARD_NEXT;
|
||||
ret = VCARD_NEXT;
|
||||
break;
|
||||
}
|
||||
/* handle file id setting */
|
||||
if (apdu->a_Lc != 2) {
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_DATA_INVALID);
|
||||
return VCARD_DONE;
|
||||
ret = VCARD_DONE;
|
||||
break;
|
||||
}
|
||||
/* CAC 1.0 only supports ef = 0 */
|
||||
ef = apdu->a_body[0] | (apdu->a_body[1] << 8);
|
||||
if (ef != 0) {
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_FILE_NOT_FOUND);
|
||||
return VCARD_DONE;
|
||||
ret = VCARD_DONE;
|
||||
break;
|
||||
}
|
||||
*response = vcard_make_response(VCARD7816_STATUS_SUCCESS);
|
||||
return VCARD_DONE;
|
||||
ret = VCARD_DONE;
|
||||
break;
|
||||
case VCARD7816_INS_GET_RESPONSE:
|
||||
case VCARD7816_INS_VERIFY:
|
||||
/* let the 7816 code handle these */
|
||||
return VCARD_NEXT;
|
||||
ret = VCARD_NEXT;
|
||||
break;
|
||||
case CAC_GET_PROPERTIES:
|
||||
case CAC_GET_ACR:
|
||||
/* skip these for now, this will probably be needed */
|
||||
*response = vcard_make_response(VCARD7816_STATUS_ERROR_P1_P2_INCORRECT);
|
||||
return VCARD_DONE;
|
||||
ret = VCARD_DONE;
|
||||
break;
|
||||
default:
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
|
||||
ret = VCARD_DONE;
|
||||
break;
|
||||
}
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
|
||||
return VCARD_DONE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -115,6 +118,7 @@ cac_applet_pki_process_apdu(VCard *card, VCardAPDU *apdu,
|
|||
int size, next;
|
||||
unsigned char *sign_buffer;
|
||||
vcard_7816_status_t status;
|
||||
VCardStatus ret = VCARD_FAIL;
|
||||
|
||||
applet_private = vcard_get_current_applet_private(card, apdu->a_channel);
|
||||
assert(applet_private);
|
||||
|
@ -124,7 +128,8 @@ cac_applet_pki_process_apdu(VCard *card, VCardAPDU *apdu,
|
|||
case CAC_UPDATE_BUFFER:
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED);
|
||||
return VCARD_DONE;
|
||||
ret = VCARD_DONE;
|
||||
break;
|
||||
case CAC_GET_CERTIFICATE:
|
||||
if ((apdu->a_p2 != 0) || (apdu->a_p1 != 0)) {
|
||||
*response = vcard_make_response(
|
||||
|
@ -154,7 +159,8 @@ cac_applet_pki_process_apdu(VCard *card, VCardAPDU *apdu,
|
|||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
|
||||
}
|
||||
return VCARD_DONE;
|
||||
ret = VCARD_DONE;
|
||||
break;
|
||||
case CAC_SIGN_DECRYPT:
|
||||
if (apdu->a_p2 != 0) {
|
||||
*response = vcard_make_response(
|
||||
|
@ -171,7 +177,8 @@ cac_applet_pki_process_apdu(VCard *card, VCardAPDU *apdu,
|
|||
pki_applet->sign_buffer_len = 0;
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
|
||||
return VCARD_DONE;
|
||||
ret = VCARD_DONE;
|
||||
break;
|
||||
}
|
||||
memcpy(sign_buffer+pki_applet->sign_buffer_len, apdu->a_body, size);
|
||||
size += pki_applet->sign_buffer_len;
|
||||
|
@ -182,7 +189,8 @@ cac_applet_pki_process_apdu(VCard *card, VCardAPDU *apdu,
|
|||
pki_applet->sign_buffer = sign_buffer;
|
||||
pki_applet->sign_buffer_len = size;
|
||||
*response = vcard_make_response(VCARD7816_STATUS_SUCCESS);
|
||||
return VCARD_DONE;
|
||||
ret = VCARD_DONE;
|
||||
break;
|
||||
case 0x00:
|
||||
/* we now have the whole buffer, do the operation, result will be
|
||||
* in the sign_buffer */
|
||||
|
@ -207,15 +215,20 @@ cac_applet_pki_process_apdu(VCard *card, VCardAPDU *apdu,
|
|||
g_free(sign_buffer);
|
||||
pki_applet->sign_buffer = NULL;
|
||||
pki_applet->sign_buffer_len = 0;
|
||||
return VCARD_DONE;
|
||||
ret = VCARD_DONE;
|
||||
break;
|
||||
case CAC_READ_BUFFER:
|
||||
/* new CAC call, go ahead and use the old version for now */
|
||||
/* TODO: implement */
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
|
||||
return VCARD_DONE;
|
||||
ret = VCARD_DONE;
|
||||
break;
|
||||
default:
|
||||
ret = cac_common_process_apdu(card, apdu, response);
|
||||
break;
|
||||
}
|
||||
return cac_common_process_apdu(card, apdu, response);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
@ -223,19 +236,26 @@ static VCardStatus
|
|||
cac_applet_id_process_apdu(VCard *card, VCardAPDU *apdu,
|
||||
VCardResponse **response)
|
||||
{
|
||||
VCardStatus ret = VCARD_FAIL;
|
||||
|
||||
switch (apdu->a_ins) {
|
||||
case CAC_UPDATE_BUFFER:
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED);
|
||||
return VCARD_DONE;
|
||||
ret = VCARD_DONE;
|
||||
break;
|
||||
case CAC_READ_BUFFER:
|
||||
/* new CAC call, go ahead and use the old version for now */
|
||||
/* TODO: implement */
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
|
||||
return VCARD_DONE;
|
||||
ret = VCARD_DONE;
|
||||
break;
|
||||
default:
|
||||
ret = cac_common_process_apdu(card, apdu, response);
|
||||
break;
|
||||
}
|
||||
return cac_common_process_apdu(card, apdu, response);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
@ -247,16 +267,20 @@ static VCardStatus
|
|||
cac_applet_container_process_apdu(VCard *card, VCardAPDU *apdu,
|
||||
VCardResponse **response)
|
||||
{
|
||||
VCardStatus ret = VCARD_FAIL;
|
||||
|
||||
switch (apdu->a_ins) {
|
||||
case CAC_READ_BUFFER:
|
||||
case CAC_UPDATE_BUFFER:
|
||||
*response = vcard_make_response(
|
||||
VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
|
||||
return VCARD_DONE;
|
||||
ret = VCARD_DONE;
|
||||
break;
|
||||
default:
|
||||
ret = cac_common_process_apdu(card, apdu, response);
|
||||
break;
|
||||
}
|
||||
return cac_common_process_apdu(card, apdu, response);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -9,6 +9,14 @@
|
|||
#define CAC_H 1
|
||||
#include "vcard.h"
|
||||
#include "vreader.h"
|
||||
|
||||
#define CAC_GET_PROPERTIES 0x56
|
||||
#define CAC_GET_ACR 0x4c
|
||||
#define CAC_READ_BUFFER 0x52
|
||||
#define CAC_UPDATE_BUFFER 0x58
|
||||
#define CAC_SIGN_DECRYPT 0x42
|
||||
#define CAC_GET_CERTIFICATE 0x36
|
||||
|
||||
/*
|
||||
* Initialize the cac card. This is the only public function in this file. All
|
||||
* the rest are connected through function pointers.
|
||||
|
|
|
@ -33,6 +33,9 @@
|
|||
#include "vreader.h"
|
||||
#include "vevent.h"
|
||||
|
||||
#include "libcacard/vcardt_internal.h"
|
||||
|
||||
|
||||
typedef enum {
|
||||
VCardEmulUnknown = -1,
|
||||
VCardEmulFalse = 0,
|
||||
|
@ -519,18 +522,23 @@ vcard_emul_reader_get_slot(VReader *vreader)
|
|||
}
|
||||
|
||||
/*
|
||||
* Card ATR's map to physical cards. VCARD_ATR_PREFIX will set appropriate
|
||||
* Card ATR's map to physical cards. vcard_alloc_atr 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' };
|
||||
static unsigned char *nss_atr;
|
||||
static int nss_atr_len;
|
||||
|
||||
void
|
||||
vcard_emul_get_atr(VCard *card, unsigned char *atr, int *atr_len)
|
||||
{
|
||||
int len = MIN(sizeof(nss_atr), *atr_len);
|
||||
int len;
|
||||
assert(atr != NULL);
|
||||
|
||||
if (nss_atr == NULL) {
|
||||
nss_atr = vcard_alloc_atr("NSS", &nss_atr_len);
|
||||
}
|
||||
len = MIN(nss_atr_len, *atr_len);
|
||||
memcpy(atr, nss_atr, len);
|
||||
*atr_len = len;
|
||||
}
|
||||
|
@ -870,7 +878,7 @@ VCardEmulError
|
|||
vcard_emul_init(const VCardEmulOptions *options)
|
||||
{
|
||||
SECStatus rv;
|
||||
PRBool ret, has_readers = PR_FALSE, need_coolkey_module;
|
||||
PRBool ret, has_readers = PR_FALSE;
|
||||
VReader *vreader;
|
||||
VReaderEmul *vreader_emul;
|
||||
SECMODListLock *module_lock;
|
||||
|
@ -893,7 +901,21 @@ vcard_emul_init(const VCardEmulOptions *options)
|
|||
if (options->nss_db) {
|
||||
rv = NSS_Init(options->nss_db);
|
||||
} else {
|
||||
rv = NSS_Init("sql:/etc/pki/nssdb");
|
||||
gchar *path;
|
||||
#ifndef _WIN32
|
||||
path = g_strdup("/etc/pki/nssdb");
|
||||
#else
|
||||
if (g_get_system_config_dirs() == NULL ||
|
||||
g_get_system_config_dirs()[0] == NULL) {
|
||||
return VCARD_EMUL_FAIL;
|
||||
}
|
||||
|
||||
path = g_build_filename(
|
||||
g_get_system_config_dirs()[0], "pki", "nssdb", NULL);
|
||||
#endif
|
||||
|
||||
rv = NSS_Init(path);
|
||||
g_free(path);
|
||||
}
|
||||
if (rv != SECSuccess) {
|
||||
return VCARD_EMUL_FAIL;
|
||||
|
@ -969,30 +991,15 @@ vcard_emul_init(const VCardEmulOptions *options)
|
|||
/* 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 */
|
||||
|
|
40
libcacard/vcardt.c
Normal file
40
libcacard/vcardt.c
Normal file
|
@ -0,0 +1,40 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include "libcacard/vcardt.h"
|
||||
|
||||
#include "libcacard/vcardt_internal.h"
|
||||
|
||||
/* create an ATR with appropriate historical bytes */
|
||||
#define ATR_TS_DIRECT_CONVENTION 0x3b
|
||||
#define ATR_TA_PRESENT 0x10
|
||||
#define ATR_TB_PRESENT 0x20
|
||||
#define ATR_TC_PRESENT 0x40
|
||||
#define ATR_TD_PRESENT 0x80
|
||||
|
||||
unsigned char *vcard_alloc_atr(const char *postfix, int *atr_len)
|
||||
{
|
||||
int postfix_len;
|
||||
const char prefix[] = "VCARD_";
|
||||
const char default_postfix[] = "DEFAULT";
|
||||
const int prefix_len = sizeof(prefix) - 1;
|
||||
int total_len;
|
||||
unsigned char *atr;
|
||||
|
||||
if (postfix == NULL) {
|
||||
postfix = default_postfix;
|
||||
}
|
||||
postfix_len = strlen(postfix);
|
||||
total_len = 3 + prefix_len + postfix_len;
|
||||
atr = g_malloc(total_len);
|
||||
atr[0] = ATR_TS_DIRECT_CONVENTION;
|
||||
atr[1] = ATR_TD_PRESENT + prefix_len + postfix_len;
|
||||
atr[2] = 0x00;
|
||||
memcpy(&atr[3], prefix, prefix_len);
|
||||
memcpy(&atr[3 + prefix_len], postfix, postfix_len);
|
||||
if (atr_len) {
|
||||
*atr_len = total_len;
|
||||
}
|
||||
return atr;
|
||||
}
|
|
@ -25,11 +25,6 @@ typedef struct VCardEmulStruct VCardEmul;
|
|||
|
||||
#define MAX_CHANNEL 4
|
||||
|
||||
/* create an ATR with appropriate historical bytes */
|
||||
#define VCARD_ATR_PREFIX(size) 0x3b, 0x68+(size), 0x00, 0xff, \
|
||||
'V', 'C', 'A', 'R', 'D', '_'
|
||||
|
||||
|
||||
typedef enum {
|
||||
VCARD_DONE,
|
||||
VCARD_NEXT,
|
||||
|
|
6
libcacard/vcardt_internal.h
Normal file
6
libcacard/vcardt_internal.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
#ifndef VCARDT_INTERNAL_H
|
||||
#define VCARDT_INTERNAL_H
|
||||
|
||||
unsigned char *vcard_alloc_atr(const char *postfix, int *atr_len);
|
||||
|
||||
#endif
|
|
@ -5,6 +5,12 @@
|
|||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifdef G_LOG_DOMAIN
|
||||
#undef G_LOG_DOMAIN
|
||||
#endif
|
||||
#define G_LOG_DOMAIN "libcacard"
|
||||
#include <glib.h>
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/thread.h"
|
||||
|
||||
|
@ -13,6 +19,9 @@
|
|||
#include "card_7816.h"
|
||||
#include "vreader.h"
|
||||
#include "vevent.h"
|
||||
#include "cac.h" /* just for debugging defines */
|
||||
|
||||
#define LIBCACARD_LOG_DOMAIN "libcacard"
|
||||
|
||||
struct VReaderStruct {
|
||||
int reference_count;
|
||||
|
@ -24,6 +33,66 @@ struct VReaderStruct {
|
|||
VReaderEmulFree reader_private_free;
|
||||
};
|
||||
|
||||
/*
|
||||
* Debug helpers
|
||||
*/
|
||||
|
||||
static const char *
|
||||
apdu_ins_to_string(int ins)
|
||||
{
|
||||
switch (ins) {
|
||||
case VCARD7816_INS_MANAGE_CHANNEL:
|
||||
return "manage channel";
|
||||
case VCARD7816_INS_EXTERNAL_AUTHENTICATE:
|
||||
return "external authenticate";
|
||||
case VCARD7816_INS_GET_CHALLENGE:
|
||||
return "get challenge";
|
||||
case VCARD7816_INS_INTERNAL_AUTHENTICATE:
|
||||
return "internal authenticate";
|
||||
case VCARD7816_INS_ERASE_BINARY:
|
||||
return "erase binary";
|
||||
case VCARD7816_INS_READ_BINARY:
|
||||
return "read binary";
|
||||
case VCARD7816_INS_WRITE_BINARY:
|
||||
return "write binary";
|
||||
case VCARD7816_INS_UPDATE_BINARY:
|
||||
return "update binary";
|
||||
case VCARD7816_INS_READ_RECORD:
|
||||
return "read record";
|
||||
case VCARD7816_INS_WRITE_RECORD:
|
||||
return "write record";
|
||||
case VCARD7816_INS_UPDATE_RECORD:
|
||||
return "update record";
|
||||
case VCARD7816_INS_APPEND_RECORD:
|
||||
return "append record";
|
||||
case VCARD7816_INS_ENVELOPE:
|
||||
return "envelope";
|
||||
case VCARD7816_INS_PUT_DATA:
|
||||
return "put data";
|
||||
case VCARD7816_INS_GET_DATA:
|
||||
return "get data";
|
||||
case VCARD7816_INS_SELECT_FILE:
|
||||
return "select file";
|
||||
case VCARD7816_INS_VERIFY:
|
||||
return "verify";
|
||||
case VCARD7816_INS_GET_RESPONSE:
|
||||
return "get response";
|
||||
case CAC_GET_PROPERTIES:
|
||||
return "get properties";
|
||||
case CAC_GET_ACR:
|
||||
return "get acr";
|
||||
case CAC_READ_BUFFER:
|
||||
return "read buffer";
|
||||
case CAC_UPDATE_BUFFER:
|
||||
return "update buffer";
|
||||
case CAC_SIGN_DECRYPT:
|
||||
return "sign decrypt";
|
||||
case CAC_GET_CERTIFICATE:
|
||||
return "get certificate";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
/* manage locking */
|
||||
static inline void
|
||||
vreader_lock(VReader *reader)
|
||||
|
@ -204,7 +273,15 @@ vreader_xfr_bytes(VReader *reader,
|
|||
response = vcard_make_response(status);
|
||||
card_status = VCARD_DONE;
|
||||
} else {
|
||||
g_debug("%s: CLS=0x%x,INS=0x%x,P1=0x%x,P2=0x%x,Lc=%d,Le=%d %s\n",
|
||||
__func__, apdu->a_cla, apdu->a_ins, apdu->a_p1, apdu->a_p2,
|
||||
apdu->a_Lc, apdu->a_Le, apdu_ins_to_string(apdu->a_ins));
|
||||
card_status = vcard_process_apdu(card, apdu, &response);
|
||||
if (response) {
|
||||
g_debug("%s: status=%d sw1=0x%x sw2=0x%x len=%d (total=%d)\n",
|
||||
__func__, response->b_status, response->b_sw1,
|
||||
response->b_sw2, response->b_len, response->b_total_len);
|
||||
}
|
||||
}
|
||||
assert(card_status == VCARD_DONE);
|
||||
if (card_status == VCARD_DONE) {
|
||||
|
|
|
@ -10,7 +10,10 @@
|
|||
* See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
#include <glib.h>
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/thread.h"
|
||||
|
@ -22,9 +25,7 @@
|
|||
#include "vcard_emul.h"
|
||||
#include "vevent.h"
|
||||
|
||||
int verbose;
|
||||
|
||||
int sock;
|
||||
static int verbose;
|
||||
|
||||
static void
|
||||
print_byte_array(
|
||||
|
@ -51,7 +52,47 @@ print_usage(void) {
|
|||
vcard_emul_usage();
|
||||
}
|
||||
|
||||
static QemuMutex write_lock;
|
||||
static GIOChannel *channel_socket;
|
||||
static GByteArray *socket_to_send;
|
||||
static QemuMutex socket_to_send_lock;
|
||||
static guint socket_tag;
|
||||
|
||||
static void
|
||||
update_socket_watch(gboolean out);
|
||||
|
||||
static gboolean
|
||||
do_socket_send(GIOChannel *source,
|
||||
GIOCondition condition,
|
||||
gpointer data)
|
||||
{
|
||||
gsize bw;
|
||||
GError *err = NULL;
|
||||
|
||||
g_return_val_if_fail(socket_to_send->len != 0, FALSE);
|
||||
g_return_val_if_fail(condition & G_IO_OUT, FALSE);
|
||||
|
||||
g_io_channel_write_chars(channel_socket,
|
||||
(gchar *)socket_to_send->data, socket_to_send->len, &bw, &err);
|
||||
if (err != NULL) {
|
||||
g_error("Error while sending socket %s", err->message);
|
||||
return FALSE;
|
||||
}
|
||||
g_byte_array_remove_range(socket_to_send, 0, bw);
|
||||
|
||||
if (socket_to_send->len == 0) {
|
||||
update_socket_watch(FALSE);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
socket_prepare_sending(gpointer user_data)
|
||||
{
|
||||
update_socket_watch(TRUE);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int
|
||||
send_msg(
|
||||
|
@ -60,10 +101,9 @@ send_msg(
|
|||
const void *msg,
|
||||
unsigned int length
|
||||
) {
|
||||
int rv;
|
||||
VSCMsgHeader mhHeader;
|
||||
|
||||
qemu_mutex_lock(&write_lock);
|
||||
qemu_mutex_lock(&socket_to_send_lock);
|
||||
|
||||
if (verbose > 10) {
|
||||
printf("sending type=%d id=%u, len =%u (0x%x)\n",
|
||||
|
@ -73,23 +113,11 @@ send_msg(
|
|||
mhHeader.type = htonl(type);
|
||||
mhHeader.reader_id = 0;
|
||||
mhHeader.length = htonl(length);
|
||||
rv = write(sock, &mhHeader, sizeof(mhHeader));
|
||||
if (rv < 0) {
|
||||
/* Error */
|
||||
fprintf(stderr, "write header error\n");
|
||||
close(sock);
|
||||
qemu_mutex_unlock(&write_lock);
|
||||
return 16;
|
||||
}
|
||||
rv = write(sock, msg, length);
|
||||
if (rv < 0) {
|
||||
/* Error */
|
||||
fprintf(stderr, "write error\n");
|
||||
close(sock);
|
||||
qemu_mutex_unlock(&write_lock);
|
||||
return 16;
|
||||
}
|
||||
qemu_mutex_unlock(&write_lock);
|
||||
g_byte_array_append(socket_to_send, (guint8 *)&mhHeader, sizeof(mhHeader));
|
||||
g_byte_array_append(socket_to_send, (guint8 *)msg, length);
|
||||
g_idle_add(socket_prepare_sending, NULL);
|
||||
|
||||
qemu_mutex_unlock(&socket_to_send_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -211,18 +239,237 @@ get_id_from_string(char *string, unsigned int default_id)
|
|||
return id;
|
||||
}
|
||||
|
||||
static void
|
||||
do_command(void)
|
||||
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;
|
||||
QemuThread 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 */
|
||||
qemu_thread_create(&thread_id, event_thread, NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
enum {
|
||||
STATE_HEADER,
|
||||
STATE_MESSAGE,
|
||||
};
|
||||
|
||||
#define APDUBufSize 270
|
||||
|
||||
static gboolean
|
||||
do_socket_read(GIOChannel *source,
|
||||
GIOCondition condition,
|
||||
gpointer data)
|
||||
{
|
||||
int rv;
|
||||
int dwSendLength;
|
||||
int dwRecvLength;
|
||||
uint8_t pbRecvBuffer[APDUBufSize];
|
||||
static uint8_t pbSendBuffer[APDUBufSize];
|
||||
VReaderStatus reader_status;
|
||||
VReader *reader = NULL;
|
||||
static VSCMsgHeader mhHeader;
|
||||
VSCMsgError *error_msg;
|
||||
GError *err = NULL;
|
||||
|
||||
static gchar *buf;
|
||||
static gsize br, to_read;
|
||||
static int state = STATE_HEADER;
|
||||
|
||||
if (state == STATE_HEADER && to_read == 0) {
|
||||
buf = (gchar *)&mhHeader;
|
||||
to_read = sizeof(mhHeader);
|
||||
}
|
||||
|
||||
if (to_read > 0) {
|
||||
g_io_channel_read_chars(source, (gchar *)buf, to_read, &br, &err);
|
||||
if (err != NULL) {
|
||||
g_error("error while reading: %s", err->message);
|
||||
}
|
||||
buf += br;
|
||||
to_read -= br;
|
||||
if (to_read != 0) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (state == STATE_HEADER) {
|
||||
mhHeader.type = ntohl(mhHeader.type);
|
||||
mhHeader.reader_id = ntohl(mhHeader.reader_id);
|
||||
mhHeader.length = ntohl(mhHeader.length);
|
||||
if (verbose) {
|
||||
printf("Header: type=%d, reader_id=%u length=%d (0x%x)\n",
|
||||
mhHeader.type, mhHeader.reader_id, mhHeader.length,
|
||||
mhHeader.length);
|
||||
}
|
||||
switch (mhHeader.type) {
|
||||
case VSC_APDU:
|
||||
case VSC_Flush:
|
||||
case VSC_Error:
|
||||
case VSC_Init:
|
||||
buf = (gchar *)pbSendBuffer;
|
||||
to_read = mhHeader.length;
|
||||
state = STATE_MESSAGE;
|
||||
return TRUE;
|
||||
default:
|
||||
fprintf(stderr, "Unexpected message of type 0x%X\n", mhHeader.type);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (state == STATE_MESSAGE) {
|
||||
switch (mhHeader.type) {
|
||||
case VSC_APDU:
|
||||
if (verbose) {
|
||||
printf(" recv APDU: ");
|
||||
print_byte_array(pbSendBuffer, mhHeader.length);
|
||||
}
|
||||
/* Transmit received APDU */
|
||||
dwSendLength = mhHeader.length;
|
||||
dwRecvLength = sizeof(pbRecvBuffer);
|
||||
reader = vreader_get_reader_by_id(mhHeader.reader_id);
|
||||
reader_status = vreader_xfr_bytes(reader,
|
||||
pbSendBuffer, dwSendLength,
|
||||
pbRecvBuffer, &dwRecvLength);
|
||||
if (reader_status == VREADER_OK) {
|
||||
mhHeader.length = dwRecvLength;
|
||||
if (verbose) {
|
||||
printf(" send response: ");
|
||||
print_byte_array(pbRecvBuffer, mhHeader.length);
|
||||
}
|
||||
send_msg(VSC_APDU, mhHeader.reader_id,
|
||||
pbRecvBuffer, dwRecvLength);
|
||||
} else {
|
||||
rv = reader_status; /* warning: not meaningful */
|
||||
send_msg(VSC_Error, mhHeader.reader_id, &rv, sizeof(uint32_t));
|
||||
}
|
||||
vreader_free(reader);
|
||||
reader = NULL; /* we've freed it, don't use it by accident
|
||||
again */
|
||||
break;
|
||||
case VSC_Flush:
|
||||
/* TODO: actually flush */
|
||||
send_msg(VSC_FlushComplete, mhHeader.reader_id, NULL, 0);
|
||||
break;
|
||||
case VSC_Error:
|
||||
error_msg = (VSCMsgError *) pbSendBuffer;
|
||||
if (error_msg->code == VSC_SUCCESS) {
|
||||
qemu_mutex_lock(&pending_reader_lock);
|
||||
if (pending_reader) {
|
||||
vreader_set_id(pending_reader, mhHeader.reader_id);
|
||||
vreader_free(pending_reader);
|
||||
pending_reader = NULL;
|
||||
qemu_cond_signal(&pending_reader_condition);
|
||||
}
|
||||
qemu_mutex_unlock(&pending_reader_lock);
|
||||
break;
|
||||
}
|
||||
printf("warning: qemu refused to add reader\n");
|
||||
if (error_msg->code == VSC_CANNOT_ADD_MORE_READERS) {
|
||||
/* clear pending reader, qemu can't handle any more */
|
||||
qemu_mutex_lock(&pending_reader_lock);
|
||||
if (pending_reader) {
|
||||
pending_reader = NULL;
|
||||
/* make sure the event loop doesn't hang */
|
||||
qemu_cond_signal(&pending_reader_condition);
|
||||
}
|
||||
qemu_mutex_unlock(&pending_reader_lock);
|
||||
}
|
||||
break;
|
||||
case VSC_Init:
|
||||
if (on_host_init(&mhHeader, (VSCMsgInit *)pbSendBuffer) < 0) {
|
||||
return FALSE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_warn_if_reached();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
state = STATE_HEADER;
|
||||
}
|
||||
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
do_socket(GIOChannel *source,
|
||||
GIOCondition condition,
|
||||
gpointer data)
|
||||
{
|
||||
/* not sure if two watches work well with a single win32 sources */
|
||||
if (condition & G_IO_OUT) {
|
||||
if (!do_socket_send(source, condition, data)) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (condition & G_IO_IN) {
|
||||
if (!do_socket_read(source, condition, data)) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
update_socket_watch(gboolean out)
|
||||
{
|
||||
if (socket_tag != 0) {
|
||||
g_source_remove(socket_tag);
|
||||
}
|
||||
|
||||
socket_tag = g_io_add_watch(channel_socket,
|
||||
G_IO_IN | (out ? G_IO_OUT : 0), do_socket, NULL);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
do_command(GIOChannel *source,
|
||||
GIOCondition condition,
|
||||
gpointer data)
|
||||
{
|
||||
char inbuf[255];
|
||||
char *string;
|
||||
VCardEmulError error;
|
||||
static unsigned int default_reader_id;
|
||||
unsigned int reader_id;
|
||||
VReader *reader = NULL;
|
||||
GError *err = NULL;
|
||||
|
||||
g_assert(condition & G_IO_IN);
|
||||
|
||||
reader_id = default_reader_id;
|
||||
string = fgets(inbuf, sizeof(inbuf), stdin);
|
||||
g_io_channel_read_line(source, &string, NULL, NULL, &err);
|
||||
if (err != NULL) {
|
||||
g_error("Error while reading command: %s", err->message);
|
||||
}
|
||||
|
||||
if (string != NULL) {
|
||||
if (strncmp(string, "exit", 4) == 0) {
|
||||
/* remove all the readers */
|
||||
|
@ -336,11 +583,11 @@ do_command(void)
|
|||
vreader_free(reader);
|
||||
printf("> ");
|
||||
fflush(stdout);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
#define APDUBufSize 270
|
||||
|
||||
/* just for ease of parsing command line arguments. */
|
||||
#define MAX_CERTS 100
|
||||
|
||||
|
@ -351,7 +598,7 @@ connect_to_qemu(
|
|||
) {
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *server;
|
||||
int ret;
|
||||
int ret, sock;
|
||||
|
||||
sock = qemu_socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock < 0) {
|
||||
|
@ -385,67 +632,26 @@ connect_to_qemu(
|
|||
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[]
|
||||
) {
|
||||
GMainLoop *loop;
|
||||
GIOChannel *channel_stdin;
|
||||
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;
|
||||
|
||||
char *cert_names[MAX_CERTS];
|
||||
char *emul_args = NULL;
|
||||
int cert_count = 0;
|
||||
int c;
|
||||
int c, sock;
|
||||
|
||||
if (socket_init() != 0)
|
||||
return 1;
|
||||
|
||||
while ((c = getopt(argc, argv, "c:e:pd:")) != -1) {
|
||||
switch (c) {
|
||||
|
@ -511,15 +717,33 @@ main(
|
|||
exit(5);
|
||||
}
|
||||
|
||||
qemu_mutex_init(&write_lock);
|
||||
socket_to_send = g_byte_array_new();
|
||||
qemu_mutex_init(&socket_to_send_lock);
|
||||
qemu_mutex_init(&pending_reader_lock);
|
||||
qemu_cond_init(&pending_reader_condition);
|
||||
|
||||
vcard_emul_init(command_line_options);
|
||||
|
||||
loop = g_main_loop_new(NULL, true);
|
||||
|
||||
printf("> ");
|
||||
fflush(stdout);
|
||||
|
||||
#ifdef _WIN32
|
||||
channel_stdin = g_io_channel_win32_new_fd(STDIN_FILENO);
|
||||
#else
|
||||
channel_stdin = g_io_channel_unix_new(STDIN_FILENO);
|
||||
#endif
|
||||
g_io_add_watch(channel_stdin, G_IO_IN, do_command, NULL);
|
||||
#ifdef _WIN32
|
||||
channel_socket = g_io_channel_win32_new_socket(sock);
|
||||
#else
|
||||
channel_socket = g_io_channel_unix_new(sock);
|
||||
#endif
|
||||
g_io_channel_set_encoding(channel_socket, NULL, NULL);
|
||||
/* we buffer ourself for thread safety reasons */
|
||||
g_io_channel_set_buffered(channel_socket, FALSE);
|
||||
|
||||
/* Send init message, Host responds (and then we send reader attachments) */
|
||||
VSCMsgInit init = {
|
||||
.version = htonl(VSCARD_VERSION),
|
||||
|
@ -528,130 +752,12 @@ main(
|
|||
};
|
||||
send_msg(VSC_Init, mhHeader.reader_id, &init, sizeof(init));
|
||||
|
||||
do {
|
||||
fd_set fds;
|
||||
g_main_loop_run(loop);
|
||||
g_main_loop_unref(loop);
|
||||
|
||||
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 {
|
||||
fprintf(stderr, "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=%u length=%d (0x%x)\n",
|
||||
mhHeader.type, mhHeader.reader_id, mhHeader.length,
|
||||
mhHeader.length);
|
||||
}
|
||||
switch (mhHeader.type) {
|
||||
case VSC_APDU:
|
||||
case VSC_Flush:
|
||||
case VSC_Error:
|
||||
case VSC_Init:
|
||||
rv = read(sock, pbSendBuffer, mhHeader.length);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unexpected message of type 0x%X\n", mhHeader.type);
|
||||
return 0;
|
||||
}
|
||||
switch (mhHeader.type) {
|
||||
case VSC_APDU:
|
||||
if (rv < 0) {
|
||||
/* Error */
|
||||
fprintf(stderr, "read error\n");
|
||||
close(sock);
|
||||
return 8;
|
||||
}
|
||||
if (verbose) {
|
||||
printf(" recv APDU: ");
|
||||
print_byte_array(pbSendBuffer, mhHeader.length);
|
||||
}
|
||||
/* Transmit received APDU */
|
||||
dwSendLength = mhHeader.length;
|
||||
dwRecvLength = sizeof(pbRecvBuffer);
|
||||
reader = vreader_get_reader_by_id(mhHeader.reader_id);
|
||||
reader_status = vreader_xfr_bytes(reader,
|
||||
pbSendBuffer, dwSendLength,
|
||||
pbRecvBuffer, &dwRecvLength);
|
||||
if (reader_status == VREADER_OK) {
|
||||
mhHeader.length = dwRecvLength;
|
||||
if (verbose) {
|
||||
printf(" send response: ");
|
||||
print_byte_array(pbRecvBuffer, mhHeader.length);
|
||||
}
|
||||
send_msg(VSC_APDU, mhHeader.reader_id,
|
||||
pbRecvBuffer, dwRecvLength);
|
||||
} else {
|
||||
rv = reader_status; /* warning: not meaningful */
|
||||
send_msg(VSC_Error, mhHeader.reader_id, &rv, sizeof(uint32_t));
|
||||
}
|
||||
vreader_free(reader);
|
||||
reader = NULL; /* we've freed it, don't use it by accident
|
||||
again */
|
||||
break;
|
||||
case VSC_Flush:
|
||||
/* TODO: actually flush */
|
||||
send_msg(VSC_FlushComplete, mhHeader.reader_id, NULL, 0);
|
||||
break;
|
||||
case VSC_Error:
|
||||
error_msg = (VSCMsgError *) pbSendBuffer;
|
||||
if (error_msg->code == VSC_SUCCESS) {
|
||||
qemu_mutex_lock(&pending_reader_lock);
|
||||
if (pending_reader) {
|
||||
vreader_set_id(pending_reader, mhHeader.reader_id);
|
||||
vreader_free(pending_reader);
|
||||
pending_reader = NULL;
|
||||
qemu_cond_signal(&pending_reader_condition);
|
||||
}
|
||||
qemu_mutex_unlock(&pending_reader_lock);
|
||||
break;
|
||||
}
|
||||
printf("warning: qemu refused to add reader\n");
|
||||
if (error_msg->code == VSC_CANNOT_ADD_MORE_READERS) {
|
||||
/* clear pending reader, qemu can't handle any more */
|
||||
qemu_mutex_lock(&pending_reader_lock);
|
||||
if (pending_reader) {
|
||||
pending_reader = NULL;
|
||||
/* make sure the event loop doesn't hang */
|
||||
qemu_cond_signal(&pending_reader_condition);
|
||||
}
|
||||
qemu_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);
|
||||
g_io_channel_unref(channel_stdin);
|
||||
g_io_channel_unref(channel_socket);
|
||||
g_byte_array_unref(socket_to_send);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -35,7 +35,9 @@ LIBTOOL += $(if $(V),,--quiet)
|
|||
LINK = $(call quiet-command,\
|
||||
$(if $(filter %.lo %.la,$^),$(LIBTOOL) --mode=link --tag=CC \
|
||||
)$(CC) $(QEMU_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ \
|
||||
$(sort $(filter %.o, $1)) $(filter-out %.o, $1) $(version-obj-y) \
|
||||
$(sort $(filter %.o, $1)) $(filter-out %.o, $1) \
|
||||
$(if $(filter %.lo %.la,$^),$(version-lobj-y),$(version-obj-y)) \
|
||||
$(if $(filter %.lo %.la,$^),$(LIBTOOLFLAGS)) \
|
||||
$(LIBS),$(if $(filter %.lo %.la,$^),"lt LINK ", " LINK ")"$(TARGET_DIR)$@")
|
||||
endif
|
||||
|
||||
|
|
|
@ -482,3 +482,26 @@ int uleb128_decode_small(const uint8_t *in, uint32_t *n)
|
|||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* helper to parse debug environment variables
|
||||
*/
|
||||
int parse_debug_env(const char *name, int max, int initial)
|
||||
{
|
||||
char *debug_env = getenv(name);
|
||||
char *inv = NULL;
|
||||
int debug;
|
||||
|
||||
if (!debug_env) {
|
||||
return initial;
|
||||
}
|
||||
debug = strtol(debug_env, &inv, 10);
|
||||
if (inv == debug_env) {
|
||||
return initial;
|
||||
}
|
||||
if (debug < 0 || debug > max) {
|
||||
fprintf(stderr, "warning: %s not in [0, %d]", name, max);
|
||||
return initial;
|
||||
}
|
||||
return debug;
|
||||
}
|
||||
|
|
23
util/osdep.c
23
util/osdep.c
|
@ -406,3 +406,26 @@ bool fips_get_state(void)
|
|||
return fips_enabled;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static void socket_cleanup(void)
|
||||
{
|
||||
WSACleanup();
|
||||
}
|
||||
#endif
|
||||
|
||||
int socket_init(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WSADATA Data;
|
||||
int ret, err;
|
||||
|
||||
ret = WSAStartup(MAKEWORD(2, 2), &Data);
|
||||
if (ret != 0) {
|
||||
err = WSAGetLastError();
|
||||
fprintf(stderr, "WSAStartup: %d\n", err);
|
||||
return -1;
|
||||
}
|
||||
atexit(socket_cleanup);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -974,27 +974,3 @@ int socket_dgram(SocketAddress *remote, SocketAddress *local, Error **errp)
|
|||
qemu_opts_del(opts);
|
||||
return fd;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static void socket_cleanup(void)
|
||||
{
|
||||
WSACleanup();
|
||||
}
|
||||
#endif
|
||||
|
||||
int socket_init(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WSADATA Data;
|
||||
int ret, err;
|
||||
|
||||
ret = WSAStartup(MAKEWORD(2,2), &Data);
|
||||
if (ret != 0) {
|
||||
err = WSAGetLastError();
|
||||
fprintf(stderr, "WSAStartup: %d\n", err);
|
||||
return -1;
|
||||
}
|
||||
atexit(socket_cleanup);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue