Bug 1577803, pk11wrap: set friendly flag if token implements CKP_PUBLIC_CERTIFICATES_TOKEN, r=rrelyea
authorDaiki Ueno <dueno@redhat.com>
Wed, 06 Nov 2019 11:33:14 +0100
changeset 15377 b39c8eeabe6a7b51cafbff1b1730ceec5aefbbc2
parent 15376 6125200fbc889fbb68447ff3fae644441842c55e
child 15378 c9014b2892d5d2d17874f269a56c592dc7007517
push id3570
push userdueno@redhat.com
push dateWed, 06 Nov 2019 10:34:15 +0000
reviewersrrelyea
bugs1577803
Bug 1577803, pk11wrap: set friendly flag if token implements CKP_PUBLIC_CERTIFICATES_TOKEN, r=rrelyea Summary: This makes NSS look for CKO_PROFILE object at token initialization time to check if it implements the [[ https://docs.oasis-open.org/pkcs11/pkcs11-profiles/v3.0/pkcs11-profiles-v3.0.pdf | Public Certificates Token profile ]] as defined in PKCS #11 v3.0. If it is found, the token is automatically marked as friendly so no authentication attempts will be made when accessing certificates. Reviewers: rrelyea Reviewed By: rrelyea Subscribers: reviewbot Bug #: 1577803 Differential Revision: https://phabricator.services.mozilla.com/D45669
gtests/pk11_gtest/pk11_module_unittest.cc
gtests/pkcs11testmodule/pkcs11testmodule.cpp
lib/pk11wrap/debug_module.c
lib/pk11wrap/pk11obj.c
lib/pk11wrap/pk11slot.c
lib/pk11wrap/secmodti.h
lib/util/pkcs11t.h
--- a/gtests/pk11_gtest/pk11_module_unittest.cc
+++ b/gtests/pk11_gtest/pk11_module_unittest.cc
@@ -46,24 +46,39 @@ TEST_F(Pkcs11ModuleTest, ListSlots) {
   EXPECT_NE(nullptr, slots);
 
   PK11SlotListElement* element = PK11_GetFirstSafe(slots.get());
   EXPECT_NE(nullptr, element);
 
   // These tokens are always present.
   const std::vector<std::string> kSlotsWithToken = {
       "NSS Internal Cryptographic Services",
-      "NSS User Private Key and Certificate Services", "Test PKCS11 Slot 二"};
+      "NSS User Private Key and Certificate Services",
+      "Test PKCS11 Public Certs Slot",
+      "Test PKCS11 Slot 二"};
   std::vector<std::string> foundSlots;
 
   do {
     std::string name = PK11_GetSlotName(element->slot);
     foundSlots.push_back(name);
     std::cerr << "loaded slot: " << name << std::endl;
   } while ((element = PK11_GetNextSafe(slots.get(), element, PR_FALSE)) !=
            nullptr);
 
   std::sort(foundSlots.begin(), foundSlots.end());
   EXPECT_TRUE(std::equal(kSlotsWithToken.begin(), kSlotsWithToken.end(),
                          foundSlots.begin()));
 }
 
+TEST_F(Pkcs11ModuleTest, PublicCertificatesToken) {
+  const std::string kRegularToken = "Test PKCS11 Tokeñ 2 Label";
+  const std::string kPublicCertificatesToken = "Test PKCS11 Public Certs Token";
+
+  ScopedPK11SlotInfo slot1(PK11_FindSlotByName(kRegularToken.c_str()));
+  EXPECT_NE(nullptr, slot1);
+  EXPECT_FALSE(PK11_IsFriendly(slot1.get()));
+
+  ScopedPK11SlotInfo slot2(PK11_FindSlotByName(kPublicCertificatesToken.c_str()));
+  EXPECT_NE(nullptr, slot2);
+  EXPECT_TRUE(PK11_IsFriendly(slot2.get()));
+}
+
 }  // namespace nss_test
