Sharing PKCS#11 modules
Multiple consumers of PKCS#11 in a process As more and more applications and libraries use PKCS#11 we run into a very basic problem. The PKCS#11 modules cannot be initialized and finalized properly without coordination between the various consumers. An example: An application might use GnuTLS for TLS connections, and use libgcr for display of certificates. Both of these want to load (and initialze) the same PKCS#11 modules. There are many places where this situation occurs, including large applications like Evolution which due to their dependencies end up using both NSS and GnuTLS. Consumer A loads a PKCS#11 module and uses the module's C_Initialize function to initialize it, which works as expected. When consumer B initializes the module (also using C_Initialize), the error code CKR_CRYPTOKI_ALREADY_INITIALIZED is correctly returned. This is normal PKCS#11 specification defined behavior for when a module is initalized twice in the same process. If consumer B is aware of this situation they may choose to ignore this error code. However when the consumer A is done with its use of the PKCS#11 module it finalizes the module using the module's C_Finalize function. This is expected of a well behaved PKCS#11 consumer. This then causes errors and/or crashes for consumer B, which cannot know that the module has now been finalized out from underneath it. It is necessary for the two consumers to coordinate their initialization and finalization in some fashion. In p11-kit we provide this coordination in a loosely coupled, backwards compatible, and flexible way.
Managed modules p11-kit wraps PKCS#11 modules to manage them and customize their functionality so that they are able to be shared between multiple callers in the same process. Each caller that uses the p11_kit_modules_load() or p11_kit_module_load() function gets independent wrapped PKCS#11 module(s). This is unless a caller or module configuration specifies that a module should be used in an unmanaged fashion. When modules are managed, the following aspects are wrapped and coordinated: Calls to C_Initialize and C_Finalize can be called by multiple callers. The first time that the managed module C_Initialize is called, the PKCS#11 module's actual C_Initialize function is called. Subsequent calls by other callers will cause p11-kit to increment an internal initialization count, rather than calling C_Initialize again. Multiple callers can call the managed C_Initialize function concurrently from different threads and p11-kit will guarantee that this managed in a thread-safe manner. When the managed module C_Finalize is used to finalize a module, each time it is called it decrements the internal initialization count for that module. When the internal initialization count reaches zero, the module's actual C_Finalize function is called. Multiple callers can call the managed C_Finalize function concurrently from different threads and p11-kit will guarantee that this managed in a thread-safe manner. Call to C_CloseAllSessions only close the sessions that the caller of the managed module has opened. This allows the C_CloseAllSessions function to be used without closing sessions for other callers of the same PKCS#11 module.