On Friday 18 April 2008, Alon Bar-Lev wrote: > And I found the he PKCS#11 provider does not refresh slot list with > C_GetSlotList is called with NULL_PTR. > > This is extremely important for tokens, as the reader hardware is removed and > inserted when > card is removed and inserted. > > Lacking Plug&Play support will not enable people to use CCID enabled tokens > to use running applications, > as most applications calls C_Initialize once. > > A found the root cause i the libopensc context management. It looks like the > libopensc itself does not support > Plug&Play, and use fixed reader list at context initialization.
I've implemented a solution, please review [1] It allows reader driver to add new readers to the end of the reader list. I am not sure it is ideal solution, but I felt better not deleting existing readers. This is implemented to pcsc only, as openct and ctapi does not have plug and play. I still have some issues with pcsc-lite, Ludovic will help. Nils, it is also a time to switch into stateless transactional mode, opening long lasting sessions to the card cause a lot of issues. The most important one is security, as other program may interfer with our session. Less important is the requirement to keep the connection open, which leads to some weird issues when reader is unplugged or software suspend (hibernate) performed. Stateless mode will simplify things, as each operation will be atomic. I am not at the level of code understanding to do this. Anyway this branch adds the following: 1. Add detect_readers() to reader opts, this adds new readers to the end of the readers list. 2. Add sc_ctx_detect_readers() that calls readers' detect_readers(). 3. Allow context to be created without readers. 4. Call sc_ctx_detect_readers() from PKCS#11 C_GetSlotList with NULL_PTR. 5. Allow no reader at detect_card, as reader my be removed. 6. Since I broke ABI, I updated the external module version requirement to match OpenSC version. In the future a separate version should be maintained for each interface, this should be unrelated to the package version. Alon. [1] svn diff -r 3480:3486 http://www.opensc-project.org/svn/opensc/branches/alonbl/pnp --- Index: src/tools/opensc-tool.c =================================================================== --- src/tools/opensc-tool.c (revision 3480) +++ src/tools/opensc-tool.c (revision 3486) @@ -246,7 +246,7 @@ unsigned int i, rcount = sc_ctx_get_reader_count(ctx); if (rcount == 0) { - printf("No readers configured!\n"); + printf("No readers found.\n"); return 0; } printf("Readers known about:\n"); Index: src/pkcs11/pkcs11-global.c =================================================================== --- src/pkcs11/pkcs11-global.c (revision 3480) +++ src/pkcs11/pkcs11-global.c (revision 3486) @@ -347,6 +347,9 @@ } sc_debug(context, "Getting slot listing\n"); + if (pSlotList == NULL_PTR) { + sc_ctx_detect_readers(context); + } card_detect_all(); numMatches = 0; Index: src/pkcs11/slot.c =================================================================== --- src/pkcs11/slot.c (revision 3480) +++ src/pkcs11/slot.c (revision 3486) @@ -86,7 +86,7 @@ sc_reader_t *rdr = sc_ctx_get_reader(context, (unsigned int)reader); if (rdr == NULL) - return CKR_GENERAL_ERROR; + return CKR_TOKEN_NOT_PRESENT; slot = virtual_slots + card->first_slot + i; strcpy_bp(slot->slot_info.slotDescription, rdr->name, 64); slot->reader = reader; Index: src/libopensc/reader-pcsc.c =================================================================== --- src/libopensc/reader-pcsc.c (revision 3480) +++ src/libopensc/reader-pcsc.c (revision 3486) @@ -644,6 +644,17 @@ rv = priv->gpriv->SCardBeginTransaction(pslot->pcsc_card); + if ((unsigned int)rv == SCARD_E_INVALID_HANDLE) { + rv = pcsc_connect(reader, slot); + if (rv != SCARD_S_SUCCESS) { + PCSC_ERROR(reader->ctx, "SCardConnect failed", rv); + return pcsc_ret_to_error(rv); + } + /* Now try to begin a new transaction after we reconnected and we fail if + some other program was faster to lock the reader */ + rv = priv->gpriv->SCardBeginTransaction(pslot->pcsc_card); + } + if ((unsigned int)rv == SCARD_W_RESET_CARD) { /* try to reconnect if the card was reset by some other application */ rv = pcsc_reconnect(reader, slot, 0); @@ -731,10 +742,6 @@ static int pcsc_init(sc_context_t *ctx, void **reader_data) { LONG rv; - DWORD reader_buf_size; - char *reader_buf = NULL, *p; - const char *mszGroups = NULL; - int r; struct pcsc_global_private_data *gpriv; scconf_block *conf_block = NULL; int ret = SC_ERROR_INTERNAL; @@ -824,10 +831,50 @@ ret = pcsc_ret_to_error(rv); goto out; } + + *reader_data = gpriv; + gpriv = NULL; + ret = SC_SUCCESS; + +out: + if (ret != SC_SUCCESS) { + if (gpriv->pcsc_ctx != 0) + gpriv->SCardReleaseContext(gpriv->pcsc_ctx); + if (gpriv->dlhandle != NULL) + lt_dlclose(gpriv->dlhandle); + if (gpriv != NULL) + free(gpriv); + } + + return 0; +} + +static int pcsc_finish(sc_context_t *ctx, void *prv_data) +{ + struct pcsc_global_private_data *priv = (struct pcsc_global_private_data *) prv_data; + + if (priv) { + priv->SCardReleaseContext(priv->pcsc_ctx); + lt_dlclose(priv->dlhandle); + free(priv); + } + + return 0; +} + +static int pcsc_detect_readers(sc_context_t *ctx, void *prv_data) +{ + struct pcsc_global_private_data *gpriv = (struct pcsc_global_private_data *) prv_data; + LONG rv; + DWORD reader_buf_size; + char *reader_buf = NULL, *p; + const char *mszGroups = NULL; + int ret = SC_ERROR_INTERNAL; + rv = gpriv->SCardListReaders(gpriv->pcsc_ctx, NULL, NULL, (LPDWORD) &reader_buf_size); - if (rv != SCARD_S_SUCCESS || reader_buf_size < 2) { - ret = pcsc_ret_to_error(rv); /* No readers configured */ + if (rv != SCARD_S_SUCCESS) { + ret = pcsc_ret_to_error(rv); goto out; } @@ -842,78 +889,93 @@ ret = pcsc_ret_to_error(rv); goto out; } - p = reader_buf; - do { - sc_reader_t *reader = (sc_reader_t *) calloc(1, sizeof(sc_reader_t)); - struct pcsc_private_data *priv = (struct pcsc_private_data *) malloc(sizeof(struct pcsc_private_data)); - struct pcsc_slot_data *pslot = (struct pcsc_slot_data *) malloc(sizeof(struct pcsc_slot_data)); - sc_slot_info_t *slot; + for (p = reader_buf; *p != '\x0'; p += strlen (p) + 1) { + sc_reader_t *reader = NULL; + struct pcsc_private_data *priv = NULL; + struct pcsc_slot_data *pslot = NULL; + sc_slot_info_t *slot = NULL; + int i; + int found = 0; - if (reader == NULL || priv == NULL || pslot == NULL) { - if (reader) - free(reader); - if (priv) - free(priv); - if (pslot) - free(pslot); - break; + for (i=0;i < sc_ctx_get_reader_count (ctx) && !found;i++) { + sc_reader_t *reader = sc_ctx_get_reader (ctx, i); + if (reader == NULL) { + ret = SC_ERROR_INTERNAL; + goto err1; + } + if (reader->ops == &pcsc_ops && !strcmp (reader->name, p)) { + found = 1; + } } + /* Reader already available, skip */ + if (found) { + continue; + } + + if ((reader = (sc_reader_t *) calloc(1, sizeof(sc_reader_t))) == NULL) { + ret = SC_ERROR_OUT_OF_MEMORY; + goto err1; + } + if ((priv = (struct pcsc_private_data *) malloc(sizeof(struct pcsc_private_data))) == NULL) { + ret = SC_ERROR_OUT_OF_MEMORY; + goto err1; + } + if ((pslot = (struct pcsc_slot_data *) malloc(sizeof(struct pcsc_slot_data))) == NULL) { + ret = SC_ERROR_OUT_OF_MEMORY; + goto err1; + } + reader->drv_data = priv; reader->ops = &pcsc_ops; reader->driver = &pcsc_drv; reader->slot_count = 1; - reader->name = strdup(p); + if ((reader->name = strdup(p)) == NULL) { + ret = SC_ERROR_OUT_OF_MEMORY; + goto err1; + } priv->gpriv = gpriv; - priv->reader_name = strdup(p); - r = _sc_add_reader(ctx, reader); - if (r) { - free(priv->reader_name); - free(priv); - free(reader->name); - free(reader); - free(pslot); - break; + if ((priv->reader_name = strdup(p)) == NULL) { + ret = SC_ERROR_OUT_OF_MEMORY; + goto err1; } slot = &reader->slot[0]; memset(slot, 0, sizeof(*slot)); slot->drv_data = pslot; memset(pslot, 0, sizeof(*pslot)); + if (_sc_add_reader(ctx, reader)) { + ret = SC_SUCCESS; /* silent ignore */ + goto err1; + } refresh_slot_attributes(reader, slot); - while (*p++ != 0); - } while (p < (reader_buf + reader_buf_size - 1)); + continue; + + err1: + if (priv != NULL) { + if (priv->reader_name) + free(priv->reader_name); + free(priv); + } + if (reader != NULL) { + if (reader->name) + free(reader->name); + free(reader); + } + if (slot != NULL) + free(pslot); - *reader_data = gpriv; - gpriv = NULL; + goto out; + } + ret = SC_SUCCESS; out: - if (ret != SC_SUCCESS) { - if (gpriv->pcsc_ctx != 0) - gpriv->SCardReleaseContext(gpriv->pcsc_ctx); - if (reader_buf != NULL) - free(reader_buf); - if (gpriv->dlhandle != NULL) - lt_dlclose(gpriv->dlhandle); - if (gpriv != NULL) - free(gpriv); - } - return 0; -} + if (reader_buf != NULL) + free (reader_buf); -static int pcsc_finish(sc_context_t *ctx, void *prv_data) -{ - struct pcsc_global_private_data *priv = (struct pcsc_global_private_data *) prv_data; - - if (priv) { - priv->SCardReleaseContext(priv->pcsc_ctx); - lt_dlclose(priv->dlhandle); - free(priv); - } - - return 0; + return ret; } static int @@ -931,6 +993,7 @@ { pcsc_ops.init = pcsc_init; pcsc_ops.finish = pcsc_finish; + pcsc_ops.detect_readers = pcsc_detect_readers; pcsc_ops.transmit = pcsc_transmit; pcsc_ops.detect_card_presence = pcsc_detect_card_presence; pcsc_ops.lock = pcsc_lock; Index: src/libopensc/internal-winscard.h =================================================================== --- src/libopensc/internal-winscard.h (revision 3480) +++ src/libopensc/internal-winscard.h (revision 3486) @@ -51,6 +51,7 @@ #define SCARD_SCOPE_USER 0x0000 /**< Scope in user space */ #define SCARD_S_SUCCESS 0x00000000 /**< No error was encountered. */ +#define SCARD_E_INVALID_HANDLE 0x80100003 /**< The supplied handle was invalid. */ #define SCARD_E_TIMEOUT 0x8010000A /**< The user-specified timeout value has expired. */ #define SCARD_E_SHARING_VIOLATION 0x8010000B /**< The smart card cannot be accessed because of other connections outstanding. */ #define SCARD_E_NOT_TRANSACTED 0x80100016 /**< An attempt was made to end a non-existent transaction. */ Index: src/libopensc/ctx.c =================================================================== --- src/libopensc/ctx.c (revision 3480) +++ src/libopensc/ctx.c (revision 3486) @@ -354,7 +354,8 @@ } /* verify module version */ version = modversion(); - if (version == NULL || strncmp(version, "0.9.", strlen("0.9.")) > 0) { + /* XXX: We really need to have ABI version for each interface */ + if (version == NULL || strncmp(version, PACKAGE_VERSION, strlen(PACKAGE_VERSION)) != 0) { sc_error(ctx,"dynamic library '%s': invalid module version\n",libname); lt_dlclose(handle); return NULL; @@ -637,6 +638,25 @@ load_parameters(ctx, ctx->conf_blocks[i], opts); } +int sc_ctx_detect_readers(sc_context_t *ctx) +{ + int i; + + sc_mutex_lock(ctx, ctx->mutex); + + for (i = 0; ctx->reader_drivers[i] != NULL; i++) { + const struct sc_reader_driver *drv = ctx->reader_drivers[i]; + + if (drv->ops->detect_readers != NULL) + drv->ops->detect_readers(ctx, ctx->reader_drv_data[i]); + } + + sc_mutex_unlock(ctx, ctx->mutex); + + /* XXX: Do not ignore erros? */ + return SC_SUCCESS; +} + sc_reader_t *sc_ctx_get_reader(sc_context_t *ctx, unsigned int i) { if (i >= (unsigned int)ctx->reader_count || i >= SC_MAX_READERS) @@ -724,10 +744,7 @@ } del_drvs(&opts, 0); del_drvs(&opts, 1); - if (ctx->reader_count == 0) { - sc_release_context(ctx); - return SC_ERROR_NO_READERS_FOUND; - } + sc_ctx_detect_readers(ctx); *ctx_out = ctx; return SC_SUCCESS; } Index: src/libopensc/libopensc.exports =================================================================== --- src/libopensc/libopensc.exports (revision 3480) +++ src/libopensc/libopensc.exports (revision 3486) @@ -37,6 +37,7 @@ sc_context_create sc_copy_asn1_entry sc_create_file +sc_ctx_detect_readers sc_ctx_get_reader sc_ctx_get_reader_count sc_ctx_suppress_errors_off Index: src/libopensc/reader-ctapi.c =================================================================== --- src/libopensc/reader-ctapi.c (revision 3480) +++ src/libopensc/reader-ctapi.c (revision 3486) @@ -611,6 +611,7 @@ { ctapi_ops.init = ctapi_init; ctapi_ops.finish = ctapi_finish; + ctapi_ops.detect_readers = NULL; ctapi_ops.transmit = ctapi_transmit; ctapi_ops.detect_card_presence = ctapi_detect_card_presence; ctapi_ops.lock = ctapi_lock; Index: src/libopensc/reader-openct.c =================================================================== --- src/libopensc/reader-openct.c (revision 3480) +++ src/libopensc/reader-openct.c (revision 3486) @@ -479,6 +479,7 @@ { openct_ops.init = openct_reader_init; openct_ops.finish = openct_reader_finish; + openct_ops.detect_readers = NULL; openct_ops.release = openct_reader_release; openct_ops.detect_card_presence = openct_reader_detect_card_presence; openct_ops.connect = openct_reader_connect; Index: src/libopensc/opensc.h =================================================================== --- src/libopensc/opensc.h (revision 3480) +++ src/libopensc/opensc.h (revision 3486) @@ -373,6 +373,9 @@ /* Called when the driver is being unloaded. finish() has to * deallocate the private data and any resources. */ int (*finish)(struct sc_context *ctx, void *priv_data); + /* Called when library wish to detect new readers + * should add only new readers. */ + int (*detect_readers)(struct sc_context *ctx, void *priv_data); /* Called when releasing a reader. release() has to * deallocate the private data. Other fields will be * freed by OpenSC. */ @@ -727,6 +730,13 @@ int sc_release_context(sc_context_t *ctx); /** + * Detect new readers available on system. + * @param ctx OpenSC context + * @return SC_SUCCESS on success and an error code otherwise. + */ +int sc_ctx_detect_readers(sc_context_t *ctx); + +/** * Returns a pointer to the specified sc_reader_t object * @param ctx OpenSC context * @param i number of the reader structure to return (starting with 0) _______________________________________________ opensc-devel mailing list [email protected] http://www.opensc-project.org/mailman/listinfo/opensc-devel