--- a/gtests/pkcs11testmodule/pkcs11testmodule.cpp
+++ b/gtests/pkcs11testmodule/pkcs11testmodule.cpp
@@ -71,53 +71,62 @@ CK_RV Test_C_GetInfo(CK_INFO_PTR pInfo) 
   pInfo->libraryVersion = TestLibraryVersion;
   return CKR_OK;
 }
 
 CK_RV Test_C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR) { return CKR_OK; }
 
 static int tokenPresent = 0;
 
+// The token in slot 4 has 2 objects. Both of them are profile object
+// and identified by object ID 1 or 2.
+static bool readingProfile = false;
+static const CK_PROFILE_ID profiles[] = {CKP_PUBLIC_CERTIFICATES_TOKEN,
+                                         CKP_BASELINE_PROVIDER};
+static int profileIndex = 0;
+
 CK_RV Test_C_GetSlotList(CK_BBOOL limitToTokensPresent,
                          CK_SLOT_ID_PTR pSlotList, CK_ULONG_PTR pulCount) {
   if (!pulCount) {
     return CKR_ARGUMENTS_BAD;
   }
 
-  CK_SLOT_ID slots[3];
+  CK_SLOT_ID slots[4];
   CK_ULONG slotCount = 0;
 
-  // We always return slot 2.
+  // We always return slot 2 and 4.
   slots[slotCount++] = 2;
+  slots[slotCount++] = 4;
 
   // Slot 1 is a removable slot where a token is present if
   // tokenPresent = CK_TRUE.
   if (tokenPresent || !limitToTokensPresent) {
     slots[slotCount++] = 1;
   }
 
   // Slot 3 is a removable slot which never has a token.
   if (!limitToTokensPresent) {
     slots[slotCount++] = 3;
   }
 
   if (pSlotList) {
     if (*pulCount < slotCount) {
       return CKR_BUFFER_TOO_SMALL;
     }
-    memcpy(pSlotList, slots, sizeof(CK_ULONG) * slotCount);
+    memcpy(pSlotList, slots, sizeof(CK_SLOT_ID) * slotCount);
   }
 
   *pulCount = slotCount;
   return CKR_OK;
 }
 
 static const char TestSlotDescription[] = "Test PKCS11 Slot";
 static const char TestSlot2Description[] = "Test PKCS11 Slot 二";
 static const char TestSlot3Description[] = "Empty PKCS11 Slot";
+static const char TestSlot4Description[] = "Test PKCS11 Public Certs Slot";
 
 CK_RV Test_C_GetSlotInfo(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo) {
   if (!pInfo) {
     return CKR_ARGUMENTS_BAD;
   }
 
   switch (slotID) {
     case 1:
@@ -128,45 +137,53 @@ CK_RV Test_C_GetSlotInfo(CK_SLOT_ID slot
     case 2:
       CopyString(pInfo->slotDescription, TestSlot2Description);
       pInfo->flags = CKF_TOKEN_PRESENT | CKF_REMOVABLE_DEVICE;
       break;
     case 3:
       CopyString(pInfo->slotDescription, TestSlot3Description);
       pInfo->flags = CKF_REMOVABLE_DEVICE;
       break;
+    case 4:
+      CopyString(pInfo->slotDescription, TestSlot4Description);
+      pInfo->flags = CKF_TOKEN_PRESENT | CKF_REMOVABLE_DEVICE;
+      break;
     default:
       return CKR_ARGUMENTS_BAD;
   }
 
   CopyString(pInfo->manufacturerID, TestManufacturerID);
   pInfo->hardwareVersion = TestLibraryVersion;
   pInfo->firmwareVersion = TestLibraryVersion;
   return CKR_OK;
 }
 
 // Deliberately include énye to ensure we're handling encoding correctly.
 // The PKCS #11 base specification v2.20 specifies that strings be encoded
 // as UTF-8.
 static const char TestTokenLabel[] = "Test PKCS11 Tokeñ Label";
 static const char TestToken2Label[] = "Test PKCS11 Tokeñ 2 Label";
+static const char TestToken4Label[] = "Test PKCS11 Public Certs Token";
 static const char TestTokenModel[] = "Test Model";
 
 CK_RV Test_C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo) {
   if (!pInfo) {
     return CKR_ARGUMENTS_BAD;
   }
 
   switch (slotID) {
     case 1:
       CopyString(pInfo->label, TestTokenLabel);
       break;
     case 2:
       CopyString(pInfo->label, TestToken2Label);
       break;
+    case 4:
+      CopyString(pInfo->label, TestToken4Label);
+      break;
     default:
       return CKR_ARGUMENTS_BAD;
   }
 
   CopyString(pInfo->manufacturerID, TestManufacturerID);
   CopyString(pInfo->model, TestTokenModel);
   memset(pInfo->serialNumber, 0, sizeof(pInfo->serialNumber));
   pInfo->flags = CKF_TOKEN_INITIALIZED;
@@ -218,16 +235,19 @@ CK_RV Test_C_OpenSession(CK_SLOT_ID slot
                          CK_SESSION_HANDLE_PTR phSession) {
   switch (slotID) {
     case 1:
       *phSession = 1;
       break;
     case 2:
       *phSession = 2;
       break;
+    case 4:
+      *phSession = 4;
+      break;
     default:
       return CKR_ARGUMENTS_BAD;
   }
 
   return CKR_OK;
 }
 
 CK_RV Test_C_CloseSession(CK_SESSION_HANDLE) { return CKR_OK; }
@@ -242,16 +262,19 @@ CK_RV Test_C_GetSessionInfo(CK_SESSION_H
 
   switch (hSession) {
     case 1:
       pInfo->slotID = 1;
       break;
     case 2:
       pInfo->slotID = 2;
       break;
+    case 4:
+      pInfo->slotID = 4;
+      break;
     default:
       return CKR_ARGUMENTS_BAD;
   }
 
   pInfo->state = CKS_RO_PUBLIC_SESSION;
   pInfo->flags = CKF_SERIAL_SESSION;
   return CKR_OK;
 }
@@ -284,37 +307,93 @@ CK_RV Test_C_CopyObject(CK_SESSION_HANDL
 CK_RV Test_C_DestroyObject(CK_SESSION_HANDLE, CK_OBJECT_HANDLE) {
   return CKR_FUNCTION_NOT_SUPPORTED;
 }
 
 CK_RV Test_C_GetObjectSize(CK_SESSION_HANDLE, CK_OBJECT_HANDLE, CK_ULONG_PTR) {
   return CKR_FUNCTION_NOT_SUPPORTED;
 }
 
-CK_RV Test_C_GetAttributeValue(CK_SESSION_HANDLE, CK_OBJECT_HANDLE,
-                               CK_ATTRIBUTE_PTR, CK_ULONG) {
+CK_RV Test_C_GetAttributeValue(CK_SESSION_HANDLE hSession,
+                               CK_OBJECT_HANDLE hObject,
+                               CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) {
+  if (hSession == 4) {
+    assert(hObject >= 1 &&
+           hObject - 1 < sizeof(profiles) / sizeof(profiles[0]));
+    for (CK_ULONG count = 0; count < ulCount; count++) {
+      if (pTemplate[count].type == CKA_PROFILE_ID) {
+        if (pTemplate[count].pValue) {
+          assert(pTemplate[count].ulValueLen == sizeof(CK_ULONG));
+          CK_ULONG value = profiles[hObject - 1];
+          memcpy(pTemplate[count].pValue, &value, sizeof(value));
+        } else {
+          pTemplate[count].ulValueLen = sizeof(CK_ULONG);
+        }
+      } else {
+        pTemplate[count].ulValueLen = (CK_ULONG)-1;
+      }
+    }
+    return CKR_OK;
+  }
   return CKR_FUNCTION_NOT_SUPPORTED;
 }
 
 CK_RV Test_C_SetAttributeValue(CK_SESSION_HANDLE, CK_OBJECT_HANDLE,
                                CK_ATTRIBUTE_PTR, CK_ULONG) {
   return CKR_FUNCTION_NOT_SUPPORTED;
 }
 
-CK_RV Test_C_FindObjectsInit(CK_SESSION_HANDLE, CK_ATTRIBUTE_PTR, CK_ULONG) {
+CK_RV Test_C_FindObjectsInit(CK_SESSION_HANDLE hSession,
+                             CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) {
+  // Slot 4
+  if (hSession == 4) {
+    for (CK_ULONG count = 0; count < ulCount; count++) {
+      CK_ATTRIBUTE attribute = pTemplate[count];
+      if (attribute.type == CKA_CLASS) {
+        assert(attribute.ulValueLen == sizeof(CK_ULONG));
+
+        CK_ULONG value;
+        memcpy(&value, attribute.pValue, attribute.ulValueLen);
+        if (value == CKO_PROFILE) {
+          readingProfile = true;
+          profileIndex = 0;
+          break;
+        }
+      }
+    }
+  }
   return CKR_OK;
 }
 
-CK_RV Test_C_FindObjects(CK_SESSION_HANDLE, CK_OBJECT_HANDLE_PTR, CK_ULONG,
+CK_RV Test_C_FindObjects(CK_SESSION_HANDLE hSession,
+                         CK_OBJECT_HANDLE_PTR phObject,
+                         CK_ULONG ulMaxObjectCount,
                          CK_ULONG_PTR pulObjectCount) {
-  *pulObjectCount = 0;
+  if (readingProfile) {
+    assert(hSession == 4);
+    CK_ULONG count = ulMaxObjectCount;
+    size_t remaining = sizeof(profiles) / sizeof(profiles[0]) - profileIndex;
+    if (count > remaining) {
+      count = remaining;
+    }
+    for (CK_ULONG i = 0; i < count; i++) {
+      phObject[i] = i + 1;
+    }
+    profileIndex += count;
+    *pulObjectCount = count;
+  } else {
+    *pulObjectCount = 0;
+  }
   return CKR_OK;
 }
 
-CK_RV Test_C_FindObjectsFinal(CK_SESSION_HANDLE) { return CKR_OK; }
+CK_RV Test_C_FindObjectsFinal(CK_SESSION_HANDLE hSession) {
+  readingProfile = false;
+  return CKR_OK;
+}
 
 CK_RV Test_C_EncryptInit(CK_SESSION_HANDLE, CK_MECHANISM_PTR,
                          CK_OBJECT_HANDLE) {
   return CKR_FUNCTION_NOT_SUPPORTED;
 }
 
 CK_RV Test_C_Encrypt(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR,
                      CK_ULONG_PTR) {
--- a/lib/pk11wrap/debug_module.c
+++ b/lib/pk11wrap/debug_module.c
@@ -129,16 +129,17 @@ get_attr_type_str(CK_ATTRIBUTE_TYPE atyp
         CASE(CKA_ECDSA_PARAMS);
         CASE(CKA_EC_POINT);
         CASE(CKA_SECONDARY_AUTH);
         CASE(CKA_AUTH_PIN_FLAGS);
         CASE(CKA_HW_FEATURE_TYPE);
         CASE(CKA_RESET_ON_INIT);
         CASE(CKA_HAS_RESET);
         CASE(CKA_VENDOR_DEFINED);
+        CASE(CKA_PROFILE_ID);
         CASE(CKA_NSS_URL);
         CASE(CKA_NSS_EMAIL);
         CASE(CKA_NSS_SMIME_INFO);
         CASE(CKA_NSS_SMIME_TIMESTAMP);
         CASE(CKA_NSS_PKCS8_SALT);
         CASE(CKA_NSS_PASSWORD_CHECK);
         CASE(CKA_NSS_EXPIRES);
         CASE(CKA_NSS_KRL);
@@ -184,30 +185,52 @@ get_obj_class(CK_OBJECT_CLASS objClass, 
     switch (objClass) {
         CASE(CKO_DATA);
         CASE(CKO_CERTIFICATE);
         CASE(CKO_PUBLIC_KEY);
         CASE(CKO_PRIVATE_KEY);
         CASE(CKO_SECRET_KEY);
         CASE(CKO_HW_FEATURE);
         CASE(CKO_DOMAIN_PARAMETERS);
+        CASE(CKO_PROFILE);
         CASE(CKO_NSS_CRL);
         CASE(CKO_NSS_SMIME);
         CASE(CKO_NSS_TRUST);
         CASE(CKO_NSS_BUILTIN_ROOT_LIST);
         default:
             break;
     }
     if (a)
         PR_snprintf(str, len, "%s", a);
     else
         PR_snprintf(str, len, "0x%p", objClass);
 }
 
 static void
+get_profile_val(CK_PROFILE_ID profile, char *str, int len)
+{
+
+    const char *a = NULL;
+
+    switch (profile) {
+        CASE(CKP_INVALID_ID);
+        CASE(CKP_BASELINE_PROVIDER);
+        CASE(CKP_EXTENDED_PROVIDER);
+        CASE(CKP_AUTHENTICATION_TOKEN);
+        CASE(CKP_PUBLIC_CERTIFICATES_TOKEN);
+        default:
+            break;
+    }
+    if (a)
+        PR_snprintf(str, len, "%s", a);
+    else
+        PR_snprintf(str, len, "0x%p", profile);
+}
+
+static void
 get_trust_val(CK_TRUST trust, char *str, int len)
 {
     const char *a = NULL;
 
     switch (trust) {
         CASE(CKT_NSS_TRUSTED);
         CASE(CKT_NSS_TRUSTED_DELEGATOR);
         CASE(CKT_NSS_NOT_TRUSTED);
@@ -683,16 +706,24 @@ print_attr_value(CK_ATTRIBUTE_PTR attr)
         case CKA_NSS_URL:
             if (attr->ulValueLen > 0 && attr->pValue) {
                 len = PR_MIN(attr->ulValueLen + 1, sizeof valstr);
                 PR_snprintf(valstr, len, "%s", attr->pValue);
                 PR_LOG(modlog, 4, (fmt_s_qsq_d,
                                    atype, valstr, attr->ulValueLen));
                 break;
             }
+        case CKA_PROFILE_ID:
+            if (attr->ulValueLen > 0 && attr->pValue) {
+                CK_PROFILE_ID profile = *((CK_PROFILE_ID *)attr->pValue);
+                get_profile_val(profile, valstr, sizeof valstr);
+                PR_LOG(modlog, 4, (fmt_s_s_d,
+                                   atype, valstr, attr->ulValueLen));
+                break;
+            }
         case CKA_ISSUER:
         case CKA_SUBJECT:
             if (attr->ulValueLen > 0 && attr->pValue) {
                 char *asciiName;
                 SECItem derName;
                 derName.type = siDERNameBuffer;
                 derName.data = attr->pValue;
                 derName.len = attr->ulValueLen;
--- a/lib/pk11wrap/pk11obj.c
+++ b/lib/pk11wrap/pk11obj.c
@@ -1825,25 +1825,34 @@ pk11_FindObjectByTemplate(PK11SlotInfo *
  * return all the object handles that matches the template
  */
 CK_OBJECT_HANDLE *
 pk11_FindObjectsByTemplate(PK11SlotInfo *slot, CK_ATTRIBUTE *findTemplate,
                            int templCount, int *object_count)
 {
     CK_OBJECT_HANDLE *objID = NULL;
     CK_ULONG returned_count = 0;
+    PRBool owner = PR_TRUE;
+    CK_SESSION_HANDLE session;
+    PRBool haslock = PR_FALSE;
     CK_RV crv = CKR_SESSION_HANDLE_INVALID;
 
-    PK11_EnterSlotMonitor(slot);
-    if (slot->session != CK_INVALID_SESSION) {
-        crv = PK11_GETTAB(slot)->C_FindObjectsInit(slot->session,
+    session = pk11_GetNewSession(slot, &owner);
+    haslock = (!owner || !(slot->isThreadSafe));
+    if (haslock) {
+        PK11_EnterSlotMonitor(slot);
+    }
+    if (session != CK_INVALID_SESSION) {
+        crv = PK11_GETTAB(slot)->C_FindObjectsInit(session,
                                                    findTemplate, templCount);
     }
     if (crv != CKR_OK) {
-        PK11_ExitSlotMonitor(slot);
+        if (haslock)
+            PK11_ExitSlotMonitor(slot);
+        pk11_CloseSession(slot, session, owner);
         PORT_SetError(PK11_MapError(crv));
         *object_count = -1;
         return NULL;
     }
 
     /*
      * collect all the Matching Objects
      */
@@ -1858,29 +1867,32 @@ pk11_FindObjectsByTemplate(PK11SlotInfo 
                                                      sizeof(CK_OBJECT_HANDLE) * (*object_count + PK11_SEARCH_CHUNKSIZE));
         }
 
         if (objID == NULL) {
             if (oldObjID)
                 PORT_Free(oldObjID);
             break;
         }
-        crv = PK11_GETTAB(slot)->C_FindObjects(slot->session,
+        crv = PK11_GETTAB(slot)->C_FindObjects(session,
                                                &objID[*object_count], PK11_SEARCH_CHUNKSIZE, &returned_count);
         if (crv != CKR_OK) {
             PORT_SetError(PK11_MapError(crv));
             PORT_Free(objID);
             objID = NULL;
             break;
         }
         *object_count += returned_count;
     } while (returned_count == PK11_SEARCH_CHUNKSIZE);
 
-    PK11_GETTAB(slot)->C_FindObjectsFinal(slot->session);
-    PK11_ExitSlotMonitor(slot);
+    PK11_GETTAB(slot)->C_FindObjectsFinal(session);
+    if (haslock) {
+        PK11_ExitSlotMonitor(slot);
+    }
+    pk11_CloseSession(slot, session, owner);
 
     if (objID && (*object_count == 0)) {
         PORT_Free(objID);
         return NULL;
     }
     if (objID == NULL)
         *object_count = -1;
     return objID;
--- a/lib/pk11wrap/pk11slot.c
+++ b/lib/pk11wrap/pk11slot.c
@@ -414,16 +414,18 @@ PK11_NewSlotInfo(SECMODModule *mod)
     slot->module = NULL;
     slot->authTransact = 0;
     slot->authTime = LL_ZERO;
     slot->minPassword = 0;
     slot->maxPassword = 0;
     slot->hasRootCerts = PR_FALSE;
     slot->hasRootTrust = PR_FALSE;
     slot->nssToken = NULL;
+    slot->profileList = NULL;
+    slot->profileCount = 0;
     return slot;
 }
 
 /* create a new reference to a slot so it doesn't go away */
 PK11SlotInfo *
 PK11_ReferenceSlot(PK11SlotInfo *slot)
 {
     PR_ATOMIC_INCREMENT(&slot->refCount);
@@ -441,16 +443,19 @@ PK11_DestroySlot(PK11SlotInfo *slot)
     if (slot->functionList) {
         PK11_GETTAB(slot)
             ->C_CloseAllSessions(slot->slotID);
     }
 
     if (slot->mechanismList) {
         PORT_Free(slot->mechanismList);
     }
+    if (slot->profileList) {
+        PORT_Free(slot->profileList);
+    }
     if (slot->isThreadSafe && slot->sessionLock) {
         PZ_DestroyLock(slot->sessionLock);
     }
     slot->sessionLock = NULL;
     if (slot->freeListLock) {
         PZ_DestroyLock(slot->freeListLock);
         slot->freeListLock = NULL;
     }
@@ -1165,16 +1170,86 @@ PK11_ReadMechanismList(PK11SlotInfo *slo
         CK_MECHANISM_TYPE mech = slot->mechanismList[i];
         if (mech < 0x7ff) {
             slot->mechanismBits[mech & 0xff] |= 1 << (mech >> 8);
         }
     }
     return SECSuccess;
 }
 
+static SECStatus
+pk11_ReadProfileList(PK11SlotInfo *slot)
+{
+    CK_ATTRIBUTE findTemp[2];
+    CK_ATTRIBUTE *attrs;
+    CK_BBOOL cktrue = CK_TRUE;
+    CK_OBJECT_CLASS oclass = CKO_PROFILE;
+    int tsize;
+    int objCount;
+    CK_OBJECT_HANDLE *handles = NULL;
+    int i;
+
+    attrs = findTemp;
+    PK11_SETATTRS(attrs, CKA_TOKEN, &cktrue, sizeof(cktrue));
+    attrs++;
+    PK11_SETATTRS(attrs, CKA_CLASS, &oclass, sizeof(oclass));
+    attrs++;
+    tsize = attrs - findTemp;
+    PORT_Assert(tsize <= sizeof(findTemp) / sizeof(CK_ATTRIBUTE));
+
+    if (slot->profileList) {
+        PORT_Free(slot->profileList);
+        slot->profileList = NULL;
+    }
+    slot->profileCount = 0;
+
+    objCount = 0;
+    handles = pk11_FindObjectsByTemplate(slot, findTemp, tsize, &objCount);
+    if (handles == NULL) {
+        if (objCount < 0) {
+            return SECFailure; /* error code is set */
+        }
+        PORT_Assert(objCount == 0);
+        return SECSuccess;
+    }
+
+    slot->profileList = (CK_PROFILE_ID *)
+        PORT_Alloc(objCount * sizeof(CK_PROFILE_ID));
+    if (slot->profileList == NULL) {
+        PORT_Free(handles);
+        return SECFailure; /* error code is set */
+    }
+
+    for (i = 0; i < objCount; i++) {
+        CK_ULONG value;
+
+        value = PK11_ReadULongAttribute(slot, handles[i], CKA_PROFILE_ID);
+        if (value == CK_UNAVAILABLE_INFORMATION) {
+            continue;
+        }
+        slot->profileList[slot->profileCount++] = value;
+    }
+
+    PORT_Free(handles);
+    return SECSuccess;
+}
+
+static PRBool
+pk11_HasProfile(PK11SlotInfo *slot, CK_PROFILE_ID id)
+{
+    int i;
+
+    for (i = 0; i < slot->profileCount; i++) {
+        if (slot->profileList[i] == id) {
+            return PR_TRUE;
+        }
+    }
+    return PR_FALSE;
+}
+
 /*
  * initialize a new token
  * unlike initialize slot, this can be called multiple times in the lifetime
  * of NSS. It reads the information associated with a card or token,
  * that is not going to change unless the card or token changes.
  */
 SECStatus
 PK11_InitToken(PK11SlotInfo *slot, PRBool loadCerts)
@@ -1286,16 +1361,21 @@ PK11_InitToken(PK11SlotInfo *slot, PRBoo
         if (!slot->isThreadSafe)
             PK11_ExitSlotMonitor(slot);
     }
 
     status = nssToken_Refresh(slot->nssToken);
     if (status != PR_SUCCESS)
         return SECFailure;
 
+    rv = pk11_ReadProfileList(slot);
+    if (rv != SECSuccess) {
+        return SECFailure;
+    }
+
     if (!(slot->isInternal) && (slot->hasRandom)) {
         /* if this slot has a random number generater, use it to add entropy
          * to the internal slot. */
         PK11SlotInfo *int_slot = PK11_GetInternalSlot();
 
         if (int_slot) {
             unsigned char random_bytes[32];
 
@@ -1690,16 +1770,17 @@ PK11_NeedLogin(PK11SlotInfo *slot)
     return slot->needLogin;
 }
 
 PRBool
 PK11_IsFriendly(PK11SlotInfo *slot)
 {
     /* internal slot always has public readable certs */
     return (PRBool)(slot->isInternal ||
+                    pk11_HasProfile(slot, CKP_PUBLIC_CERTIFICATES_TOKEN) ||
                     ((slot->defaultFlags & SECMOD_FRIENDLY_FLAG) ==
                      SECMOD_FRIENDLY_FLAG));
 }
 
 char *
 PK11_GetTokenName(PK11SlotInfo *slot)
 {
     return slot->token_name;
--- a/lib/pk11wrap/secmodti.h
+++ b/lib/pk11wrap/secmodti.h
@@ -106,16 +106,18 @@ struct PK11SlotInfoStr {
     PRIntervalTime lastLoginCheck;
     unsigned int lastState;
     /* for Stan */
     NSSToken *nssToken;
     /* the tokeninfo struct */
     CK_TOKEN_INFO tokenInfo;
     /* fast mechanism lookup */
     char mechanismBits[256];
+    CK_PROFILE_ID *profileList;
+    int profileCount;
 };
 
 /* Symetric Key structure. Reference Counted */
 struct PK11SymKeyStr {
     CK_MECHANISM_TYPE type;    /* type of operation this key was created for*/
     CK_OBJECT_HANDLE objectID; /* object id of this key in the slot */
     PK11SlotInfo *slot;        /* Slot this key is loaded into */
     void *cx;                  /* window context in case we need to loggin */
--- a/lib/util/pkcs11t.h
+++ b/lib/util/pkcs11t.h
@@ -308,28 +308,42 @@ typedef CK_OBJECT_HANDLE CK_PTR CK_OBJEC
 /* CK_OBJECT_CLASS was changed from CK_USHORT to CK_ULONG for
  * v2.0 */
 typedef CK_ULONG CK_OBJECT_CLASS;
 
 /* The following classes of objects are defined: */
 /* CKO_HW_FEATURE is new for v2.10 */
 /* CKO_DOMAIN_PARAMETERS is new for v2.11 */
 /* CKO_MECHANISM is new for v2.20 */
+/* CKO_PROFILE is new for v3.00 */
 #define CKO_DATA 0x00000000
 #define CKO_CERTIFICATE 0x00000001
 #define CKO_PUBLIC_KEY 0x00000002
 #define CKO_PRIVATE_KEY 0x00000003
 #define CKO_SECRET_KEY 0x00000004
 #define CKO_HW_FEATURE 0x00000005
 #define CKO_DOMAIN_PARAMETERS 0x00000006
 #define CKO_MECHANISM 0x00000007
+#define CKO_PROFILE 0x00000009
 #define CKO_VENDOR_DEFINED 0x80000000
 
 typedef CK_OBJECT_CLASS CK_PTR CK_OBJECT_CLASS_PTR;
 
+/* CK_PROFILE_ID is new for v3.00. CK_PROFILE_ID is a value that
+ * identifies the profile that the token supports. */
+typedef CK_ULONG CK_PROFILE_ID;
+
+/* Profile ID's */
+#define CKP_INVALID_ID 0x00000000UL
+#define CKP_BASELINE_PROVIDER 0x00000001UL
+#define CKP_EXTENDED_PROVIDER 0x00000002UL
+#define CKP_AUTHENTICATION_TOKEN 0x00000003UL
+#define CKP_PUBLIC_CERTIFICATES_TOKEN 0x00000004UL
+#define CKP_VENDOR_DEFINED 0x80000000UL
+
 /* CK_HW_FEATURE_TYPE is new for v2.10. CK_HW_FEATURE_TYPE is a
  * value that identifies the hardware feature type of an object
  * with CK_OBJECT_CLASS equal to CKO_HW_FEATURE. */
 typedef CK_ULONG CK_HW_FEATURE_TYPE;
 
 /* The following hardware feature types are defined */
 /* CKH_USER_INTERFACE is new for v2.20 */
 #define CKH_MONOTONIC_COUNTER 0x00000001
@@ -531,16 +545,17 @@ typedef CK_ULONG CK_ATTRIBUTE_TYPE;
 #define CKA_CHAR_SETS 0x00000480
 #define CKA_ENCODING_METHODS 0x00000481
 #define CKA_MIME_TYPES 0x00000482
 #define CKA_MECHANISM_TYPE 0x00000500
 #define CKA_REQUIRED_CMS_ATTRIBUTES 0x00000501
 #define CKA_DEFAULT_CMS_ATTRIBUTES 0x00000502
 #define CKA_SUPPORTED_CMS_ATTRIBUTES 0x00000503
 #define CKA_ALLOWED_MECHANISMS (CKF_ARRAY_ATTRIBUTE | 0x00000600)
+#define CKA_PROFILE_ID 0x00000601UL
 
 #define CKA_VENDOR_DEFINED 0x80000000
 
 /* CK_ATTRIBUTE is a structure that includes the type, length
  * and value of an attribute */
 typedef struct CK_ATTRIBUTE {
     CK_ATTRIBUTE_TYPE type;
     CK_VOID_PTR pValue;