diff --git a/doc/tools/pkcs11-tool.1.xml b/doc/tools/pkcs11-tool.1.xml
index fee05864..a0f1ed4c 100644
--- a/doc/tools/pkcs11-tool.1.xml
+++ b/doc/tools/pkcs11-tool.1.xml
@@ -426,6 +426,22 @@
Specify the index of the object to use.
+
+
+
+
+ Tell pkcs11 module it should use OS thread locking.
+
+
+
+
+
+ options
+
+ Test a pkcs11 module's thread implication. (See source code).
+
+
+
label
diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am
index 1d4363c5..5689bb78 100644
--- a/src/tools/Makefile.am
+++ b/src/tools/Makefile.am
@@ -55,9 +55,10 @@ opensc_explorer_LDADD = $(OPTIONAL_READLINE_LIBS)
pkcs15_tool_SOURCES = pkcs15-tool.c util.c ../pkcs11/pkcs11-display.c ../pkcs11/pkcs11-display.h
pkcs15_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS)
pkcs11_tool_SOURCES = pkcs11-tool.c util.c
+pkcs11_tool_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(PTHREAD_CFLAGS)
pkcs11_tool_LDADD = \
$(top_builddir)/src/common/libpkcs11.la \
- $(OPTIONAL_OPENSSL_LIBS)
+ $(OPTIONAL_OPENSSL_LIBS) $(PTHREAD_CFLAGS)
if ENABLE_SHARED
else
pkcs11_tool_LDADD += \
diff --git a/src/tools/pkcs11-tool.c b/src/tools/pkcs11-tool.c
index c0056744..7971b0b9 100644
--- a/src/tools/pkcs11-tool.c
+++ b/src/tools/pkcs11-tool.c
@@ -29,6 +29,9 @@
#include
#include
#include
+#ifdef HAVE_PTHREAD
+#include
+#endif
#else
#include
#include
@@ -72,6 +75,10 @@
extern CK_FUNCTION_LIST_3_0 pkcs11_function_list_3_0;
#endif
+#if defined(_WIN32) || defined(HAVE_PTHREAD)
+#define MAX_TEST_THREADS 10
+#endif
+
#define NEED_SESSION_RO 0x01
#define NEED_SESSION_RW 0x02
@@ -151,6 +158,11 @@ enum {
OPT_DERIVE_PASS_DER,
OPT_DECRYPT,
OPT_TEST_FORK,
+#if defined(_WIN32) || defined(HAVE_PTHREAD)
+ OPT_TEST_THREADS,
+ OPT_USE_LOCKING,
+#endif
+
OPT_GENERATE_KEY,
OPT_GENERATE_RANDOM,
OPT_HASH_ALGORITHM,
@@ -236,6 +248,10 @@ static const struct option options[] = {
{ "test-ec", 0, NULL, OPT_TEST_EC },
#ifndef _WIN32
{ "test-fork", 0, NULL, OPT_TEST_FORK },
+#endif
+ { "use-locking", 0, NULL, OPT_USE_LOCKING },
+#if defined(_WIN32) || defined(HAVE_PTHREAD)
+ { "test-threads", 1, NULL, OPT_TEST_THREADS },
#endif
{ "generate-random", 1, NULL, OPT_GENERATE_RANDOM },
{ "allow-sw", 0, NULL, OPT_ALLOW_SW },
@@ -314,9 +330,13 @@ static const char *option_help[] = {
"Test EC (best used with the --login or --pin option)",
#ifndef _WIN32
"Test forking and calling C_Initialize() in the child",
+#endif
+ "Call C_initialize() with CKF_OS_LOCKING_OK.",
+#if defined(_WIN32) || defined(HAVE_PTHREAD)
+ "Test threads. Multiple times to start additional threads, arg is string or 2 byte commands",
#endif
"Generate given amount of random data",
- "Allow using software mechanisms (without CKF_HW)"
+ "Allow using software mechanisms (without CKF_HW)",
};
static const char * app_name = "pkcs11-tool"; /* for utils.c */
@@ -380,6 +400,24 @@ static CK_FUNCTION_LIST_3_0_PTR p11 = NULL;
static CK_SLOT_ID_PTR p11_slots = NULL;
static CK_ULONG p11_num_slots = 0;
static int suppress_warn = 0;
+static CK_C_INITIALIZE_ARGS_PTR c_initialize_args_ptr = NULL;
+
+#if defined(_WIN32) || defined(HAVE_PTHREAD)
+static CK_C_INITIALIZE_ARGS c_initialize_args_OS = {NULL, NULL, NULL, NULL, CKF_OS_LOCKING_OK, NULL};
+#ifdef _WIN32
+static HANDLE test_threads_handles[MAX_TEST_THREADS];
+#else
+static pthread_t test_threads_handles[MAX_TEST_THREADS];
+#endif
+struct test_threads_data {
+ int tnum;
+ char * tests;
+ volatile int state;
+ volatile CK_RV rv;
+};
+static struct test_threads_data test_threads_datas[MAX_TEST_THREADS];
+static int test_threads_num = 0;
+#endif /* defined(_WIN32) || defined(HAVE_PTHREAD) */
struct flag_info {
CK_FLAGS value;
@@ -480,6 +518,16 @@ static void test_ec(CK_SLOT_ID slot, CK_SESSION_HANDLE session);
#ifndef _WIN32
static void test_fork(void);
#endif
+#if defined(_WIN32) || defined(HAVE_PTHREAD)
+static void test_threads();
+static int test_threads_start(int tnum);
+static int test_threads_cleanup();
+#ifdef _WIN32
+static DWORD WINAPI test_threads_run(_In_ LPVOID pttd);
+#else
+static void * test_threads_run(void * pttd);
+#endif
+#endif /* defined(_WIN32) || defiend(HAVE_PTHREAD) */
static void generate_random(CK_SESSION_HANDLE session);
static CK_RV find_object_with_attributes(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE *out,
CK_ATTRIBUTE *attrs, CK_ULONG attrsLen, CK_ULONG obj_index);
@@ -572,7 +620,6 @@ VARATTR_METHOD(EC_POINT, unsigned char); /* getEC_POINT */
VARATTR_METHOD(EC_PARAMS, unsigned char); /* getEC_PARAMS */
VARATTR_METHOD(ALLOWED_MECHANISMS, CK_MECHANISM_TYPE); /* getALLOWED_MECHANISMS */
-
int main(int argc, char * argv[])
{
CK_SESSION_HANDLE session = CK_INVALID_HANDLE;
@@ -600,6 +647,9 @@ int main(int argc, char * argv[])
int do_test_ec = 0;
#ifndef _WIN32
int do_test_fork = 0;
+#endif
+#if defined(_WIN32) || defined(HAVE_PTHREAD)
+ int do_test_threads = 0;
#endif
int need_session = 0;
int opt_login = 0;
@@ -928,6 +978,23 @@ int main(int argc, char * argv[])
do_test_fork = 1;
action_count++;
break;
+#endif
+ case OPT_USE_LOCKING:
+ c_initialize_args_ptr = &c_initialize_args_OS;
+ break;
+#if defined(_WIN32) || defined(HAVE_PTHREAD)
+ case OPT_TEST_THREADS:
+ do_test_threads = 1;
+ if (test_threads_num < MAX_TEST_THREADS) {
+ test_threads_datas[test_threads_num].tnum = test_threads_num;
+ test_threads_datas[test_threads_num].tests = optarg;
+ test_threads_num++;
+ } else {
+ fprintf(stderr,"Too many --test_threads options limit is %d\n", MAX_TEST_THREADS);
+ util_print_usage_and_die(app_name, options, option_help, NULL);
+ }
+ action_count++;
+ break;
#endif
case OPT_GENERATE_RANDOM:
need_session |= NEED_SESSION_RO;
@@ -994,10 +1061,23 @@ int main(int argc, char * argv[])
if (do_list_interfaces)
list_interfaces();
- rv = p11->C_Initialize(NULL);
+#if defined(_WIN32) || defined(HAVE_PTHREAD)
+ if (do_test_threads)
+ test_threads();
+#endif
+
+ rv = p11->C_Initialize(c_initialize_args_ptr);
+
+#if defined(_WIN32) || defined(HAVE_PTHREAD)
+ if (do_test_threads || rv != CKR_OK)
+ fprintf(stderr,"Main C_Initialize(%s) rv:%s\n",
+ (c_initialize_args_ptr) ? "CKF_OS_LOCKING_OK" : "NULL", CKR2Str(rv));
+#else
if (rv == CKR_CRYPTOKI_ALREADY_INITIALIZED)
fprintf(stderr, "\n*** Cryptoki library has already been initialized ***\n");
- else if (rv != CKR_OK)
+#endif /* defined(_WIN32) || defined(HAVE_PTHREAD) */
+
+ if (rv != CKR_OK && rv != CKR_CRYPTOKI_ALREADY_INITIALIZED)
p11_fatal("C_Initialize", rv);
#ifndef _WIN32
@@ -1242,6 +1322,11 @@ end:
p11_fatal("C_CloseSession", rv);
}
+#if defined(_WIN32) || defined(HAVE_PTHREAD)
+ if (do_test_threads)
+ test_threads_cleanup();
+#endif /* defined(_WIN32) || defiend(HAVE_PTHREAD) */
+
if (p11)
p11->C_Finalize(NULL_PTR);
if (module)
@@ -6298,7 +6383,7 @@ static CK_SESSION_HANDLE test_kpgen_certwrite(CK_SLOT_ID slot, CK_SESSION_HANDLE
if (module == NULL)
util_fatal("Failed to load pkcs11 module");
- rv = p11->C_Initialize(NULL);
+ rv = p11->C_Initialize(c_initialize_args_ptr);
if (rv == CKR_CRYPTOKI_ALREADY_INITIALIZED)
printf("\n*** Cryptoki library has already been initialized ***\n");
else if (rv != CKR_OK)
@@ -6456,7 +6541,7 @@ static void test_fork(void)
if (!pid) {
printf("*** Calling C_Initialize in forked child process ***\n");
- rv = p11->C_Initialize(NULL);
+ rv = p11->C_Initialize(c_initialize_args_ptr);
if (rv != CKR_OK)
p11_fatal("C_Initialize in child\n", rv);
exit(0);
@@ -7134,3 +7219,224 @@ static const char * CKR2Str(CK_ULONG res)
}
return "unknown PKCS11 error";
}
+
+#if defined(_WIN32) || defined(HAVE_PTHREAD)
+#ifdef _WIN32
+static DWORD WINAPI test_threads_run(_In_ LPVOID pttd)
+#else
+static void * test_threads_run(void * pttd)
+#endif
+{
+ int r = 0;
+ CK_RV rv = CKR_OK;
+ CK_INFO info;
+ int l_slots = 0;
+ int state = 0;
+ CK_ULONG l_p11_num_slots = 0;
+ CK_SLOT_ID_PTR l_p11_slots = NULL;
+ char * pctest;
+ struct test_threads_data * ttd = (struct test_threads_data *)pttd;
+
+ fprintf(stderr, "Test thread %d started with options:%s\n", ttd->tnum, ttd->tests);
+ /* call selected C_* routines with different options */
+ pctest = ttd-> tests;
+
+ /* series of two chatacter commands */
+ while (pctest && *pctest && *(pctest + 1)) {
+ ttd->state = state++;
+
+ /* Pn - pause where n is 0 to 9 iseconds */
+ if (*pctest == 'P' && *(pctest + 1) >= '0' && *(pctest + 1) <= '9') {
+ fprintf(stderr, "Test thread %d pauseing for %d seconds\n", ttd->tnum, (*(pctest + 1) - '0'));
+#ifdef _WIN32
+ Sleep((*(pctest + 1) - '0') * 1000);
+#else
+ sleep(*(pctest + 1) - '0');
+#endif
+ }
+
+ /* IN - C_Initialize with NULL args */
+ else if (*pctest == 'I') {
+ if (*(pctest + 1) == 'N') {
+ fprintf(stderr, "Test thread %d C_Initialize(NULL)\n", ttd->tnum);
+ rv = p11->C_Initialize(NULL);
+ ttd->rv = rv;
+ fprintf(stderr, "Test thread %d C_Initialize returned %s\n", ttd->tnum, CKR2Str(rv));
+ }
+ /* CL C_Initialize with CKF_OS_LOCKING_OK */
+ else if (*(pctest + 1) == 'L') {
+ fprintf(stderr, "Test thread %d C_Initialize CKF_OS_LOCKING_OK \n", ttd->tnum);
+ rv = p11->C_Initialize(&c_initialize_args_OS);
+ ttd->rv = rv;
+ fprintf(stderr, "Test thread %d C_Initialize returned %s\n", ttd->tnum, CKR2Str(rv));
+ }
+ else
+ goto err;
+ }
+
+ /* GI - C_GetInfo */
+ else if (*pctest == 'G' && *(pctest + 1) == 'I') {
+ fprintf(stderr, "Test thread %d C_GetInfo\n", ttd->tnum);
+ rv = p11->C_GetInfo(&info);
+ ttd->rv = rv;
+ fprintf(stderr, "Test thread %d C_GetInfo returned %s\n", ttd->tnum, CKR2Str(rv));
+ }
+
+ /* SL - C_GetSlotList */
+ else if (*pctest == 'S' && *(pctest + 1) == 'L') {
+ fprintf(stderr, "Test thread %d C_GetSlotList to get l_p11_num_slots\n", ttd->tnum);
+ rv = p11->C_GetSlotList(1, NULL, &l_p11_num_slots);
+ ttd->rv = rv;
+ fprintf(stderr, "Test thread %d C_GetSlotList returned %s\n", ttd->tnum, CKR2Str(rv));
+ fprintf(stderr, "Test thread %d l_p11_num_slots:%ld\n", ttd->tnum, l_p11_num_slots);
+ if (rv == CKR_OK) {
+ free(l_p11_slots);
+ if (l_p11_num_slots > 0) {
+ l_p11_slots = calloc(l_p11_num_slots, sizeof(CK_SLOT_ID));
+ fprintf(stderr, "Test thread %d C_GetSlotList\n", ttd->tnum);
+ rv = p11->C_GetSlotList(1, l_p11_slots, &l_p11_num_slots);
+ ttd->rv = rv;
+ fprintf(stderr, "Test thread %d C_GetSlotList returned %s\n", ttd->tnum, CKR2Str(rv));
+ fprintf(stderr, "Test thread %d l_p11_num_slots:%ld\n", ttd->tnum, l_p11_num_slots);
+ if (rv == CKR_OK && l_p11_num_slots && l_p11_slots)
+ l_slots = 1;
+ }
+ }
+ }
+
+ /* Tn Get token from slot_index n C_GetTokenInfo, where n is 0 to 9 */
+ else if (*pctest == 'T' && *(pctest + 1) >= '0' && *(pctest + 1) <= '9') {
+ fprintf(stderr, "Test thread %d C_GetTokenInfo from slot_index %d using show_token\n", ttd->tnum, (*(pctest + 1) - '0'));
+ if (l_slots) {
+ show_token(l_p11_slots[(*(pctest + 1) - '0')]);
+ } else {
+ show_token(p11_slots[(*(pctest + 1) - '0')]);
+ }
+ }
+
+ else {
+ err:
+ rv = CKR_GENERAL_ERROR; /* could be vendor error, */
+ ttd->rv = rv;
+ fprintf(stderr, "Test thread %d Unknown test '%c%c'\n", ttd->tnum, *pctest, *(pctest + 1));
+ break;
+ }
+
+ pctest ++;
+ if (*pctest != 0x00)
+ pctest ++;
+ if (*pctest == ':')
+ pctest++;
+
+
+ if (rv != CKR_OK && rv != CKR_CRYPTOKI_ALREADY_INITIALIZED)
+ /* IN C_Initialize with NULL args */
+ break;
+ }
+
+ free(l_p11_slots);
+ fprintf(stderr, "Test thread %d returning rv = %d\n", ttd->tnum, r);
+ ttd->state = -1; /* done */
+ ttd->rv = rv;
+#ifdef _WIN32
+ ExitThread(0);
+#else
+ pthread_exit(NULL);
+#endif
+}
+
+static int test_threads_cleanup()
+{
+
+ int i, j;
+ int ended = 0;
+ int ended_ok = 0;
+
+ fprintf(stderr,"test_threads cleanup starting\n");
+ for (j = 0; j < 4; j++) {
+ ended = 0;
+ ended_ok = 0;
+
+ for (i = 0; i < test_threads_num; i++) {
+ if (test_threads_datas[i].state == -1) {
+ ended++;
+ }
+ if (test_threads_datas[i].rv == CKR_OK) {
+ ended_ok++;
+ }
+ }
+
+ if (ended == test_threads_num) {
+ fprintf(stderr,"test_threads all threads have ended %s\n",
+ (ended_ok == test_threads_num)? "with CKR_OK": "some errors");
+ break;
+ } else {
+ fprintf(stderr,"test_threads threads stills active:%d\n", (test_threads_num - ended));
+ for (i = 0; i < test_threads_num; i++) {
+ fprintf(stderr,"test_threads thread:%d state:%d, rv:%s\n",
+ i, test_threads_datas[i].state, CKR2Str(test_threads_datas[i].rv));
+ }
+ fprintf(stderr,"\ntest_threads waiting for 30 seconds ...\n");
+#ifdef _WIN32
+ Sleep(30*1000);
+#else
+ sleep(30);
+#endif
+ }
+ }
+
+ for (i = 0; i < test_threads_num; i++) {
+ fprintf(stderr,"test_threads thread:%d state:%d, rv:%s\n",
+ i, test_threads_datas[i].state, CKR2Str(test_threads_datas[i].rv));
+ if (test_threads_datas[i].state == -1) {
+#ifdef _WIN32
+ TerminateThread(test_threads_handles[i], 0);
+#else
+ pthread_join(test_threads_handles[i], NULL);
+ } else {
+ pthread_cancel(test_threads_handles[i]);
+#endif
+ }
+ }
+
+ fprintf(stderr,"test_threads cleanup finished\n");
+ return 0;
+}
+
+static int test_threads_start(int tnum)
+{
+ int r = 0;
+
+#ifdef _WIN32
+ test_threads_handles[tnum] = CreateThread(NULL, 0, test_threads_run, (LPVOID) &test_threads_datas[tnum],
+ 0, NULL);
+ if (test_threads_handles[tnum] == NULL) {
+ r = GetLastError();
+ }
+#else
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+ r = pthread_create(&test_threads_handles[tnum], &attr, test_threads_run, (void *) &test_threads_datas[tnum]);
+#endif
+ if (r != 0) {
+ fprintf(stderr,"test_threads pthread_create failed %d for thread %d\n", r, tnum);
+ /* system error */
+ }
+ return r;
+}
+
+/*********************************************************************************************/
+static void test_threads()
+{
+ int i;
+
+ /* call test_threads_start for each --test-thread option */
+
+ /* upon return, C_Initialize will be called, from main code */
+ for (i = 0; i < test_threads_num && i < MAX_TEST_THREADS; i++) {
+ test_threads_start(i);
+ }
+}
+#endif /* defined(_WIN32) || defiend(HAVE_PTHREAD) */
+