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

Reply via email to