andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public andre@0: * License, v. 2.0. If a copy of the MPL was not distributed with this andre@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ andre@0: andre@0: #include "pkcs11.h" andre@0: andre@0: #ifndef DEVM_H andre@0: #include "devm.h" andre@0: #endif /* DEVM_H */ andre@0: andre@0: #ifndef CKHELPER_H andre@0: #include "ckhelper.h" andre@0: #endif /* CKHELPER_H */ andre@0: andre@0: extern const NSSError NSS_ERROR_DEVICE_ERROR; andre@0: andre@0: static const CK_BBOOL s_true = CK_TRUE; andre@0: NSS_IMPLEMENT_DATA const NSSItem andre@0: g_ck_true = { (CK_VOID_PTR)&s_true, sizeof(s_true) }; andre@0: andre@0: static const CK_BBOOL s_false = CK_FALSE; andre@0: NSS_IMPLEMENT_DATA const NSSItem andre@0: g_ck_false = { (CK_VOID_PTR)&s_false, sizeof(s_false) }; andre@0: andre@0: static const CK_OBJECT_CLASS s_class_cert = CKO_CERTIFICATE; andre@0: NSS_IMPLEMENT_DATA const NSSItem andre@0: g_ck_class_cert = { (CK_VOID_PTR)&s_class_cert, sizeof(s_class_cert) }; andre@0: andre@0: static const CK_OBJECT_CLASS s_class_pubkey = CKO_PUBLIC_KEY; andre@0: NSS_IMPLEMENT_DATA const NSSItem andre@0: g_ck_class_pubkey = { (CK_VOID_PTR)&s_class_pubkey, sizeof(s_class_pubkey) }; andre@0: andre@0: static const CK_OBJECT_CLASS s_class_privkey = CKO_PRIVATE_KEY; andre@0: NSS_IMPLEMENT_DATA const NSSItem andre@0: g_ck_class_privkey = { (CK_VOID_PTR)&s_class_privkey, sizeof(s_class_privkey) }; andre@0: andre@0: static PRBool andre@0: is_string_attribute ( andre@0: CK_ATTRIBUTE_TYPE aType andre@0: ) andre@0: { andre@0: PRBool isString; andre@0: switch (aType) { andre@0: case CKA_LABEL: andre@0: case CKA_NSS_EMAIL: andre@0: isString = PR_TRUE; andre@0: break; andre@0: default: andre@0: isString = PR_FALSE; andre@0: break; andre@0: } andre@0: return isString; andre@0: } andre@0: andre@0: NSS_IMPLEMENT PRStatus andre@0: nssCKObject_GetAttributes ( andre@0: CK_OBJECT_HANDLE object, andre@0: CK_ATTRIBUTE_PTR obj_template, andre@0: CK_ULONG count, andre@0: NSSArena *arenaOpt, andre@0: nssSession *session, andre@0: NSSSlot *slot andre@0: ) andre@0: { andre@0: nssArenaMark *mark = NULL; andre@0: CK_SESSION_HANDLE hSession; andre@0: CK_ULONG i = 0; andre@0: CK_RV ckrv; andre@0: PRStatus nssrv; andre@0: PRBool alloced = PR_FALSE; andre@0: void *epv = nssSlot_GetCryptokiEPV(slot); andre@0: hSession = session->handle; andre@0: if (arenaOpt) { andre@0: mark = nssArena_Mark(arenaOpt); andre@0: if (!mark) { andre@0: goto loser; andre@0: } andre@0: } andre@0: nssSession_EnterMonitor(session); andre@0: /* XXX kinda hacky, if the storage size is already in the first template andre@0: * item, then skip the alloc portion andre@0: */ andre@0: if (obj_template[0].ulValueLen == 0) { andre@0: /* Get the storage size needed for each attribute */ andre@0: ckrv = CKAPI(epv)->C_GetAttributeValue(hSession, andre@0: object, obj_template, count); andre@0: if (ckrv != CKR_OK && andre@0: ckrv != CKR_ATTRIBUTE_TYPE_INVALID && andre@0: ckrv != CKR_ATTRIBUTE_SENSITIVE) andre@0: { andre@0: nssSession_ExitMonitor(session); andre@0: nss_SetError(NSS_ERROR_DEVICE_ERROR); andre@0: goto loser; andre@0: } andre@0: /* Allocate memory for each attribute. */ andre@0: for (i=0; iC_GetAttributeValue(hSession, andre@0: object, obj_template, count); andre@0: nssSession_ExitMonitor(session); andre@0: if (ckrv != CKR_OK && andre@0: ckrv != CKR_ATTRIBUTE_TYPE_INVALID && andre@0: ckrv != CKR_ATTRIBUTE_SENSITIVE) andre@0: { andre@0: nss_SetError(NSS_ERROR_DEVICE_ERROR); andre@0: goto loser; andre@0: } andre@0: if (alloced && arenaOpt) { andre@0: nssrv = nssArena_Unmark(arenaOpt, mark); andre@0: if (nssrv != PR_SUCCESS) { andre@0: goto loser; andre@0: } andre@0: } andre@0: andre@0: if (count > 1 && ((ckrv == CKR_ATTRIBUTE_TYPE_INVALID) || andre@0: (ckrv == CKR_ATTRIBUTE_SENSITIVE))) { andre@0: /* old tokens would keep the length of '0' and not deal with any andre@0: * of the attributes we passed. For those tokens read them one at andre@0: * a time */ andre@0: for (i=0; i < count; i++) { andre@0: if ((obj_template[i].ulValueLen == 0) andre@0: || (obj_template[i].ulValueLen == -1)) { andre@0: obj_template[i].ulValueLen=0; andre@0: (void) nssCKObject_GetAttributes(object,&obj_template[i], 1, andre@0: arenaOpt, session, slot); andre@0: } andre@0: } andre@0: } andre@0: return PR_SUCCESS; andre@0: loser: andre@0: if (alloced) { andre@0: if (arenaOpt) { andre@0: /* release all arena memory allocated before the failure. */ andre@0: (void)nssArena_Release(arenaOpt, mark); andre@0: } else { andre@0: CK_ULONG j; andre@0: /* free each heap object that was allocated before the failure. */ andre@0: for (j=0; jdata = (void *)attr.pValue; andre@0: rvItem->size = (PRUint32)attr.ulValueLen; andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: NSS_IMPLEMENT PRBool andre@0: nssCKObject_IsAttributeTrue ( andre@0: CK_OBJECT_HANDLE object, andre@0: CK_ATTRIBUTE_TYPE attribute, andre@0: nssSession *session, andre@0: NSSSlot *slot, andre@0: PRStatus *rvStatus andre@0: ) andre@0: { andre@0: CK_BBOOL bool; andre@0: CK_ATTRIBUTE_PTR attr; andre@0: CK_ATTRIBUTE atemplate = { 0, NULL, 0 }; andre@0: CK_RV ckrv; andre@0: void *epv = nssSlot_GetCryptokiEPV(slot); andre@0: attr = &atemplate; andre@0: NSS_CK_SET_ATTRIBUTE_VAR(attr, attribute, bool); andre@0: nssSession_EnterMonitor(session); andre@0: ckrv = CKAPI(epv)->C_GetAttributeValue(session->handle, object, andre@0: &atemplate, 1); andre@0: nssSession_ExitMonitor(session); andre@0: if (ckrv != CKR_OK) { andre@0: *rvStatus = PR_FAILURE; andre@0: return PR_FALSE; andre@0: } andre@0: *rvStatus = PR_SUCCESS; andre@0: return (PRBool)(bool == CK_TRUE); andre@0: } andre@0: andre@0: NSS_IMPLEMENT PRStatus andre@0: nssCKObject_SetAttributes ( andre@0: CK_OBJECT_HANDLE object, andre@0: CK_ATTRIBUTE_PTR obj_template, andre@0: CK_ULONG count, andre@0: nssSession *session, andre@0: NSSSlot *slot andre@0: ) andre@0: { andre@0: CK_RV ckrv; andre@0: void *epv = nssSlot_GetCryptokiEPV(slot); andre@0: nssSession_EnterMonitor(session); andre@0: ckrv = CKAPI(epv)->C_SetAttributeValue(session->handle, object, andre@0: obj_template, count); andre@0: nssSession_ExitMonitor(session); andre@0: if (ckrv == CKR_OK) { andre@0: return PR_SUCCESS; andre@0: } else { andre@0: return PR_FAILURE; andre@0: } andre@0: } andre@0: andre@0: NSS_IMPLEMENT PRBool andre@0: nssCKObject_IsTokenObjectTemplate ( andre@0: CK_ATTRIBUTE_PTR objectTemplate, andre@0: CK_ULONG otsize andre@0: ) andre@0: { andre@0: CK_ULONG ul; andre@0: for (ul=0; ulpValue) { andre@0: /* default to PKIX */ andre@0: return NSSCertificateType_PKIX; andre@0: } andre@0: ckCertType = *((CK_ULONG *)attrib->pValue); andre@0: switch (ckCertType) { andre@0: case CKC_X_509: andre@0: return NSSCertificateType_PKIX; andre@0: default: andre@0: break; andre@0: } andre@0: return NSSCertificateType_Unknown; andre@0: } andre@0: andre@0: /* incoming pointers must be valid */ andre@0: NSS_IMPLEMENT PRStatus andre@0: nssCryptokiCertificate_GetAttributes ( andre@0: nssCryptokiObject *certObject, andre@0: nssSession *sessionOpt, andre@0: NSSArena *arenaOpt, andre@0: NSSCertificateType *certTypeOpt, andre@0: NSSItem *idOpt, andre@0: NSSDER *encodingOpt, andre@0: NSSDER *issuerOpt, andre@0: NSSDER *serialOpt, andre@0: NSSDER *subjectOpt andre@0: ) andre@0: { andre@0: PRStatus status; andre@0: PRUint32 i; andre@0: nssSession *session; andre@0: NSSSlot *slot; andre@0: CK_ULONG template_size; andre@0: CK_ATTRIBUTE_PTR attr; andre@0: CK_ATTRIBUTE cert_template[6]; andre@0: /* Set up a template of all options chosen by caller */ andre@0: NSS_CK_TEMPLATE_START(cert_template, attr, template_size); andre@0: if (certTypeOpt) { andre@0: NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_CERTIFICATE_TYPE); andre@0: } andre@0: if (idOpt) { andre@0: NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_ID); andre@0: } andre@0: if (encodingOpt) { andre@0: NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_VALUE); andre@0: } andre@0: if (issuerOpt) { andre@0: NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_ISSUER); andre@0: } andre@0: if (serialOpt) { andre@0: NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_SERIAL_NUMBER); andre@0: } andre@0: if (subjectOpt) { andre@0: NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_SUBJECT); andre@0: } andre@0: NSS_CK_TEMPLATE_FINISH(cert_template, attr, template_size); andre@0: if (template_size == 0) { andre@0: /* caller didn't want anything */ andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: status = nssToken_GetCachedObjectAttributes(certObject->token, arenaOpt, andre@0: certObject, CKO_CERTIFICATE, andre@0: cert_template, template_size); andre@0: if (status != PR_SUCCESS) { andre@0: andre@0: session = sessionOpt ? andre@0: sessionOpt : andre@0: nssToken_GetDefaultSession(certObject->token); andre@0: if (!session) { andre@0: nss_SetError(NSS_ERROR_INVALID_ARGUMENT); andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: slot = nssToken_GetSlot(certObject->token); andre@0: status = nssCKObject_GetAttributes(certObject->handle, andre@0: cert_template, template_size, andre@0: arenaOpt, session, slot); andre@0: nssSlot_Destroy(slot); andre@0: if (status != PR_SUCCESS) { andre@0: return status; andre@0: } andre@0: } andre@0: andre@0: i=0; andre@0: if (certTypeOpt) { andre@0: *certTypeOpt = nss_cert_type_from_ck_attrib(&cert_template[i]); i++; andre@0: } andre@0: if (idOpt) { andre@0: NSS_CK_ATTRIBUTE_TO_ITEM(&cert_template[i], idOpt); i++; andre@0: } andre@0: if (encodingOpt) { andre@0: NSS_CK_ATTRIBUTE_TO_ITEM(&cert_template[i], encodingOpt); i++; andre@0: } andre@0: if (issuerOpt) { andre@0: NSS_CK_ATTRIBUTE_TO_ITEM(&cert_template[i], issuerOpt); i++; andre@0: } andre@0: if (serialOpt) { andre@0: NSS_CK_ATTRIBUTE_TO_ITEM(&cert_template[i], serialOpt); i++; andre@0: } andre@0: if (subjectOpt) { andre@0: NSS_CK_ATTRIBUTE_TO_ITEM(&cert_template[i], subjectOpt); i++; andre@0: } andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: static nssTrustLevel andre@0: get_nss_trust ( andre@0: CK_TRUST ckt andre@0: ) andre@0: { andre@0: nssTrustLevel t; andre@0: switch (ckt) { andre@0: case CKT_NSS_NOT_TRUSTED: t = nssTrustLevel_NotTrusted; break; andre@0: case CKT_NSS_TRUSTED_DELEGATOR: t = nssTrustLevel_TrustedDelegator; andre@0: break; andre@0: case CKT_NSS_VALID_DELEGATOR: t = nssTrustLevel_ValidDelegator; break; andre@0: case CKT_NSS_TRUSTED: t = nssTrustLevel_Trusted; break; andre@0: case CKT_NSS_MUST_VERIFY_TRUST: t = nssTrustLevel_MustVerify; break; andre@0: case CKT_NSS_TRUST_UNKNOWN: andre@0: default: andre@0: t = nssTrustLevel_Unknown; break; andre@0: } andre@0: return t; andre@0: } andre@0: andre@0: NSS_IMPLEMENT PRStatus andre@0: nssCryptokiTrust_GetAttributes ( andre@0: nssCryptokiObject *trustObject, andre@0: nssSession *sessionOpt, andre@0: NSSItem *sha1_hash, andre@0: nssTrustLevel *serverAuth, andre@0: nssTrustLevel *clientAuth, andre@0: nssTrustLevel *codeSigning, andre@0: nssTrustLevel *emailProtection, andre@0: PRBool *stepUpApproved andre@0: ) andre@0: { andre@0: PRStatus status; andre@0: NSSSlot *slot; andre@0: nssSession *session; andre@0: CK_BBOOL isToken = PR_FALSE; andre@0: CK_BBOOL stepUp = PR_FALSE; andre@0: CK_TRUST saTrust = CKT_NSS_TRUST_UNKNOWN; andre@0: CK_TRUST caTrust = CKT_NSS_TRUST_UNKNOWN; andre@0: CK_TRUST epTrust = CKT_NSS_TRUST_UNKNOWN; andre@0: CK_TRUST csTrust = CKT_NSS_TRUST_UNKNOWN; andre@0: CK_ATTRIBUTE_PTR attr; andre@0: CK_ATTRIBUTE trust_template[7]; andre@0: CK_ATTRIBUTE_PTR sha1_hash_attr; andre@0: CK_ULONG trust_size; andre@0: andre@0: /* Use the trust object to find the trust settings */ andre@0: NSS_CK_TEMPLATE_START(trust_template, attr, trust_size); andre@0: NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TOKEN, isToken); andre@0: NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TRUST_SERVER_AUTH, saTrust); andre@0: NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TRUST_CLIENT_AUTH, caTrust); andre@0: NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TRUST_EMAIL_PROTECTION, epTrust); andre@0: NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TRUST_CODE_SIGNING, csTrust); andre@0: NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TRUST_STEP_UP_APPROVED, stepUp); andre@0: sha1_hash_attr = attr; andre@0: NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CERT_SHA1_HASH, sha1_hash); andre@0: NSS_CK_TEMPLATE_FINISH(trust_template, attr, trust_size); andre@0: andre@0: status = nssToken_GetCachedObjectAttributes(trustObject->token, NULL, andre@0: trustObject, andre@0: CKO_NSS_TRUST, andre@0: trust_template, trust_size); andre@0: if (status != PR_SUCCESS) { andre@0: session = sessionOpt ? andre@0: sessionOpt : andre@0: nssToken_GetDefaultSession(trustObject->token); andre@0: if (!session) { andre@0: nss_SetError(NSS_ERROR_INVALID_ARGUMENT); andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: slot = nssToken_GetSlot(trustObject->token); andre@0: status = nssCKObject_GetAttributes(trustObject->handle, andre@0: trust_template, trust_size, andre@0: NULL, session, slot); andre@0: nssSlot_Destroy(slot); andre@0: if (status != PR_SUCCESS) { andre@0: return status; andre@0: } andre@0: } andre@0: andre@0: if (sha1_hash_attr->ulValueLen == -1) { andre@0: /* The trust object does not have the CKA_CERT_SHA1_HASH attribute. */ andre@0: sha1_hash_attr->ulValueLen = 0; andre@0: } andre@0: sha1_hash->size = sha1_hash_attr->ulValueLen; andre@0: *serverAuth = get_nss_trust(saTrust); andre@0: *clientAuth = get_nss_trust(caTrust); andre@0: *emailProtection = get_nss_trust(epTrust); andre@0: *codeSigning = get_nss_trust(csTrust); andre@0: *stepUpApproved = stepUp; andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: NSS_IMPLEMENT PRStatus andre@0: nssCryptokiCRL_GetAttributes ( andre@0: nssCryptokiObject *crlObject, andre@0: nssSession *sessionOpt, andre@0: NSSArena *arenaOpt, andre@0: NSSItem *encodingOpt, andre@0: NSSItem *subjectOpt, andre@0: CK_ULONG* crl_class, andre@0: NSSUTF8 **urlOpt, andre@0: PRBool *isKRLOpt andre@0: ) andre@0: { andre@0: PRStatus status; andre@0: NSSSlot *slot; andre@0: nssSession *session; andre@0: CK_ATTRIBUTE_PTR attr; andre@0: CK_ATTRIBUTE crl_template[7]; andre@0: CK_ULONG crl_size; andre@0: PRUint32 i; andre@0: andre@0: NSS_CK_TEMPLATE_START(crl_template, attr, crl_size); andre@0: if (crl_class) { andre@0: NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_CLASS); andre@0: } andre@0: if (encodingOpt) { andre@0: NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_VALUE); andre@0: } andre@0: if (urlOpt) { andre@0: NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_NSS_URL); andre@0: } andre@0: if (isKRLOpt) { andre@0: NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_NSS_KRL); andre@0: } andre@0: if (subjectOpt) { andre@0: NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_SUBJECT); andre@0: } andre@0: NSS_CK_TEMPLATE_FINISH(crl_template, attr, crl_size); andre@0: andre@0: status = nssToken_GetCachedObjectAttributes(crlObject->token, NULL, andre@0: crlObject, andre@0: CKO_NSS_CRL, andre@0: crl_template, crl_size); andre@0: if (status != PR_SUCCESS) { andre@0: session = sessionOpt ? andre@0: sessionOpt : andre@0: nssToken_GetDefaultSession(crlObject->token); andre@0: if (session == NULL) { andre@0: nss_SetError(NSS_ERROR_INVALID_ARGUMENT); andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: slot = nssToken_GetSlot(crlObject->token); andre@0: status = nssCKObject_GetAttributes(crlObject->handle, andre@0: crl_template, crl_size, andre@0: arenaOpt, session, slot); andre@0: nssSlot_Destroy(slot); andre@0: if (status != PR_SUCCESS) { andre@0: return status; andre@0: } andre@0: } andre@0: andre@0: i=0; andre@0: if (crl_class) { andre@0: NSS_CK_ATTRIBUTE_TO_ULONG(&crl_template[i], *crl_class); i++; andre@0: } andre@0: if (encodingOpt) { andre@0: NSS_CK_ATTRIBUTE_TO_ITEM(&crl_template[i], encodingOpt); i++; andre@0: } andre@0: if (urlOpt) { andre@0: NSS_CK_ATTRIBUTE_TO_UTF8(&crl_template[i], *urlOpt); i++; andre@0: } andre@0: if (isKRLOpt) { andre@0: NSS_CK_ATTRIBUTE_TO_BOOL(&crl_template[i], *isKRLOpt); i++; andre@0: } andre@0: if (subjectOpt) { andre@0: NSS_CK_ATTRIBUTE_TO_ITEM(&crl_template[i], subjectOpt); i++; andre@0: } andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: NSS_IMPLEMENT PRStatus andre@0: nssCryptokiPrivateKey_SetCertificate ( andre@0: nssCryptokiObject *keyObject, andre@0: nssSession *sessionOpt, andre@0: const NSSUTF8 *nickname, andre@0: NSSItem *id, andre@0: NSSDER *subject andre@0: ) andre@0: { andre@0: CK_RV ckrv; andre@0: CK_ATTRIBUTE_PTR attr; andre@0: CK_ATTRIBUTE key_template[3]; andre@0: CK_ULONG key_size; andre@0: void *epv = nssToken_GetCryptokiEPV(keyObject->token); andre@0: nssSession *session; andre@0: NSSToken *token = keyObject->token; andre@0: nssSession *defaultSession = nssToken_GetDefaultSession(token); andre@0: PRBool createdSession = PR_FALSE; andre@0: andre@0: NSS_CK_TEMPLATE_START(key_template, attr, key_size); andre@0: NSS_CK_SET_ATTRIBUTE_UTF8(attr, CKA_LABEL, nickname); andre@0: NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ID, id); andre@0: NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SUBJECT, subject); andre@0: NSS_CK_TEMPLATE_FINISH(key_template, attr, key_size); andre@0: andre@0: if (sessionOpt) { andre@0: if (!nssSession_IsReadWrite(sessionOpt)) { andre@0: return PR_FAILURE; andre@0: } andre@0: session = sessionOpt; andre@0: } else if (defaultSession && nssSession_IsReadWrite(defaultSession)) { andre@0: session = defaultSession; andre@0: } else { andre@0: NSSSlot *slot = nssToken_GetSlot(token); andre@0: session = nssSlot_CreateSession(token->slot, NULL, PR_TRUE); andre@0: nssSlot_Destroy(slot); andre@0: if (!session) { andre@0: return PR_FAILURE; andre@0: } andre@0: createdSession = PR_TRUE; andre@0: } andre@0: andre@0: ckrv = CKAPI(epv)->C_SetAttributeValue(session->handle, andre@0: keyObject->handle, andre@0: key_template, andre@0: key_size); andre@0: andre@0: if (createdSession) { andre@0: nssSession_Destroy(session); andre@0: } andre@0: andre@0: return (ckrv == CKR_OK) ? PR_SUCCESS : PR_FAILURE; andre@0: } andre@0: