diff options
author | Robert Relyea <rrelyea@redhat.com> | 2010-11-28 16:36:38 +0200 |
---|---|---|
committer | Anthony Liguori <aliguori@us.ibm.com> | 2011-04-01 19:07:48 -0500 |
commit | 111a38b018c86e6651750c5a548ad534f80b5bb5 (patch) | |
tree | e9397c478ead6463da198dd141c61cfd7cba189e /libcacard/vcard_emul_nss.c | |
parent | edbb21363fbfe40e050f583df921484cbc31c79d (diff) |
libcacard: initial commit
libcacard emulates a Common Access Card (CAC) which is a standard
for smartcards. It is used by the emulated ccid card introduced in
a following patch. Docs are available in docs/libcacard.txt
Signed-off-by: Alon Levy <alevy@redhat.com>
---
changes from v24->v25:
* Fix out of tree builds.
* Fix build with linux-user targets.
changes from v23->v24: (Jes Sorensen review 2)
* Makefile.target: use obj-$(CONFIG_*) +=
* remove unrequired includes, include qemu-common before qemu-thread
* required adding #define NO_NSPR_10_SUPPORT (harmless)
changes from v22->v23:
* configure fixes: (reported by Stefan Hajnoczi)
* test a = b, not a == b (second isn't portable)
* quote $source_path in case it contains spaces
- this doesn't really help since there are many other places
that need similar fixes, not introduced by this patch.
changes from v21->v22:
* fix configure to not link libcacard if nss not found
(reported by Stefan Hajnoczi)
* fix vscclient linkage with simpletrace backend
(reported by Stefan Hajnoczi)
* card_7816.c: add missing break in ERROR_DATA_NOT_FOUND
(reported by William van de Velde)
changes from v20->v21: (Jes Sorensen review)
* use qemu infrastructure: qemu-thread, qemu-common (qemu_malloc
and qemu_free), error_report
* assert instead of ASSERT
* cosmetic fixes
* use strpbrk and isspace
* add --disable-nss --enable-nss here, instead of in the final patch.
* split vscclient, passthru and docs to following patches.
changes from v19->v20:
* checkpatch.pl
changes from v15->v16:
Build:
* don't erase self with distclean
* fix make clean after make distclean
* Makefile: make vscclient link quiet
Behavioral:
* vcard_emul_nss: load coolkey in more situations
* vscclient:
* use hton,ntoh
* send init on connect, only start vevent thread on response
* read payload after header check, before type switch
* remove Reconnect
* update for vscard_common changes, empty Flush implementation
Style/Whitespace:
* fix wrong variable usage
* remove unused variable
* use only C style comments
* add copyright header
* fix tabulation
Signed-off-by: Alon Levy <alevy@redhat.com>
libcacard: fix out of tree builds
Diffstat (limited to 'libcacard/vcard_emul_nss.c')
-rw-r--r-- | libcacard/vcard_emul_nss.c | 1157 |
1 files changed, 1157 insertions, 0 deletions
diff --git a/libcacard/vcard_emul_nss.c b/libcacard/vcard_emul_nss.c new file mode 100644 index 0000000000..71f2ba3ae5 --- /dev/null +++ b/libcacard/vcard_emul_nss.c @@ -0,0 +1,1157 @@ +/* + * 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 "qemu-common.h" + +#include "vcard.h" +#include "card_7816t.h" +#include "vcard_emul.h" +#include "vreader.h" +#include "vevent.h" + +struct VCardKeyStruct { + CERTCertificate *cert; + PK11SlotInfo *slot; + SECKEYPrivateKey *key; +}; + + +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 PRBool +vcard_emul_alloc_arrays(unsigned char ***certsp, int **cert_lenp, + VCardKey ***keysp, int cert_count) +{ + *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); + return PR_TRUE; +} + +/* + * 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 = (VCardKey *)qemu_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); + 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); + } + return; +} + +/* + * 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; + SECStatus rv; + + 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); + + /* + * 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; + } + rv = PK11_PrivDecryptRaw(priv_key, buffer, &signature_len, signature_len, + buffer, buffer_size); + if (rv != SECSuccess) { + return vcard_emul_map_error(PORT_GetError()); + } + assert(buffer_size == signature_len); + return VCARD7816_STATUS_SUCCESS; +} + +/* + * 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 = NULL; + 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 = qemu_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 */ + qemu_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_reset(VCard *card, VCardPower power) +{ + PK11SlotInfo *slot; + + if (!nss_emul_init) { + return; + } + + /* + * if we reset the card (either power on or power off), we lose our login + * state + */ + /* 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; +} + + +static VReader * +vcard_emul_find_vreader_from_slot(PK11SlotInfo *slot) +{ + VReaderList *reader_list = vreader_get_reader_list(); + VReaderListEntry *current_entry = NULL; + + 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) { + return reader; + } + vreader_free(reader); + } + + 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 = (VReaderEmul *)qemu_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->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); + } + if (vreader_emul->type_params) { + qemu_free(vreader_emul->type_params); + } + qemu_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_ATR_PREFIX 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' }; + +void +vcard_emul_get_atr(VCard *card, unsigned char *atr, int *atr_len) +{ + int len = MIN(sizeof(nss_atr), *atr_len); + assert(atr != NULL); + + memcpy(atr, nss_atr, len); + *atr_len = len; + return; +} + +/* + * 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; + PRBool ret; + + 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++; + } + + if (cert_count == 0) { + PK11_DestroyGenericObjects(firstObj); + return NULL; + } + + /* allocate the arrays */ + ret = vcard_emul_alloc_arrays(&certs, &cert_len, &keys, cert_count); + if (ret == PR_FALSE) { + return NULL; + } + + /* 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 */ + return vcard_emul_make_card(vreader, certs, cert_len, keys, cert_count); +} + +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 { + slot = SECMOD_WaitForAnyTokenEvent(module, 0, 500); + if (slot == NULL) { + 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 ret, has_readers = PR_FALSE, need_coolkey_module; + 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 { + rv = NSS_Init("sql:/etc/pki/nssdb"); + } + 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); + cert_count = options->vreader[i].cert_count; + + ret = vcard_emul_alloc_arrays(&certs, &cert_len, &keys, + options->vreader[i].cert_count); + if (ret == PR_FALSE) { + continue; + } + 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; + } + } + + /* 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(); + 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); + + 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; + } + + 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; + } + 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); + vreader_insert_card(vreader, vcard); + vcard_emul_init_series(vreader, vcard); + vcard_free(vcard); + } + } + if (has_emul_slots) { + vcard_emul_new_event_thread(module); + } + } + SECMOD_ReleaseReadLock(module_lock); + nss_emul_init = has_readers; + + 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 = NULL; + 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); + } +} + +/* + * 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) +{ + 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 consistant look */ +static VCardEmulOptions options; +#define READER_STEP 4 + +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)); + 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; + const char *vname; + const char *type_params; + VCardEmulType type; + int name_length, vname_length, type_params_length, 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; + 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++); + if (*args == 0) { + break; + } + + if (opts->vreader_count >= reader_count) { + reader_count += READER_STEP; + vreaderOpt = realloc(opts->vreader, + reader_count * sizeof(*vreaderOpt)); + if (vreaderOpt == NULL) { + return opts; /* we're done */ + } + } + opts->vreader = vreaderOpt; + vreaderOpt = &vreaderOpt[opts->vreader_count]; + vreaderOpt->name = copy_string(name, name_length); + vreaderOpt->vname = copy_string(vname, vname_length); + vreaderOpt->card_type = type; + vreaderOpt->type_params = + copy_string(type_params, type_params_length); + count = count_tokens(args, ',', ')'); + vreaderOpt->cert_count = count; + vreaderOpt->cert_name = (char **)qemu_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); + } + 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 = copy_string(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 = copy_string(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} Virutal 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"); +} |