diff options
| author | fishsoupisgood <github@madingley.org> | 2019-04-29 01:17:54 +0100 | 
|---|---|---|
| committer | fishsoupisgood <github@madingley.org> | 2019-05-27 03:43:43 +0100 | 
| commit | 3f2546b2ef55b661fd8dd69682b38992225e86f6 (patch) | |
| tree | 65ca85f13617aee1dce474596800950f266a456c /libcacard | |
| download | qemu-master.tar.gz qemu-master.tar.bz2 qemu-master.zip  | |
Diffstat (limited to 'libcacard')
| -rw-r--r-- | libcacard/Makefile | 45 | ||||
| -rw-r--r-- | libcacard/cac.c | 414 | ||||
| -rw-r--r-- | libcacard/cac.h | 31 | ||||
| -rw-r--r-- | libcacard/card_7816.c | 757 | ||||
| -rw-r--r-- | libcacard/card_7816.h | 62 | ||||
| -rw-r--r-- | libcacard/card_7816t.h | 165 | ||||
| -rw-r--r-- | libcacard/event.c | 103 | ||||
| -rw-r--r-- | libcacard/eventt.h | 29 | ||||
| -rw-r--r-- | libcacard/libcacard.pc.in | 13 | ||||
| -rw-r--r-- | libcacard/libcacard.syms | 77 | ||||
| -rw-r--r-- | libcacard/link_test.c | 22 | ||||
| -rw-r--r-- | libcacard/vcard.c | 325 | ||||
| -rw-r--r-- | libcacard/vcard.h | 86 | ||||
| -rw-r--r-- | libcacard/vcard_emul.h | 66 | ||||
| -rw-r--r-- | libcacard/vcard_emul_nss.c | 1274 | ||||
| -rw-r--r-- | libcacard/vcard_emul_type.c | 57 | ||||
| -rw-r--r-- | libcacard/vcard_emul_type.h | 32 | ||||
| -rw-r--r-- | libcacard/vcardt.c | 40 | ||||
| -rw-r--r-- | libcacard/vcardt.h | 59 | ||||
| -rw-r--r-- | libcacard/vcardt_internal.h | 6 | ||||
| -rw-r--r-- | libcacard/vevent.h | 27 | ||||
| -rw-r--r-- | libcacard/vreader.c | 578 | ||||
| -rw-r--r-- | libcacard/vreader.h | 55 | ||||
| -rw-r--r-- | libcacard/vreadert.h | 24 | ||||
| -rw-r--r-- | libcacard/vscard_common.h | 178 | ||||
| -rw-r--r-- | libcacard/vscclient.c | 785 | 
26 files changed, 5310 insertions, 0 deletions
diff --git a/libcacard/Makefile b/libcacard/Makefile new file mode 100644 index 00000000..b5eddff1 --- /dev/null +++ b/libcacard/Makefile @@ -0,0 +1,45 @@ +libcacard_includedir=$(includedir)/cacard + +TOOLS += vscclient$(EXESUF) + +# objects linked into a shared library, built with libtool with -fPIC if required +libcacard-obj-y = $(libcacard-y) +libcacard-lobj-y=$(patsubst %.o,%.lo,$(libcacard-obj-y)) + +# libtool will build the .o files, too +$(libcacard-obj-y): | $(libcacard-lobj-y) + +all: libcacard.la libcacard.pc + +vscclient$(EXESUF): libcacard/vscclient.o libcacard.la +	$(call LINK,$^) + +######################################################################### +# Rules for building libcacard standalone library + +libcacard.la: LDFLAGS += -rpath $(libdir) -no-undefined \ +	-export-symbols $(SRC_PATH)/libcacard/libcacard.syms +# Prevent libcacard.so linking against the entire world of 3rd party libs +libcacard.la: LIBS = +libcacard.la: $(libcacard-lobj-y) +	$(call LINK,$^) + +libcacard.pc: $(SRC_PATH)/libcacard/libcacard.pc.in +	$(call quiet-command,sed -e 's|@LIBDIR@|$(libdir)|' \ +		-e 's|@INCLUDEDIR@|$(libcacard_includedir)|' \ +	    -e 's|@VERSION@|$(shell cat $(SRC_PATH)/VERSION)|' \ +		-e 's|@PREFIX@|$(prefix)|' $< > libcacard.pc,\ +	"  GEN   $@") + +.PHONY: install-libcacard + +install: install-libcacard +install-libcacard: libcacard.pc libcacard.la +	$(INSTALL_DIR) "$(DESTDIR)$(libdir)" +	$(INSTALL_DIR) "$(DESTDIR)$(libdir)/pkgconfig" +	$(INSTALL_DIR) "$(DESTDIR)$(libcacard_includedir)" +	$(INSTALL_LIB) libcacard.la "$(DESTDIR)$(libdir)" +	$(INSTALL_DATA) libcacard.pc "$(DESTDIR)$(libdir)/pkgconfig" +	for inc in $(SRC_PATH)/libcacard/*.h; do \ +		$(INSTALL_DATA) $$inc "$(DESTDIR)$(libcacard_includedir)"; \ +	done diff --git a/libcacard/cac.c b/libcacard/cac.c new file mode 100644 index 00000000..bc84534f --- /dev/null +++ b/libcacard/cac.c @@ -0,0 +1,414 @@ +/* + * implement the applets for the CAC card. + * + * This code is licensed under the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "glib-compat.h" + +#include <string.h> +#include <stdbool.h> + +#include "cac.h" +#include "vcard.h" +#include "vcard_emul.h" +#include "card_7816.h" + +/* private data for PKI applets */ +typedef struct CACPKIAppletDataStruct { +    unsigned char *cert; +    int cert_len; +    unsigned char *cert_buffer; +    int cert_buffer_len; +    unsigned char *sign_buffer; +    int sign_buffer_len; +    VCardKey *key; +} CACPKIAppletData; + +/* + * CAC applet private data + */ +struct VCardAppletPrivateStruct { +    union { +        CACPKIAppletData pki_data; +        void *reserved; +    } u; +}; + +/* + * handle all the APDU's that are common to all CAC applets + */ +static VCardStatus +cac_common_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response) +{ +    int ef; +    VCardStatus ret = VCARD_FAIL; + +    switch (apdu->a_ins) { +    case VCARD7816_INS_SELECT_FILE: +        if (apdu->a_p1 != 0x02) { +            /* let the 7816 code handle applet switches */ +            ret = VCARD_NEXT; +            break; +        } +        /* handle file id setting */ +        if (apdu->a_Lc != 2) { +            *response = vcard_make_response( +                VCARD7816_STATUS_ERROR_DATA_INVALID); +            ret = VCARD_DONE; +            break; +        } +        /* CAC 1.0 only supports ef = 0 */ +        ef = apdu->a_body[0] | (apdu->a_body[1] << 8); +        if (ef != 0) { +            *response = vcard_make_response( +                VCARD7816_STATUS_ERROR_FILE_NOT_FOUND); +            ret = VCARD_DONE; +            break; +        } +        *response = vcard_make_response(VCARD7816_STATUS_SUCCESS); +        ret = VCARD_DONE; +        break; +    case VCARD7816_INS_GET_RESPONSE: +    case VCARD7816_INS_VERIFY: +        /* let the 7816 code handle these */ +        ret = VCARD_NEXT; +        break; +    case CAC_GET_PROPERTIES: +    case CAC_GET_ACR: +        /* skip these for now, this will probably be needed */ +        *response = vcard_make_response(VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); +        ret = VCARD_DONE; +        break; +    default: +        *response = vcard_make_response( +            VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); +        ret = VCARD_DONE; +        break; +    } +    return ret; +} + +/* + *  reset the inter call state between applet selects + */ +static VCardStatus +cac_applet_pki_reset(VCard *card, int channel) +{ +    VCardAppletPrivate *applet_private; +    CACPKIAppletData *pki_applet; +    applet_private = vcard_get_current_applet_private(card, channel); +    assert(applet_private); +    pki_applet = &(applet_private->u.pki_data); + +    pki_applet->cert_buffer = NULL; +    g_free(pki_applet->sign_buffer); +    pki_applet->sign_buffer = NULL; +    pki_applet->cert_buffer_len = 0; +    pki_applet->sign_buffer_len = 0; +    return VCARD_DONE; +} + +static VCardStatus +cac_applet_pki_process_apdu(VCard *card, VCardAPDU *apdu, +                            VCardResponse **response) +{ +    CACPKIAppletData *pki_applet; +    VCardAppletPrivate *applet_private; +    int size, next; +    unsigned char *sign_buffer; +    bool retain_sign_buffer = FALSE; +    vcard_7816_status_t status; +    VCardStatus ret = VCARD_FAIL; + +    applet_private = vcard_get_current_applet_private(card, apdu->a_channel); +    assert(applet_private); +    pki_applet = &(applet_private->u.pki_data); + +    switch (apdu->a_ins) { +    case CAC_UPDATE_BUFFER: +        *response = vcard_make_response( +            VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED); +        ret = VCARD_DONE; +        break; +    case CAC_GET_CERTIFICATE: +        if ((apdu->a_p2 != 0) || (apdu->a_p1 != 0)) { +            *response = vcard_make_response( +                             VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); +            break; +        } +        assert(pki_applet->cert != NULL); +        size = apdu->a_Le; +        if (pki_applet->cert_buffer == NULL) { +            pki_applet->cert_buffer = pki_applet->cert; +            pki_applet->cert_buffer_len = pki_applet->cert_len; +        } +        size = MIN(size, pki_applet->cert_buffer_len); +        next = MIN(255, pki_applet->cert_buffer_len - size); +        *response = vcard_response_new_bytes( +                        card, pki_applet->cert_buffer, size, +                        apdu->a_Le, next ? +                        VCARD7816_SW1_WARNING_CHANGE : +                        VCARD7816_SW1_SUCCESS, +                        next); +        pki_applet->cert_buffer += size; +        pki_applet->cert_buffer_len -= size; +        if ((*response == NULL) || (next == 0)) { +            pki_applet->cert_buffer = NULL; +        } +        if (*response == NULL) { +            *response = vcard_make_response( +                            VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); +        } +        ret = VCARD_DONE; +        break; +    case CAC_SIGN_DECRYPT: +        if (apdu->a_p2 != 0) { +            *response = vcard_make_response( +                             VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); +            break; +        } +        size = apdu->a_Lc; + +        sign_buffer = g_realloc(pki_applet->sign_buffer, +                                pki_applet->sign_buffer_len + size); +        memcpy(sign_buffer+pki_applet->sign_buffer_len, apdu->a_body, size); +        size += pki_applet->sign_buffer_len; +        switch (apdu->a_p1) { +        case  0x80: +            /* p1 == 0x80 means we haven't yet sent the whole buffer, wait for +             * the rest */ +            pki_applet->sign_buffer = sign_buffer; +            pki_applet->sign_buffer_len = size; +            *response = vcard_make_response(VCARD7816_STATUS_SUCCESS); +            retain_sign_buffer = TRUE; +            break; +        case 0x00: +            /* we now have the whole buffer, do the operation, result will be +             * in the sign_buffer */ +            status = vcard_emul_rsa_op(card, pki_applet->key, +                                       sign_buffer, size); +            if (status != VCARD7816_STATUS_SUCCESS) { +                *response = vcard_make_response(status); +                break; +            } +            *response = vcard_response_new(card, sign_buffer, size, apdu->a_Le, +                                                     VCARD7816_STATUS_SUCCESS); +            if (*response == NULL) { +                *response = vcard_make_response( +                                VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); +            } +            break; +        default: +           *response = vcard_make_response( +                                VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); +            break; +        } +        if (!retain_sign_buffer) { +            g_free(sign_buffer); +            pki_applet->sign_buffer = NULL; +            pki_applet->sign_buffer_len = 0; +        } +        ret = VCARD_DONE; +        break; +    case CAC_READ_BUFFER: +        /* new CAC call, go ahead and use the old version for now */ +        /* TODO: implement */ +        *response = vcard_make_response( +                                VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); +        ret = VCARD_DONE; +        break; +    default: +        ret = cac_common_process_apdu(card, apdu, response); +        break; +    } +    return ret; +} + + +static VCardStatus +cac_applet_id_process_apdu(VCard *card, VCardAPDU *apdu, +                           VCardResponse **response) +{ +    VCardStatus ret = VCARD_FAIL; + +    switch (apdu->a_ins) { +    case CAC_UPDATE_BUFFER: +        *response = vcard_make_response( +                        VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED); +        ret = VCARD_DONE; +        break; +    case CAC_READ_BUFFER: +        /* new CAC call, go ahead and use the old version for now */ +        /* TODO: implement */ +        *response = vcard_make_response( +                        VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); +        ret = VCARD_DONE; +        break; +    default: +        ret = cac_common_process_apdu(card, apdu, response); +        break; +    } +    return ret; +} + + +/* + * TODO: if we ever want to support general CAC middleware, we will need to + * implement the various containers. + */ +static VCardStatus +cac_applet_container_process_apdu(VCard *card, VCardAPDU *apdu, +                                  VCardResponse **response) +{ +    VCardStatus ret = VCARD_FAIL; + +    switch (apdu->a_ins) { +    case CAC_READ_BUFFER: +    case CAC_UPDATE_BUFFER: +        *response = vcard_make_response( +                        VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); +        ret = VCARD_DONE; +        break; +    default: +        ret = cac_common_process_apdu(card, apdu, response); +        break; +    } +    return ret; +} + +/* + * utilities for creating and destroying the private applet data + */ +static void +cac_delete_pki_applet_private(VCardAppletPrivate *applet_private) +{ +    CACPKIAppletData *pki_applet_data; + +    if (applet_private == NULL) { +        return; +    } +    pki_applet_data = &(applet_private->u.pki_data); +    g_free(pki_applet_data->cert); +    g_free(pki_applet_data->sign_buffer); +    if (pki_applet_data->key != NULL) { +        vcard_emul_delete_key(pki_applet_data->key); +    } +    g_free(applet_private); +} + +static VCardAppletPrivate * +cac_new_pki_applet_private(const unsigned char *cert, +                           int cert_len, VCardKey *key) +{ +    CACPKIAppletData *pki_applet_data; +    VCardAppletPrivate *applet_private; + +    applet_private = g_new0(VCardAppletPrivate, 1); +    pki_applet_data = &(applet_private->u.pki_data); +    pki_applet_data->cert = (unsigned char *)g_malloc(cert_len+1); +    /* +     * if we want to support compression, then we simply change the 0 to a 1 +     * and compress the cert data with libz +     */ +    pki_applet_data->cert[0] = 0; /* not compressed */ +    memcpy(&pki_applet_data->cert[1], cert, cert_len); +    pki_applet_data->cert_len = cert_len+1; + +    pki_applet_data->key = key; +    return applet_private; +} + + +/* + * create a new cac applet which links to a given cert + */ +static VCardApplet * +cac_new_pki_applet(int i, const unsigned char *cert, +                   int cert_len, VCardKey *key) +{ +    VCardAppletPrivate *applet_private; +    VCardApplet *applet; +    unsigned char pki_aid[] = { 0xa0, 0x00, 0x00, 0x00, 0x79, 0x01, 0x00 }; +    int pki_aid_len = sizeof(pki_aid); + +    pki_aid[pki_aid_len-1] = i; + +    applet_private = cac_new_pki_applet_private(cert, cert_len, key); +    if (applet_private == NULL) { +        goto failure; +    } +    applet = vcard_new_applet(cac_applet_pki_process_apdu, cac_applet_pki_reset, +                              pki_aid, pki_aid_len); +    if (applet == NULL) { +        goto failure; +    } +    vcard_set_applet_private(applet, applet_private, +                             cac_delete_pki_applet_private); +    applet_private = NULL; + +    return applet; + +failure: +    if (applet_private != NULL) { +        cac_delete_pki_applet_private(applet_private); +    } +    return NULL; +} + + +static unsigned char cac_default_container_aid[] = { +    0xa0, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00 }; +static unsigned char cac_id_aid[] = { +    0xa0, 0x00, 0x00, 0x00, 0x79, 0x03, 0x00 }; +/* + * Initialize the cac card. This is the only public function in this file. All + * the rest are connected through function pointers. + */ +VCardStatus +cac_card_init(VReader *reader, VCard *card, +              const char *params, +              unsigned char * const *cert, +              int cert_len[], +              VCardKey *key[] /* adopt the keys*/, +              int cert_count) +{ +    int i; +    VCardApplet *applet; + +    /* CAC Cards are VM Cards */ +    vcard_set_type(card, VCARD_VM); + +    /* create one PKI applet for each cert */ +    for (i = 0; i < cert_count; i++) { +        applet = cac_new_pki_applet(i, cert[i], cert_len[i], key[i]); +        if (applet == NULL) { +            goto failure; +        } +        vcard_add_applet(card, applet); +    } + +    /* create a default blank container applet */ +    applet = vcard_new_applet(cac_applet_container_process_apdu, +                              NULL, cac_default_container_aid, +                              sizeof(cac_default_container_aid)); +    if (applet == NULL) { +        goto failure; +    } +    vcard_add_applet(card, applet); + +    /* create a default blank container applet */ +    applet = vcard_new_applet(cac_applet_id_process_apdu, +                              NULL, cac_id_aid, +                              sizeof(cac_id_aid)); +    if (applet == NULL) { +        goto failure; +    } +    vcard_add_applet(card, applet); +    return VCARD_DONE; + +failure: +    return VCARD_FAIL; +} + diff --git a/libcacard/cac.h b/libcacard/cac.h new file mode 100644 index 00000000..d24a2a84 --- /dev/null +++ b/libcacard/cac.h @@ -0,0 +1,31 @@ +/* + * defines the entry point for the cac card. Only used by cac.c anc + * vcard_emul_type.c + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ +#ifndef CAC_H +#define CAC_H 1 +#include "vcard.h" +#include "vreader.h" + +#define CAC_GET_PROPERTIES  0x56 +#define CAC_GET_ACR         0x4c +#define CAC_READ_BUFFER     0x52 +#define CAC_UPDATE_BUFFER   0x58 +#define CAC_SIGN_DECRYPT    0x42 +#define CAC_GET_CERTIFICATE 0x36 + +/* + * Initialize the cac card. This is the only public function in this file. All + * the rest are connected through function pointers. + */ +VCardStatus cac_card_init(VReader *reader, VCard *card, const char *params, +              unsigned char * const *cert, int cert_len[], +              VCardKey *key[] /* adopt the keys*/, +              int cert_count); + +/* not yet implemented */ +VCardStatus cac_is_cac_card(VReader *reader); +#endif diff --git a/libcacard/card_7816.c b/libcacard/card_7816.c new file mode 100644 index 00000000..22fd334d --- /dev/null +++ b/libcacard/card_7816.c @@ -0,0 +1,757 @@ +/* + * Implement the 7816 portion of the card spec + * + * This code is licensed under the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "glib-compat.h" + +#include <string.h> + +#include "vcard.h" +#include "vcard_emul.h" +#include "card_7816.h" + +/* + * set the status bytes based on the status word + */ +static void +vcard_response_set_status(VCardResponse *response, vcard_7816_status_t status) +{ +    unsigned char sw1, sw2; +    response->b_status = status; /* make sure the status and swX representations +                                  * are consistent */ +    sw1 = (status >> 8) & 0xff; +    sw2 = status & 0xff; +    response->b_sw1 = sw1; +    response->b_sw2 = sw2; +    response->b_data[response->b_len] = sw1; +    response->b_data[response->b_len+1] = sw2; +} + +/* + * set the status bytes in a response buffer + */ +static void +vcard_response_set_status_bytes(VCardResponse *response, +                               unsigned char sw1, unsigned char sw2) +{ +    response->b_status = sw1 << 8 | sw2; +    response->b_sw1 = sw1; +    response->b_sw2 = sw2; +    response->b_data[response->b_len] = sw1; +    response->b_data[response->b_len+1] = sw2; +} + +/* + * allocate a VCardResponse structure, plus space for the data buffer, and + * set up everything but the resonse bytes. + */ +VCardResponse * +vcard_response_new_data(unsigned char *buf, int len) +{ +    VCardResponse *new_response; + +    new_response = g_new(VCardResponse, 1); +    new_response->b_data = g_malloc(len + 2); +    memcpy(new_response->b_data, buf, len); +    new_response->b_total_len = len+2; +    new_response->b_len = len; +    new_response->b_type = VCARD_MALLOC; +    return new_response; +} + +static VCardResponse * +vcard_init_buffer_response(VCard *card, unsigned char *buf, int len) +{ +    VCardResponse *response; +    VCardBufferResponse *buffer_response; + +    buffer_response = vcard_get_buffer_response(card); +    if (buffer_response) { +        vcard_set_buffer_response(card, NULL); +        vcard_buffer_response_delete(buffer_response); +    } +    buffer_response = vcard_buffer_response_new(buf, len); +    if (buffer_response == NULL) { +        return NULL; +    } +    response = vcard_response_new_status_bytes(VCARD7816_SW1_RESPONSE_BYTES, +                                               len > 255 ? 0 : len); +    if (response == NULL) { +        return NULL; +    } +    vcard_set_buffer_response(card, buffer_response); +    return response; +} + +/* + * general buffer to hold results from APDU calls + */ +VCardResponse * +vcard_response_new(VCard *card, unsigned char *buf, +                   int len, int Le, vcard_7816_status_t status) +{ +    VCardResponse *new_response; + +    if (len > Le) { +        return vcard_init_buffer_response(card, buf, len); +    } +    new_response = vcard_response_new_data(buf, len); +    if (new_response == NULL) { +        return NULL; +    } +    vcard_response_set_status(new_response, status); +    return new_response; +} + +/* + * general buffer to hold results from APDU calls + */ +VCardResponse * +vcard_response_new_bytes(VCard *card, unsigned char *buf, int len, int Le, +                         unsigned char sw1, unsigned char sw2) +{ +    VCardResponse *new_response; + +    if (len > Le) { +        return vcard_init_buffer_response(card, buf, len); +    } +    new_response = vcard_response_new_data(buf, len); +    if (new_response == NULL) { +        return NULL; +    } +    vcard_response_set_status_bytes(new_response, sw1, sw2); +    return new_response; +} + +/* + * get a new Response buffer that only has a status. + */ +static VCardResponse * +vcard_response_new_status(vcard_7816_status_t status) +{ +    VCardResponse *new_response; + +    new_response = g_new(VCardResponse, 1); +    new_response->b_data = &new_response->b_sw1; +    new_response->b_len = 0; +    new_response->b_total_len = 2; +    new_response->b_type = VCARD_MALLOC_STRUCT; +    vcard_response_set_status(new_response, status); +    return new_response; +} + +/* + * same as above, but specify the status as separate bytes + */ +VCardResponse * +vcard_response_new_status_bytes(unsigned char sw1, unsigned char sw2) +{ +    VCardResponse *new_response; + +    new_response = g_new(VCardResponse, 1); +    new_response->b_data = &new_response->b_sw1; +    new_response->b_len = 0; +    new_response->b_total_len = 2; +    new_response->b_type = VCARD_MALLOC_STRUCT; +    vcard_response_set_status_bytes(new_response, sw1, sw2); +    return new_response; +} + + +/* + * free the response buffer. The Buffer has a type to handle the buffer + * allocated in other ways than through malloc. + */ +void +vcard_response_delete(VCardResponse *response) +{ +    if (response == NULL) { +        return; +    } +    switch (response->b_type) { +    case VCARD_MALLOC: +        /* everything was malloc'ed */ +        g_free(response->b_data); +        g_free(response); +        break; +    case VCARD_MALLOC_DATA: +        /* only the data buffer was malloc'ed */ +        g_free(response->b_data); +        break; +    case VCARD_MALLOC_STRUCT: +        /* only the structure was malloc'ed */ +        g_free(response); +        break; +    case VCARD_STATIC: +        break; +    } +} + +/* + * decode the class bit and set our generic type field, channel, and + * secure messaging values. + */ +static vcard_7816_status_t +vcard_apdu_set_class(VCardAPDU *apdu) { +    apdu->a_channel = 0; +    apdu->a_secure_messaging = 0; +    apdu->a_type = apdu->a_cla & 0xf0; +    apdu->a_gen_type = VCARD_7816_ISO; + +    /* parse the class  tables 8 & 9 of the 7816-4 Part 4 spec */ +    switch (apdu->a_type) { +        /* we only support the basic types */ +    case 0x00: +    case 0x80: +    case 0x90: +    case 0xa0: +        apdu->a_channel = apdu->a_cla & 3; +        apdu->a_secure_messaging = apdu->a_cla & 0xe; +        break; +    case 0xb0: +    case 0xc0: +        break; + +    case 0x10: +    case 0x20: +    case 0x30: +    case 0x40: +    case 0x50: +    case 0x60: +    case 0x70: +        /* Reserved for future use */ +        apdu->a_gen_type = VCARD_7816_RFU; +        break; +    case 0xd0: +    case 0xe0: +    case 0xf0: +    default: +        apdu->a_gen_type = +            (apdu->a_cla == 0xff) ? VCARD_7816_PTS : VCARD_7816_PROPRIETARY; +        break; +    } +    return VCARD7816_STATUS_SUCCESS; +} + +/* + * set the Le and Lc fields according to table 5 of the + * 7816-4 part 4 spec + */ +static vcard_7816_status_t +vcard_apdu_set_length(VCardAPDU *apdu) +{ +    int L, Le; + +    /* process according to table 5 of the 7816-4 Part 4 spec. +     * variable names match the variables in the spec */ +    L = apdu->a_len-4; /* fixed APDU header */ +    apdu->a_Lc = 0; +    apdu->a_Le = 0; +    apdu->a_body = NULL; +    switch (L) { +    case 0: +        /* 1 minimal apdu */ +        return VCARD7816_STATUS_SUCCESS; +    case 1: +        /* 2S only return values apdu */ +        /*   zero maps to 256 here */ +        apdu->a_Le = apdu->a_header->ah_Le ? +                         apdu->a_header->ah_Le : 256; +        return VCARD7816_STATUS_SUCCESS; +    default: +        /* if the ah_Le byte is zero and we have more than +         * 1 byte in the header, then we must be using extended Le and Lc. +         * process the extended now. */ +        if (apdu->a_header->ah_Le == 0) { +            if (L < 3) { +                /* coding error, need at least 3 bytes */ +                return VCARD7816_STATUS_ERROR_WRONG_LENGTH; +            } +            /* calculate the first extended value. Could be either Le or Lc */ +            Le = (apdu->a_header->ah_body[0] << 8) +               || apdu->a_header->ah_body[1]; +            if (L == 3) { +                /* 2E extended, return data only */ +                /*   zero maps to 65536 */ +                apdu->a_Le = Le ? Le : 65536; +                return VCARD7816_STATUS_SUCCESS; +            } +            if (Le == 0) { +                /* reserved for future use, probably for next time we need +                 * to extend the lengths */ +                return VCARD7816_STATUS_ERROR_WRONG_LENGTH; +            } +            /* we know that the first extended value is Lc now */ +            apdu->a_Lc = Le; +            apdu->a_body = &apdu->a_header->ah_body[2]; +            if (L == Le+3) { +                /* 3E extended, only body parameters */ +                return VCARD7816_STATUS_SUCCESS; +            } +            if (L == Le+5) { +                /* 4E extended, parameters and return data */ +                Le = (apdu->a_data[apdu->a_len-2] << 8) +                   || apdu->a_data[apdu->a_len-1]; +                apdu->a_Le = Le ? Le : 65536; +                return VCARD7816_STATUS_SUCCESS; +            } +            return VCARD7816_STATUS_ERROR_WRONG_LENGTH; +        } +        /* not extended */ +        apdu->a_Lc = apdu->a_header->ah_Le; +        apdu->a_body = &apdu->a_header->ah_body[0]; +        if (L ==  apdu->a_Lc + 1) { +            /* 3S only body parameters */ +            return VCARD7816_STATUS_SUCCESS; +        } +        if (L ==  apdu->a_Lc + 2) { +            /* 4S parameters and return data */ +            Le = apdu->a_data[apdu->a_len-1]; +            apdu->a_Le = Le ?  Le : 256; +            return VCARD7816_STATUS_SUCCESS; +        } +        break; +    } +    return VCARD7816_STATUS_ERROR_WRONG_LENGTH; +} + +/* + * create a new APDU from a raw set of bytes. This will decode all the + * above fields. users of VCARDAPDU's can then depend on the already decoded + * values. + */ +VCardAPDU * +vcard_apdu_new(unsigned char *raw_apdu, int len, vcard_7816_status_t *status) +{ +    VCardAPDU *new_apdu; + +    *status = VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE; +    if (len < 4) { +        *status = VCARD7816_STATUS_ERROR_WRONG_LENGTH; +        return NULL; +    } + +    new_apdu = g_new(VCardAPDU, 1); +    new_apdu->a_data = g_memdup(raw_apdu, len); +    new_apdu->a_len = len; +    *status = vcard_apdu_set_class(new_apdu); +    if (*status != VCARD7816_STATUS_SUCCESS) { +        g_free(new_apdu); +        return NULL; +    } +    *status = vcard_apdu_set_length(new_apdu); +    if (*status != VCARD7816_STATUS_SUCCESS) { +        g_free(new_apdu); +        new_apdu = NULL; +    } +    return new_apdu; +} + +void +vcard_apdu_delete(VCardAPDU *apdu) +{ +    if (apdu == NULL) { +        return; +    } +    g_free(apdu->a_data); +    g_free(apdu); +} + + +/* + * declare response buffers for all the 7816 defined error codes + */ +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_SUCCESS) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_RET_CORUPT) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_CHANGE) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_FILE_FILLED) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR_CHANGE) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_LENGTH) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED) +VCARD_RESPONSE_NEW_STATIC_STATUS( +                    VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_INVALID) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_NO_EF) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_PARAMETERS) +VCARD_RESPONSE_NEW_STATIC_STATUS( +                            VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_FILE_NOT_FOUND) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_P1_P2_INCORRECT) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_NOT_FOUND) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_INS_CODE_INVALID) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CLA_INVALID) +VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_GENERAL) + +/* + * return a single response code. This function cannot fail. It will always + * return a response. + */ +VCardResponse * +vcard_make_response(vcard_7816_status_t status) +{ +    VCardResponse *response; + +    switch (status) { +    /* known 7816 response codes */ +    case VCARD7816_STATUS_SUCCESS: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_SUCCESS); +    case VCARD7816_STATUS_WARNING: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_WARNING); +    case VCARD7816_STATUS_WARNING_RET_CORUPT: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_WARNING_RET_CORUPT); +    case VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE); +    case VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED); +    case VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID); +    case VCARD7816_STATUS_WARNING_CHANGE: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_WARNING_CHANGE); +    case VCARD7816_STATUS_WARNING_FILE_FILLED: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_WARNING_FILE_FILLED); +    case VCARD7816_STATUS_EXC_ERROR: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_EXC_ERROR); +    case VCARD7816_STATUS_EXC_ERROR_CHANGE: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_EXC_ERROR_CHANGE); +    case VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); +    case VCARD7816_STATUS_ERROR_WRONG_LENGTH: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_ERROR_WRONG_LENGTH); +    case VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED); +    case VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED); +    case VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED); +    case VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); +    case VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE); +    case VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED); +    case VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED); +    case VCARD7816_STATUS_ERROR_DATA_INVALID: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_ERROR_DATA_INVALID); +    case VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED); +    case VCARD7816_STATUS_ERROR_DATA_NO_EF: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_ERROR_DATA_NO_EF); +    case VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING); +    case VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT); +    case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_ERROR_WRONG_PARAMETERS); +    case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA); +    case VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED); +    case VCARD7816_STATUS_ERROR_FILE_NOT_FOUND: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_ERROR_FILE_NOT_FOUND); +    case VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND); +    case VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE); +    case VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT); +    case VCARD7816_STATUS_ERROR_P1_P2_INCORRECT: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); +    case VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT); +    case VCARD7816_STATUS_ERROR_DATA_NOT_FOUND: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_ERROR_DATA_NOT_FOUND); +    case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2); +    case VCARD7816_STATUS_ERROR_INS_CODE_INVALID: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_ERROR_INS_CODE_INVALID); +    case VCARD7816_STATUS_ERROR_CLA_INVALID: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_ERROR_CLA_INVALID); +    case VCARD7816_STATUS_ERROR_GENERAL: +        return VCARD_RESPONSE_GET_STATIC( +                    VCARD7816_STATUS_ERROR_GENERAL); +    default: +        /* we don't know this status code, create a response buffer to +         * hold it */ +        response = vcard_response_new_status(status); +        if (response == NULL) { +            /* couldn't allocate the buffer, return memmory error */ +            return VCARD_RESPONSE_GET_STATIC( +                        VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); +        } +        return response; +    } +} + +/* + * Add File card support here if you need it. + */ +static VCardStatus +vcard7816_file_system_process_apdu(VCard *card, VCardAPDU *apdu, +                                   VCardResponse **response) +{ +    /* TODO: if we want to support a virtual file system card, we do it here. +     * It would probably be a pkcs #15 card type */ +    *response = vcard_make_response( +                    VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); +    return VCARD_DONE; +} + +/* + * VM card (including java cards) + */ +static VCardStatus +vcard7816_vm_process_apdu(VCard *card, VCardAPDU *apdu, +                          VCardResponse **response) +{ +    int bytes_to_copy, next_byte_count, count; +    VCardApplet *current_applet; +    VCardBufferResponse *buffer_response; +    vcard_7816_status_t status; + +    /* parse the class first */ +    if (apdu->a_gen_type !=  VCARD_7816_ISO) { +        *response = vcard_make_response( +                        VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); +        return VCARD_DONE; +    } + +    /* use a switch so that if we need to support secure channel stuff later, +     * we know where to put it */ +    switch (apdu->a_secure_messaging) { +    case 0x0: /* no SM */ +        break; +    case 0x4: /* proprietary SM */ +    case 0x8: /* header not authenticated */ +    case 0xc: /* header authenticated */ +    default: +        /* for now, don't try to support secure channel stuff in the +         * virtual card. */ +        *response = vcard_make_response( +                        VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED); +        return VCARD_DONE; +    } + +    /* now parse the instruction */ +    switch (apdu->a_ins) { +    case  VCARD7816_INS_MANAGE_CHANNEL: /* secure channel op */ +    case  VCARD7816_INS_EXTERNAL_AUTHENTICATE: /* secure channel op */ +    case  VCARD7816_INS_GET_CHALLENGE: /* secure channel op */ +    case  VCARD7816_INS_INTERNAL_AUTHENTICATE: /* secure channel op */ +    case  VCARD7816_INS_ERASE_BINARY: /* applet control op */ +    case  VCARD7816_INS_READ_BINARY: /* applet control op */ +    case  VCARD7816_INS_WRITE_BINARY: /* applet control op */ +    case  VCARD7816_INS_UPDATE_BINARY: /* applet control op */ +    case  VCARD7816_INS_READ_RECORD: /* file op */ +    case  VCARD7816_INS_WRITE_RECORD: /* file op */ +    case  VCARD7816_INS_UPDATE_RECORD: /* file op */ +    case  VCARD7816_INS_APPEND_RECORD: /* file op */ +    case  VCARD7816_INS_ENVELOPE: +    case  VCARD7816_INS_PUT_DATA: +        *response = vcard_make_response( +                            VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); +        break; + +    case  VCARD7816_INS_SELECT_FILE: +        if (apdu->a_p1 != 0x04) { +            *response = vcard_make_response( +                            VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED); +            break; +        } + +        /* side effect, deselect the current applet if no applet has been found +         * */ +        current_applet = vcard_find_applet(card, apdu->a_body, apdu->a_Lc); +        vcard_select_applet(card, apdu->a_channel, current_applet); +        if (current_applet) { +            unsigned char *aid; +            int aid_len; +            aid = vcard_applet_get_aid(current_applet, &aid_len); +            *response = vcard_response_new(card, aid, aid_len, apdu->a_Le, +                                          VCARD7816_STATUS_SUCCESS); +        } else { +            *response = vcard_make_response( +                             VCARD7816_STATUS_ERROR_FILE_NOT_FOUND); +        } +        break; + +    case  VCARD7816_INS_VERIFY: +        if ((apdu->a_p1 != 0x00) || (apdu->a_p2 != 0x00)) { +            *response = vcard_make_response( +                            VCARD7816_STATUS_ERROR_WRONG_PARAMETERS); +        } else { +            if (apdu->a_Lc == 0) { +                /* handle pin count if possible */ +                count = vcard_emul_get_login_count(card); +                if (count < 0) { +                    *response = vcard_make_response( +                                    VCARD7816_STATUS_ERROR_DATA_NOT_FOUND); +                } else { +                    if (count > 0xf) { +                        count = 0xf; +                    } +                    *response = vcard_response_new_status_bytes( +                                                VCARD7816_SW1_WARNING_CHANGE, +                                                                0xc0 | count); +                    if (*response == NULL) { +                        *response = vcard_make_response( +                                    VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); +                    } +                } +            } else { +                    status = vcard_emul_login(card, apdu->a_body, apdu->a_Lc); +                *response = vcard_make_response(status); +            } +        } +        break; + +    case VCARD7816_INS_GET_RESPONSE: +        buffer_response = vcard_get_buffer_response(card); +        if (!buffer_response) { +            *response = vcard_make_response( +                            VCARD7816_STATUS_ERROR_DATA_NOT_FOUND); +            /* handle error */ +            break; +        } +        bytes_to_copy = MIN(buffer_response->len, apdu->a_Le); +        next_byte_count = MIN(256, buffer_response->len - bytes_to_copy); +        *response = vcard_response_new_bytes( +                        card, buffer_response->current, bytes_to_copy, +                        apdu->a_Le, +                        next_byte_count ? +                        VCARD7816_SW1_RESPONSE_BYTES : VCARD7816_SW1_SUCCESS, +                        next_byte_count); +        buffer_response->current += bytes_to_copy; +        buffer_response->len -= bytes_to_copy; +        if (*response == NULL || (next_byte_count == 0)) { +            vcard_set_buffer_response(card, NULL); +            vcard_buffer_response_delete(buffer_response); +        } +        if (*response == NULL) { +            *response = +                vcard_make_response(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); +        } +        break; + +    case VCARD7816_INS_GET_DATA: +        *response = +            vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); +        break; + +    default: +        *response = +            vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); +        break; +    } + +    /* response should have been set somewhere */ +    assert(*response != NULL); +    return VCARD_DONE; +} + + +/* + * APDU processing starts here. This routes the card processing stuff to the + * right location. + */ +VCardStatus +vcard_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response) +{ +    VCardStatus status; +    VCardBufferResponse *buffer_response; + +    /* first handle any PTS commands, which aren't really APDU's */ +    if (apdu->a_type == VCARD_7816_PTS) { +        /* the PTS responses aren't really responses either */ +        *response = vcard_response_new_data(apdu->a_data, apdu->a_len); +        /* PTS responses have no status bytes */ +        (*response)->b_total_len = (*response)->b_len; +        return VCARD_DONE; +    } +    buffer_response = vcard_get_buffer_response(card); +    if (buffer_response && apdu->a_ins != VCARD7816_INS_GET_RESPONSE) { +        /* clear out buffer_response, return an error */ +        vcard_set_buffer_response(card, NULL); +        vcard_buffer_response_delete(buffer_response); +        *response = vcard_make_response(VCARD7816_STATUS_EXC_ERROR); +        return VCARD_DONE; +    } + +    status = vcard_process_applet_apdu(card, apdu, response); +    if (status != VCARD_NEXT) { +        return status; +    } +    switch (vcard_get_type(card)) { +    case VCARD_FILE_SYSTEM: +        return vcard7816_file_system_process_apdu(card, apdu, response); +    case VCARD_VM: +        return vcard7816_vm_process_apdu(card, apdu, response); +    case VCARD_DIRECT: +        /* if we are type direct, then the applet should handle everything */ +        assert(!"VCARD_DIRECT: applet failure"); +        break; +    } +    *response = +        vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); +    return VCARD_DONE; +} diff --git a/libcacard/card_7816.h b/libcacard/card_7816.h new file mode 100644 index 00000000..4a01993d --- /dev/null +++ b/libcacard/card_7816.h @@ -0,0 +1,62 @@ +/* + * Implement the 7816 portion of the card spec + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ +#ifndef CARD_7816_H +#define CARD_7816_H  1 + +#include "card_7816t.h" +#include "vcardt.h" + +/* + * constructors for VCardResponse's + */ +/* response from a return buffer and a status */ +VCardResponse *vcard_response_new(VCard *card, unsigned char *buf, int len, +                                  int Le, vcard_7816_status_t status); +/* response from a return buffer and status bytes */ +VCardResponse *vcard_response_new_bytes(VCard *card, unsigned char *buf, +                                        int len, int Le, +                                        unsigned char sw1, unsigned char sw2); +/* response from just status bytes */ +VCardResponse *vcard_response_new_status_bytes(unsigned char sw1, +                                               unsigned char sw2); +/* response from just status: NOTE this cannot fail, it will always return a + * valid response, if it can't allocate memory, the response will be + * VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE */ +VCardResponse *vcard_make_response(vcard_7816_status_t status); + +/* create a raw response (status has already been encoded */ +VCardResponse *vcard_response_new_data(unsigned char *buf, int len); + + + + +/* + * destructor for VCardResponse. + *  Can be called with a NULL response + */ +void vcard_response_delete(VCardResponse *response); + +/* + * constructor for VCardAPDU + */ +VCardAPDU *vcard_apdu_new(unsigned char *raw_apdu, int len, +                          unsigned short *status); + +/* + * destructor for VCardAPDU + *  Can be called with a NULL apdu + */ +void vcard_apdu_delete(VCardAPDU *apdu); + +/* + * APDU processing starts here. This routes the card processing stuff to the + * right location. Always returns a valid response. + */ +VCardStatus vcard_process_apdu(VCard *card, VCardAPDU *apdu, +                               VCardResponse **response); + +#endif diff --git a/libcacard/card_7816t.h b/libcacard/card_7816t.h new file mode 100644 index 00000000..8eef0ce4 --- /dev/null +++ b/libcacard/card_7816t.h @@ -0,0 +1,165 @@ +/* + * Implement the 7816 portion of the card spec + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ +#ifndef CARD_7816T_H +#define CARD_7816T_H 1 + +typedef unsigned short vcard_7816_status_t; + +struct VCardResponseStruct { +    unsigned char *b_data; +    vcard_7816_status_t b_status; +    unsigned char b_sw1; +    unsigned char b_sw2; +    int b_len; +    int b_total_len; +    enum VCardResponseBufferType { +        VCARD_MALLOC, +        VCARD_MALLOC_DATA, +        VCARD_MALLOC_STRUCT, +        VCARD_STATIC +    } b_type; +}; + +#define VCARD_RESPONSE_NEW_STATIC_STATUS(stat) \ +static const VCardResponse VCardResponse##stat = \ +        {(unsigned char *)&VCardResponse##stat.b_sw1, (stat), ((stat) >> 8), \ +         ((stat) & 0xff), 0, 2, VCARD_STATIC}; + +#define VCARD_RESPONSE_NEW_STATIC_STATUS_BYTES(sw1, sw2) \ +static const VCardResponse VCARDResponse##sw1 = \ +        {(unsigned char *)&VCardResponse##name.b_sw1, ((sw1) << 8 | (sw2)), \ +         (sw1), (sw2), 0, 2, VCARD_STATIC}; + +/* cast away the const, callers need may need to 'free' the + * result, and const implies that they don't */ +#define VCARD_RESPONSE_GET_STATIC(name) \ +        ((VCardResponse *)(&VCardResponse##name)) + +typedef enum { +    VCARD_7816_ISO, +    VCARD_7816_RFU, +    VCARD_7816_PTS, +    VCARD_7816_PROPRIETARY +} VCardAPDUType; + + +/* + * 7816 header. All APDU's have this header. + * They must be laid out in this order. + */ +struct VCardAPDUHeader { +    unsigned char ah_cla; +    unsigned char ah_ins; +    unsigned char ah_p1; +    unsigned char ah_p2; +    unsigned char ah_Le; +    unsigned char ah_body[1]; /* 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 00000000..63f4057f --- /dev/null +++ b/libcacard/event.c @@ -0,0 +1,103 @@ +/* + * event queue implementation. + * + * This code is licensed under the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "glib-compat.h" + +#include "vcard.h" +#include "vreader.h" +#include "vevent.h" + +VEvent * +vevent_new(VEventType type, VReader *reader, VCard *card) +{ +    VEvent *new_vevent; + +    new_vevent = g_new(VEvent, 1); +    new_vevent->next = NULL; +    new_vevent->type = type; +    new_vevent->reader = vreader_reference(reader); +    new_vevent->card = vcard_reference(card); + +    return new_vevent; +} + +void +vevent_delete(VEvent *vevent) +{ +    if (vevent == NULL) { +        return; +    } +    vreader_free(vevent->reader); +    vcard_free(vevent->card); +    g_free(vevent); +} + +/* + * VEvent queue management + */ + +static VEvent *vevent_queue_head; +static VEvent *vevent_queue_tail; +static CompatGMutex vevent_queue_lock; +static CompatGCond vevent_queue_condition; + +void vevent_queue_init(void) +{ +    vevent_queue_head = vevent_queue_tail = NULL; +} + +void +vevent_queue_vevent(VEvent *vevent) +{ +    vevent->next = NULL; +    g_mutex_lock(&vevent_queue_lock); +    if (vevent_queue_head) { +        assert(vevent_queue_tail); +        vevent_queue_tail->next = vevent; +    } else { +        vevent_queue_head = vevent; +    } +    vevent_queue_tail = vevent; +    g_cond_signal(&vevent_queue_condition); +    g_mutex_unlock(&vevent_queue_lock); +} + +/* must have lock */ +static VEvent * +vevent_dequeue_vevent(void) +{ +    VEvent *vevent = NULL; +    if (vevent_queue_head) { +        vevent = vevent_queue_head; +        vevent_queue_head = vevent->next; +        vevent->next = NULL; +    } +    return vevent; +} + +VEvent *vevent_wait_next_vevent(void) +{ +    VEvent *vevent; + +    g_mutex_lock(&vevent_queue_lock); +    while ((vevent = vevent_dequeue_vevent()) == NULL) { +        g_cond_wait(&vevent_queue_condition, &vevent_queue_lock); +    } +    g_mutex_unlock(&vevent_queue_lock); +    return vevent; +} + +VEvent *vevent_get_next_vevent(void) +{ +    VEvent *vevent; + +    g_mutex_lock(&vevent_queue_lock); +    vevent = vevent_dequeue_vevent(); +    g_mutex_unlock(&vevent_queue_lock); +    return vevent; +} + diff --git a/libcacard/eventt.h b/libcacard/eventt.h new file mode 100644 index 00000000..0dc7bd46 --- /dev/null +++ b/libcacard/eventt.h @@ -0,0 +1,29 @@ +/* + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#ifndef EVENTT_H +#define EVENTT_H 1 +#include "vreadert.h" +#include "vcardt.h" + +typedef struct VEventStruct VEvent; + +typedef enum { +    VEVENT_READER_INSERT, +    VEVENT_READER_REMOVE, +    VEVENT_CARD_INSERT, +    VEVENT_CARD_REMOVE, +    VEVENT_LAST, +} VEventType; + +struct VEventStruct { +    VEvent *next; +    VEventType type; +    VReader *reader; +    VCard *card; +}; +#endif + + diff --git a/libcacard/libcacard.pc.in b/libcacard/libcacard.pc.in new file mode 100644 index 00000000..4b60023c --- /dev/null +++ b/libcacard/libcacard.pc.in @@ -0,0 +1,13 @@ +prefix=@PREFIX@ +exec_prefix=${prefix} +libdir=@LIBDIR@ +includedir=@INCLUDEDIR@ + +Name: cacard +Description: CA Card library +Version: @VERSION@ + +Requires.private: nss glib-2.0 +Libs: -L${libdir} -lcacard +Libs.private: +Cflags: -I${includedir} diff --git a/libcacard/libcacard.syms b/libcacard/libcacard.syms new file mode 100644 index 00000000..1697515a --- /dev/null +++ b/libcacard/libcacard.syms @@ -0,0 +1,77 @@ +cac_card_init +cac_is_cac_card +vcard_add_applet +vcard_apdu_delete +vcard_apdu_new +vcard_applet_get_aid +vcard_buffer_response_delete +vcard_buffer_response_new +vcard_delete_applet +vcard_emul_delete_key +vcard_emul_force_card_insert +vcard_emul_force_card_remove +vcard_emul_get_atr +vcard_emul_get_login_count +vcard_emul_init +vcard_emul_login +vcard_emul_options +vcard_emul_replay_insertion_events +vcard_emul_reset +vcard_emul_rsa_op +vcard_emul_type_from_string +vcard_emul_type_select +vcard_emul_usage +vcard_find_applet +vcard_free +vcard_get_atr +vcard_get_buffer_response +vcard_get_current_applet_private +vcard_get_private +vcard_get_type +vcard_init +vcard_make_response +vcard_new +vcard_new_applet +vcard_process_apdu +vcard_process_applet_apdu +vcard_reference +vcard_reset +vcard_response_delete +vcard_response_new +vcard_response_new_bytes +vcard_response_new_data +vcard_response_new_status_bytes +vcard_select_applet +vcard_set_applet_private +vcard_set_atr_func +vcard_set_buffer_response +vcard_set_type +vevent_delete +vevent_get_next_vevent +vevent_new +vevent_queue_init +vevent_queue_vevent +vevent_wait_next_vevent +vreader_add_reader +vreader_card_is_present +vreader_free +vreader_get_id +vreader_get_name +vreader_get_private +vreader_get_reader_by_id +vreader_get_reader_by_name +vreader_get_reader_list +vreader_init +vreader_insert_card +vreader_list_delete +vreader_list_get_first +vreader_list_get_next +vreader_list_get_reader +vreader_new +vreader_power_off +vreader_power_on +vreader_queue_card_event +vreader_reference +vreader_remove_reader +vreader_set_id +vreader_xfr_bytes diff --git a/libcacard/link_test.c b/libcacard/link_test.c new file mode 100644 index 00000000..6f67a23d --- /dev/null +++ b/libcacard/link_test.c @@ -0,0 +1,22 @@ +/* + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include <stdio.h> +#include "vcard.h" + +VCardStatus cac_card_init(const char *flags, VCard *card, +                const unsigned char *cert[], +                int cert_len[], VCardKey *key[] /* adopt the keys*/, +                int cert_count); +/* + * this will crash... just test the linkage right now + */ + +main(int argc, char **argv) +{ +    VCard *card; /* no constructor yet */ +    cac_card_init("", card, NULL, 0, NULL, 0); +} + diff --git a/libcacard/vcard.c b/libcacard/vcard.c new file mode 100644 index 00000000..1a87208f --- /dev/null +++ b/libcacard/vcard.c @@ -0,0 +1,325 @@ +/* + * implement the Java card standard. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "glib-compat.h" + +#include <string.h> + +#include "vcard.h" +#include "vcard_emul.h" +#include "card_7816t.h" + +struct VCardAppletStruct { +    VCardApplet   *next; +    VCardProcessAPDU process_apdu; +    VCardResetApplet reset_applet; +    unsigned char *aid; +    int aid_len; +    void *applet_private; +    VCardAppletPrivateFree applet_private_free; +}; + +struct VCardStruct { +    int reference_count; +    VCardApplet *applet_list; +    VCardApplet *current_applet[MAX_CHANNEL]; +    VCardBufferResponse *vcard_buffer_response; +    VCardType type; +    VCardEmul *vcard_private; +    VCardEmulFree vcard_private_free; +    VCardGetAtr vcard_get_atr; +}; + +VCardBufferResponse * +vcard_buffer_response_new(unsigned char *buffer, int size) +{ +    VCardBufferResponse *new_buffer; + +    new_buffer = g_new(VCardBufferResponse, 1); +    new_buffer->buffer = (unsigned char *)g_memdup(buffer, size); +    new_buffer->buffer_len = size; +    new_buffer->current = new_buffer->buffer; +    new_buffer->len = size; +    return new_buffer; +} + +void +vcard_buffer_response_delete(VCardBufferResponse *buffer_response) +{ +    if (buffer_response == NULL) { +        return; +    } +    g_free(buffer_response->buffer); +    g_free(buffer_response); +} + + +/* + * clean up state after a reset + */ +void +vcard_reset(VCard *card, VCardPower power) +{ +    int i; +    VCardApplet *applet = NULL; + +    if (card->type ==  VCARD_DIRECT) { +        /* select the last applet */ +        VCardApplet *current_applet = NULL; +        for (current_applet = card->applet_list; current_applet; +                                       current_applet = current_applet->next) { +            applet = current_applet; +        } +    } +    for (i = 0; i < MAX_CHANNEL; i++) { +        card->current_applet[i] = applet; +    } +    if (card->vcard_buffer_response) { +        vcard_buffer_response_delete(card->vcard_buffer_response); +        card->vcard_buffer_response = NULL; +    } +    vcard_emul_reset(card, power); +    if (applet) { +        applet->reset_applet(card, 0); +    } +} + +/* applet utilities */ + +/* + * applet utilities + */ +/* constructor */ +VCardApplet * +vcard_new_applet(VCardProcessAPDU applet_process_function, +                 VCardResetApplet applet_reset_function, +                 unsigned char *aid, int aid_len) +{ +    VCardApplet *applet; + +    applet = g_new0(VCardApplet, 1); +    applet->process_apdu = applet_process_function; +    applet->reset_applet = applet_reset_function; + +    applet->aid = g_memdup(aid, aid_len); +    applet->aid_len = aid_len; +    return applet; +} + +/* destructor */ +void +vcard_delete_applet(VCardApplet *applet) +{ +    if (applet == NULL) { +        return; +    } +    if (applet->applet_private_free) { +        applet->applet_private_free(applet->applet_private); +    } +    g_free(applet->aid); +    g_free(applet); +} + +/* accessor */ +void +vcard_set_applet_private(VCardApplet *applet, VCardAppletPrivate *private, +                         VCardAppletPrivateFree private_free) +{ +    if (applet->applet_private_free) { +        applet->applet_private_free(applet->applet_private); +    } +    applet->applet_private = private; +    applet->applet_private_free = private_free; +} + +VCard * +vcard_new(VCardEmul *private, VCardEmulFree private_free) +{ +    VCard *new_card; + +    new_card = g_new0(VCard, 1); +    new_card->type = VCARD_VM; +    new_card->vcard_private = private; +    new_card->vcard_private_free = private_free; +    new_card->reference_count = 1; +    return new_card; +} + +VCard * +vcard_reference(VCard *vcard) +{ +    if (vcard == NULL) { +        return NULL; +    } +    vcard->reference_count++; +    return vcard; +} + +void +vcard_free(VCard *vcard) +{ +    VCardApplet *current_applet; +    VCardApplet *next_applet; + +    if (vcard == NULL) { +        return; +    } +    vcard->reference_count--; +    if (vcard->reference_count != 0) { +        return; +    } +    if (vcard->vcard_private_free) { +        (*vcard->vcard_private_free)(vcard->vcard_private); +    } +    for (current_applet = vcard->applet_list; current_applet; +                                        current_applet = next_applet) { +        next_applet = current_applet->next; +        vcard_delete_applet(current_applet); +    } +    vcard_buffer_response_delete(vcard->vcard_buffer_response); +    g_free(vcard); +} + +void +vcard_get_atr(VCard *vcard, unsigned char *atr, int *atr_len) +{ +    if (vcard->vcard_get_atr) { +        (*vcard->vcard_get_atr)(vcard, atr, atr_len); +        return; +    } +    vcard_emul_get_atr(vcard, atr, atr_len); +} + +void +vcard_set_atr_func(VCard *card, VCardGetAtr vcard_get_atr) +{ +    card->vcard_get_atr = vcard_get_atr; +} + + +VCardStatus +vcard_add_applet(VCard *card, VCardApplet *applet) +{ +    applet->next = card->applet_list; +    card->applet_list = applet; +    /* if our card-type is direct, always call the applet */ +    if (card->type ==  VCARD_DIRECT) { +        int i; + +        for (i = 0; i < MAX_CHANNEL; i++) { +            card->current_applet[i] = applet; +        } +    } +    return VCARD_DONE; +} + +/* + * manage applets + */ +VCardApplet * +vcard_find_applet(VCard *card, unsigned char *aid, int aid_len) +{ +    VCardApplet *current_applet; + +    for (current_applet = card->applet_list; current_applet; +                                        current_applet = current_applet->next) { +        if (current_applet->aid_len != aid_len) { +            continue; +        } +        if (memcmp(current_applet->aid, aid, aid_len) == 0) { +            break; +        } +    } +    return current_applet; +} + +unsigned char * +vcard_applet_get_aid(VCardApplet *applet, int *aid_len) +{ +    if (applet == NULL) { +        return NULL; +    } +    *aid_len = applet->aid_len; +    return applet->aid; +} + + +void +vcard_select_applet(VCard *card, int channel, VCardApplet *applet) +{ +    assert(channel < MAX_CHANNEL); + +    /* If using an emulated card, make sure to log out of any already logged in +     * session. */ +    vcard_emul_logout(card); + +    card->current_applet[channel] = applet; +    /* reset the applet */ +    if (applet && applet->reset_applet) { +        applet->reset_applet(card, channel); +    } +} + +VCardAppletPrivate * +vcard_get_current_applet_private(VCard *card, int channel) +{ +    VCardApplet *applet = card->current_applet[channel]; + +    if (applet == NULL) { +        return NULL; +    } +    return applet->applet_private; +} + +VCardStatus +vcard_process_applet_apdu(VCard *card, VCardAPDU *apdu, +                          VCardResponse **response) +{ +    if (card->current_applet[apdu->a_channel]) { +        return card->current_applet[apdu->a_channel]->process_apdu( +                                                        card, apdu, response); +    } +    return VCARD_NEXT; +} + +/* + * Accessor functions + */ +/* accessor functions for the response buffer */ +VCardBufferResponse * +vcard_get_buffer_response(VCard *card) +{ +    return card->vcard_buffer_response; +} + +void +vcard_set_buffer_response(VCard *card, VCardBufferResponse *buffer) +{ +    card->vcard_buffer_response = buffer; +} + + +/* accessor functions for the type */ +VCardType +vcard_get_type(VCard *card) +{ +    return card->type; +} + +void +vcard_set_type(VCard *card, VCardType type) +{ +    card->type = type; +} + +/* accessor for private data */ +VCardEmul * +vcard_get_private(VCard *vcard) +{ +    return vcard->vcard_private; +} + diff --git a/libcacard/vcard.h b/libcacard/vcard.h new file mode 100644 index 00000000..47dc7038 --- /dev/null +++ b/libcacard/vcard.h @@ -0,0 +1,86 @@ +/* + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ +#ifndef VCARD_H +#define VCARD_H 1 + +#include "vcardt.h" + +/* + * response buffer constructors and destructors. + * + * response buffers are used when we need to return more data than will fit in + * a normal APDU response (nominally 254 bytes). + */ +VCardBufferResponse *vcard_buffer_response_new(unsigned char *buffer, int size); +void vcard_buffer_response_delete(VCardBufferResponse *buffer_response); + + +/* + * clean up state on reset + */ +void vcard_reset(VCard *card, VCardPower power); + +/* + * applet utilities + */ +/* + * Constructor for a VCardApplet + */ +VCardApplet *vcard_new_applet(VCardProcessAPDU applet_process_function, +                              VCardResetApplet applet_reset_function, +                              unsigned char *aid, int aid_len); + +/* + * destructor for a VCardApplet + *  Can be called with a NULL applet + */ +void vcard_delete_applet(VCardApplet *applet); + +/* accessor - set the card type specific private data */ +void vcard_set_applet_private(VCardApplet *applet, VCardAppletPrivate *_private, +                              VCardAppletPrivateFree private_free); + +/* set type of vcard */ +void vcard_set_type(VCard *card, VCardType type); + +/* + * utilities interacting with the current applet + */ +/* add a new applet to a card */ +VCardStatus vcard_add_applet(VCard *card, VCardApplet *applet); +/* find the applet on the card with the given aid */ +VCardApplet *vcard_find_applet(VCard *card, unsigned char *aid, int aid_len); +/* set the following applet to be current on the given channel */ +void vcard_select_applet(VCard *card, int channel, VCardApplet *applet); +/* get the card type specific private data on the given channel */ +VCardAppletPrivate *vcard_get_current_applet_private(VCard *card, int channel); +/* fetch the applet's id */ +unsigned char *vcard_applet_get_aid(VCardApplet *applet, int *aid_len); + +/* process the apdu for the current selected applet/file */ +VCardStatus vcard_process_applet_apdu(VCard *card, VCardAPDU *apdu, +                                      VCardResponse **response); +/* + * VCard utilities + */ +/* constructor */ +VCard *vcard_new(VCardEmul *_private, VCardEmulFree private_free); +/* get a reference */ +VCard *vcard_reference(VCard *); +/* destructor (reference counted) */ +void vcard_free(VCard *); +/* get the atr from the card */ +void vcard_get_atr(VCard *card, unsigned char *atr, int *atr_len); +void vcard_set_atr_func(VCard *card, VCardGetAtr vcard_get_atr); + +/* accessor functions for the response buffer */ +VCardBufferResponse *vcard_get_buffer_response(VCard *card); +void vcard_set_buffer_response(VCard *card, VCardBufferResponse *buffer); +/* accessor functions for the type */ +VCardType vcard_get_type(VCard *card); +/* get the private data */ +VCardEmul *vcard_get_private(VCard *card); + +#endif diff --git a/libcacard/vcard_emul.h b/libcacard/vcard_emul.h new file mode 100644 index 00000000..f09ee98d --- /dev/null +++ b/libcacard/vcard_emul.h @@ -0,0 +1,66 @@ +/* + * This is the actual card emulator. + * + * These functions can be implemented in different ways on different platforms + * using the underlying system primitives. For Linux it uses NSS, though direct + * to PKCS #11, openssl+pkcs11, or even gnu crypto libraries+pkcs #11 could be + * used. On Windows CAPI could be used. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#ifndef VCARD_EMUL_H +#define VCARD_EMUL_H 1 + +#include "card_7816t.h" +#include "vcard.h" +#include "vcard_emul_type.h" + +/* + * types + */ +typedef enum { +    VCARD_EMUL_OK = 0, +    VCARD_EMUL_FAIL, +    /* return values by vcard_emul_init */ +    VCARD_EMUL_INIT_ALREADY_INITED, +} VCardEmulError; + +/* options are emul specific. call card_emul_parse_args to change a string + * To an options struct */ +typedef struct VCardEmulOptionsStruct VCardEmulOptions; + +/* + * Login functions + */ +/* return the number of login attempts still possible on the card. if unknown, + * return -1 */ +int vcard_emul_get_login_count(VCard *card); +/* login into the card, return the 7816 status word (sw2 || sw1) */ +vcard_7816_status_t vcard_emul_login(VCard *card, unsigned char *pin, +                                     int pin_len); +void vcard_emul_logout(VCard *card); + +/* + * key functions + */ +/* delete a key */ +void vcard_emul_delete_key(VCardKey *key); +/* RSA sign/decrypt with the key, signature happens 'in place' */ +vcard_7816_status_t vcard_emul_rsa_op(VCard *card, VCardKey *key, +                                  unsigned char *buffer, int buffer_size); + +void vcard_emul_reset(VCard *card, VCardPower power); +void vcard_emul_get_atr(VCard *card, unsigned char *atr, int *atr_len); + +/* Re-insert of a card that has been removed by force removal */ +VCardEmulError vcard_emul_force_card_insert(VReader *vreader); +/* Force a card removal even if the card is not physically removed */ +VCardEmulError vcard_emul_force_card_remove(VReader *vreader); + +VCardEmulOptions *vcard_emul_options(const char *args); +VCardEmulError vcard_emul_init(const VCardEmulOptions *options); +void vcard_emul_replay_insertion_events(void); +void vcard_emul_usage(void); +#endif diff --git a/libcacard/vcard_emul_nss.c b/libcacard/vcard_emul_nss.c new file mode 100644 index 00000000..d9761eed --- /dev/null +++ b/libcacard/vcard_emul_nss.c @@ -0,0 +1,1274 @@ +/* + * This is the actual card emulator. + * + * These functions can be implemented in different ways on different platforms + * using the underlying system primitives. For Linux it uses NSS, though direct + * to PKCS #11, openssl+pkcs11, or even gnu crypto libraries+pkcs #11 could be + * used. On Windows CAPI could be used. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +/* + * NSS headers + */ + +/* avoid including prototypes.h that redefines uint32 */ +#define NO_NSPR_10_SUPPORT + +#include <nss.h> +#include <pk11pub.h> +#include <cert.h> +#include <key.h> +#include <secmod.h> +#include <prthread.h> +#include <secerr.h> + +#include "glib-compat.h" + +#include "vcard.h" +#include "card_7816t.h" +#include "vcard_emul.h" +#include "vreader.h" +#include "vevent.h" + +#include "vcardt_internal.h" + + +typedef enum { +    VCardEmulUnknown = -1, +    VCardEmulFalse = 0, +    VCardEmulTrue = 1 +} VCardEmulTriState; + +struct VCardKeyStruct { +    CERTCertificate *cert; +    PK11SlotInfo *slot; +    SECKEYPrivateKey *key; +    VCardEmulTriState failedX509; +}; + + +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; + +/* if we have more that just the slot, define + * VCardEmulStruct here */ + +/* + * allocate the set of arrays for certs, cert_len, key + */ +static void +vcard_emul_alloc_arrays(unsigned char ***certsp, int **cert_lenp, +                        VCardKey ***keysp, int cert_count) +{ +    *certsp = g_new(unsigned char *, cert_count); +    *cert_lenp = g_new(int, cert_count); +    *keysp = g_new(VCardKey *, cert_count); +} + +/* + * 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 = g_new(VCardKey, 1); +    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); +    key->failedX509 = VCardEmulUnknown; +    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); +    } +} + +/* + * 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; +    PK11SlotInfo *slot; +    SECStatus rv; +    unsigned char buf[2048]; +    unsigned char *bp = NULL; +    int pad_len; +    vcard_7816_status_t ret = VCARD7816_STATUS_SUCCESS; + +    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); +    if (priv_key == NULL) { +        /* couldn't get the key, indicate that we aren't logged in */ +        return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED; +    } +    slot = vcard_emul_card_get_slot(card); + +    /* +     * 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; +    } +    /* be able to handle larger keys if necessariy */ +    bp = &buf[0]; +    if (sizeof(buf) < signature_len) { +        bp = g_malloc(signature_len); +    } + +    /* +     * do the raw operations. Some tokens claim to do CKM_RSA_X_509, but then +     * choke when they try to do the actual operations. Try to detect +     * those cases and treat them as if the token didn't claim support for +     * X_509. +     */ +    if (key->failedX509 != VCardEmulTrue +                              && PK11_DoesMechanism(slot, CKM_RSA_X_509)) { +        rv = PK11_PrivDecryptRaw(priv_key, bp, &signature_len, signature_len, +                                 buffer, buffer_size); +        if (rv == SECSuccess) { +            assert(buffer_size == signature_len); +            memcpy(buffer, bp, signature_len); +            key->failedX509 = VCardEmulFalse; +            goto cleanup; +        } +        /* +         * we've had a successful X509 operation, this failure must be +         * somethine else +         */ +        if (key->failedX509 == VCardEmulFalse) { +            ret = vcard_emul_map_error(PORT_GetError()); +            goto cleanup; +        } +        /* +         * key->failedX509 must be Unknown at this point, try the +         * non-x_509 case +         */ +    } +    /* token does not support CKM_RSA_X509, emulate that with CKM_RSA_PKCS */ +    /* is this a PKCS #1 formatted signature? */ +    if ((buffer[0] == 0) && (buffer[1] == 1)) { +        int i; + +        for (i = 2; i < buffer_size; i++) { +            /* rsa signature pad */ +            if (buffer[i] != 0xff) { +                break; +            } +        } +        if ((i < buffer_size) && (buffer[i] == 0)) { +            /* yes, we have a properly formatted PKCS #1 signature */ +            /* +             * NOTE: even if we accidentally got an encrypt buffer, which +             * through sheer luck started with 00, 01, ff, 00, it won't matter +             * because the resulting Sign operation will effectively decrypt +             * the real buffer. +             */ +            SECItem signature; +            SECItem hash; + +            i++; +            hash.data = &buffer[i]; +            hash.len = buffer_size - i; +            signature.data = bp; +            signature.len = signature_len; +            rv = PK11_Sign(priv_key,  &signature, &hash); +            if (rv != SECSuccess) { +                ret = vcard_emul_map_error(PORT_GetError()); +                goto cleanup; +            } +            assert(buffer_size == signature.len); +            memcpy(buffer, bp, signature.len); +            /* +             * we got here because either the X509 attempt failed, or the +             * token couldn't do the X509 operation, in either case stay +             * with the PKCS version for future operations on this key +             */ +            key->failedX509 = VCardEmulTrue; +            goto cleanup; +        } +    } +    pad_len = buffer_size - signature_len; +    assert(pad_len < 4); +    /* +     * OK now we've decrypted the payload, package it up in PKCS #1 for the +     * upper layer. +     */ +    buffer[0] = 0; +    buffer[1] = 2; /* RSA_encrypt  */ +    pad_len -= 3; /* format is 0 || 2 || pad || 0 || data */ +    /* +     * padding for PKCS #1 encrypted data is a string of random bytes. The +     * random butes protect against potential decryption attacks against RSA. +     * Since PrivDecrypt has already stripped those bytes, we can't reconstruct +     * them. This shouldn't matter to the upper level code which should just +     * strip this code out anyway, so We'll pad with a constant 3. +     */ +    memset(&buffer[2], 0x03, pad_len); +    pad_len += 2; /* index to the end of the pad */ +    buffer[pad_len] = 0; +    pad_len++; /* index to the start of the data */ +    memcpy(&buffer[pad_len], bp, signature_len); +    /* +     * we got here because either the X509 attempt failed, or the +     * token couldn't do the X509 operation, in either case stay +     * with the PKCS version for future operations on this key +     */ +    key->failedX509 = VCardEmulTrue; +cleanup: +    if (bp != buf) { +        g_free(bp); +    } +    return ret; +} + +/* + * 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; +    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 = g_malloc(pin_len+1); +    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 */ +    g_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_logout(VCard *card) +{ +    PK11SlotInfo *slot; + +    if (!nss_emul_init) { +        return; +    } + +    slot = vcard_emul_card_get_slot(card); +    if (PK11_IsLoggedIn(slot, NULL)) { +        PK11_Logout(slot); /* NOTE: ignoring SECStatus return value */ +    } +} + +void +vcard_emul_reset(VCard *card, VCardPower power) +{ +    /* +     * if we reset the card (either power on or power off), we lose our login +     * state +     */ +    vcard_emul_logout(card); + +    /* TODO: we may also need to send insertion/removal events? */ +} + +static VReader * +vcard_emul_find_vreader_from_slot(PK11SlotInfo *slot) +{ +    VReaderList *reader_list = vreader_get_reader_list(); +    VReaderListEntry *current_entry; + +    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) { +            vreader_list_delete(reader_list); +            return reader; +        } +        vreader_free(reader); +    } + +    vreader_list_delete(reader_list); +    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 = g_new(VReaderEmul, 1); + +    new_reader_emul->slot = PK11_ReferenceSlot(slot); +    new_reader_emul->default_type = type; +    new_reader_emul->type_params = g_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); +    } +    g_free(vreader_emul->type_params); +    g_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_alloc_atr will set appropriate + *  historical bytes for any software emulated card. The remaining bytes can be + *  used to indicate the actual emulator + */ +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; +    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; +} + +/* + * 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; +    VCard *card; + +    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++; +    } + +    /* allocate the arrays */ +    vcard_emul_alloc_arrays(&certs, &cert_len, &keys, cert_count); + +    /* 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 */ +    card = vcard_emul_make_card(vreader, certs, cert_len, keys, cert_count); +    g_free(certs); +    g_free(cert_len); +    g_free(keys); + +    return card; +} + +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 { +        /* +         * XXX - the latency value doesn't matter one bit. you only get no +         * blocking (flags |= CKF_DONT_BLOCK) or PKCS11_WAIT_LATENCY (==500), +         * hard coded in coolkey.  And it isn't coolkey's fault - the timeout +         * value we pass get's dropped on the floor before C_WaitForSlotEvent +         * is called. +         */ +        slot = SECMOD_WaitForAnyTokenEvent(module, 0, 500); +        if (slot == NULL) { +            /* this could be just a no event indication */ +            if (PORT_GetError() == SEC_ERROR_NO_EVENT) { +                continue; +            } +            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; + +VCardEmulError +vcard_emul_init(const VCardEmulOptions *options) +{ +    SECStatus rv; +    PRBool has_readers = PR_FALSE; +    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 { +        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; +    } +    /* 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); + +        vcard_emul_alloc_arrays(&certs, &cert_len, &keys, +                                options->vreader[i].cert_count); + +        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; +        } +        g_free(certs); +        g_free(cert_len); +        g_free(keys); +    } + +    /* 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(); +    SECMOD_GetReadLock(module_lock); +    for (mlp = module_list; mlp; mlp = mlp->next) { +        SECMODModule *module = mlp->module; +        if (module_has_removable_hw_slots(module)) { +            break; +        } +    } +    SECMOD_ReleaseReadLock(module_lock); + +    /* 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 = g_strdup(options->hw_type_params); + +    SECMOD_GetReadLock(module_lock); +    for (mlp = module_list; mlp; mlp = mlp->next) { +        SECMODModule *module = mlp->module; + +        /* Ignore the internal module */ +        if (module == NULL || module == SECMOD_GetInternalModule()) { +            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; +            } +            if (strcmp("E-Gate 0 0", PK11_GetSlotName(slot)) == 0) { +                /* +                 * coolkey <= 1.1.0-20 emulates this reader if it can't find +                 * any hardware readers. This causes problems, warn user of +                 * problems. +                 */ +                fprintf(stderr, "known bad coolkey version - see " +                        "https://bugzilla.redhat.com/show_bug.cgi?id=802435\n"); +                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); + +            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); +            } +        } +        vcard_emul_new_event_thread(module); +    } +    SECMOD_ReleaseReadLock(module_lock); +    nss_emul_init = PR_TRUE; + +    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; +    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); +    } + +    vreader_list_delete(list); +} + +/* + *  Silly little functions to help parsing our argument string + */ +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 * +strip(const char *str) +{ +    for (; *str && isspace(*str); str++) { +    } +    return str; +} + +static const char * +find_blank(const char *str) +{ +    for (; *str && !isspace(*str); str++) { +    } +    return str; +} + + +/* + *  We really want to use some existing argument parsing library here. That + *  would give us a consistent look */ +static VCardEmulOptions options; +#define READER_STEP 4 + +/* Expects "args" to be at the beginning of a token (ie right after the ',' + * ending the previous token), and puts the next token start in "token", + * and its length in "token_length". "token" will not be nul-terminated. + * After calling the macro, "args" will be advanced to the beginning of + * the next token. + * This macro may call continue or break. + */ +#define NEXT_TOKEN(token) \ +            (token) = args; \ +            args = strpbrk(args, ",)"); \ +            if (*args == 0) { \ +                break; \ +            } \ +            if (*args == ')') { \ +                args++; \ +                continue; \ +            } \ +            (token##_length) = args - (token); \ +            args = strip(args+1); + +VCardEmulOptions * +vcard_emul_options(const char *args) +{ +    int reader_count = 0; +    VCardEmulOptions *opts; + +    /* 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; +            size_t name_length; +            const char *vname; +            size_t vname_length; +            const char *type_params; +            size_t type_params_length; +            char type_str[100]; +            VCardEmulType type; +            int count, i; +            VirtualReaderOptions *vreaderOpt; + +            args = strip(args + 5); +            if (*args != '(') { +                continue; +            } +            args = strip(args+1); + +            NEXT_TOKEN(name) +            NEXT_TOKEN(vname) +            NEXT_TOKEN(type_params) +            type_params_length = MIN(type_params_length, sizeof(type_str)-1); +            memcpy(type_str, type_params, type_params_length); +            type_str[type_params_length] = '\0'; +            type = vcard_emul_type_from_string(type_str); + +            NEXT_TOKEN(type_params) + +            if (*args == 0) { +                break; +            } + +            if (opts->vreader_count >= reader_count) { +                reader_count += READER_STEP; +                opts->vreader = g_renew(VirtualReaderOptions, opts->vreader, +                                        reader_count); +            } +            vreaderOpt = &opts->vreader[opts->vreader_count]; +            vreaderOpt->name = g_strndup(name, name_length); +            vreaderOpt->vname = g_strndup(vname, vname_length); +            vreaderOpt->card_type = type; +            vreaderOpt->type_params = +                g_strndup(type_params, type_params_length); +            count = count_tokens(args, ',', ')') + 1; +            vreaderOpt->cert_count = count; +            vreaderOpt->cert_name = g_new(char *, count); +            for (i = 0; i < count; i++) { +                const char *cert = args; +                args = strpbrk(args, ",)"); +                vreaderOpt->cert_name[i] = g_strndup(cert, args - cert); +                args = strip(args+1); +            } +            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 = g_strndup(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 = strpbrk(args, "\"\n"); +            opts->nss_db = g_strndup(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}          Virtual 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 00000000..59a14582 --- /dev/null +++ b/libcacard/vcard_emul_type.c @@ -0,0 +1,57 @@ +/* + *  This file contains utility functions which abstract the different card + *  types.  The goal is that new card types can easily be added by simply + *  changing this file and vcard_emul_type.h. It is currently not a requirement + *  to dynamically add new card types. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include <strings.h> +#include "vcardt.h" +#include "vcard_emul_type.h" +#include "cac.h" + +VCardStatus vcard_init(VReader *vreader, VCard *vcard, +                       VCardEmulType type, const char *params, +                       unsigned char *const *cert, int cert_len[], +                       VCardKey *key[], int cert_count) +{ +    switch (type) { +    case VCARD_EMUL_NONE: +        break; +    case VCARD_EMUL_CAC: +        return cac_card_init(vreader, vcard, params, +                             cert, cert_len, key,  cert_count); +    /* add new ones here */ +    default: +        break; +    } +    return VCARD_FAIL; +} + +VCardEmulType vcard_emul_type_select(VReader *vreader) +{ +#ifdef notdef +    /* since there is only one emulator no need to call this function */ +    if (cac_is_cac_card(vreader) == VCARD_DONE) { +        return VCARD_EMUL_CAC; +    } +#endif +    /* return the default */ +    return VCARD_EMUL_CAC; +} + +VCardEmulType vcard_emul_type_from_string(const char *type_string) +{ +     if (strcasecmp(type_string, "CAC") == 0) { +        return VCARD_EMUL_CAC; +     } +#ifdef USE_PASSTHRU +     if (strcasecmp(type_string, "PASSTHRU") == 0) { +        return VCARD_EMUL_PASSTHRU; +     } +#endif +     return VCARD_EMUL_NONE; +} diff --git a/libcacard/vcard_emul_type.h b/libcacard/vcard_emul_type.h new file mode 100644 index 00000000..0242f40e --- /dev/null +++ b/libcacard/vcard_emul_type.h @@ -0,0 +1,32 @@ +/* + *  This header file abstracts the different card types. The goal is new card + *  types can easily be added by simply changing this file and + *  vcard_emul_type.c. It is currently not a requirement to dynamically add new + *  card types. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#ifndef VCARD_EMUL_TYPE_H +#define VCARD_EMUL_TYPE_H 1 +#include "vcardt.h" +#include "vreadert.h" + +/* + * types + */ +typedef enum { +     VCARD_EMUL_NONE = 0, +     VCARD_EMUL_CAC, +     VCARD_EMUL_PASSTHRU +} VCardEmulType; + +/* functions used by the rest of the emulator */ +VCardStatus vcard_init(VReader *vreader, VCard *vcard, VCardEmulType type, +                       const char *params, unsigned char * const *cert, +                       int cert_len[], VCardKey *key[], int cert_count); +VCardEmulType vcard_emul_type_select(VReader *vreader); +VCardEmulType vcard_emul_type_from_string(const char *type_string); + +#endif diff --git a/libcacard/vcardt.c b/libcacard/vcardt.c new file mode 100644 index 00000000..c67de2f3 --- /dev/null +++ b/libcacard/vcardt.c @@ -0,0 +1,40 @@ +#include <stdlib.h> +#include <string.h> +#include <glib.h> + +#include "vcardt.h" + +#include "vcardt_internal.h" + +/* create an ATR with appropriate historical bytes */ +#define ATR_TS_DIRECT_CONVENTION 0x3b +#define ATR_TA_PRESENT 0x10 +#define ATR_TB_PRESENT 0x20 +#define ATR_TC_PRESENT 0x40 +#define ATR_TD_PRESENT 0x80 + +unsigned char *vcard_alloc_atr(const char *postfix, int *atr_len) +{ +    int postfix_len; +    const char prefix[] = "VCARD_"; +    const char default_postfix[] = "DEFAULT"; +    const int prefix_len = sizeof(prefix) - 1; +    int total_len; +    unsigned char *atr; + +    if (postfix == NULL) { +        postfix = default_postfix; +    } +    postfix_len = strlen(postfix); +    total_len = 3 + prefix_len + postfix_len; +    atr = g_malloc(total_len); +    atr[0] = ATR_TS_DIRECT_CONVENTION; +    atr[1] = ATR_TD_PRESENT + prefix_len + postfix_len; +    atr[2] = 0x00; +    memcpy(&atr[3], prefix, prefix_len); +    memcpy(&atr[3 + prefix_len], postfix, postfix_len); +    if (atr_len) { +        *atr_len = total_len; +    } +    return atr; +} diff --git a/libcacard/vcardt.h b/libcacard/vcardt.h new file mode 100644 index 00000000..795e2653 --- /dev/null +++ b/libcacard/vcardt.h @@ -0,0 +1,59 @@ +/* + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ +#ifndef VCARDT_H +#define VCARDT_H 1 + +/* + * these should come from some common spice header file + */ +#include <assert.h> +#ifndef MIN +#define MIN(x, y) ((x) > (y) ? (y) : (x)) +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#endif + +typedef struct VCardStruct VCard; +typedef struct VCardAPDUStruct VCardAPDU; +typedef struct VCardResponseStruct VCardResponse; +typedef struct VCardBufferResponseStruct VCardBufferResponse; +typedef struct VCardAppletStruct VCardApplet; +typedef struct VCardAppletPrivateStruct VCardAppletPrivate; +typedef struct VCardKeyStruct VCardKey;  /* opaque */ +typedef struct VCardEmulStruct VCardEmul; + +#define MAX_CHANNEL 4 + +typedef enum { +    VCARD_DONE, +    VCARD_NEXT, +    VCARD_FAIL +} VCardStatus; + +typedef enum { +    VCARD_FILE_SYSTEM, +    VCARD_VM, +    VCARD_DIRECT +} VCardType; + +typedef enum { +    VCARD_POWER_ON, +    VCARD_POWER_OFF +} VCardPower; + +typedef VCardStatus (*VCardProcessAPDU)(VCard *card, VCardAPDU *apdu, +                                        VCardResponse **response); +typedef VCardStatus (*VCardResetApplet)(VCard *card, int channel); +typedef void (*VCardAppletPrivateFree) (VCardAppletPrivate *); +typedef void (*VCardEmulFree) (VCardEmul *); +typedef void (*VCardGetAtr) (VCard *, unsigned char *atr, int *atr_len); + +struct VCardBufferResponseStruct { +    unsigned char *buffer; +    int buffer_len; +    unsigned char *current; +    int len; +}; + +#endif diff --git a/libcacard/vcardt_internal.h b/libcacard/vcardt_internal.h new file mode 100644 index 00000000..e5c8d2dd --- /dev/null +++ b/libcacard/vcardt_internal.h @@ -0,0 +1,6 @@ +#ifndef VCARDT_INTERNAL_H +#define VCARDT_INTERNAL_H + +unsigned char *vcard_alloc_atr(const char *postfix, int *atr_len); + +#endif diff --git a/libcacard/vevent.h b/libcacard/vevent.h new file mode 100644 index 00000000..38c3482c --- /dev/null +++ b/libcacard/vevent.h @@ -0,0 +1,27 @@ +/* + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ +#ifndef EVENT_H +#define EVENT_H 1 +#include "eventt.h" +#include "vreadert.h" +#include "vcardt.h" + +VEvent *vevent_new(VEventType type, VReader *reader, VCard *card); +void vevent_delete(VEvent *); + +/* + * VEvent queueing services + */ +void vevent_queue_vevent(VEvent *); +void vevent_queue_init(void); + +/* + *  VEvent dequeing services + */ +VEvent *vevent_wait_next_vevent(void); +VEvent *vevent_get_next_vevent(void); + + +#endif diff --git a/libcacard/vreader.c b/libcacard/vreader.c new file mode 100644 index 00000000..9725f46a --- /dev/null +++ b/libcacard/vreader.c @@ -0,0 +1,578 @@ +/* + * emulate the reader + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#ifdef G_LOG_DOMAIN +#undef G_LOG_DOMAIN +#endif +#define G_LOG_DOMAIN "libcacard" + +#include "glib-compat.h" + +#include <string.h> + +#include "vcard.h" +#include "vcard_emul.h" +#include "card_7816.h" +#include "vreader.h" +#include "vevent.h" +#include "cac.h" /* just for debugging defines */ + +#define LIBCACARD_LOG_DOMAIN "libcacard" + +struct VReaderStruct { +    int    reference_count; +    VCard *card; +    char *name; +    vreader_id_t id; +    CompatGMutex lock; +    VReaderEmul  *reader_private; +    VReaderEmulFree reader_private_free; +}; + +/* + * Debug helpers + */ + +static const char * +apdu_ins_to_string(int ins) +{ +    switch (ins) { +    case VCARD7816_INS_MANAGE_CHANNEL: +        return "manage channel"; +    case VCARD7816_INS_EXTERNAL_AUTHENTICATE: +        return "external authenticate"; +    case VCARD7816_INS_GET_CHALLENGE: +        return "get challenge"; +    case VCARD7816_INS_INTERNAL_AUTHENTICATE: +        return "internal authenticate"; +    case VCARD7816_INS_ERASE_BINARY: +        return "erase binary"; +    case VCARD7816_INS_READ_BINARY: +        return "read binary"; +    case VCARD7816_INS_WRITE_BINARY: +        return "write binary"; +    case VCARD7816_INS_UPDATE_BINARY: +        return "update binary"; +    case VCARD7816_INS_READ_RECORD: +        return "read record"; +    case VCARD7816_INS_WRITE_RECORD: +        return "write record"; +    case VCARD7816_INS_UPDATE_RECORD: +        return "update record"; +    case VCARD7816_INS_APPEND_RECORD: +        return "append record"; +    case VCARD7816_INS_ENVELOPE: +        return "envelope"; +    case VCARD7816_INS_PUT_DATA: +        return "put data"; +    case VCARD7816_INS_GET_DATA: +        return "get data"; +    case VCARD7816_INS_SELECT_FILE: +        return "select file"; +    case VCARD7816_INS_VERIFY: +        return "verify"; +    case VCARD7816_INS_GET_RESPONSE: +        return "get response"; +    case CAC_GET_PROPERTIES: +        return "get properties"; +    case CAC_GET_ACR: +        return "get acr"; +    case CAC_READ_BUFFER: +        return "read buffer"; +    case CAC_UPDATE_BUFFER: +        return "update buffer"; +    case CAC_SIGN_DECRYPT: +        return "sign decrypt"; +    case CAC_GET_CERTIFICATE: +        return "get certificate"; +    } +    return "unknown"; +} + +/* manage locking */ +static inline void +vreader_lock(VReader *reader) +{ +    g_mutex_lock(&reader->lock); +} + +static inline void +vreader_unlock(VReader *reader) +{ +    g_mutex_unlock(&reader->lock); +} + +/* + * vreader constructor + */ +VReader * +vreader_new(const char *name, VReaderEmul *private, +            VReaderEmulFree private_free) +{ +    VReader *reader; + +    reader = g_new(VReader, 1); +    g_mutex_init(&reader->lock); +    reader->reference_count = 1; +    reader->name = g_strdup(name); +    reader->card = NULL; +    reader->id = (vreader_id_t)-1; +    reader->reader_private = private; +    reader->reader_private_free = private_free; +    return reader; +} + +/* get a reference */ +VReader* +vreader_reference(VReader *reader) +{ +    if (reader == NULL) { +        return NULL; +    } +    vreader_lock(reader); +    reader->reference_count++; +    vreader_unlock(reader); +    return reader; +} + +/* free a reference */ +void +vreader_free(VReader *reader) +{ +    if (reader == NULL) { +        return; +    } +    vreader_lock(reader); +    if (reader->reference_count-- > 1) { +        vreader_unlock(reader); +        return; +    } +    vreader_unlock(reader); +    g_mutex_clear(&reader->lock); +    if (reader->card) { +        vcard_free(reader->card); +    } +    g_free(reader->name); +    if (reader->reader_private_free) { +        reader->reader_private_free(reader->reader_private); +    } +    g_free(reader); +} + +static VCard * +vreader_get_card(VReader *reader) +{ +    VCard *card; + +    vreader_lock(reader); +    card = vcard_reference(reader->card); +    vreader_unlock(reader); +    return card; +} + +VReaderStatus +vreader_card_is_present(VReader *reader) +{ +    VCard *card = vreader_get_card(reader); + +    if (card == NULL) { +        return VREADER_NO_CARD; +    } +    vcard_free(card); +    return VREADER_OK; +} + +vreader_id_t +vreader_get_id(VReader *reader) +{ +    if (reader == NULL) { +        return (vreader_id_t)-1; +    } +    return reader->id; +} + +VReaderStatus +vreader_set_id(VReader *reader, vreader_id_t id) +{ +    if (reader == NULL) { +        return VREADER_NO_CARD; +    } +    reader->id = id; +    return VREADER_OK; +} + +const char * +vreader_get_name(VReader *reader) +{ +    if (reader == NULL) { +        return NULL; +    } +    return reader->name; +} + +VReaderEmul * +vreader_get_private(VReader *reader) +{ +    return reader->reader_private; +} + +static VReaderStatus +vreader_reset(VReader *reader, VCardPower power, unsigned char *atr, int *len) +{ +    VCard *card = vreader_get_card(reader); + +    if (card == NULL) { +        return VREADER_NO_CARD; +    } +    /* +     * clean up our state +     */ +    vcard_reset(card, power); +    if (atr) { +        vcard_get_atr(card, atr, len); +    } +    vcard_free(card); /* free our reference */ +    return VREADER_OK; +} + +VReaderStatus +vreader_power_on(VReader *reader, unsigned char *atr, int *len) +{ +    return vreader_reset(reader, VCARD_POWER_ON, atr, len); +} + +VReaderStatus +vreader_power_off(VReader *reader) +{ +    return vreader_reset(reader, VCARD_POWER_OFF, NULL, 0); +} + + +VReaderStatus +vreader_xfr_bytes(VReader *reader, +                  unsigned char *send_buf, int send_buf_len, +                  unsigned char *receive_buf, int *receive_buf_len) +{ +    VCardAPDU *apdu; +    VCardResponse *response = NULL; +    VCardStatus card_status; +    unsigned short status; +    VCard *card = vreader_get_card(reader); + +    if (card == NULL) { +        return VREADER_NO_CARD; +    } + +    apdu = vcard_apdu_new(send_buf, send_buf_len, &status); +    if (apdu == NULL) { +        response = vcard_make_response(status); +        card_status = VCARD_DONE; +    } else { +        g_debug("%s: CLS=0x%x,INS=0x%x,P1=0x%x,P2=0x%x,Lc=%d,Le=%d %s", +              __func__, apdu->a_cla, apdu->a_ins, apdu->a_p1, apdu->a_p2, +              apdu->a_Lc, apdu->a_Le, apdu_ins_to_string(apdu->a_ins)); +        card_status = vcard_process_apdu(card, apdu, &response); +        if (response) { +            g_debug("%s: status=%d sw1=0x%x sw2=0x%x len=%d (total=%d)", +                  __func__, response->b_status, response->b_sw1, +                  response->b_sw2, response->b_len, response->b_total_len); +        } +    } +    assert(card_status == VCARD_DONE && response); +    int size = MIN(*receive_buf_len, response->b_total_len); +    memcpy(receive_buf, response->b_data, size); +    *receive_buf_len = size; +    vcard_response_delete(response); +    vcard_apdu_delete(apdu); +    vcard_free(card); /* free our reference */ +    return VREADER_OK; +} + +struct VReaderListStruct { +    VReaderListEntry *head; +    VReaderListEntry *tail; +}; + +struct VReaderListEntryStruct { +    VReaderListEntry *next; +    VReaderListEntry *prev; +    VReader *reader; +}; + + +static VReaderListEntry * +vreader_list_entry_new(VReader *reader) +{ +    VReaderListEntry *new_reader_list_entry; + +    new_reader_list_entry = g_new0(VReaderListEntry, 1); +    new_reader_list_entry->reader = vreader_reference(reader); +    return new_reader_list_entry; +} + +static void +vreader_list_entry_delete(VReaderListEntry *entry) +{ +    if (entry == NULL) { +        return; +    } +    vreader_free(entry->reader); +    g_free(entry); +} + + +static VReaderList * +vreader_list_new(void) +{ +    VReaderList *new_reader_list; + +    new_reader_list = g_new0(VReaderList, 1); +    return new_reader_list; +} + +void +vreader_list_delete(VReaderList *list) +{ +    VReaderListEntry *current_entry; +    VReaderListEntry *next_entry; +    for (current_entry = vreader_list_get_first(list); current_entry; +         current_entry = next_entry) { +        next_entry = vreader_list_get_next(current_entry); +        vreader_list_entry_delete(current_entry); +    } +    g_free(list); +} + + +VReaderListEntry * +vreader_list_get_first(VReaderList *list) +{ +    return list ? list->head : NULL; +} + +VReaderListEntry * +vreader_list_get_next(VReaderListEntry *current) +{ +    return current ? current->next : NULL; +} + +VReader * +vreader_list_get_reader(VReaderListEntry *entry) +{ +    return entry ? vreader_reference(entry->reader) : NULL; +} + +static void +vreader_queue(VReaderList *list, VReaderListEntry *entry) +{ +    if (entry == NULL) { +        return; +    } +    entry->next = NULL; +    entry->prev = list->tail; +    if (list->head) { +        list->tail->next = entry; +    } else { +        list->head = entry; +    } +    list->tail = entry; +} + +static void +vreader_dequeue(VReaderList *list, VReaderListEntry *entry) +{ +    if (entry == NULL) { +        return; +    } +    if (entry->next == NULL) { +        list->tail = entry->prev; +    } else if (entry->prev == NULL) { +        list->head = entry->next; +    } else { +        entry->prev->next = entry->next; +        entry->next->prev = entry->prev; +    } +    if ((list->tail == NULL) || (list->head == NULL)) { +        list->head = list->tail = NULL; +    } +    entry->next = entry->prev = NULL; +} + +static VReaderList *vreader_list; +static CompatGMutex vreader_list_mutex; + +static void +vreader_list_init(void) +{ +    vreader_list = vreader_list_new(); +} + +static void +vreader_list_lock(void) +{ +    g_mutex_lock(&vreader_list_mutex); +} + +static void +vreader_list_unlock(void) +{ +    g_mutex_unlock(&vreader_list_mutex); +} + +static VReaderList * +vreader_copy_list(VReaderList *list) +{ +    VReaderList *new_list; +    VReaderListEntry *current_entry; + +    new_list = vreader_list_new(); +    if (new_list == NULL) { +        return NULL; +    } +    for (current_entry = vreader_list_get_first(list); current_entry; +         current_entry = vreader_list_get_next(current_entry)) { +        VReader *reader = vreader_list_get_reader(current_entry); +        VReaderListEntry *new_entry = vreader_list_entry_new(reader); + +        vreader_free(reader); +        vreader_queue(new_list, new_entry); +    } +    return new_list; +} + +VReaderList * +vreader_get_reader_list(void) +{ +    VReaderList *new_reader_list; + +    vreader_list_lock(); +    new_reader_list = vreader_copy_list(vreader_list); +    vreader_list_unlock(); +    return new_reader_list; +} + +VReader * +vreader_get_reader_by_id(vreader_id_t id) +{ +    VReader *reader = NULL; +    VReaderListEntry *current_entry; + +    if (id == (vreader_id_t) -1) { +        return NULL; +    } + +    vreader_list_lock(); +    for (current_entry = vreader_list_get_first(vreader_list); current_entry; +            current_entry = vreader_list_get_next(current_entry)) { +        VReader *creader = vreader_list_get_reader(current_entry); +        if (creader->id == id) { +            reader = creader; +            break; +        } +        vreader_free(creader); +    } +    vreader_list_unlock(); +    return reader; +} + +VReader * +vreader_get_reader_by_name(const char *name) +{ +    VReader *reader = NULL; +    VReaderListEntry *current_entry; + +    vreader_list_lock(); +    for (current_entry = vreader_list_get_first(vreader_list); current_entry; +            current_entry = vreader_list_get_next(current_entry)) { +        VReader *creader = vreader_list_get_reader(current_entry); +        if (strcmp(creader->name, name) == 0) { +            reader = creader; +            break; +        } +        vreader_free(creader); +    } +    vreader_list_unlock(); +    return reader; +} + +/* called from card_emul to initialize the readers */ +VReaderStatus +vreader_add_reader(VReader *reader) +{ +    VReaderListEntry *reader_entry; + +    reader_entry = vreader_list_entry_new(reader); +    if (reader_entry == NULL) { +        return VREADER_OUT_OF_MEMORY; +    } +    vreader_list_lock(); +    vreader_queue(vreader_list, reader_entry); +    vreader_list_unlock(); +    vevent_queue_vevent(vevent_new(VEVENT_READER_INSERT, reader, NULL)); +    return VREADER_OK; +} + + +VReaderStatus +vreader_remove_reader(VReader *reader) +{ +    VReaderListEntry *current_entry; + +    vreader_list_lock(); +    for (current_entry = vreader_list_get_first(vreader_list); current_entry; +         current_entry = vreader_list_get_next(current_entry)) { +        if (current_entry->reader == reader) { +            break; +        } +    } +    vreader_dequeue(vreader_list, current_entry); +    vreader_list_unlock(); +    vreader_list_entry_delete(current_entry); +    vevent_queue_vevent(vevent_new(VEVENT_READER_REMOVE, reader, NULL)); +    return VREADER_OK; +} + +/* + * Generate VEVENT_CARD_INSERT or VEVENT_CARD_REMOVE based on vreader + * state. Separated from vreader_insert_card to allow replaying events + * for a given state. + */ +void +vreader_queue_card_event(VReader *reader) +{ +    vevent_queue_vevent(vevent_new( +        reader->card ? VEVENT_CARD_INSERT : VEVENT_CARD_REMOVE, reader, +        reader->card)); +} + +/* + * insert/remove a new card. for removal, card == NULL + */ +VReaderStatus +vreader_insert_card(VReader *reader, VCard *card) +{ +    vreader_lock(reader); +    if (reader->card) { +        /* decrement reference count */ +        vcard_free(reader->card); +        reader->card = NULL; +    } +    reader->card = vcard_reference(card); +    vreader_unlock(reader); +    vreader_queue_card_event(reader); +    return VREADER_OK; +} + +/* + * initialize all the static reader structures + */ +void +vreader_init(void) +{ +    vreader_list_init(); +} + diff --git a/libcacard/vreader.h b/libcacard/vreader.h new file mode 100644 index 00000000..ec204213 --- /dev/null +++ b/libcacard/vreader.h @@ -0,0 +1,55 @@ +/* + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#ifndef VREADER_H +#define VREADER_H 1 + +#include "eventt.h" +#include "vreadert.h" +#include "vcardt.h" + +/* + * calls for reader front end + */ +VReaderStatus vreader_power_on(VReader *reader, unsigned char *atr, int *len); +VReaderStatus vreader_power_off(VReader *reader); +VReaderStatus vreader_xfr_bytes(VReader *reader, unsigned char *send_buf, +                                int send_buf_len, unsigned char *receive_buf, +                                int *receive_buf_len); + +/* constructor */ +VReader *vreader_new(const char *readerName, VReaderEmul *emul_private, +                     VReaderEmulFree private_free); +/* get a new reference to a reader */ +VReader *vreader_reference(VReader *reader); +/* "destructor" (readers are reference counted) */ +void vreader_free(VReader *reader); + +/* accessors */ +VReaderEmul *vreader_get_private(VReader *); +VReaderStatus vreader_card_is_present(VReader *reader); +void vreader_queue_card_event(VReader *reader); +const char *vreader_get_name(VReader *reader); +vreader_id_t vreader_get_id(VReader *reader); +VReaderStatus vreader_set_id(VReader *reader, vreader_id_t id); + +/* list operations */ +VReaderList *vreader_get_reader_list(void); +void vreader_list_delete(VReaderList *list); +VReader *vreader_list_get_reader(VReaderListEntry *entry); +VReaderListEntry *vreader_list_get_first(VReaderList *list); +VReaderListEntry *vreader_list_get_next(VReaderListEntry *list); +VReader *vreader_get_reader_by_id(vreader_id_t id); +VReader *vreader_get_reader_by_name(const char *name); + +/* + * list tools for vcard_emul + */ +void vreader_init(void); +VReaderStatus vreader_add_reader(VReader *reader); +VReaderStatus vreader_remove_reader(VReader *reader); +VReaderStatus vreader_insert_card(VReader *reader, VCard *card); + +#endif diff --git a/libcacard/vreadert.h b/libcacard/vreadert.h new file mode 100644 index 00000000..f97e0a79 --- /dev/null +++ b/libcacard/vreadert.h @@ -0,0 +1,24 @@ +/* + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#ifndef VREADERT_H +#define VREADERT_H 1 + +typedef enum { +    VREADER_OK = 0, +    VREADER_NO_CARD, +    VREADER_OUT_OF_MEMORY +} VReaderStatus; + +typedef unsigned int vreader_id_t; +typedef struct VReaderStruct VReader; +typedef struct VReaderListStruct VReaderList; +typedef struct VReaderListEntryStruct VReaderListEntry; + +typedef struct VReaderEmulStruct VReaderEmul; +typedef void (*VReaderEmulFree)(VReaderEmul *); + +#endif + diff --git a/libcacard/vscard_common.h b/libcacard/vscard_common.h new file mode 100644 index 00000000..08f68e4d --- /dev/null +++ b/libcacard/vscard_common.h @@ -0,0 +1,178 @@ +/* Virtual Smart Card protocol definition + * + * This protocol is between a host using virtual smart card readers, + * and a client providing the smart cards, perhaps by emulating them or by + * access to real cards. + * + * Definitions for this protocol: + *  Host   - user of the card + *  Client - owner of the card + * + * The current implementation passes the raw APDU's from 7816 and additionally + * contains messages to setup and teardown readers, handle insertion and + * removal of cards, negotiate the protocol via capabilities and provide + * for error responses. + * + * Copyright (c) 2011 Red Hat. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#ifndef VSCARD_COMMON_H +#define VSCARD_COMMON_H + +#include <stdint.h> + +#define VERSION_MAJOR_BITS 11 +#define VERSION_MIDDLE_BITS 11 +#define VERSION_MINOR_BITS 10 + +#define MAKE_VERSION(major, middle, minor) \ +     ((major  << (VERSION_MINOR_BITS + VERSION_MIDDLE_BITS)) \ +      | (middle <<  VERSION_MINOR_BITS) \ +      | (minor)) + +/* + * IMPORTANT NOTE on VERSION + * + * The version below MUST be changed whenever a change in this file is made. + * + * The last digit, the minor, is for bug fix changes only. + * + * The middle digit is for backward / forward compatible changes, updates + * to the existing messages, addition of fields. + * + * The major digit is for a breaking change of protocol, presumably + * something that cannot be accommodated with the existing protocol. + */ + +#define VSCARD_VERSION MAKE_VERSION(0, 0, 2) + +typedef enum VSCMsgType { +    VSC_Init = 1, +    VSC_Error, +    VSC_ReaderAdd, +    VSC_ReaderRemove, +    VSC_ATR, +    VSC_CardRemove, +    VSC_APDU, +    VSC_Flush, +    VSC_FlushComplete +} VSCMsgType; + +typedef enum VSCErrorCode { +    VSC_SUCCESS = 0, +    VSC_GENERAL_ERROR = 1, +    VSC_CANNOT_ADD_MORE_READERS, +    VSC_CARD_ALREAY_INSERTED, +} VSCErrorCode; + +#define VSCARD_UNDEFINED_READER_ID  0xffffffff +#define VSCARD_MINIMAL_READER_ID    0 + +#define VSCARD_MAGIC (*(uint32_t *)"VSCD") + +/* + * Header + * Each message starts with the header. + * type - message type + * reader_id - used by messages that are reader specific + * length - length of payload (not including header, i.e. zero for + *  messages containing empty payloads) + */ +typedef struct VSCMsgHeader { +    uint32_t   type; +    uint32_t   reader_id; +    uint32_t   length; +    uint8_t    data[0]; +} VSCMsgHeader; + +/* + * VSCMsgInit               Client <-> Host + * Client sends it on connection, with its own capabilities. + * Host replies with VSCMsgInit filling in its capabilities. + * + * It is not meant to be used for negotiation, i.e. sending more then + * once from any side, but could be used for that in the future. + */ +typedef struct VSCMsgInit { +    uint32_t   magic; +    uint32_t   version; +    uint32_t   capabilities[1]; /* receiver must check length, +                                   array may grow in the future*/ +} VSCMsgInit; + +/* + * VSCMsgError              Client <-> Host + * This message is a response to any of: + *  Reader Add + *  Reader Remove + *  Card Remove + * If the operation was successful then VSC_SUCCESS + * is returned, other wise a specific error code. + */ +typedef struct VSCMsgError { +    uint32_t   code; +} VSCMsgError; + +/* + * VSCMsgReaderAdd          Client -> Host + * Host replies with allocated reader id in VSCMsgError with code==SUCCESS. + * + * name - name of the reader on client side, UTF-8 encoded. Only used + *  for client presentation (may be translated to the device presented to the + *  guest), protocol wise only reader_id is important. + */ +typedef struct VSCMsgReaderAdd { +    uint8_t    name[0]; +} VSCMsgReaderAdd; + +/* + * VSCMsgReaderRemove       Client -> Host + * The client's reader has been removed. + */ +typedef struct VSCMsgReaderRemove { +} VSCMsgReaderRemove; + +/* + * VSCMsgATR                Client -> Host + * Answer to reset. Sent for card insertion or card reset. The reset/insertion + * happens on the client side, they do not require any action from the host. + */ +typedef struct VSCMsgATR { +    uint8_t     atr[0]; +} VSCMsgATR; + +/* + * VSCMsgCardRemove         Client -> Host + * The client card has been removed. + */ +typedef struct VSCMsgCardRemove { +} VSCMsgCardRemove; + +/* + * VSCMsgAPDU               Client <-> Host + * Main reason of existence. Transfer a single APDU in either direction. + */ +typedef struct VSCMsgAPDU { +    uint8_t    data[0]; +} VSCMsgAPDU; + +/* + * VSCMsgFlush               Host -> Client + * Request client to send a FlushComplete message when it is done + * servicing all outstanding APDUs + */ +typedef struct VSCMsgFlush { +} VSCMsgFlush; + +/* + * VSCMsgFlush               Client -> Host + * Client response to Flush after all APDUs have been processed and + * responses sent. + */ +typedef struct VSCMsgFlushComplete { +} VSCMsgFlushComplete; + +#endif /* VSCARD_COMMON_H */ diff --git a/libcacard/vscclient.c b/libcacard/vscclient.c new file mode 100644 index 00000000..06526844 --- /dev/null +++ b/libcacard/vscclient.c @@ -0,0 +1,785 @@ +/* + * Tester for VSCARD protocol, client side. + * + * Can be used with ccid-card-passthru. + * + * Copyright (c) 2011 Red Hat. + * Written by Alon Levy. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifndef _WIN32 +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <unistd.h> +#define closesocket(x) close(x) +#else +#include <getopt.h> +#endif + +#include "glib-compat.h" + +#include "vscard_common.h" + +#include "vreader.h" +#include "vcard_emul.h" +#include "vevent.h" + +static int verbose; + +static void +print_byte_array( +    uint8_t *arrBytes, +    unsigned int nSize +) { +    int i; +    for (i = 0; i < nSize; i++) { +        printf("%02X ", arrBytes[i]); +    } +    printf("\n"); +} + +static void +print_usage(void) { +    printf("vscclient [-c <certname> .. -e <emul_args> -d <level>%s] " +            "<host> <port>\n", +#ifdef USE_PASSTHRU +    " -p"); +    printf(" -p use passthrough mode\n"); +#else +   ""); +#endif +    vcard_emul_usage(); +} + +static GIOChannel *channel_socket; +static GByteArray *socket_to_send; +static CompatGMutex socket_to_send_lock; +static guint socket_tag; + +static void +update_socket_watch(void); + +static gboolean +do_socket_send(GIOChannel *source, +               GIOCondition condition, +               gpointer data) +{ +    gsize bw; +    GError *err = NULL; + +    g_return_val_if_fail(socket_to_send->len != 0, FALSE); +    g_return_val_if_fail(condition & G_IO_OUT, FALSE); + +    g_io_channel_write_chars(channel_socket, +        (gchar *)socket_to_send->data, socket_to_send->len, &bw, &err); +    if (err != NULL) { +        g_error("Error while sending socket %s", err->message); +        return FALSE; +    } +    g_byte_array_remove_range(socket_to_send, 0, bw); + +    if (socket_to_send->len == 0) { +        update_socket_watch(); +        return FALSE; +    } +    return TRUE; +} + +static gboolean +socket_prepare_sending(gpointer user_data) +{ +    update_socket_watch(); + +    return FALSE; +} + +static int +send_msg( +    VSCMsgType type, +    uint32_t reader_id, +    const void *msg, +    unsigned int length +) { +    VSCMsgHeader mhHeader; + +    g_mutex_lock(&socket_to_send_lock); + +    if (verbose > 10) { +        printf("sending type=%d id=%u, len =%u (0x%x)\n", +               type, reader_id, length, length); +    } + +    mhHeader.type = htonl(type); +    mhHeader.reader_id = 0; +    mhHeader.length = htonl(length); +    g_byte_array_append(socket_to_send, (guint8 *)&mhHeader, sizeof(mhHeader)); +    g_byte_array_append(socket_to_send, (guint8 *)msg, length); +    g_idle_add(socket_prepare_sending, NULL); + +    g_mutex_unlock(&socket_to_send_lock); + +    return 0; +} + +static VReader *pending_reader; +static CompatGMutex pending_reader_lock; +static CompatGCond pending_reader_condition; + +#define MAX_ATR_LEN 40 +static gpointer +event_thread(gpointer arg) +{ +    unsigned char atr[MAX_ATR_LEN]; +    int atr_len; +    VEvent *event; +    unsigned int reader_id; + + +    while (1) { +        const char *reader_name; + +        event = vevent_wait_next_vevent(); +        if (event == NULL) { +            break; +        } +        reader_id = vreader_get_id(event->reader); +        if (reader_id == VSCARD_UNDEFINED_READER_ID && +            event->type != VEVENT_READER_INSERT) { +            /* ignore events from readers qemu has rejected */ +            /* if qemu is still deciding on this reader, wait to see if need to +             * forward this event */ +            g_mutex_lock(&pending_reader_lock); +            if (!pending_reader || (pending_reader != event->reader)) { +                /* wasn't for a pending reader, this reader has already been +                 * rejected by qemu */ +                g_mutex_unlock(&pending_reader_lock); +                vevent_delete(event); +                continue; +            } +            /* this reader hasn't been told its status from qemu yet, wait for +             * that status */ +            while (pending_reader != NULL) { +                g_cond_wait(&pending_reader_condition, &pending_reader_lock); +            } +            g_mutex_unlock(&pending_reader_lock); +            /* now recheck the id */ +            reader_id = vreader_get_id(event->reader); +            if (reader_id == VSCARD_UNDEFINED_READER_ID) { +                /* this reader was rejected */ +                vevent_delete(event); +                continue; +            } +            /* reader was accepted, now forward the event */ +        } +        switch (event->type) { +        case VEVENT_READER_INSERT: +            /* tell qemu to insert a new CCID reader */ +            /* wait until qemu has responded to our first reader insert +             * before we send a second. That way we won't confuse the responses +             * */ +            g_mutex_lock(&pending_reader_lock); +            while (pending_reader != NULL) { +                g_cond_wait(&pending_reader_condition, &pending_reader_lock); +            } +            pending_reader = vreader_reference(event->reader); +            g_mutex_unlock(&pending_reader_lock); +            reader_name = vreader_get_name(event->reader); +            if (verbose > 10) { +                printf(" READER INSERT: %s\n", reader_name); +            } +            send_msg(VSC_ReaderAdd, +                reader_id, /* currerntly VSCARD_UNDEFINED_READER_ID */ +                NULL, 0 /* TODO reader_name, strlen(reader_name) */); +            break; +        case VEVENT_READER_REMOVE: +            /* future, tell qemu that an old CCID reader has been removed */ +            if (verbose > 10) { +                printf(" READER REMOVE: %u\n", reader_id); +            } +            send_msg(VSC_ReaderRemove, reader_id, NULL, 0); +            break; +        case VEVENT_CARD_INSERT: +            /* get the ATR (intended as a response to a power on from the +             * reader */ +            atr_len = MAX_ATR_LEN; +            vreader_power_on(event->reader, atr, &atr_len); +            /* ATR call functions as a Card Insert event */ +            if (verbose > 10) { +                printf(" CARD INSERT %u: ", reader_id); +                print_byte_array(atr, atr_len); +            } +            send_msg(VSC_ATR, reader_id, atr, atr_len); +            break; +        case VEVENT_CARD_REMOVE: +            /* Card removed */ +            if (verbose > 10) { +                printf(" CARD REMOVE %u:\n", reader_id); +            } +            send_msg(VSC_CardRemove, reader_id, NULL, 0); +            break; +        default: +            break; +        } +        vevent_delete(event); +    } +    return NULL; +} + + +static unsigned int +get_id_from_string(char *string, unsigned int default_id) +{ +    unsigned int id = atoi(string); + +    /* don't accidentally swith to zero because no numbers have been supplied */ +    if ((id == 0) && *string != '0') { +        return default_id; +    } +    return id; +} + +static int +on_host_init(VSCMsgHeader *mhHeader, VSCMsgInit *incoming) +{ +    uint32_t *capabilities = (incoming->capabilities); +    int num_capabilities = +        1 + ((mhHeader->length - sizeof(VSCMsgInit)) / sizeof(uint32_t)); +    int i; + +    incoming->version = ntohl(incoming->version); +    if (incoming->version != VSCARD_VERSION) { +        if (verbose > 0) { +            printf("warning: host has version %d, we have %d\n", +                verbose, VSCARD_VERSION); +        } +    } +    if (incoming->magic != VSCARD_MAGIC) { +        printf("unexpected magic: got %d, expected %d\n", +            incoming->magic, VSCARD_MAGIC); +        return -1; +    } +    for (i = 0 ; i < num_capabilities; ++i) { +        capabilities[i] = ntohl(capabilities[i]); +    } +    /* Future: check capabilities */ +    /* remove whatever reader might be left in qemu, +     * in case of an unclean previous exit. */ +    send_msg(VSC_ReaderRemove, VSCARD_MINIMAL_READER_ID, NULL, 0); +    /* launch the event_thread. This will trigger reader adds for all the +     * existing readers */ +    g_thread_new("vsc/event", event_thread, NULL); +    return 0; +} + + +enum { +    STATE_HEADER, +    STATE_MESSAGE, +}; + +#define APDUBufSize 270 + +static gboolean +do_socket_read(GIOChannel *source, +               GIOCondition condition, +               gpointer data) +{ +    int rv; +    int dwSendLength; +    int dwRecvLength; +    uint8_t pbRecvBuffer[APDUBufSize]; +    static uint8_t pbSendBuffer[APDUBufSize]; +    VReaderStatus reader_status; +    VReader *reader = NULL; +    static VSCMsgHeader mhHeader; +    VSCMsgError *error_msg; +    GError *err = NULL; + +    static gchar *buf; +    static gsize br, to_read; +    static int state = STATE_HEADER; + +    if (state == STATE_HEADER && to_read == 0) { +        buf = (gchar *)&mhHeader; +        to_read = sizeof(mhHeader); +    } + +    if (to_read > 0) { +        g_io_channel_read_chars(source, (gchar *)buf, to_read, &br, &err); +        if (err != NULL) { +            g_error("error while reading: %s", err->message); +        } +        buf += br; +        to_read -= br; +        if (to_read != 0) { +            return TRUE; +        } +    } + +    if (state == STATE_HEADER) { +        mhHeader.type = ntohl(mhHeader.type); +        mhHeader.reader_id = ntohl(mhHeader.reader_id); +        mhHeader.length = ntohl(mhHeader.length); +        if (verbose) { +            printf("Header: type=%d, reader_id=%u length=%d (0x%x)\n", +                   mhHeader.type, mhHeader.reader_id, mhHeader.length, +                   mhHeader.length); +        } +        switch (mhHeader.type) { +        case VSC_APDU: +        case VSC_Flush: +        case VSC_Error: +        case VSC_Init: +            buf = (gchar *)pbSendBuffer; +            to_read = mhHeader.length; +            state = STATE_MESSAGE; +            return TRUE; +        default: +            fprintf(stderr, "Unexpected message of type 0x%X\n", mhHeader.type); +            return FALSE; +        } +    } + +    if (state == STATE_MESSAGE) { +        switch (mhHeader.type) { +        case VSC_APDU: +            if (verbose) { +                printf(" recv APDU: "); +                print_byte_array(pbSendBuffer, mhHeader.length); +            } +            /* Transmit received APDU */ +            dwSendLength = mhHeader.length; +            dwRecvLength = sizeof(pbRecvBuffer); +            reader = vreader_get_reader_by_id(mhHeader.reader_id); +            reader_status = vreader_xfr_bytes(reader, +                                              pbSendBuffer, dwSendLength, +                                              pbRecvBuffer, &dwRecvLength); +            if (reader_status == VREADER_OK) { +                mhHeader.length = dwRecvLength; +                if (verbose) { +                    printf(" send response: "); +                    print_byte_array(pbRecvBuffer, mhHeader.length); +                } +                send_msg(VSC_APDU, mhHeader.reader_id, +                         pbRecvBuffer, dwRecvLength); +            } else { +                rv = reader_status; /* warning: not meaningful */ +                send_msg(VSC_Error, mhHeader.reader_id, &rv, sizeof(uint32_t)); +            } +            vreader_free(reader); +            reader = NULL; /* we've freed it, don't use it by accident +                              again */ +            break; +        case VSC_Flush: +            /* TODO: actually flush */ +            send_msg(VSC_FlushComplete, mhHeader.reader_id, NULL, 0); +            break; +        case VSC_Error: +            error_msg = (VSCMsgError *) pbSendBuffer; +            if (error_msg->code == VSC_SUCCESS) { +                g_mutex_lock(&pending_reader_lock); +                if (pending_reader) { +                    vreader_set_id(pending_reader, mhHeader.reader_id); +                    vreader_free(pending_reader); +                    pending_reader = NULL; +                    g_cond_signal(&pending_reader_condition); +                } +                g_mutex_unlock(&pending_reader_lock); +                break; +            } +            printf("warning: qemu refused to add reader\n"); +            if (error_msg->code == VSC_CANNOT_ADD_MORE_READERS) { +                /* clear pending reader, qemu can't handle any more */ +                g_mutex_lock(&pending_reader_lock); +                if (pending_reader) { +                    pending_reader = NULL; +                    /* make sure the event loop doesn't hang */ +                    g_cond_signal(&pending_reader_condition); +                } +                g_mutex_unlock(&pending_reader_lock); +            } +            break; +        case VSC_Init: +            if (on_host_init(&mhHeader, (VSCMsgInit *)pbSendBuffer) < 0) { +                return FALSE; +            } +            break; +        default: +            g_assert_not_reached(); +            return FALSE; +        } + +        state = STATE_HEADER; +    } + + +    return TRUE; +} + +static gboolean +do_socket(GIOChannel *source, +          GIOCondition condition, +          gpointer data) +{ +    /* not sure if two watches work well with a single win32 sources */ +    if (condition & G_IO_OUT) { +        if (!do_socket_send(source, condition, data)) { +            return FALSE; +        } +    } + +    if (condition & G_IO_IN) { +        if (!do_socket_read(source, condition, data)) { +            return FALSE; +        } +    } + +    return TRUE; +} + +static void +update_socket_watch(void) +{ +    gboolean out = socket_to_send->len > 0; + +    if (socket_tag != 0) { +        g_source_remove(socket_tag); +    } + +    socket_tag = g_io_add_watch(channel_socket, +        G_IO_IN | (out ? G_IO_OUT : 0), do_socket, NULL); +} + +static gboolean +do_command(GIOChannel *source, +           GIOCondition condition, +           gpointer data) +{ +    char *string; +    VCardEmulError error; +    static unsigned int default_reader_id; +    unsigned int reader_id; +    VReader *reader = NULL; +    GError *err = NULL; + +    g_assert(condition & G_IO_IN); + +    reader_id = default_reader_id; +    g_io_channel_read_line(source, &string, NULL, NULL, &err); +    if (err != NULL) { +        g_error("Error while reading command: %s", err->message); +    } + +    if (string != NULL) { +        if (strncmp(string, "exit", 4) == 0) { +            /* remove all the readers */ +            VReaderList *list = vreader_get_reader_list(); +            VReaderListEntry *reader_entry; +            printf("Active Readers:\n"); +            for (reader_entry = vreader_list_get_first(list); reader_entry; +                 reader_entry = vreader_list_get_next(reader_entry)) { +                VReader *reader = vreader_list_get_reader(reader_entry); +                vreader_id_t reader_id; +                reader_id = vreader_get_id(reader); +                if (reader_id == -1) { +                    continue; +                } +                /* be nice and signal card removal first (qemu probably should +                 * do this itself) */ +                if (vreader_card_is_present(reader) == VREADER_OK) { +                    send_msg(VSC_CardRemove, reader_id, NULL, 0); +                } +                send_msg(VSC_ReaderRemove, reader_id, NULL, 0); +            } +            exit(0); +        } else if (strncmp(string, "insert", 6) == 0) { +            if (string[6] == ' ') { +                reader_id = get_id_from_string(&string[7], reader_id); +            } +            reader = vreader_get_reader_by_id(reader_id); +            if (reader != NULL) { +                error = vcard_emul_force_card_insert(reader); +                printf("insert %s, returned %d\n", +                       vreader_get_name(reader), error); +            } else { +                printf("no reader by id %u found\n", reader_id); +            } +        } else if (strncmp(string, "remove", 6) == 0) { +            if (string[6] == ' ') { +                reader_id = get_id_from_string(&string[7], reader_id); +            } +            reader = vreader_get_reader_by_id(reader_id); +            if (reader != NULL) { +                error = vcard_emul_force_card_remove(reader); +                printf("remove %s, returned %d\n", +                       vreader_get_name(reader), error); +            } else { +                printf("no reader by id %u found\n", reader_id); +            } +        } else if (strncmp(string, "select", 6) == 0) { +            if (string[6] == ' ') { +                reader_id = get_id_from_string(&string[7], +                                               VSCARD_UNDEFINED_READER_ID); +            } +            if (reader_id != VSCARD_UNDEFINED_READER_ID) { +                reader = vreader_get_reader_by_id(reader_id); +            } +            if (reader) { +                printf("Selecting reader %u, %s\n", reader_id, +                        vreader_get_name(reader)); +                default_reader_id = reader_id; +            } else { +                printf("Reader with id %u not found\n", reader_id); +            } +        } else if (strncmp(string, "debug", 5) == 0) { +            if (string[5] == ' ') { +                verbose = get_id_from_string(&string[6], 0); +            } +            printf("debug level = %d\n", verbose); +        } else if (strncmp(string, "list", 4) == 0) { +            VReaderList *list = vreader_get_reader_list(); +            VReaderListEntry *reader_entry; +            printf("Active Readers:\n"); +            for (reader_entry = vreader_list_get_first(list); reader_entry; +                 reader_entry = vreader_list_get_next(reader_entry)) { +                VReader *reader = vreader_list_get_reader(reader_entry); +                vreader_id_t reader_id; +                reader_id = vreader_get_id(reader); +                if (reader_id == -1) { +                    continue; +                } +                printf("%3u %s %s\n", reader_id, +                       vreader_card_is_present(reader) == VREADER_OK ? +                       "CARD_PRESENT" : "            ", +                       vreader_get_name(reader)); +            } +            printf("Inactive Readers:\n"); +            for (reader_entry = vreader_list_get_first(list); reader_entry; +                 reader_entry = vreader_list_get_next(reader_entry)) { +                VReader *reader = vreader_list_get_reader(reader_entry); +                vreader_id_t reader_id; +                reader_id = vreader_get_id(reader); +                if (reader_id != -1) { +                    continue; +                } + +                printf("INA %s %s\n", +                       vreader_card_is_present(reader) == VREADER_OK ? +                       "CARD_PRESENT" : "            ", +                       vreader_get_name(reader)); +            } +            vreader_list_delete(list); +        } else if (*string != 0) { +            printf("valid commands:\n"); +            printf("insert [reader_id]\n"); +            printf("remove [reader_id]\n"); +            printf("select reader_id\n"); +            printf("list\n"); +            printf("debug [level]\n"); +            printf("exit\n"); +        } +    } +    vreader_free(reader); +    printf("> "); +    fflush(stdout); + +    return TRUE; +} + + +/* just for ease of parsing command line arguments. */ +#define MAX_CERTS 100 + +static int +connect_to_qemu( +    const char *host, +    const char *port +) { +    struct addrinfo hints; +    struct addrinfo *server = NULL; +    int ret, sock; + +    sock = socket(AF_INET, SOCK_STREAM, 0); +    if (sock < 0) { +        /* Error */ +        fprintf(stderr, "Error opening socket!\n"); +        return -1; +    } + +    memset(&hints, 0, sizeof(struct addrinfo)); +    hints.ai_family = AF_UNSPEC; +    hints.ai_socktype = SOCK_STREAM; +    hints.ai_flags = 0; +    hints.ai_protocol = 0;          /* Any protocol */ + +    ret = getaddrinfo(host, port, &hints, &server); + +    if (ret != 0) { +        /* Error */ +        fprintf(stderr, "getaddrinfo failed\n"); +        goto cleanup_socket; +    } + +    if (connect(sock, server->ai_addr, server->ai_addrlen) < 0) { +        /* Error */ +        fprintf(stderr, "Could not connect\n"); +        goto cleanup_socket; +    } +    if (verbose) { +        printf("Connected (sizeof Header=%zd)!\n", sizeof(VSCMsgHeader)); +    } + +    freeaddrinfo(server); +    return sock; + +cleanup_socket: +    if (server) { +        freeaddrinfo(server); +    } +    closesocket(sock); +    return -1; +} + +int +main( +    int argc, +    char *argv[] +) { +    GMainLoop *loop; +    GIOChannel *channel_stdin; +    char *qemu_host; +    char *qemu_port; + +    VCardEmulOptions *command_line_options = NULL; + +    char *cert_names[MAX_CERTS]; +    char *emul_args = NULL; +    int cert_count = 0; +    int c, sock; + +#ifdef _WIN32 +    WSADATA Data; + +    if (WSAStartup(MAKEWORD(2, 2), &Data) != 0) { +        c = WSAGetLastError(); +        fprintf(stderr, "WSAStartup: %d\n", c); +        return 1; +    } +#endif +#if !GLIB_CHECK_VERSION(2, 31, 0) +    if (!g_thread_supported()) { +         g_thread_init(NULL); +    } +#endif + +    while ((c = getopt(argc, argv, "c:e:pd:")) != -1) { +        switch (c) { +        case 'c': +            if (cert_count >= MAX_CERTS) { +                printf("too many certificates (max = %d)\n", MAX_CERTS); +                exit(5); +            } +            cert_names[cert_count++] = optarg; +            break; +        case 'e': +            emul_args = optarg; +            break; +        case 'p': +            print_usage(); +            exit(4); +            break; +        case 'd': +            verbose = get_id_from_string(optarg, 1); +            break; +        } +    } + +    if (argc - optind != 2) { +        print_usage(); +        exit(4); +    } + +    if (cert_count > 0) { +        char *new_args; +        int len, i; +        /* if we've given some -c options, we clearly we want do so some +         * software emulation.  add that emulation now. this is NSS Emulator +         * specific */ +        if (emul_args == NULL) { +            emul_args = (char *)"db=\"/etc/pki/nssdb\""; +        } +#define SOFT_STRING ",soft=(,Virtual Reader,CAC,," +             /* 2 == close paren & null */ +        len = strlen(emul_args) + strlen(SOFT_STRING) + 2; +        for (i = 0; i < cert_count; i++) { +            len += strlen(cert_names[i])+1; /* 1 == comma */ +        } +        new_args = g_malloc(len); +        strcpy(new_args, emul_args); +        strcat(new_args, SOFT_STRING); +        for (i = 0; i < cert_count; i++) { +            strcat(new_args, cert_names[i]); +            strcat(new_args, ","); +        } +        strcat(new_args, ")"); +        emul_args = new_args; +    } +    if (emul_args) { +        command_line_options = vcard_emul_options(emul_args); +    } + +    qemu_host = g_strdup(argv[argc - 2]); +    qemu_port = g_strdup(argv[argc - 1]); +    sock = connect_to_qemu(qemu_host, qemu_port); +    if (sock == -1) { +        fprintf(stderr, "error opening socket, exiting.\n"); +        exit(5); +    } + +    socket_to_send = g_byte_array_new(); +    vcard_emul_init(command_line_options); +    loop = g_main_loop_new(NULL, TRUE); + +    printf("> "); +    fflush(stdout); + +#ifdef _WIN32 +    channel_stdin = g_io_channel_win32_new_fd(STDIN_FILENO); +#else +    channel_stdin = g_io_channel_unix_new(STDIN_FILENO); +#endif +    g_io_add_watch(channel_stdin, G_IO_IN, do_command, NULL); +#ifdef _WIN32 +    channel_socket = g_io_channel_win32_new_socket(sock); +#else +    channel_socket = g_io_channel_unix_new(sock); +#endif +    g_io_channel_set_encoding(channel_socket, NULL, NULL); +    /* we buffer ourself for thread safety reasons */ +    g_io_channel_set_buffered(channel_socket, FALSE); + +    /* Send init message, Host responds (and then we send reader attachments) */ +    VSCMsgInit init = { +        .version = htonl(VSCARD_VERSION), +        .magic = VSCARD_MAGIC, +        .capabilities = {0} +    }; +    send_msg(VSC_Init, 0, &init, sizeof(init)); + +    g_main_loop_run(loop); +    g_main_loop_unref(loop); + +    g_io_channel_unref(channel_stdin); +    g_io_channel_unref(channel_socket); +    g_byte_array_free(socket_to_send, TRUE); + +    closesocket(sock); +    return 0; +}  | 
