1223bc4cee
When a process in a guest uses an emulated smartcard, libcacard running on the host passes the PIN from the guest to the PK11_Authenticate NSS function. The first time PK11_Authenticate is called the passed in PIN is used to unlock the certificate database. Subsequent calls to PK11_Authenticate will transparently succeed, regardless of the passed in PIN. This is a convenience for applications provided by NSS. Of course, the guest may have many applications using the one emulated smart card all driven from the same host QEMU process. That means if a user enters the right PIN in one program in the guest, and then enters the wrong PIN in another program in the guest, the wrong PIN will still successfully unlock the virtual smartcard. This commit forces the NSS certificate database to be locked anytime an applet is selected on an emulated smartcard by calling vcard_emul_logout. Signed-off-by: Ray Strode <rstrode@redhat.com> Reviewed-By: Robert Relyea <rrelyea@redhat.com> Reviewed-By: Alon Levy <alevy@redhat.com> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
324 lines
7.2 KiB
C
324 lines
7.2 KiB
C
/*
|
|
* 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 "qemu-common.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;
|
|
}
|
|
|