|  | /* | 
|  | * 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 "qemu-common.h" | 
|  |  | 
|  | #include "cac.h" | 
|  | #include "vcard.h" | 
|  | #include "vcard_emul.h" | 
|  | #include "card_7816.h" | 
|  |  | 
|  | #define CAC_GET_PROPERTIES  0x56 | 
|  | #define CAC_GET_ACR         0x4c | 
|  | #define CAC_READ_BUFFER     0x52 | 
|  | #define CAC_UPDATE_BUFFER   0x58 | 
|  | #define CAC_SIGN_DECRYPT    0x42 | 
|  | #define CAC_GET_CERTIFICATE 0x36 | 
|  |  | 
|  | /* private data for PKI applets */ | 
|  | typedef struct CACPKIAppletDataStruct { | 
|  | unsigned char *cert; | 
|  | int cert_len; | 
|  | unsigned char *cert_buffer; | 
|  | int cert_buffer_len; | 
|  | unsigned char *sign_buffer; | 
|  | int sign_buffer_len; | 
|  | VCardKey *key; | 
|  | } CACPKIAppletData; | 
|  |  | 
|  | /* | 
|  | * CAC applet private data | 
|  | */ | 
|  | struct VCardAppletPrivateStruct { | 
|  | union { | 
|  | CACPKIAppletData pki_data; | 
|  | void *reserved; | 
|  | } u; | 
|  | }; | 
|  |  | 
|  | /* | 
|  | * handle all the APDU's that are common to all CAC applets | 
|  | */ | 
|  | static VCardStatus | 
|  | cac_common_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response) | 
|  | { | 
|  | int ef; | 
|  |  | 
|  | switch (apdu->a_ins) { | 
|  | case VCARD7816_INS_SELECT_FILE: | 
|  | if (apdu->a_p1 != 0x02) { | 
|  | /* let the 7816 code handle applet switches */ | 
|  | return VCARD_NEXT; | 
|  | } | 
|  | /* handle file id setting */ | 
|  | if (apdu->a_Lc != 2) { | 
|  | *response = vcard_make_response( | 
|  | VCARD7816_STATUS_ERROR_DATA_INVALID); | 
|  | return VCARD_DONE; | 
|  | } | 
|  | /* CAC 1.0 only supports ef = 0 */ | 
|  | ef = apdu->a_body[0] | (apdu->a_body[1] << 8); | 
|  | if (ef != 0) { | 
|  | *response = vcard_make_response( | 
|  | VCARD7816_STATUS_ERROR_FILE_NOT_FOUND); | 
|  | return VCARD_DONE; | 
|  | } | 
|  | *response = vcard_make_response(VCARD7816_STATUS_SUCCESS); | 
|  | return VCARD_DONE; | 
|  | case VCARD7816_INS_GET_RESPONSE: | 
|  | case VCARD7816_INS_VERIFY: | 
|  | /* let the 7816 code handle these */ | 
|  | return VCARD_NEXT; | 
|  | case CAC_GET_PROPERTIES: | 
|  | case CAC_GET_ACR: | 
|  | /* skip these for now, this will probably be needed */ | 
|  | *response = vcard_make_response(VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); | 
|  | return VCARD_DONE; | 
|  | } | 
|  | *response = vcard_make_response( | 
|  | VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); | 
|  | return VCARD_DONE; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  reset the inter call state between applet selects | 
|  | */ | 
|  | static VCardStatus | 
|  | cac_applet_pki_reset(VCard *card, int channel) | 
|  | { | 
|  | VCardAppletPrivate *applet_private = NULL; | 
|  | CACPKIAppletData *pki_applet = NULL; | 
|  | applet_private = vcard_get_current_applet_private(card, channel); | 
|  | assert(applet_private); | 
|  | pki_applet = &(applet_private->u.pki_data); | 
|  |  | 
|  | pki_applet->cert_buffer = NULL; | 
|  | if (pki_applet->sign_buffer) { | 
|  | 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 = NULL; | 
|  | VCardAppletPrivate *applet_private = NULL; | 
|  | int size, next; | 
|  | unsigned char *sign_buffer; | 
|  | vcard_7816_status_t status; | 
|  |  | 
|  | applet_private = vcard_get_current_applet_private(card, apdu->a_channel); | 
|  | assert(applet_private); | 
|  | pki_applet = &(applet_private->u.pki_data); | 
|  |  | 
|  | switch (apdu->a_ins) { | 
|  | case CAC_UPDATE_BUFFER: | 
|  | *response = vcard_make_response( | 
|  | VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED); | 
|  | return VCARD_DONE; | 
|  | case CAC_GET_CERTIFICATE: | 
|  | if ((apdu->a_p2 != 0) || (apdu->a_p1 != 0)) { | 
|  | *response = vcard_make_response( | 
|  | VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); | 
|  | break; | 
|  | } | 
|  | assert(pki_applet->cert != NULL); | 
|  | size = apdu->a_Le; | 
|  | if (pki_applet->cert_buffer == NULL) { | 
|  | pki_applet->cert_buffer = pki_applet->cert; | 
|  | pki_applet->cert_buffer_len = pki_applet->cert_len; | 
|  | } | 
|  | size = MIN(size, pki_applet->cert_buffer_len); | 
|  | next = MIN(255, pki_applet->cert_buffer_len - size); | 
|  | *response = vcard_response_new_bytes( | 
|  | card, pki_applet->cert_buffer, size, | 
|  | apdu->a_Le, next ? | 
|  | VCARD7816_SW1_WARNING_CHANGE : | 
|  | VCARD7816_SW1_SUCCESS, | 
|  | next); | 
|  | pki_applet->cert_buffer += size; | 
|  | pki_applet->cert_buffer_len -= size; | 
|  | if ((*response == NULL) || (next == 0)) { | 
|  | pki_applet->cert_buffer = NULL; | 
|  | } | 
|  | if (*response == NULL) { | 
|  | *response = vcard_make_response( | 
|  | VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); | 
|  | } | 
|  | return VCARD_DONE; | 
|  | case CAC_SIGN_DECRYPT: | 
|  | if (apdu->a_p2 != 0) { | 
|  | *response = vcard_make_response( | 
|  | VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); | 
|  | break; | 
|  | } | 
|  | size = apdu->a_Lc; | 
|  |  | 
|  | sign_buffer = realloc(pki_applet->sign_buffer, | 
|  | pki_applet->sign_buffer_len+size); | 
|  | if (sign_buffer == NULL) { | 
|  | g_free(pki_applet->sign_buffer); | 
|  | pki_applet->sign_buffer = NULL; | 
|  | pki_applet->sign_buffer_len = 0; | 
|  | *response = vcard_make_response( | 
|  | VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); | 
|  | return VCARD_DONE; | 
|  | } | 
|  | memcpy(sign_buffer+pki_applet->sign_buffer_len, apdu->a_body, size); | 
|  | size += pki_applet->sign_buffer_len; | 
|  | switch (apdu->a_p1) { | 
|  | case  0x80: | 
|  | /* p1 == 0x80 means we haven't yet sent the whole buffer, wait for | 
|  | * the rest */ | 
|  | pki_applet->sign_buffer = sign_buffer; | 
|  | pki_applet->sign_buffer_len = size; | 
|  | *response = vcard_make_response(VCARD7816_STATUS_SUCCESS); | 
|  | return VCARD_DONE; | 
|  | case 0x00: | 
|  | /* we now have the whole buffer, do the operation, result will be | 
|  | * in the sign_buffer */ | 
|  | status = vcard_emul_rsa_op(card, pki_applet->key, | 
|  | sign_buffer, size); | 
|  | if (status != VCARD7816_STATUS_SUCCESS) { | 
|  | *response = vcard_make_response(status); | 
|  | break; | 
|  | } | 
|  | *response = vcard_response_new(card, sign_buffer, size, apdu->a_Le, | 
|  | VCARD7816_STATUS_SUCCESS); | 
|  | if (*response == NULL) { | 
|  | *response = vcard_make_response( | 
|  | VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE); | 
|  | } | 
|  | break; | 
|  | default: | 
|  | *response = vcard_make_response( | 
|  | VCARD7816_STATUS_ERROR_P1_P2_INCORRECT); | 
|  | break; | 
|  | } | 
|  | g_free(sign_buffer); | 
|  | pki_applet->sign_buffer = NULL; | 
|  | pki_applet->sign_buffer_len = 0; | 
|  | return VCARD_DONE; | 
|  | case CAC_READ_BUFFER: | 
|  | /* new CAC call, go ahead and use the old version for now */ | 
|  | /* TODO: implement */ | 
|  | *response = vcard_make_response( | 
|  | VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); | 
|  | return VCARD_DONE; | 
|  | } | 
|  | return cac_common_process_apdu(card, apdu, response); | 
|  | } | 
|  |  | 
|  |  | 
|  | static VCardStatus | 
|  | cac_applet_id_process_apdu(VCard *card, VCardAPDU *apdu, | 
|  | VCardResponse **response) | 
|  | { | 
|  | switch (apdu->a_ins) { | 
|  | case CAC_UPDATE_BUFFER: | 
|  | *response = vcard_make_response( | 
|  | VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED); | 
|  | return VCARD_DONE; | 
|  | case CAC_READ_BUFFER: | 
|  | /* new CAC call, go ahead and use the old version for now */ | 
|  | /* TODO: implement */ | 
|  | *response = vcard_make_response( | 
|  | VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); | 
|  | return VCARD_DONE; | 
|  | } | 
|  | return cac_common_process_apdu(card, apdu, response); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * TODO: if we ever want to support general CAC middleware, we will need to | 
|  | * implement the various containers. | 
|  | */ | 
|  | static VCardStatus | 
|  | cac_applet_container_process_apdu(VCard *card, VCardAPDU *apdu, | 
|  | VCardResponse **response) | 
|  | { | 
|  | switch (apdu->a_ins) { | 
|  | case CAC_READ_BUFFER: | 
|  | case CAC_UPDATE_BUFFER: | 
|  | *response = vcard_make_response( | 
|  | VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED); | 
|  | return VCARD_DONE; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | return cac_common_process_apdu(card, apdu, response); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * utilities for creating and destroying the private applet data | 
|  | */ | 
|  | static void | 
|  | cac_delete_pki_applet_private(VCardAppletPrivate *applet_private) | 
|  | { | 
|  | CACPKIAppletData *pki_applet_data = NULL; | 
|  |  | 
|  | if (applet_private == NULL) { | 
|  | return; | 
|  | } | 
|  | pki_applet_data = &(applet_private->u.pki_data); | 
|  | if (pki_applet_data->cert != NULL) { | 
|  | g_free(pki_applet_data->cert); | 
|  | } | 
|  | if (pki_applet_data->sign_buffer != NULL) { | 
|  | 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 = NULL; | 
|  | VCardAppletPrivate *applet_private = NULL; | 
|  | applet_private = (VCardAppletPrivate *)g_malloc(sizeof(VCardAppletPrivate)); | 
|  |  | 
|  | pki_applet_data = &(applet_private->u.pki_data); | 
|  | pki_applet_data->cert_buffer = NULL; | 
|  | pki_applet_data->cert_buffer_len = 0; | 
|  | pki_applet_data->sign_buffer = NULL; | 
|  | pki_applet_data->sign_buffer_len = 0; | 
|  | pki_applet_data->key = NULL; | 
|  | pki_applet_data->cert = (unsigned char *)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 = NULL; | 
|  | VCardApplet *applet = NULL; | 
|  | unsigned char pki_aid[] = { 0xa0, 0x00, 0x00, 0x00, 0x79, 0x01, 0x00 }; | 
|  | int pki_aid_len = sizeof(pki_aid); | 
|  |  | 
|  | pki_aid[pki_aid_len-1] = i; | 
|  |  | 
|  | applet_private = cac_new_pki_applet_private(cert, cert_len, key); | 
|  | if (applet_private == NULL) { | 
|  | goto failure; | 
|  | } | 
|  | applet = vcard_new_applet(cac_applet_pki_process_apdu, cac_applet_pki_reset, | 
|  | pki_aid, pki_aid_len); | 
|  | if (applet == NULL) { | 
|  | goto failure; | 
|  | } | 
|  | vcard_set_applet_private(applet, applet_private, | 
|  | cac_delete_pki_applet_private); | 
|  | applet_private = NULL; | 
|  |  | 
|  | return applet; | 
|  |  | 
|  | failure: | 
|  | if (applet_private != NULL) { | 
|  | cac_delete_pki_applet_private(applet_private); | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  |  | 
|  | static unsigned char cac_default_container_aid[] = { | 
|  | 0xa0, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00 }; | 
|  | static unsigned char cac_id_aid[] = { | 
|  | 0xa0, 0x00, 0x00, 0x00, 0x79, 0x03, 0x00 }; | 
|  | /* | 
|  | * Initialize the cac card. This is the only public function in this file. All | 
|  | * the rest are connected through function pointers. | 
|  | */ | 
|  | VCardStatus | 
|  | cac_card_init(VReader *reader, VCard *card, | 
|  | const char *params, | 
|  | unsigned char * const *cert, | 
|  | int cert_len[], | 
|  | VCardKey *key[] /* adopt the keys*/, | 
|  | int cert_count) | 
|  | { | 
|  | int i; | 
|  | VCardApplet *applet; | 
|  |  | 
|  | /* CAC Cards are VM Cards */ | 
|  | vcard_set_type(card, VCARD_VM); | 
|  |  | 
|  | /* create one PKI applet for each cert */ | 
|  | for (i = 0; i < cert_count; i++) { | 
|  | applet = cac_new_pki_applet(i, cert[i], cert_len[i], key[i]); | 
|  | if (applet == NULL) { | 
|  | goto failure; | 
|  | } | 
|  | vcard_add_applet(card, applet); | 
|  | } | 
|  |  | 
|  | /* create a default blank container applet */ | 
|  | applet = vcard_new_applet(cac_applet_container_process_apdu, | 
|  | NULL, cac_default_container_aid, | 
|  | sizeof(cac_default_container_aid)); | 
|  | if (applet == NULL) { | 
|  | goto failure; | 
|  | } | 
|  | vcard_add_applet(card, applet); | 
|  |  | 
|  | /* create a default blank container applet */ | 
|  | applet = vcard_new_applet(cac_applet_id_process_apdu, | 
|  | NULL, cac_id_aid, | 
|  | sizeof(cac_id_aid)); | 
|  | if (applet == NULL) { | 
|  | goto failure; | 
|  | } | 
|  | vcard_add_applet(card, applet); | 
|  | return VCARD_DONE; | 
|  |  | 
|  | failure: | 
|  | return VCARD_FAIL; | 
|  | } | 
|  |  |