Hi,
To port the pam_pkcs11 module to OpenSolaris, we made a change in the
slot/token selection scheme to resolve an incompatibility issue between
the OpenSC/pam_pkcs11 module and Solaris Cryptographic Framework library
(a PKCS#11 implementation). We would like to share with you why the
changes are necessary and hopefully they will be adopted by the community.
======================================
Problem
======================================
Currently, the pam_pkcs11 module uses slot_num for the slot/token
selection. However, this is not safe because there is no guaranteed
ordering of slots returned from the PKCS#11 C_GetSlotList() function,
according to the RSA PKCS#11 specification.
On Solaris OS, the libpkcs11 library uses plug-ins to provide the
slots. An administrator can install or uninstall a plug-in by using a
utility command called "cryptoadm" on Solaris. Therefore, a particular
slot may be associated with different slot numbers on different systems
or even on the same system on different runs.
======================================
Solution
======================================
To address the above issue, our solution is to replace the "slot_num"
entry with a "slot_description" entry in the "pam_pkcs11.conf"
configuration file for the slot/token selection as illustrated below.
--- pam_pkcs11.conf ----
pkcs11_module default {
module = /usr/lib/libpkcs11.so;
description = "Solaris PKCS#11 Cryptographic Framework library";
#
# Which slot to use. The slot is specified by the slot description.
# For example, slot_description = "Sun Crypto Softtoken".
#
# An administrator can use the "cryotoadm list -v" command to find
# all the available slots and their slot descriptions. For more #
information, see the libpkcs11(3LIB) and cryptoadm(1m) man pages.
#
# The default value is "none" which means to use the first slot with
# an available token.
#
slot_description = "none"
...
}
The slot_description field will be used to specify the slot to be
used. Its value should be the slot label for the slot, which is
basically same as the slotDescription string in the CK_SLOT_INFO
structure returned from the C_GetSlotInfo()function.
In the original slot_num design, when slot_num is 0, it means to use the
first slot with an available token. To provide a similar
functionality, an administrator can specify slot_description to be
"none", which also means to use the first slot with an available token.
======================
Souce changes
======================
To implement the solution, we added and modified several functions to
the pam_pkcs11 source. We also successfully tested on Solaris OS.
Attached is the patch file for the source changes. Please let me know
if you have any questions or comments.
Thanks,
Huie-Ying
Index: src/pam_pkcs11/pam_pkcs11.c
===================================================================
--- src/pam_pkcs11/pam_pkcs11.c (revision 326)
+++ src/pam_pkcs11/pam_pkcs11.c (working copy)
@@ -314,8 +314,13 @@
}
/* open pkcs #11 session */
+#ifdef SUN_SOLARIS
+ rv = find_slot_by_slotlabel_and_tokenlabel(ph,
+ configuration->slot_description, login_token_name, &slot_num);
+#else
rv = find_slot_by_number_and_label(ph, configuration->slot_num,
login_token_name, &slot_num);
+#endif
if (rv != 0) {
ERR("no suitable token available");
pam_syslog(pamh, LOG_ERR, "no suitable token available");
@@ -337,8 +342,14 @@
pam_prompt(pamh, PAM_TEXT_INFO, NULL,
_("Please insert your smart card."));
}
+
+#ifdef SUN_SOLARIS
+ rv = wait_for_token(ph, configuration->slot_description,
+ login_token_name, &slot_num);
+#else
rv = wait_for_token(ph, configuration->slot_num,
login_token_name, &slot_num);
+#endif
if (rv != 0) {
release_pkcs11_module(ph);
return pkcs11_pam_fail;
@@ -356,7 +367,12 @@
/* check one last time for the smart card before bouncing to the next
* module */
+#ifdef SUN_SOLARIS
+ rv = find_slot_by_slotlabel(ph, configuration->slot_description,
+ &slot_num);
+#else
rv = find_slot_by_number(ph, configuration->slot_num, &slot_num);
+#endif
if (rv != 0) {
/* user gave us a user id and no smart card go to next module */
release_pkcs11_module(ph);
Index: src/pam_pkcs11/pam_config.c
===================================================================
--- src/pam_pkcs11/pam_config.c (revision 326)
+++ src/pam_pkcs11/pam_config.c (working copy)
@@ -45,7 +45,11 @@
"default", /* const char *pkcs11_module; */
CONFDIR "/pkcs11_module.so",/* const char *pkcs11_module_path; */
NULL, /* screen savers */
+#ifdef SUN_SOLARIS
+ NULL, /* slot_description */
+#else
0, /* int slot_num; */
+#endif
0, /* support threads */
/* cert policy; */
{
@@ -145,8 +149,13 @@
scconf_get_str(pkcs11_mblk,"crl_dir",configuration.policy.crl_dir);
configuration.policy.nss_dir = (char *)
scconf_get_str(pkcs11_mblk,"nss_dir",configuration.policy.nss_dir);
+#ifdef SUN_SOLARIS
+ configuration.slot_description = (char *)
+
scconf_get_str(pkcs11_mblk,"slot_description",configuration.slot_description);
+#else
configuration.slot_num =
scconf_get_int(pkcs11_mblk,"slot_num",configuration.slot_num);
+#endif
configuration.support_threads =
scconf_get_bool(pkcs11_mblk,"support_threads",configuration.support_threads);
policy_list= scconf_find_list(pkcs11_mblk,"cert_policy");
@@ -255,10 +264,17 @@
res=sscanf(argv[i],"pkcs11_module=%255s",configuration.pkcs11_module);
continue;
}
+#ifdef SUN_SOLARIS
+ if (strstr(argv[i],"slot_description=") ) {
+
res=sscanf(argv[i],"slot_description=%255s",&configuration.slot_description);
+ continue;
+ }
+#else
if (strstr(argv[i],"slot_num=") ) {
- res=sscanf(argv[i],"slot_nume=%d",&configuration.slot_num);
+ res=sscanf(argv[i],"slot_num=%d",&configuration.slot_num);
continue;
}
+#endif
if (strstr(argv[i],"ca_dir=") ) {
res=sscanf(argv[i],"ca_dir=%255s",configuration.policy.ca_dir);
continue;
@@ -308,4 +324,3 @@
}
return &configuration;
}
-
Index: src/pam_pkcs11/pam_config.h
===================================================================
--- src/pam_pkcs11/pam_config.h (revision 326)
+++ src/pam_pkcs11/pam_config.h (working copy)
@@ -38,7 +38,11 @@
char *pkcs11_module;
char *pkcs11_modulepath;
char **screen_savers;
+#ifdef SUN_SOLARIS
+ char *slot_description;
+#else
int slot_num;
+#endif
int support_threads;
cert_policy policy;
char *username; /* provided user name */
Index: src/pam_pkcs11/Makefile.am
===================================================================
--- src/pam_pkcs11/Makefile.am (revision 326)
+++ src/pam_pkcs11/Makefile.am (working copy)
@@ -2,8 +2,8 @@
MAINTAINERCLEANFILES = Makefile.in
-AM_CFLAGS = -Wall -fno-strict-aliasing $(CRYPTO_CFLAGS)
-AM_CPPFLAGS = -Wall -fno-strict-aliasing $(CRYPTO_CFLAGS)
+AM_CFLAGS = -DSUN_SOLARIS -Wall -fno-strict-aliasing $(CRYPTO_CFLAGS)
+AM_CPPFLAGS = -DSUN_SOLARIS -Wall -fno-strict-aliasing $(CRYPTO_CFLAGS)
lib_LTLIBRARIES = pam_pkcs11.la
Index: src/tools/pkcs11_listcerts.c
===================================================================
--- src/tools/pkcs11_listcerts.c (revision 326)
+++ src/tools/pkcs11_listcerts.c (working copy)
@@ -78,7 +78,11 @@
}
/* open pkcs #11 session */
+#ifdef SUN_SOLARIS
+ rv = find_slot_by_slotlabel(ph,configuration->slot_description, &slot_num);
+#else
rv = find_slot_by_number(ph, configuration->slot_num, &slot_num);
+#endif
if (rv != 0) {
release_pkcs11_module(ph);
DBG("no token available");
Index: src/tools/pkcs11_inspect.c
===================================================================
--- src/tools/pkcs11_inspect.c (revision 326)
+++ src/tools/pkcs11_inspect.c (working copy)
@@ -79,7 +79,11 @@
}
/* open pkcs #11 session */
+#ifdef SUN_SOLARIS
+ rv = find_slot_by_slotlabel(ph, configuration->slot_description, &slot_num);
+#else
rv = find_slot_by_number(ph, configuration->slot_num, &slot_num);
+#endif
if (rv != 0) {
release_pkcs11_module(ph);
DBG("no token available");
Index: src/tools/Makefile.am
===================================================================
--- src/tools/Makefile.am (revision 326)
+++ src/tools/Makefile.am (working copy)
@@ -4,7 +4,7 @@
FINDER_OBJS = ../pam_pkcs11/mapper_mgr.o ../pam_pkcs11/pam_config.o
-INCLUDES = $(PCSC_CFLAGS) $(CRYPTO_CFLAGS)
+INCLUDES = -DSUN_SOLARIS $(PCSC_CFLAGS) $(CRYPTO_CFLAGS)
AM_LDFLAGS = $(PCSC_LIBS)
if HAVE_PCSC
Index: src/tools/pklogin_finder.c
===================================================================
--- src/tools/pklogin_finder.c (revision 326)
+++ src/tools/pklogin_finder.c (working copy)
@@ -80,7 +80,11 @@
}
/* open pkcs #11 session */
+#ifdef SUN_SOLARIS
+ rv = find_slot_by_slotlabel(ph,configuration->slot_description, &slot_num);
+#else
rv = find_slot_by_number(ph,configuration->slot_num, &slot_num);
+#endif
if (rv != 0) {
release_pkcs11_module(ph);
DBG("no token available");
Index: src/common/Makefile.am
===================================================================
--- src/common/Makefile.am (revision 326)
+++ src/common/Makefile.am (working copy)
@@ -2,8 +2,8 @@
MAINTAINERCLEANFILES = Makefile.in
-AM_CFLAGS = $(CRYPTO_CFLAGS)
-AM_CPPFLAGS = $(CRYPTO_CFLAGS)
+AM_CFLAGS = -DSUN_SOLARIS $(CRYPTO_CFLAGS)
+AM_CPPFLAGS = -DSUN_SOLARIS $(CRYPTO_CFLAGS)
SUBDIRS = . rsaref
Index: src/common/pkcs11_lib.c
===================================================================
--- src/common/pkcs11_lib.c (revision 326)
+++ src/common/pkcs11_lib.c (working copy)
@@ -709,6 +709,9 @@
CK_SLOT_ID id;
CK_BBOOL token_present;
CK_UTF8CHAR label[33];
+#ifdef SUN_SOLARIS
+ CK_UTF8CHAR slotDescription[64];
+#endif
} slot_t;
struct pkcs11_handle_str {
@@ -758,7 +761,11 @@
DBG3("module permissions: uid = %d, gid = %d, mode = %o",
module_stat.st_uid, module_stat.st_gid, module_stat.st_mode & 0777);
if (module_stat.st_mode & S_IWGRP || module_stat.st_mode & S_IWOTH
+#ifdef SUN_SOLARIS
+ || module_stat.st_uid != 0) {
+#else
|| module_stat.st_uid != 0 || module_stat.st_gid != 0) {
+#endif
set_error("the pkcs #11 module MUST be owned by root and MUST NOT "
"be writeable by the group or others");
free(h);
@@ -807,6 +814,11 @@
set_error("C_GetSlotInfo() failed: 0x%08lX", rv);
return -1;
}
+
+#ifdef SUN_SOLARIS
+ (void) memcpy(h->slots[i].slotDescription, sinfo.slotDescription, 64);
+#endif
+
DBG1("- description: %.64s", sinfo.slotDescription);
DBG1("- manufacturer: %.32s", sinfo.manufacturerID);
DBG1("- flags: %04lx", sinfo.flags);
@@ -985,7 +997,176 @@
return -1;
}
+#ifdef SUN_SOLARIS
+/*
+ * memcmp_pad_max() is a specialized version of memcmp() which
+ * compares two pieces of data up to a maximum length. If the
+ * the two data match up the maximum length, they are considered
+ * matching. Trailing blanks do not cause the match to fail if
+ * one of the data is shorted.
+ *
+ * Examples of matches:
+ * "one" |
+ * "one " |
+ * ^maximum length
+ *
+ * "Number One | X" (X is beyond maximum length)
+ * "Number One " |
+ * ^maximum length
+ *
+ * Examples of mismatches:
+ * " one"
+ * "one"
+ *
+ * "Number One X|"
+ * "Number One |"
+ * ^maximum length
+ */
+static int
+memcmp_pad_max(void *d1, size_t d1_len, void *d2, size_t d2_len,
+ size_t max_sz)
+{
+ size_t len, extra_len;
+ char *marker;
+
+ /* No point in comparing anything beyond max_sz */
+ if (d1_len > max_sz)
+ d1_len = max_sz;
+ if (d2_len > max_sz)
+ d2_len = max_sz;
+
+ /* Find shorter of the two data. */
+ if (d1_len <= d2_len) {
+ len = d1_len;
+ extra_len = d2_len;
+ marker = d2;
+ } else { /* d1_len > d2_len */
+ len = d2_len;
+ extra_len = d1_len;
+ marker = d1;
+ }
+
+ /* Have a match in the shortest length of data? */
+ if (memcmp(d1, d2, len) != 0)
+ /* CONSTCOND */
+ return (1);
+
+ /* If the rest of longer data is nulls or blanks, call it a match. */
+ while (len < extra_len && marker[len])
+ if (!isspace(marker[len++]))
+ /* CONSTCOND */
+ return (1);
+ return (0);
+}
+
+
+/*
+ * This function will search the slot list to find a slot based on the slot
+ * label. If the wanted_slot_label is "none", then we will return the first
slot
+ * with the token presented.
+ *
+ * This function return 0 if it found a matching slot; otherwise, it returns
+ * -1.
+ */
+int
+find_slot_by_slotlabel(pkcs11_handle_t *h, const char *wanted_slot_label,
+ unsigned int *slot_num)
+{
+ unsigned long index;
+ size_t len;
+
+ if (slot_num == NULL || wanted_slot_label == NULL ||
+ strlen(wanted_slot_label) == 0)
+ return (-1);
+
+ if (strcmp(wanted_slot_label, "none") == 0) {
+ for (index = 0; index < h->slot_count; index++) {
+ if (h->slots[index].token_present) {
+ *slot_num = index;
+ return (0);
+ }
+ }
+ } else {
+ /* Look up the slot by it's slotDescription */
+ len = strlen(wanted_slot_label);
+ for (index = 0; index < h->slot_count; index++) {
+ if (memcmp_pad_max(h->slots[index].slotDescription, 64,
+ (void *)wanted_slot_label, len, 64) == 0) {
+ *slot_num = index;
+ return (0);
+ }
+ }
+ }
+
+ return (-1);
+}
+
+
+int
+find_slot_by_slotlabel_and_tokenlabel(pkcs11_handle_t *h,
+ const char *wanted_slot_label, const char *wanted_token_label,
+ unsigned int *slot_num)
+{
+ unsigned long i;
+ int rv;
+
+ if (slot_num == NULL)
+ return (-1);
+
+ if (wanted_token_label == NULL) {
+ rv = find_slot_by_slotlabel(h, wanted_slot_label, slot_num);
+ return (rv);
+ }
+
+ /* wanted_token_label != NULL */
+ if (strcmp(wanted_slot_label, "none") == 0) {
+ for (i= 0; i < h->slot_count; i++) {
+ if (h->slots[i].token_present &&
+ strcmp(wanted_token_label, h->slots[i].label) == 0)
{
+ *slot_num = i;
+ return (0);
+ }
+ }
+ return (-1);
+ } else {
+ for (i = 0; i < h->slot_count; i++) {
+ if (h->slots[i].token_present &&
+ strcmp(wanted_token_label, h->slots[i].label) == 0
&&
+ strcmp(wanted_slot_label,
h->slots[i].slotDescription)
+ == 0) {
+ *slot_num = i;
+ return (0);
+ }
+ }
+ return (-1);
+ }
+}
+
int wait_for_token(pkcs11_handle_t *h,
+ const char *wanted_slot_label,
+ const char *wanted_token_label,
+ unsigned int *slot_num)
+{
+ int rv;
+
+ rv = -1;
+ do {
+ /* see if the card we're looking for is inserted */
+ rv = find_slot_by_slotlabel_and_tokenlabel (h, wanted_slot_label,
+ wanted_token_label, slot_num);
+ if (rv != 0) {
+ /* could call C_WaitForSlotEvent, for now just poll */
+ sleep(10);
+ refresh_slots(h);
+ continue;
+ }
+ } while (rv != 0);
+
+ return rv;
+}
+
+#else
+int wait_for_token(pkcs11_handle_t *h,
int wanted_slot_id,
const char *wanted_slot_label,
unsigned int *slot_num)
@@ -1007,6 +1188,7 @@
return rv;
}
+#endif
int open_pkcs11_session(pkcs11_handle_t *h, unsigned int slot)
{
@@ -1179,7 +1361,12 @@
/* Pass 3: store certificate */
/* convert to X509 data structure */
+#ifdef SUN_SOLARIS
+ x509 = d2i_X509(NULL, (const uchar_t **)&cert_template[3].pValue,
cert_template[3].ulValueLen);
+#else
x509 = d2i_X509(NULL, (CK_BYTE **)&cert_template[3].pValue,
cert_template[3].ulValueLen);
+#endif
+
if (x509 == NULL) {
free(id_value);
free(cert_value);
Index: src/common/pkcs11_lib.h
===================================================================
--- src/common/pkcs11_lib.h (revision 326)
+++ src/common/pkcs11_lib.h (working copy)
@@ -36,11 +36,31 @@
PKCS11_EXTERN int find_slot_by_number_and_label(pkcs11_handle_t *h,
int slot_num, const char *slot_label,
unsigned int *slot);
+
+#ifdef SUN_SOLARIS
+PKCS11_EXTERN int find_slot_by_slotlabel(pkcs11_handle_t *h,
+ const char *wanted_slot_label,
+ unsigned int *slot);
+PKCS11_EXTERN int find_slot_by_slotlabel_and_tokenlabel(pkcs11_handle_t *h,
+ const char *wanted_slot_label,
+ const char *wanted_token_label,
+ unsigned int *slot);
+#endif
+
PKCS11_EXTERN const char *get_slot_label(pkcs11_handle_t *h);
+
+#ifdef SUN_SOLARIS
PKCS11_EXTERN int wait_for_token(pkcs11_handle_t *h,
+ const char *wanted_slot_label,
+ const char *wanted_token_label,
+ unsigned int *slot);
+#else
+PKCS11_EXTERN int wait_for_token(pkcs11_handle_t *h,
int wanted_slot_num,
const char *wanted_slot_label,
unsigned int *slot);
+#endif
+
PKCS11_EXTERN const X509 *get_X509_certificate(cert_object_t *cert);
PKCS11_EXTERN void release_pkcs11_module(pkcs11_handle_t *h);
PKCS11_EXTERN int open_pkcs11_session(pkcs11_handle_t *h, unsigned int slot);
Index: etc/pam_pkcs11.conf.example
===================================================================
--- etc/pam_pkcs11.conf.example (revision 326)
+++ etc/pam_pkcs11.conf.example (working copy)
@@ -33,6 +33,13 @@
# on. The default value is zero which means to use the first slot
# with an available token.
slot_num = 0;
+ #
+ # Note:
+ # For Sun Solaris OS, the slot is specified by the slot description.
+ # For example, slot_description = "Sun Crypto Softtoken".
+ # The default value is "none" which means to use the first slot with
+ # an available token.
+ # slot_description = "none";
# Where are CA certificates stored?
# You can setup this value to:
_______________________________________________
opensc-devel mailing list
opensc-devel@lists.opensc-project.org
http://www.opensc-project.org/mailman/listinfo/opensc-devel