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

Reply via email to