#include "vreader.h"
#include "vevent.h"
+#include "libcacard/vcardt_internal.h"
+
+
+typedef enum {
+ VCardEmulUnknown = -1,
+ VCardEmulFalse = 0,
+ VCardEmulTrue = 1
+} VCardEmulTriState;
+
struct VCardKeyStruct {
CERTCertificate *cert;
PK11SlotInfo *slot;
SECKEYPrivateKey *key;
+ VCardEmulTriState failedX509;
};
*certsp = NULL;
*cert_lenp = NULL;
*keysp = NULL;
- *certsp = (unsigned char **)qemu_malloc(sizeof(unsigned char *)*cert_count);
- *cert_lenp = (int *)qemu_malloc(sizeof(int)*cert_count);
- *keysp = (VCardKey **)qemu_malloc(sizeof(VCardKey *)*cert_count);
+ *certsp = (unsigned char **)g_malloc(sizeof(unsigned char *)*cert_count);
+ *cert_lenp = (int *)g_malloc(sizeof(int)*cert_count);
+ *keysp = (VCardKey **)g_malloc(sizeof(VCardKey *)*cert_count);
return PR_TRUE;
}
{
VCardKey *key;
- key = (VCardKey *)qemu_malloc(sizeof(VCardKey));
+ key = (VCardKey *)g_malloc(sizeof(VCardKey));
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;
}
if (key->slot) {
PK11_FreeSlot(key->slot);
}
- return;
}
/*
{
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
if (buffer_size != signature_len) {
return VCARD7816_STATUS_ERROR_DATA_INVALID;
}
- rv = PK11_PrivDecryptRaw(priv_key, buffer, &signature_len, signature_len,
- buffer, buffer_size);
- if (rv != SECSuccess) {
- return vcard_emul_map_error(PORT_GetError());
+ /* 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 formated PKCS #1 signature */
+ /*
+ * NOTE: even if we accidentally got an encrypt buffer, which
+ * through shear 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);
}
- assert(buffer_size == signature_len);
- return VCARD7816_STATUS_SUCCESS;
+ return ret;
}
/*
* to handle multiple guests from one process, then we would need to keep
* a lot of extra state in our card structure
* */
- pin_string = qemu_malloc(pin_len+1);
+ pin_string = g_malloc(pin_len+1);
memcpy(pin_string, pin, pin_len);
pin_string[pin_len] = 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 */
- qemu_free(pin_string);
+ g_free(pin_string);
if (rv == SECSuccess) {
return VCARD7816_STATUS_SUCCESS;
}
/* TODO: we may also need to send insertion/removal events? */
slot = vcard_emul_card_get_slot(card);
PK11_Logout(slot); /* NOTE: ignoring SECStatus return value */
- return;
}
{
VReaderEmul *new_reader_emul;
- new_reader_emul = (VReaderEmul *)qemu_malloc(sizeof(VReaderEmul));
+ new_reader_emul = (VReaderEmul *)g_malloc(sizeof(VReaderEmul));
new_reader_emul->slot = PK11_ReferenceSlot(slot);
new_reader_emul->default_type = type;
- new_reader_emul->type_params = strdup(params);
+ 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;
PK11_FreeSlot(vreader_emul->slot);
}
if (vreader_emul->type_params) {
- qemu_free(vreader_emul->type_params);
+ g_free(vreader_emul->type_params);
}
- qemu_free(vreader_emul);
+ g_free(vreader_emul);
}
/*
}
/*
- * Card ATR's map to physical cards. VCARD_ATR_PREFIX will set appropriate
+ * Card ATR's map to physical cards. vcard_alloc_atr will set appropriate
* historical bytes for any software emulated card. The remaining bytes can be
* used to indicate the actual emulator
*/
-static const unsigned char nss_atr[] = { VCARD_ATR_PREFIX(3), 'N', 'S', 'S' };
+static unsigned char *nss_atr;
+static int nss_atr_len;
void
vcard_emul_get_atr(VCard *card, unsigned char *atr, int *atr_len)
{
- int len = MIN(sizeof(nss_atr), *atr_len);
+ int len;
assert(atr != NULL);
+ if (nss_atr == NULL) {
+ nss_atr = vcard_alloc_atr("NSS", &nss_atr_len);
+ }
+ len = MIN(nss_atr_len, *atr_len);
memcpy(atr, nss_atr, len);
*atr_len = len;
- return;
}
/*
VCardKey **keys;
PK11SlotInfo *slot;
PRBool ret;
+ VCard *card;
slot = vcard_emul_reader_get_slot(vreader);
if (slot == NULL) {
}
/* now create the card */
- return vcard_emul_make_card(vreader, certs, cert_len, keys, cert_count);
+ 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;
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);
vcard_emul_init(const VCardEmulOptions *options)
{
SECStatus rv;
- PRBool ret, has_readers = PR_FALSE, need_coolkey_module;
+ PRBool ret, has_readers = PR_FALSE;
VReader *vreader;
VReaderEmul *vreader_emul;
SECMODListLock *module_lock;
if (options->nss_db) {
rv = NSS_Init(options->nss_db);
} else {
- rv = NSS_Init("sql:/etc/pki/nssdb");
+ gchar *path;
+#ifndef _WIN32
+ path = g_strdup("/etc/pki/nssdb");
+#else
+ if (g_get_system_config_dirs() == NULL ||
+ g_get_system_config_dirs()[0] == NULL) {
+ return VCARD_EMUL_FAIL;
+ }
+
+ path = g_build_filename(
+ g_get_system_config_dirs()[0], "pki", "nssdb", NULL);
+#endif
+
+ rv = NSS_Init(path);
+ g_free(path);
}
if (rv != SECSuccess) {
return VCARD_EMUL_FAIL;
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 */
/* make sure we have some PKCS #11 module loaded */
module_lock = SECMOD_GetDefaultModuleListLock();
module_list = SECMOD_GetDefaultModuleList();
- need_coolkey_module = !has_readers;
SECMOD_GetReadLock(module_lock);
for (mlp = module_list; mlp; mlp = mlp->next) {
SECMODModule *module = mlp->module;
if (module_has_removable_hw_slots(module)) {
- need_coolkey_module = PR_FALSE;
break;
}
}
SECMOD_ReleaseReadLock(module_lock);
- if (need_coolkey_module) {
- SECMODModule *module;
- module = SECMOD_LoadUserModule(
- (char *)"library=libcoolkeypk11.so name=Coolkey",
- NULL, PR_FALSE);
- if (module == NULL) {
- return VCARD_EMUL_FAIL;
- }
- SECMOD_DestroyModule(module); /* free our reference, Module will still
- * be on the list.
- * until we destroy it */
- }
-
/* now examine all the slots, finding which should be readers */
/* We should control this with options. For now we mirror out any
* removable hardware slot */
default_card_type = options->hw_card_type;
- default_type_params = strdup(options->hw_type_params);
+ 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;
- PRBool has_emul_slots = PR_FALSE;
- if (module == NULL) {
- continue;
+ /* Ignore the internal module */
+ if (module == NULL || module == SECMOD_GetInternalModule()) {
+ continue;
}
for (i = 0; i < module->slotCount; i++) {
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);
- has_readers = PR_TRUE;
- has_emul_slots = PR_TRUE;
-
if (PK11_IsPresent(slot)) {
VCard *vcard;
vcard = vcard_emul_mirror_card(vreader);
vcard_free(vcard);
}
}
- if (has_emul_slots) {
- vcard_emul_new_event_thread(module);
- }
+ vcard_emul_new_event_thread(module);
}
SECMOD_ReleaseReadLock(module_lock);
- nss_emul_init = has_readers;
+ nss_emul_init = PR_TRUE;
return VCARD_EMUL_OK;
}
/*
* Silly little functions to help parsing our argument string
*/
-static char *
-copy_string(const char *str, int str_len)
-{
- char *new_str;
-
- new_str = qemu_malloc(str_len+1);
- memcpy(new_str, str, str_len);
- new_str[str_len] = 0;
- return new_str;
-}
-
static int
count_tokens(const char *str, char token, char token_end)
{
static const char *
strip(const char *str)
{
- for (; *str && !isspace(*str); str++) {
+ for (; *str && isspace(*str); str++) {
}
return str;
}
static const char *
find_blank(const char *str)
{
- for (; *str && isspace(*str); str++) {
+ for (; *str && !isspace(*str); str++) {
}
return str;
}
/*
* We really want to use some existing argument parsing library here. That
- * would give us a consistant look */
+ * 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;
- char type_str[100];
- int type_len;
/* Allow the future use of allocating the options structure on the fly */
memcpy(&options, &default_options, sizeof(options));
* 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 name_length, vname_length, type_params_length, count, i;
+ int count, i;
VirtualReaderOptions *vreaderOpt = NULL;
args = strip(args + 5);
if (*args != '(') {
continue;
}
- name = args;
- args = strpbrk(args + 1, ",)");
- if (*args == 0) {
- break;
- }
- if (*args == ')') {
- args++;
- continue;
- }
- args = strip(args+1);
- name_length = args - name - 2;
- vname = args;
- args = strpbrk(args + 1, ",)");
- if (*args == 0) {
- break;
- }
- if (*args == ')') {
- args++;
- continue;
- }
- vname_length = args - name - 2;
args = strip(args+1);
- type_len = strpbrk(args, ",)") - args;
- assert(sizeof(type_str) > type_len);
- strncpy(type_str, args, type_len);
- type_str[type_len] = 0;
+
+ NEXT_TOKEN(name)
+ NEXT_TOKEN(vname)
+ NEXT_TOKEN(type_params)
+ type_params_length = MIN(type_params_length, sizeof(type_str)-1);
+ pstrcpy(type_str, type_params_length, type_params);
type = vcard_emul_type_from_string(type_str);
- args = strpbrk(args, ",)");
- if (*args == 0) {
- break;
- }
- if (*args == ')') {
- args++;
- continue;
- }
- args = strip(args++);
- type_params = args;
- args = strpbrk(args + 1, ",)");
- if (*args == 0) {
- break;
- }
- if (*args == ')') {
- args++;
- continue;
- }
- type_params_length = args - name;
- args = strip(args++);
+
+ NEXT_TOKEN(type_params)
+
if (*args == 0) {
break;
}
}
opts->vreader = vreaderOpt;
vreaderOpt = &vreaderOpt[opts->vreader_count];
- vreaderOpt->name = copy_string(name, name_length);
- vreaderOpt->vname = copy_string(vname, vname_length);
+ vreaderOpt->name = g_strndup(name, name_length);
+ vreaderOpt->vname = g_strndup(vname, vname_length);
vreaderOpt->card_type = type;
vreaderOpt->type_params =
- copy_string(type_params, type_params_length);
- count = count_tokens(args, ',', ')');
+ g_strndup(type_params, type_params_length);
+ count = count_tokens(args, ',', ')') + 1;
vreaderOpt->cert_count = count;
- vreaderOpt->cert_name = (char **)qemu_malloc(count*sizeof(char *));
+ vreaderOpt->cert_name = (char **)g_malloc(count*sizeof(char *));
for (i = 0; i < count; i++) {
- const char *cert = args + 1;
- args = strpbrk(args + 1, ",)");
- vreaderOpt->cert_name[i] = copy_string(cert, args - cert);
+ const char *cert = args;
+ args = strpbrk(args, ",)");
+ vreaderOpt->cert_name[i] = g_strndup(cert, args - cert);
+ args = strip(args+1);
}
if (*args == ')') {
args++;
args = strip(args+10);
params = args;
args = find_blank(args);
- opts->hw_type_params = copy_string(params, args-params);
+ opts->hw_type_params = g_strndup(params, args-params);
/* db="/data/base/path" */
} else if (strncmp(args, "db=", 3) == 0) {
const char *db;
args++;
db = args;
args = strpbrk(args, "\"\n");
- opts->nss_db = copy_string(db, args-db);
+ opts->nss_db = g_strndup(db, args-db);
if (*args != 0) {
args++;
}
" {card_type_to_emulate} What card interface to present to the guest\n"
" {param_for_card} Card interface specific parameters\n"
" {slot_name} NSS slot that contains the certs\n"
-" {vreader_name} Virutal reader name to present to the guest\n"
+" {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."