Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | /* |
michael@0 | 5 | * This file manages PKCS #11 instances of certificates. |
michael@0 | 6 | */ |
michael@0 | 7 | |
michael@0 | 8 | #include "secport.h" |
michael@0 | 9 | #include "seccomon.h" |
michael@0 | 10 | #include "secmod.h" |
michael@0 | 11 | #include "secmodi.h" |
michael@0 | 12 | #include "secmodti.h" |
michael@0 | 13 | #include "pkcs11.h" |
michael@0 | 14 | #include "pk11func.h" |
michael@0 | 15 | #include "cert.h" |
michael@0 | 16 | #include "certi.h" |
michael@0 | 17 | #include "secitem.h" |
michael@0 | 18 | #include "key.h" |
michael@0 | 19 | #include "secoid.h" |
michael@0 | 20 | #include "pkcs7t.h" |
michael@0 | 21 | #include "cmsreclist.h" |
michael@0 | 22 | |
michael@0 | 23 | #include "certdb.h" |
michael@0 | 24 | #include "secerr.h" |
michael@0 | 25 | #include "sslerr.h" |
michael@0 | 26 | |
michael@0 | 27 | #include "pki3hack.h" |
michael@0 | 28 | #include "dev3hack.h" |
michael@0 | 29 | |
michael@0 | 30 | #include "devm.h" |
michael@0 | 31 | #include "nsspki.h" |
michael@0 | 32 | #include "pki.h" |
michael@0 | 33 | #include "pkim.h" |
michael@0 | 34 | #include "pkitm.h" |
michael@0 | 35 | #include "pkistore.h" /* to remove temp cert */ |
michael@0 | 36 | #include "devt.h" |
michael@0 | 37 | |
michael@0 | 38 | extern const NSSError NSS_ERROR_NOT_FOUND; |
michael@0 | 39 | extern const NSSError NSS_ERROR_INVALID_CERTIFICATE; |
michael@0 | 40 | |
michael@0 | 41 | struct nss3_cert_cbstr { |
michael@0 | 42 | SECStatus(* callback)(CERTCertificate*, void *); |
michael@0 | 43 | nssList *cached; |
michael@0 | 44 | void *arg; |
michael@0 | 45 | }; |
michael@0 | 46 | |
michael@0 | 47 | /* Translate from NSSCertificate to CERTCertificate, then pass the latter |
michael@0 | 48 | * to a callback. |
michael@0 | 49 | */ |
michael@0 | 50 | static PRStatus convert_cert(NSSCertificate *c, void *arg) |
michael@0 | 51 | { |
michael@0 | 52 | CERTCertificate *nss3cert; |
michael@0 | 53 | SECStatus secrv; |
michael@0 | 54 | struct nss3_cert_cbstr *nss3cb = (struct nss3_cert_cbstr *)arg; |
michael@0 | 55 | /* 'c' is not adopted. caller will free it */ |
michael@0 | 56 | nss3cert = STAN_GetCERTCertificate(c); |
michael@0 | 57 | if (!nss3cert) return PR_FAILURE; |
michael@0 | 58 | secrv = (*nss3cb->callback)(nss3cert, nss3cb->arg); |
michael@0 | 59 | return (secrv) ? PR_FAILURE : PR_SUCCESS; |
michael@0 | 60 | } |
michael@0 | 61 | |
michael@0 | 62 | /* |
michael@0 | 63 | * build a cert nickname based on the token name and the label of the |
michael@0 | 64 | * certificate If the label in NULL, build a label based on the ID. |
michael@0 | 65 | */ |
michael@0 | 66 | static int toHex(int x) { return (x < 10) ? (x+'0') : (x+'a'-10); } |
michael@0 | 67 | #define MAX_CERT_ID 4 |
michael@0 | 68 | #define DEFAULT_STRING "Cert ID " |
michael@0 | 69 | static char * |
michael@0 | 70 | pk11_buildNickname(PK11SlotInfo *slot,CK_ATTRIBUTE *cert_label, |
michael@0 | 71 | CK_ATTRIBUTE *key_label, CK_ATTRIBUTE *cert_id) |
michael@0 | 72 | { |
michael@0 | 73 | int prefixLen = PORT_Strlen(slot->token_name); |
michael@0 | 74 | int suffixLen = 0; |
michael@0 | 75 | char *suffix = NULL; |
michael@0 | 76 | char buildNew[sizeof(DEFAULT_STRING)+MAX_CERT_ID*2]; |
michael@0 | 77 | char *next,*nickname; |
michael@0 | 78 | |
michael@0 | 79 | if (cert_label && (cert_label->ulValueLen)) { |
michael@0 | 80 | suffixLen = cert_label->ulValueLen; |
michael@0 | 81 | suffix = (char*)cert_label->pValue; |
michael@0 | 82 | } else if (key_label && (key_label->ulValueLen)) { |
michael@0 | 83 | suffixLen = key_label->ulValueLen; |
michael@0 | 84 | suffix = (char*)key_label->pValue; |
michael@0 | 85 | } else if (cert_id && cert_id->ulValueLen > 0) { |
michael@0 | 86 | int i,first = cert_id->ulValueLen - MAX_CERT_ID; |
michael@0 | 87 | int offset = sizeof(DEFAULT_STRING); |
michael@0 | 88 | char *idValue = (char *)cert_id->pValue; |
michael@0 | 89 | |
michael@0 | 90 | PORT_Memcpy(buildNew,DEFAULT_STRING,sizeof(DEFAULT_STRING)-1); |
michael@0 | 91 | next = buildNew + offset; |
michael@0 | 92 | if (first < 0) first = 0; |
michael@0 | 93 | for (i=first; i < (int) cert_id->ulValueLen; i++) { |
michael@0 | 94 | *next++ = toHex((idValue[i] >> 4) & 0xf); |
michael@0 | 95 | *next++ = toHex(idValue[i] & 0xf); |
michael@0 | 96 | } |
michael@0 | 97 | *next++ = 0; |
michael@0 | 98 | suffix = buildNew; |
michael@0 | 99 | suffixLen = PORT_Strlen(buildNew); |
michael@0 | 100 | } else { |
michael@0 | 101 | PORT_SetError( SEC_ERROR_LIBRARY_FAILURE ); |
michael@0 | 102 | return NULL; |
michael@0 | 103 | } |
michael@0 | 104 | |
michael@0 | 105 | /* if is internal key slot, add code to skip the prefix!! */ |
michael@0 | 106 | next = nickname = (char *)PORT_Alloc(prefixLen+1+suffixLen+1); |
michael@0 | 107 | if (nickname == NULL) return NULL; |
michael@0 | 108 | |
michael@0 | 109 | PORT_Memcpy(next,slot->token_name,prefixLen); |
michael@0 | 110 | next += prefixLen; |
michael@0 | 111 | *next++ = ':'; |
michael@0 | 112 | PORT_Memcpy(next,suffix,suffixLen); |
michael@0 | 113 | next += suffixLen; |
michael@0 | 114 | *next++ = 0; |
michael@0 | 115 | return nickname; |
michael@0 | 116 | } |
michael@0 | 117 | |
michael@0 | 118 | PRBool |
michael@0 | 119 | PK11_IsUserCert(PK11SlotInfo *slot, CERTCertificate *cert, |
michael@0 | 120 | CK_OBJECT_HANDLE certID) |
michael@0 | 121 | { |
michael@0 | 122 | CK_OBJECT_CLASS theClass; |
michael@0 | 123 | |
michael@0 | 124 | if (slot == NULL) return PR_FALSE; |
michael@0 | 125 | if (cert == NULL) return PR_FALSE; |
michael@0 | 126 | |
michael@0 | 127 | theClass = CKO_PRIVATE_KEY; |
michael@0 | 128 | if (pk11_LoginStillRequired(slot,NULL)) { |
michael@0 | 129 | theClass = CKO_PUBLIC_KEY; |
michael@0 | 130 | } |
michael@0 | 131 | if (PK11_MatchItem(slot, certID , theClass) != CK_INVALID_HANDLE) { |
michael@0 | 132 | return PR_TRUE; |
michael@0 | 133 | } |
michael@0 | 134 | |
michael@0 | 135 | if (theClass == CKO_PUBLIC_KEY) { |
michael@0 | 136 | SECKEYPublicKey *pubKey= CERT_ExtractPublicKey(cert); |
michael@0 | 137 | CK_ATTRIBUTE theTemplate; |
michael@0 | 138 | |
michael@0 | 139 | if (pubKey == NULL) { |
michael@0 | 140 | return PR_FALSE; |
michael@0 | 141 | } |
michael@0 | 142 | |
michael@0 | 143 | PK11_SETATTRS(&theTemplate,0,NULL,0); |
michael@0 | 144 | switch (pubKey->keyType) { |
michael@0 | 145 | case rsaKey: |
michael@0 | 146 | PK11_SETATTRS(&theTemplate,CKA_MODULUS, pubKey->u.rsa.modulus.data, |
michael@0 | 147 | pubKey->u.rsa.modulus.len); |
michael@0 | 148 | break; |
michael@0 | 149 | case dsaKey: |
michael@0 | 150 | PK11_SETATTRS(&theTemplate,CKA_VALUE, pubKey->u.dsa.publicValue.data, |
michael@0 | 151 | pubKey->u.dsa.publicValue.len); |
michael@0 | 152 | break; |
michael@0 | 153 | case dhKey: |
michael@0 | 154 | PK11_SETATTRS(&theTemplate,CKA_VALUE, pubKey->u.dh.publicValue.data, |
michael@0 | 155 | pubKey->u.dh.publicValue.len); |
michael@0 | 156 | break; |
michael@0 | 157 | case ecKey: |
michael@0 | 158 | PK11_SETATTRS(&theTemplate,CKA_EC_POINT, |
michael@0 | 159 | pubKey->u.ec.publicValue.data, |
michael@0 | 160 | pubKey->u.ec.publicValue.len); |
michael@0 | 161 | break; |
michael@0 | 162 | case keaKey: |
michael@0 | 163 | case fortezzaKey: |
michael@0 | 164 | case nullKey: |
michael@0 | 165 | /* fall through and return false */ |
michael@0 | 166 | break; |
michael@0 | 167 | } |
michael@0 | 168 | |
michael@0 | 169 | if (theTemplate.ulValueLen == 0) { |
michael@0 | 170 | SECKEY_DestroyPublicKey(pubKey); |
michael@0 | 171 | return PR_FALSE; |
michael@0 | 172 | } |
michael@0 | 173 | pk11_SignedToUnsigned(&theTemplate); |
michael@0 | 174 | if (pk11_FindObjectByTemplate(slot,&theTemplate,1) != CK_INVALID_HANDLE) { |
michael@0 | 175 | SECKEY_DestroyPublicKey(pubKey); |
michael@0 | 176 | return PR_TRUE; |
michael@0 | 177 | } |
michael@0 | 178 | SECKEY_DestroyPublicKey(pubKey); |
michael@0 | 179 | } |
michael@0 | 180 | return PR_FALSE; |
michael@0 | 181 | } |
michael@0 | 182 | |
michael@0 | 183 | /* |
michael@0 | 184 | * Check out if a cert has ID of zero. This is a magic ID that tells |
michael@0 | 185 | * NSS that this cert may be an automagically trusted cert. |
michael@0 | 186 | * The Cert has to be self signed as well. That check is done elsewhere. |
michael@0 | 187 | * |
michael@0 | 188 | */ |
michael@0 | 189 | PRBool |
michael@0 | 190 | pk11_isID0(PK11SlotInfo *slot, CK_OBJECT_HANDLE certID) |
michael@0 | 191 | { |
michael@0 | 192 | CK_ATTRIBUTE keyID = {CKA_ID, NULL, 0}; |
michael@0 | 193 | PRBool isZero = PR_FALSE; |
michael@0 | 194 | int i; |
michael@0 | 195 | CK_RV crv; |
michael@0 | 196 | |
michael@0 | 197 | |
michael@0 | 198 | crv = PK11_GetAttributes(NULL,slot,certID,&keyID,1); |
michael@0 | 199 | if (crv != CKR_OK) { |
michael@0 | 200 | return isZero; |
michael@0 | 201 | } |
michael@0 | 202 | |
michael@0 | 203 | if (keyID.ulValueLen != 0) { |
michael@0 | 204 | char *value = (char *)keyID.pValue; |
michael@0 | 205 | isZero = PR_TRUE; /* ID exists, may be zero */ |
michael@0 | 206 | for (i=0; i < (int) keyID.ulValueLen; i++) { |
michael@0 | 207 | if (value[i] != 0) { |
michael@0 | 208 | isZero = PR_FALSE; /* nope */ |
michael@0 | 209 | break; |
michael@0 | 210 | } |
michael@0 | 211 | } |
michael@0 | 212 | } |
michael@0 | 213 | PORT_Free(keyID.pValue); |
michael@0 | 214 | return isZero; |
michael@0 | 215 | |
michael@0 | 216 | } |
michael@0 | 217 | |
michael@0 | 218 | /* |
michael@0 | 219 | * Create an NSSCertificate from a slot/certID pair, return it as a |
michael@0 | 220 | * CERTCertificate. Optionally, output the nickname string. |
michael@0 | 221 | */ |
michael@0 | 222 | static CERTCertificate * |
michael@0 | 223 | pk11_fastCert(PK11SlotInfo *slot, CK_OBJECT_HANDLE certID, |
michael@0 | 224 | CK_ATTRIBUTE *privateLabel, char **nickptr) |
michael@0 | 225 | { |
michael@0 | 226 | NSSCertificate *c; |
michael@0 | 227 | nssCryptokiObject *co = NULL; |
michael@0 | 228 | nssPKIObject *pkio; |
michael@0 | 229 | NSSToken *token; |
michael@0 | 230 | NSSTrustDomain *td = STAN_GetDefaultTrustDomain(); |
michael@0 | 231 | PRStatus status; |
michael@0 | 232 | |
michael@0 | 233 | /* Get the cryptoki object from the handle */ |
michael@0 | 234 | token = PK11Slot_GetNSSToken(slot); |
michael@0 | 235 | if (token->defaultSession) { |
michael@0 | 236 | co = nssCryptokiObject_Create(token, token->defaultSession, certID); |
michael@0 | 237 | } else { |
michael@0 | 238 | PORT_SetError(SEC_ERROR_NO_TOKEN); |
michael@0 | 239 | } |
michael@0 | 240 | if (!co) { |
michael@0 | 241 | return NULL; |
michael@0 | 242 | } |
michael@0 | 243 | |
michael@0 | 244 | /* Create a PKI object from the cryptoki instance */ |
michael@0 | 245 | pkio = nssPKIObject_Create(NULL, co, td, NULL, nssPKIMonitor); |
michael@0 | 246 | if (!pkio) { |
michael@0 | 247 | nssCryptokiObject_Destroy(co); |
michael@0 | 248 | return NULL; |
michael@0 | 249 | } |
michael@0 | 250 | |
michael@0 | 251 | /* Create a certificate */ |
michael@0 | 252 | c = nssCertificate_Create(pkio); |
michael@0 | 253 | if (!c) { |
michael@0 | 254 | nssPKIObject_Destroy(pkio); |
michael@0 | 255 | return NULL; |
michael@0 | 256 | } |
michael@0 | 257 | |
michael@0 | 258 | /* Build and output a nickname, if desired. |
michael@0 | 259 | * This must be done before calling nssTrustDomain_AddCertsToCache |
michael@0 | 260 | * because that function may destroy c, pkio and co! |
michael@0 | 261 | */ |
michael@0 | 262 | if ((nickptr) && (co->label)) { |
michael@0 | 263 | CK_ATTRIBUTE label, id; |
michael@0 | 264 | |
michael@0 | 265 | label.type = CKA_LABEL; |
michael@0 | 266 | label.pValue = co->label; |
michael@0 | 267 | label.ulValueLen = PORT_Strlen(co->label); |
michael@0 | 268 | |
michael@0 | 269 | id.type = CKA_ID; |
michael@0 | 270 | id.pValue = c->id.data; |
michael@0 | 271 | id.ulValueLen = c->id.size; |
michael@0 | 272 | |
michael@0 | 273 | *nickptr = pk11_buildNickname(slot, &label, privateLabel, &id); |
michael@0 | 274 | } |
michael@0 | 275 | |
michael@0 | 276 | /* This function may destroy the cert in "c" and all its subordinate |
michael@0 | 277 | * structures, and replace the value in "c" with the address of a |
michael@0 | 278 | * different NSSCertificate that it found in the cache. |
michael@0 | 279 | * Presumably, the nickname which we just output above remains valid. :) |
michael@0 | 280 | */ |
michael@0 | 281 | status = nssTrustDomain_AddCertsToCache(td, &c, 1); |
michael@0 | 282 | return STAN_GetCERTCertificateOrRelease(c); |
michael@0 | 283 | } |
michael@0 | 284 | |
michael@0 | 285 | /* |
michael@0 | 286 | * Build an CERTCertificate structure from a PKCS#11 object ID.... certID |
michael@0 | 287 | * Must be a CertObject. This code does not explicitly checks that. |
michael@0 | 288 | */ |
michael@0 | 289 | CERTCertificate * |
michael@0 | 290 | PK11_MakeCertFromHandle(PK11SlotInfo *slot,CK_OBJECT_HANDLE certID, |
michael@0 | 291 | CK_ATTRIBUTE *privateLabel) |
michael@0 | 292 | { |
michael@0 | 293 | char * nickname = NULL; |
michael@0 | 294 | CERTCertificate *cert = NULL; |
michael@0 | 295 | CERTCertTrust *trust; |
michael@0 | 296 | PRBool isFortezzaRootCA = PR_FALSE; |
michael@0 | 297 | PRBool swapNickname = PR_FALSE; |
michael@0 | 298 | |
michael@0 | 299 | cert = pk11_fastCert(slot,certID,privateLabel, &nickname); |
michael@0 | 300 | if (cert == NULL) |
michael@0 | 301 | goto loser; |
michael@0 | 302 | |
michael@0 | 303 | if (nickname) { |
michael@0 | 304 | if (cert->nickname != NULL) { |
michael@0 | 305 | cert->dbnickname = cert->nickname; |
michael@0 | 306 | } |
michael@0 | 307 | cert->nickname = PORT_ArenaStrdup(cert->arena,nickname); |
michael@0 | 308 | PORT_Free(nickname); |
michael@0 | 309 | nickname = NULL; |
michael@0 | 310 | swapNickname = PR_TRUE; |
michael@0 | 311 | } |
michael@0 | 312 | |
michael@0 | 313 | /* remember where this cert came from.... If we have just looked |
michael@0 | 314 | * it up from the database and it already has a slot, don't add a new |
michael@0 | 315 | * one. */ |
michael@0 | 316 | if (cert->slot == NULL) { |
michael@0 | 317 | cert->slot = PK11_ReferenceSlot(slot); |
michael@0 | 318 | cert->pkcs11ID = certID; |
michael@0 | 319 | cert->ownSlot = PR_TRUE; |
michael@0 | 320 | cert->series = slot->series; |
michael@0 | 321 | } |
michael@0 | 322 | |
michael@0 | 323 | trust = (CERTCertTrust*)PORT_ArenaAlloc(cert->arena, sizeof(CERTCertTrust)); |
michael@0 | 324 | if (trust == NULL) |
michael@0 | 325 | goto loser; |
michael@0 | 326 | PORT_Memset(trust,0, sizeof(CERTCertTrust)); |
michael@0 | 327 | |
michael@0 | 328 | if(! pk11_HandleTrustObject(slot, cert, trust) ) { |
michael@0 | 329 | unsigned int type; |
michael@0 | 330 | |
michael@0 | 331 | /* build some cert trust flags */ |
michael@0 | 332 | if (CERT_IsCACert(cert, &type)) { |
michael@0 | 333 | unsigned int trustflags = CERTDB_VALID_CA; |
michael@0 | 334 | |
michael@0 | 335 | /* Allow PKCS #11 modules to give us trusted CA's. We only accept |
michael@0 | 336 | * valid CA's which are self-signed here. They must have an object |
michael@0 | 337 | * ID of '0'. */ |
michael@0 | 338 | if (pk11_isID0(slot,certID) && |
michael@0 | 339 | cert->isRoot) { |
michael@0 | 340 | trustflags |= CERTDB_TRUSTED_CA; |
michael@0 | 341 | /* is the slot a fortezza card? allow the user or |
michael@0 | 342 | * admin to turn on objectSigning, but don't turn |
michael@0 | 343 | * full trust on explicitly */ |
michael@0 | 344 | if (PK11_DoesMechanism(slot,CKM_KEA_KEY_DERIVE)) { |
michael@0 | 345 | trust->objectSigningFlags |= CERTDB_VALID_CA; |
michael@0 | 346 | isFortezzaRootCA = PR_TRUE; |
michael@0 | 347 | } |
michael@0 | 348 | } |
michael@0 | 349 | if ((type & NS_CERT_TYPE_SSL_CA) == NS_CERT_TYPE_SSL_CA) { |
michael@0 | 350 | trust->sslFlags |= trustflags; |
michael@0 | 351 | } |
michael@0 | 352 | if ((type & NS_CERT_TYPE_EMAIL_CA) == NS_CERT_TYPE_EMAIL_CA) { |
michael@0 | 353 | trust->emailFlags |= trustflags; |
michael@0 | 354 | } |
michael@0 | 355 | if ((type & NS_CERT_TYPE_OBJECT_SIGNING_CA) |
michael@0 | 356 | == NS_CERT_TYPE_OBJECT_SIGNING_CA) { |
michael@0 | 357 | trust->objectSigningFlags |= trustflags; |
michael@0 | 358 | } |
michael@0 | 359 | } |
michael@0 | 360 | } |
michael@0 | 361 | |
michael@0 | 362 | if (PK11_IsUserCert(slot,cert,certID)) { |
michael@0 | 363 | trust->sslFlags |= CERTDB_USER; |
michael@0 | 364 | trust->emailFlags |= CERTDB_USER; |
michael@0 | 365 | /* trust->objectSigningFlags |= CERTDB_USER; */ |
michael@0 | 366 | } |
michael@0 | 367 | CERT_LockCertTrust(cert); |
michael@0 | 368 | cert->trust = trust; |
michael@0 | 369 | CERT_UnlockCertTrust(cert); |
michael@0 | 370 | |
michael@0 | 371 | return cert; |
michael@0 | 372 | |
michael@0 | 373 | loser: |
michael@0 | 374 | if (nickname) |
michael@0 | 375 | PORT_Free(nickname); |
michael@0 | 376 | if (cert) |
michael@0 | 377 | CERT_DestroyCertificate(cert); |
michael@0 | 378 | return NULL; |
michael@0 | 379 | } |
michael@0 | 380 | |
michael@0 | 381 | |
michael@0 | 382 | /* |
michael@0 | 383 | * Build get a certificate from a private key |
michael@0 | 384 | */ |
michael@0 | 385 | CERTCertificate * |
michael@0 | 386 | PK11_GetCertFromPrivateKey(SECKEYPrivateKey *privKey) |
michael@0 | 387 | { |
michael@0 | 388 | PK11SlotInfo *slot = privKey->pkcs11Slot; |
michael@0 | 389 | CK_OBJECT_HANDLE handle = privKey->pkcs11ID; |
michael@0 | 390 | CK_OBJECT_HANDLE certID = |
michael@0 | 391 | PK11_MatchItem(slot,handle,CKO_CERTIFICATE); |
michael@0 | 392 | CERTCertificate *cert; |
michael@0 | 393 | |
michael@0 | 394 | if (certID == CK_INVALID_HANDLE) { |
michael@0 | 395 | PORT_SetError(SSL_ERROR_NO_CERTIFICATE); |
michael@0 | 396 | return NULL; |
michael@0 | 397 | } |
michael@0 | 398 | cert = PK11_MakeCertFromHandle(slot,certID,NULL); |
michael@0 | 399 | return (cert); |
michael@0 | 400 | |
michael@0 | 401 | } |
michael@0 | 402 | |
michael@0 | 403 | /* |
michael@0 | 404 | * delete a cert and it's private key (if no other certs are pointing to the |
michael@0 | 405 | * private key. |
michael@0 | 406 | */ |
michael@0 | 407 | SECStatus |
michael@0 | 408 | PK11_DeleteTokenCertAndKey(CERTCertificate *cert,void *wincx) |
michael@0 | 409 | { |
michael@0 | 410 | SECKEYPrivateKey *privKey = PK11_FindKeyByAnyCert(cert,wincx); |
michael@0 | 411 | CK_OBJECT_HANDLE pubKey; |
michael@0 | 412 | PK11SlotInfo *slot = NULL; |
michael@0 | 413 | |
michael@0 | 414 | pubKey = pk11_FindPubKeyByAnyCert(cert, &slot, wincx); |
michael@0 | 415 | if (privKey) { |
michael@0 | 416 | /* For 3.4, utilize the generic cert delete function */ |
michael@0 | 417 | SEC_DeletePermCertificate(cert); |
michael@0 | 418 | PK11_DeleteTokenPrivateKey(privKey, PR_FALSE); |
michael@0 | 419 | } |
michael@0 | 420 | if ((pubKey != CK_INVALID_HANDLE) && (slot != NULL)) { |
michael@0 | 421 | PK11_DestroyTokenObject(slot,pubKey); |
michael@0 | 422 | PK11_FreeSlot(slot); |
michael@0 | 423 | } |
michael@0 | 424 | return SECSuccess; |
michael@0 | 425 | } |
michael@0 | 426 | |
michael@0 | 427 | /* |
michael@0 | 428 | * cert callback structure |
michael@0 | 429 | */ |
michael@0 | 430 | typedef struct pk11DoCertCallbackStr { |
michael@0 | 431 | SECStatus(* callback)(PK11SlotInfo *slot, CERTCertificate*, void *); |
michael@0 | 432 | SECStatus(* noslotcallback)(CERTCertificate*, void *); |
michael@0 | 433 | SECStatus(* itemcallback)(CERTCertificate*, SECItem *, void *); |
michael@0 | 434 | void *callbackArg; |
michael@0 | 435 | } pk11DoCertCallback; |
michael@0 | 436 | |
michael@0 | 437 | |
michael@0 | 438 | typedef struct pk11CertCallbackStr { |
michael@0 | 439 | SECStatus(* callback)(CERTCertificate*,SECItem *,void *); |
michael@0 | 440 | void *callbackArg; |
michael@0 | 441 | } pk11CertCallback; |
michael@0 | 442 | |
michael@0 | 443 | struct fake_der_cb_argstr |
michael@0 | 444 | { |
michael@0 | 445 | SECStatus(* callback)(CERTCertificate*, SECItem *, void *); |
michael@0 | 446 | void *arg; |
michael@0 | 447 | }; |
michael@0 | 448 | |
michael@0 | 449 | static SECStatus fake_der_cb(CERTCertificate *c, void *a) |
michael@0 | 450 | { |
michael@0 | 451 | struct fake_der_cb_argstr *fda = (struct fake_der_cb_argstr *)a; |
michael@0 | 452 | return (*fda->callback)(c, &c->derCert, fda->arg); |
michael@0 | 453 | } |
michael@0 | 454 | |
michael@0 | 455 | /* |
michael@0 | 456 | * Extract all the certs on a card from a slot. |
michael@0 | 457 | */ |
michael@0 | 458 | SECStatus |
michael@0 | 459 | PK11_TraverseSlotCerts(SECStatus(* callback)(CERTCertificate*,SECItem *,void *), |
michael@0 | 460 | void *arg, void *wincx) |
michael@0 | 461 | { |
michael@0 | 462 | NSSTrustDomain *defaultTD = STAN_GetDefaultTrustDomain(); |
michael@0 | 463 | struct fake_der_cb_argstr fda; |
michael@0 | 464 | struct nss3_cert_cbstr pk11cb; |
michael@0 | 465 | |
michael@0 | 466 | /* authenticate to the tokens first */ |
michael@0 | 467 | (void) pk11_TraverseAllSlots( NULL, NULL, PR_TRUE, wincx); |
michael@0 | 468 | |
michael@0 | 469 | fda.callback = callback; |
michael@0 | 470 | fda.arg = arg; |
michael@0 | 471 | pk11cb.callback = fake_der_cb; |
michael@0 | 472 | pk11cb.arg = &fda; |
michael@0 | 473 | NSSTrustDomain_TraverseCertificates(defaultTD, convert_cert, &pk11cb); |
michael@0 | 474 | return SECSuccess; |
michael@0 | 475 | } |
michael@0 | 476 | |
michael@0 | 477 | static void |
michael@0 | 478 | transfer_token_certs_to_collection(nssList *certList, NSSToken *token, |
michael@0 | 479 | nssPKIObjectCollection *collection) |
michael@0 | 480 | { |
michael@0 | 481 | NSSCertificate **certs; |
michael@0 | 482 | PRUint32 i, count; |
michael@0 | 483 | NSSToken **tokens, **tp; |
michael@0 | 484 | count = nssList_Count(certList); |
michael@0 | 485 | if (count == 0) { |
michael@0 | 486 | return; |
michael@0 | 487 | } |
michael@0 | 488 | certs = nss_ZNEWARRAY(NULL, NSSCertificate *, count); |
michael@0 | 489 | if (!certs) { |
michael@0 | 490 | return; |
michael@0 | 491 | } |
michael@0 | 492 | nssList_GetArray(certList, (void **)certs, count); |
michael@0 | 493 | for (i=0; i<count; i++) { |
michael@0 | 494 | tokens = nssPKIObject_GetTokens(&certs[i]->object, NULL); |
michael@0 | 495 | if (tokens) { |
michael@0 | 496 | for (tp = tokens; *tp; tp++) { |
michael@0 | 497 | if (*tp == token) { |
michael@0 | 498 | nssPKIObjectCollection_AddObject(collection, |
michael@0 | 499 | (nssPKIObject *)certs[i]); |
michael@0 | 500 | } |
michael@0 | 501 | } |
michael@0 | 502 | nssTokenArray_Destroy(tokens); |
michael@0 | 503 | } |
michael@0 | 504 | CERT_DestroyCertificate(STAN_GetCERTCertificateOrRelease(certs[i])); |
michael@0 | 505 | } |
michael@0 | 506 | nss_ZFreeIf(certs); |
michael@0 | 507 | } |
michael@0 | 508 | |
michael@0 | 509 | CERTCertificate * |
michael@0 | 510 | PK11_FindCertFromNickname(const char *nickname, void *wincx) |
michael@0 | 511 | { |
michael@0 | 512 | PRStatus status; |
michael@0 | 513 | CERTCertificate *rvCert = NULL; |
michael@0 | 514 | NSSCertificate *cert = NULL; |
michael@0 | 515 | NSSCertificate **certs = NULL; |
michael@0 | 516 | static const NSSUsage usage = {PR_TRUE /* ... */ }; |
michael@0 | 517 | NSSToken *token; |
michael@0 | 518 | NSSTrustDomain *defaultTD = STAN_GetDefaultTrustDomain(); |
michael@0 | 519 | PK11SlotInfo *slot = NULL; |
michael@0 | 520 | SECStatus rv; |
michael@0 | 521 | char *nickCopy; |
michael@0 | 522 | char *delimit = NULL; |
michael@0 | 523 | char *tokenName; |
michael@0 | 524 | |
michael@0 | 525 | nickCopy = PORT_Strdup(nickname); |
michael@0 | 526 | if (!nickCopy) { |
michael@0 | 527 | /* error code is set */ |
michael@0 | 528 | return NULL; |
michael@0 | 529 | } |
michael@0 | 530 | if ((delimit = PORT_Strchr(nickCopy,':')) != NULL) { |
michael@0 | 531 | tokenName = nickCopy; |
michael@0 | 532 | nickname = delimit + 1; |
michael@0 | 533 | *delimit = '\0'; |
michael@0 | 534 | /* find token by name */ |
michael@0 | 535 | token = NSSTrustDomain_FindTokenByName(defaultTD, (NSSUTF8 *)tokenName); |
michael@0 | 536 | if (token) { |
michael@0 | 537 | slot = PK11_ReferenceSlot(token->pk11slot); |
michael@0 | 538 | } else { |
michael@0 | 539 | PORT_SetError(SEC_ERROR_NO_TOKEN); |
michael@0 | 540 | } |
michael@0 | 541 | *delimit = ':'; |
michael@0 | 542 | } else { |
michael@0 | 543 | slot = PK11_GetInternalKeySlot(); |
michael@0 | 544 | token = PK11Slot_GetNSSToken(slot); |
michael@0 | 545 | } |
michael@0 | 546 | if (token) { |
michael@0 | 547 | nssList *certList; |
michael@0 | 548 | nssCryptokiObject **instances; |
michael@0 | 549 | nssPKIObjectCollection *collection; |
michael@0 | 550 | nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly; |
michael@0 | 551 | if (!PK11_IsPresent(slot)) { |
michael@0 | 552 | goto loser; |
michael@0 | 553 | } |
michael@0 | 554 | rv = pk11_AuthenticateUnfriendly(slot, PR_TRUE, wincx); |
michael@0 | 555 | if (rv != SECSuccess) { |
michael@0 | 556 | goto loser; |
michael@0 | 557 | } |
michael@0 | 558 | collection = nssCertificateCollection_Create(defaultTD, NULL); |
michael@0 | 559 | if (!collection) { |
michael@0 | 560 | goto loser; |
michael@0 | 561 | } |
michael@0 | 562 | certList = nssList_Create(NULL, PR_FALSE); |
michael@0 | 563 | if (!certList) { |
michael@0 | 564 | nssPKIObjectCollection_Destroy(collection); |
michael@0 | 565 | goto loser; |
michael@0 | 566 | } |
michael@0 | 567 | (void)nssTrustDomain_GetCertsForNicknameFromCache(defaultTD, |
michael@0 | 568 | nickname, |
michael@0 | 569 | certList); |
michael@0 | 570 | transfer_token_certs_to_collection(certList, token, collection); |
michael@0 | 571 | instances = nssToken_FindCertificatesByNickname(token, |
michael@0 | 572 | NULL, |
michael@0 | 573 | nickname, |
michael@0 | 574 | tokenOnly, |
michael@0 | 575 | 0, |
michael@0 | 576 | &status); |
michael@0 | 577 | nssPKIObjectCollection_AddInstances(collection, instances, 0); |
michael@0 | 578 | nss_ZFreeIf(instances); |
michael@0 | 579 | /* if it wasn't found, repeat the process for email address */ |
michael@0 | 580 | if (nssPKIObjectCollection_Count(collection) == 0 && |
michael@0 | 581 | PORT_Strchr(nickname, '@') != NULL) |
michael@0 | 582 | { |
michael@0 | 583 | char* lowercaseName = CERT_FixupEmailAddr(nickname); |
michael@0 | 584 | if (lowercaseName) { |
michael@0 | 585 | (void)nssTrustDomain_GetCertsForEmailAddressFromCache(defaultTD, |
michael@0 | 586 | lowercaseName, |
michael@0 | 587 | certList); |
michael@0 | 588 | transfer_token_certs_to_collection(certList, token, collection); |
michael@0 | 589 | instances = nssToken_FindCertificatesByEmail(token, |
michael@0 | 590 | NULL, |
michael@0 | 591 | lowercaseName, |
michael@0 | 592 | tokenOnly, |
michael@0 | 593 | 0, |
michael@0 | 594 | &status); |
michael@0 | 595 | nssPKIObjectCollection_AddInstances(collection, instances, 0); |
michael@0 | 596 | nss_ZFreeIf(instances); |
michael@0 | 597 | PORT_Free(lowercaseName); |
michael@0 | 598 | } |
michael@0 | 599 | } |
michael@0 | 600 | certs = nssPKIObjectCollection_GetCertificates(collection, |
michael@0 | 601 | NULL, 0, NULL); |
michael@0 | 602 | nssPKIObjectCollection_Destroy(collection); |
michael@0 | 603 | if (certs) { |
michael@0 | 604 | cert = nssCertificateArray_FindBestCertificate(certs, NULL, |
michael@0 | 605 | &usage, NULL); |
michael@0 | 606 | if (cert) { |
michael@0 | 607 | rvCert = STAN_GetCERTCertificateOrRelease(cert); |
michael@0 | 608 | } |
michael@0 | 609 | nssCertificateArray_Destroy(certs); |
michael@0 | 610 | } |
michael@0 | 611 | nssList_Destroy(certList); |
michael@0 | 612 | } |
michael@0 | 613 | if (slot) { |
michael@0 | 614 | PK11_FreeSlot(slot); |
michael@0 | 615 | } |
michael@0 | 616 | if (nickCopy) PORT_Free(nickCopy); |
michael@0 | 617 | return rvCert; |
michael@0 | 618 | loser: |
michael@0 | 619 | if (slot) { |
michael@0 | 620 | PK11_FreeSlot(slot); |
michael@0 | 621 | } |
michael@0 | 622 | if (nickCopy) PORT_Free(nickCopy); |
michael@0 | 623 | return NULL; |
michael@0 | 624 | } |
michael@0 | 625 | |
michael@0 | 626 | /* Traverse slots callback */ |
michael@0 | 627 | typedef struct FindCertsEmailArgStr { |
michael@0 | 628 | char *email; |
michael@0 | 629 | CERTCertList *certList; |
michael@0 | 630 | } FindCertsEmailArg; |
michael@0 | 631 | |
michael@0 | 632 | SECStatus |
michael@0 | 633 | FindCertsEmailCallback(CERTCertificate *cert, SECItem *item, void *arg) |
michael@0 | 634 | { |
michael@0 | 635 | FindCertsEmailArg *cbparam = (FindCertsEmailArg *) arg; |
michael@0 | 636 | const char *cert_email = CERT_GetFirstEmailAddress(cert); |
michael@0 | 637 | PRBool found = PR_FALSE; |
michael@0 | 638 | |
michael@0 | 639 | /* Email address present in certificate? */ |
michael@0 | 640 | if (cert_email == NULL){ |
michael@0 | 641 | return SECSuccess; |
michael@0 | 642 | } |
michael@0 | 643 | |
michael@0 | 644 | /* Parameter correctly set? */ |
michael@0 | 645 | if (cbparam->email == NULL) { |
michael@0 | 646 | return SECFailure; |
michael@0 | 647 | } |
michael@0 | 648 | |
michael@0 | 649 | /* Loop over all email addresses */ |
michael@0 | 650 | do { |
michael@0 | 651 | if (!strcmp(cert_email, cbparam->email)) { |
michael@0 | 652 | /* found one matching email address */ |
michael@0 | 653 | PRTime now = PR_Now(); |
michael@0 | 654 | found = PR_TRUE; |
michael@0 | 655 | CERT_AddCertToListSorted(cbparam->certList, |
michael@0 | 656 | CERT_DupCertificate(cert), |
michael@0 | 657 | CERT_SortCBValidity, &now); |
michael@0 | 658 | } |
michael@0 | 659 | cert_email = CERT_GetNextEmailAddress(cert, cert_email); |
michael@0 | 660 | } while (cert_email && !found); |
michael@0 | 661 | |
michael@0 | 662 | return SECSuccess; |
michael@0 | 663 | } |
michael@0 | 664 | |
michael@0 | 665 | /* Find all certificates with matching email address */ |
michael@0 | 666 | CERTCertList * |
michael@0 | 667 | PK11_FindCertsFromEmailAddress(const char *email, void *wincx) |
michael@0 | 668 | { |
michael@0 | 669 | FindCertsEmailArg cbparam; |
michael@0 | 670 | SECStatus rv; |
michael@0 | 671 | |
michael@0 | 672 | cbparam.certList = CERT_NewCertList(); |
michael@0 | 673 | if (cbparam.certList == NULL) { |
michael@0 | 674 | return NULL; |
michael@0 | 675 | } |
michael@0 | 676 | |
michael@0 | 677 | cbparam.email = CERT_FixupEmailAddr(email); |
michael@0 | 678 | if (cbparam.email == NULL) { |
michael@0 | 679 | CERT_DestroyCertList(cbparam.certList); |
michael@0 | 680 | return NULL; |
michael@0 | 681 | } |
michael@0 | 682 | |
michael@0 | 683 | rv = PK11_TraverseSlotCerts(FindCertsEmailCallback, &cbparam, NULL); |
michael@0 | 684 | if (rv != SECSuccess) { |
michael@0 | 685 | CERT_DestroyCertList(cbparam.certList); |
michael@0 | 686 | PORT_Free(cbparam.email); |
michael@0 | 687 | return NULL; |
michael@0 | 688 | } |
michael@0 | 689 | |
michael@0 | 690 | /* empty list? */ |
michael@0 | 691 | if (CERT_LIST_HEAD(cbparam.certList) == NULL || |
michael@0 | 692 | CERT_LIST_END(CERT_LIST_HEAD(cbparam.certList), cbparam.certList)) { |
michael@0 | 693 | CERT_DestroyCertList(cbparam.certList); |
michael@0 | 694 | cbparam.certList = NULL; |
michael@0 | 695 | } |
michael@0 | 696 | |
michael@0 | 697 | PORT_Free(cbparam.email); |
michael@0 | 698 | return cbparam.certList; |
michael@0 | 699 | } |
michael@0 | 700 | |
michael@0 | 701 | |
michael@0 | 702 | CERTCertList * |
michael@0 | 703 | PK11_FindCertsFromNickname(const char *nickname, void *wincx) |
michael@0 | 704 | { |
michael@0 | 705 | char *nickCopy; |
michael@0 | 706 | char *delimit = NULL; |
michael@0 | 707 | char *tokenName; |
michael@0 | 708 | int i; |
michael@0 | 709 | CERTCertList *certList = NULL; |
michael@0 | 710 | nssPKIObjectCollection *collection = NULL; |
michael@0 | 711 | NSSCertificate **foundCerts = NULL; |
michael@0 | 712 | NSSTrustDomain *defaultTD = STAN_GetDefaultTrustDomain(); |
michael@0 | 713 | NSSCertificate *c; |
michael@0 | 714 | NSSToken *token; |
michael@0 | 715 | PK11SlotInfo *slot; |
michael@0 | 716 | SECStatus rv; |
michael@0 | 717 | |
michael@0 | 718 | nickCopy = PORT_Strdup(nickname); |
michael@0 | 719 | if (!nickCopy) { |
michael@0 | 720 | /* error code is set */ |
michael@0 | 721 | return NULL; |
michael@0 | 722 | } |
michael@0 | 723 | if ((delimit = PORT_Strchr(nickCopy,':')) != NULL) { |
michael@0 | 724 | tokenName = nickCopy; |
michael@0 | 725 | nickname = delimit + 1; |
michael@0 | 726 | *delimit = '\0'; |
michael@0 | 727 | /* find token by name */ |
michael@0 | 728 | token = NSSTrustDomain_FindTokenByName(defaultTD, (NSSUTF8 *)tokenName); |
michael@0 | 729 | if (token) { |
michael@0 | 730 | slot = PK11_ReferenceSlot(token->pk11slot); |
michael@0 | 731 | } else { |
michael@0 | 732 | PORT_SetError(SEC_ERROR_NO_TOKEN); |
michael@0 | 733 | slot = NULL; |
michael@0 | 734 | } |
michael@0 | 735 | *delimit = ':'; |
michael@0 | 736 | } else { |
michael@0 | 737 | slot = PK11_GetInternalKeySlot(); |
michael@0 | 738 | token = PK11Slot_GetNSSToken(slot); |
michael@0 | 739 | } |
michael@0 | 740 | if (token) { |
michael@0 | 741 | PRStatus status; |
michael@0 | 742 | nssList *nameList; |
michael@0 | 743 | nssCryptokiObject **instances; |
michael@0 | 744 | nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly; |
michael@0 | 745 | rv = pk11_AuthenticateUnfriendly(slot, PR_TRUE, wincx); |
michael@0 | 746 | if (rv != SECSuccess) { |
michael@0 | 747 | PK11_FreeSlot(slot); |
michael@0 | 748 | if (nickCopy) PORT_Free(nickCopy); |
michael@0 | 749 | return NULL; |
michael@0 | 750 | } |
michael@0 | 751 | collection = nssCertificateCollection_Create(defaultTD, NULL); |
michael@0 | 752 | if (!collection) { |
michael@0 | 753 | PK11_FreeSlot(slot); |
michael@0 | 754 | if (nickCopy) PORT_Free(nickCopy); |
michael@0 | 755 | return NULL; |
michael@0 | 756 | } |
michael@0 | 757 | nameList = nssList_Create(NULL, PR_FALSE); |
michael@0 | 758 | if (!nameList) { |
michael@0 | 759 | PK11_FreeSlot(slot); |
michael@0 | 760 | if (nickCopy) PORT_Free(nickCopy); |
michael@0 | 761 | return NULL; |
michael@0 | 762 | } |
michael@0 | 763 | (void)nssTrustDomain_GetCertsForNicknameFromCache(defaultTD, |
michael@0 | 764 | nickname, |
michael@0 | 765 | nameList); |
michael@0 | 766 | transfer_token_certs_to_collection(nameList, token, collection); |
michael@0 | 767 | instances = nssToken_FindCertificatesByNickname(token, |
michael@0 | 768 | NULL, |
michael@0 | 769 | nickname, |
michael@0 | 770 | tokenOnly, |
michael@0 | 771 | 0, |
michael@0 | 772 | &status); |
michael@0 | 773 | nssPKIObjectCollection_AddInstances(collection, instances, 0); |
michael@0 | 774 | nss_ZFreeIf(instances); |
michael@0 | 775 | |
michael@0 | 776 | /* if it wasn't found, repeat the process for email address */ |
michael@0 | 777 | if (nssPKIObjectCollection_Count(collection) == 0 && |
michael@0 | 778 | PORT_Strchr(nickname, '@') != NULL) |
michael@0 | 779 | { |
michael@0 | 780 | char* lowercaseName = CERT_FixupEmailAddr(nickname); |
michael@0 | 781 | if (lowercaseName) { |
michael@0 | 782 | (void)nssTrustDomain_GetCertsForEmailAddressFromCache(defaultTD, |
michael@0 | 783 | lowercaseName, |
michael@0 | 784 | nameList); |
michael@0 | 785 | transfer_token_certs_to_collection(nameList, token, collection); |
michael@0 | 786 | instances = nssToken_FindCertificatesByEmail(token, |
michael@0 | 787 | NULL, |
michael@0 | 788 | lowercaseName, |
michael@0 | 789 | tokenOnly, |
michael@0 | 790 | 0, |
michael@0 | 791 | &status); |
michael@0 | 792 | nssPKIObjectCollection_AddInstances(collection, instances, 0); |
michael@0 | 793 | nss_ZFreeIf(instances); |
michael@0 | 794 | PORT_Free(lowercaseName); |
michael@0 | 795 | } |
michael@0 | 796 | } |
michael@0 | 797 | |
michael@0 | 798 | nssList_Destroy(nameList); |
michael@0 | 799 | foundCerts = nssPKIObjectCollection_GetCertificates(collection, |
michael@0 | 800 | NULL, 0, NULL); |
michael@0 | 801 | nssPKIObjectCollection_Destroy(collection); |
michael@0 | 802 | } |
michael@0 | 803 | if (slot) { |
michael@0 | 804 | PK11_FreeSlot(slot); |
michael@0 | 805 | } |
michael@0 | 806 | if (nickCopy) PORT_Free(nickCopy); |
michael@0 | 807 | if (foundCerts) { |
michael@0 | 808 | PRTime now = PR_Now(); |
michael@0 | 809 | certList = CERT_NewCertList(); |
michael@0 | 810 | for (i=0, c = *foundCerts; c; c = foundCerts[++i]) { |
michael@0 | 811 | if (certList) { |
michael@0 | 812 | CERTCertificate *certCert = STAN_GetCERTCertificateOrRelease(c); |
michael@0 | 813 | /* c may be invalid after this, don't reference it */ |
michael@0 | 814 | if (certCert) { |
michael@0 | 815 | /* CERT_AddCertToListSorted adopts certCert */ |
michael@0 | 816 | CERT_AddCertToListSorted(certList, certCert, |
michael@0 | 817 | CERT_SortCBValidity, &now); |
michael@0 | 818 | } |
michael@0 | 819 | } else { |
michael@0 | 820 | nssCertificate_Destroy(c); |
michael@0 | 821 | } |
michael@0 | 822 | } |
michael@0 | 823 | if (certList && CERT_LIST_HEAD(certList) == NULL) { |
michael@0 | 824 | CERT_DestroyCertList(certList); |
michael@0 | 825 | certList = NULL; |
michael@0 | 826 | } |
michael@0 | 827 | /* all the certs have been adopted or freed, free the raw array */ |
michael@0 | 828 | nss_ZFreeIf(foundCerts); |
michael@0 | 829 | } |
michael@0 | 830 | return certList; |
michael@0 | 831 | } |
michael@0 | 832 | |
michael@0 | 833 | /* |
michael@0 | 834 | * extract a key ID for a certificate... |
michael@0 | 835 | * NOTE: We call this function from PKCS11.c If we ever use |
michael@0 | 836 | * pkcs11 to extract the public key (we currently do not), this will break. |
michael@0 | 837 | */ |
michael@0 | 838 | SECItem * |
michael@0 | 839 | PK11_GetPubIndexKeyID(CERTCertificate *cert) |
michael@0 | 840 | { |
michael@0 | 841 | SECKEYPublicKey *pubk; |
michael@0 | 842 | SECItem *newItem = NULL; |
michael@0 | 843 | |
michael@0 | 844 | pubk = CERT_ExtractPublicKey(cert); |
michael@0 | 845 | if (pubk == NULL) return NULL; |
michael@0 | 846 | |
michael@0 | 847 | switch (pubk->keyType) { |
michael@0 | 848 | case rsaKey: |
michael@0 | 849 | newItem = SECITEM_DupItem(&pubk->u.rsa.modulus); |
michael@0 | 850 | break; |
michael@0 | 851 | case dsaKey: |
michael@0 | 852 | newItem = SECITEM_DupItem(&pubk->u.dsa.publicValue); |
michael@0 | 853 | break; |
michael@0 | 854 | case dhKey: |
michael@0 | 855 | newItem = SECITEM_DupItem(&pubk->u.dh.publicValue); |
michael@0 | 856 | break; |
michael@0 | 857 | case ecKey: |
michael@0 | 858 | newItem = SECITEM_DupItem(&pubk->u.ec.publicValue); |
michael@0 | 859 | break; |
michael@0 | 860 | case fortezzaKey: |
michael@0 | 861 | default: |
michael@0 | 862 | newItem = NULL; /* Fortezza Fix later... */ |
michael@0 | 863 | } |
michael@0 | 864 | SECKEY_DestroyPublicKey(pubk); |
michael@0 | 865 | /* make hash of it */ |
michael@0 | 866 | return newItem; |
michael@0 | 867 | } |
michael@0 | 868 | |
michael@0 | 869 | /* |
michael@0 | 870 | * generate a CKA_ID from a certificate. |
michael@0 | 871 | */ |
michael@0 | 872 | SECItem * |
michael@0 | 873 | pk11_mkcertKeyID(CERTCertificate *cert) |
michael@0 | 874 | { |
michael@0 | 875 | SECItem *pubKeyData = PK11_GetPubIndexKeyID(cert) ; |
michael@0 | 876 | SECItem *certCKA_ID; |
michael@0 | 877 | |
michael@0 | 878 | if (pubKeyData == NULL) return NULL; |
michael@0 | 879 | |
michael@0 | 880 | certCKA_ID = PK11_MakeIDFromPubKey(pubKeyData); |
michael@0 | 881 | SECITEM_FreeItem(pubKeyData,PR_TRUE); |
michael@0 | 882 | return certCKA_ID; |
michael@0 | 883 | } |
michael@0 | 884 | |
michael@0 | 885 | /* |
michael@0 | 886 | * Write the cert into the token. |
michael@0 | 887 | */ |
michael@0 | 888 | SECStatus |
michael@0 | 889 | PK11_ImportCert(PK11SlotInfo *slot, CERTCertificate *cert, |
michael@0 | 890 | CK_OBJECT_HANDLE key, const char *nickname, |
michael@0 | 891 | PRBool includeTrust) |
michael@0 | 892 | { |
michael@0 | 893 | PRStatus status; |
michael@0 | 894 | NSSCertificate *c; |
michael@0 | 895 | nssCryptokiObject *keyobj, *certobj; |
michael@0 | 896 | NSSToken *token = PK11Slot_GetNSSToken(slot); |
michael@0 | 897 | SECItem *keyID = pk11_mkcertKeyID(cert); |
michael@0 | 898 | char *emailAddr = NULL; |
michael@0 | 899 | nssCertificateStoreTrace lockTrace = {NULL, NULL, PR_FALSE, PR_FALSE}; |
michael@0 | 900 | nssCertificateStoreTrace unlockTrace = {NULL, NULL, PR_FALSE, PR_FALSE}; |
michael@0 | 901 | |
michael@0 | 902 | if (keyID == NULL) { |
michael@0 | 903 | goto loser; /* error code should be set already */ |
michael@0 | 904 | } |
michael@0 | 905 | if (!token) { |
michael@0 | 906 | PORT_SetError(SEC_ERROR_NO_TOKEN); |
michael@0 | 907 | goto loser; |
michael@0 | 908 | } |
michael@0 | 909 | |
michael@0 | 910 | if (PK11_IsInternal(slot) && cert->emailAddr && cert->emailAddr[0]) { |
michael@0 | 911 | emailAddr = cert->emailAddr; |
michael@0 | 912 | } |
michael@0 | 913 | |
michael@0 | 914 | /* need to get the cert as a stan cert */ |
michael@0 | 915 | if (cert->nssCertificate) { |
michael@0 | 916 | c = cert->nssCertificate; |
michael@0 | 917 | } else { |
michael@0 | 918 | c = STAN_GetNSSCertificate(cert); |
michael@0 | 919 | if (c == NULL) { |
michael@0 | 920 | goto loser; |
michael@0 | 921 | } |
michael@0 | 922 | } |
michael@0 | 923 | |
michael@0 | 924 | /* set the id for the cert */ |
michael@0 | 925 | nssItem_Create(c->object.arena, &c->id, keyID->len, keyID->data); |
michael@0 | 926 | if (!c->id.data) { |
michael@0 | 927 | goto loser; |
michael@0 | 928 | } |
michael@0 | 929 | |
michael@0 | 930 | if (key != CK_INVALID_HANDLE) { |
michael@0 | 931 | /* create an object for the key, ... */ |
michael@0 | 932 | keyobj = nss_ZNEW(NULL, nssCryptokiObject); |
michael@0 | 933 | if (!keyobj) { |
michael@0 | 934 | goto loser; |
michael@0 | 935 | } |
michael@0 | 936 | keyobj->token = nssToken_AddRef(token); |
michael@0 | 937 | keyobj->handle = key; |
michael@0 | 938 | keyobj->isTokenObject = PR_TRUE; |
michael@0 | 939 | |
michael@0 | 940 | /* ... in order to set matching attributes for the key */ |
michael@0 | 941 | status = nssCryptokiPrivateKey_SetCertificate(keyobj, NULL, nickname, |
michael@0 | 942 | &c->id, &c->subject); |
michael@0 | 943 | nssCryptokiObject_Destroy(keyobj); |
michael@0 | 944 | if (status != PR_SUCCESS) { |
michael@0 | 945 | goto loser; |
michael@0 | 946 | } |
michael@0 | 947 | } |
michael@0 | 948 | |
michael@0 | 949 | /* do the token import */ |
michael@0 | 950 | certobj = nssToken_ImportCertificate(token, NULL, |
michael@0 | 951 | NSSCertificateType_PKIX, |
michael@0 | 952 | &c->id, |
michael@0 | 953 | nickname, |
michael@0 | 954 | &c->encoding, |
michael@0 | 955 | &c->issuer, |
michael@0 | 956 | &c->subject, |
michael@0 | 957 | &c->serial, |
michael@0 | 958 | emailAddr, |
michael@0 | 959 | PR_TRUE); |
michael@0 | 960 | if (!certobj) { |
michael@0 | 961 | if (NSS_GetError() == NSS_ERROR_INVALID_CERTIFICATE) { |
michael@0 | 962 | PORT_SetError(SEC_ERROR_REUSED_ISSUER_AND_SERIAL); |
michael@0 | 963 | SECITEM_FreeItem(keyID,PR_TRUE); |
michael@0 | 964 | return SECFailure; |
michael@0 | 965 | } |
michael@0 | 966 | goto loser; |
michael@0 | 967 | } |
michael@0 | 968 | |
michael@0 | 969 | if (c->object.cryptoContext) { |
michael@0 | 970 | /* Delete the temp instance */ |
michael@0 | 971 | NSSCryptoContext *cc = c->object.cryptoContext; |
michael@0 | 972 | nssCertificateStore_Lock(cc->certStore, &lockTrace); |
michael@0 | 973 | nssCertificateStore_RemoveCertLOCKED(cc->certStore, c); |
michael@0 | 974 | nssCertificateStore_Unlock(cc->certStore, &lockTrace, &unlockTrace); |
michael@0 | 975 | c->object.cryptoContext = NULL; |
michael@0 | 976 | cert->istemp = PR_FALSE; |
michael@0 | 977 | cert->isperm = PR_TRUE; |
michael@0 | 978 | } |
michael@0 | 979 | |
michael@0 | 980 | /* add the new instance to the cert, force an update of the |
michael@0 | 981 | * CERTCertificate, and finish |
michael@0 | 982 | */ |
michael@0 | 983 | nssPKIObject_AddInstance(&c->object, certobj); |
michael@0 | 984 | /* nssTrustDomain_AddCertsToCache may release a reference to 'c' and |
michael@0 | 985 | * replace 'c' by a different value. So we add a reference to 'c' to |
michael@0 | 986 | * prevent 'c' from being destroyed. */ |
michael@0 | 987 | nssCertificate_AddRef(c); |
michael@0 | 988 | nssTrustDomain_AddCertsToCache(STAN_GetDefaultTrustDomain(), &c, 1); |
michael@0 | 989 | /* XXX should we pass the original value of 'c' to |
michael@0 | 990 | * STAN_ForceCERTCertificateUpdate? */ |
michael@0 | 991 | (void)STAN_ForceCERTCertificateUpdate(c); |
michael@0 | 992 | nssCertificate_Destroy(c); |
michael@0 | 993 | SECITEM_FreeItem(keyID,PR_TRUE); |
michael@0 | 994 | return SECSuccess; |
michael@0 | 995 | loser: |
michael@0 | 996 | CERT_MapStanError(); |
michael@0 | 997 | SECITEM_FreeItem(keyID,PR_TRUE); |
michael@0 | 998 | if (PORT_GetError() != SEC_ERROR_TOKEN_NOT_LOGGED_IN) { |
michael@0 | 999 | PORT_SetError(SEC_ERROR_ADDING_CERT); |
michael@0 | 1000 | } |
michael@0 | 1001 | return SECFailure; |
michael@0 | 1002 | } |
michael@0 | 1003 | |
michael@0 | 1004 | SECStatus |
michael@0 | 1005 | PK11_ImportDERCert(PK11SlotInfo *slot, SECItem *derCert, |
michael@0 | 1006 | CK_OBJECT_HANDLE key, char *nickname, PRBool includeTrust) |
michael@0 | 1007 | { |
michael@0 | 1008 | CERTCertificate *cert; |
michael@0 | 1009 | SECStatus rv; |
michael@0 | 1010 | |
michael@0 | 1011 | cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), |
michael@0 | 1012 | derCert, NULL, PR_FALSE, PR_TRUE); |
michael@0 | 1013 | if (cert == NULL) return SECFailure; |
michael@0 | 1014 | |
michael@0 | 1015 | rv = PK11_ImportCert(slot, cert, key, nickname, includeTrust); |
michael@0 | 1016 | CERT_DestroyCertificate (cert); |
michael@0 | 1017 | return rv; |
michael@0 | 1018 | } |
michael@0 | 1019 | |
michael@0 | 1020 | /* |
michael@0 | 1021 | * get a certificate handle, look at the cached handle first.. |
michael@0 | 1022 | */ |
michael@0 | 1023 | CK_OBJECT_HANDLE |
michael@0 | 1024 | pk11_getcerthandle(PK11SlotInfo *slot, CERTCertificate *cert, |
michael@0 | 1025 | CK_ATTRIBUTE *theTemplate,int tsize) |
michael@0 | 1026 | { |
michael@0 | 1027 | CK_OBJECT_HANDLE certh; |
michael@0 | 1028 | |
michael@0 | 1029 | if (cert->slot == slot) { |
michael@0 | 1030 | certh = cert->pkcs11ID; |
michael@0 | 1031 | if ((certh == CK_INVALID_HANDLE) || |
michael@0 | 1032 | (cert->series != slot->series)) { |
michael@0 | 1033 | certh = pk11_FindObjectByTemplate(slot,theTemplate,tsize); |
michael@0 | 1034 | cert->pkcs11ID = certh; |
michael@0 | 1035 | cert->series = slot->series; |
michael@0 | 1036 | } |
michael@0 | 1037 | } else { |
michael@0 | 1038 | certh = pk11_FindObjectByTemplate(slot,theTemplate,tsize); |
michael@0 | 1039 | } |
michael@0 | 1040 | return certh; |
michael@0 | 1041 | } |
michael@0 | 1042 | |
michael@0 | 1043 | /* |
michael@0 | 1044 | * return the private key From a given Cert |
michael@0 | 1045 | */ |
michael@0 | 1046 | SECKEYPrivateKey * |
michael@0 | 1047 | PK11_FindPrivateKeyFromCert(PK11SlotInfo *slot, CERTCertificate *cert, |
michael@0 | 1048 | void *wincx) |
michael@0 | 1049 | { |
michael@0 | 1050 | int err; |
michael@0 | 1051 | CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; |
michael@0 | 1052 | CK_ATTRIBUTE theTemplate[] = { |
michael@0 | 1053 | { CKA_VALUE, NULL, 0 }, |
michael@0 | 1054 | { CKA_CLASS, NULL, 0 } |
michael@0 | 1055 | }; |
michael@0 | 1056 | /* if you change the array, change the variable below as well */ |
michael@0 | 1057 | int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]); |
michael@0 | 1058 | CK_OBJECT_HANDLE certh; |
michael@0 | 1059 | CK_OBJECT_HANDLE keyh; |
michael@0 | 1060 | CK_ATTRIBUTE *attrs = theTemplate; |
michael@0 | 1061 | PRBool needLogin; |
michael@0 | 1062 | SECStatus rv; |
michael@0 | 1063 | |
michael@0 | 1064 | PK11_SETATTRS(attrs, CKA_VALUE, cert->derCert.data, |
michael@0 | 1065 | cert->derCert.len); attrs++; |
michael@0 | 1066 | PK11_SETATTRS(attrs, CKA_CLASS, &certClass, sizeof(certClass)); |
michael@0 | 1067 | |
michael@0 | 1068 | /* |
michael@0 | 1069 | * issue the find |
michael@0 | 1070 | */ |
michael@0 | 1071 | rv = pk11_AuthenticateUnfriendly(slot, PR_TRUE, wincx); |
michael@0 | 1072 | if (rv != SECSuccess) { |
michael@0 | 1073 | return NULL; |
michael@0 | 1074 | } |
michael@0 | 1075 | |
michael@0 | 1076 | certh = pk11_getcerthandle(slot,cert,theTemplate,tsize); |
michael@0 | 1077 | if (certh == CK_INVALID_HANDLE) { |
michael@0 | 1078 | return NULL; |
michael@0 | 1079 | } |
michael@0 | 1080 | /* |
michael@0 | 1081 | * prevent a login race condition. If slot is logged in between |
michael@0 | 1082 | * our call to pk11_LoginStillRequired and the |
michael@0 | 1083 | * PK11_MatchItem. The matchItem call will either succeed, or |
michael@0 | 1084 | * we will call it one more time after calling PK11_Authenticate |
michael@0 | 1085 | * (which is a noop on an authenticated token). |
michael@0 | 1086 | */ |
michael@0 | 1087 | needLogin = pk11_LoginStillRequired(slot,wincx); |
michael@0 | 1088 | keyh = PK11_MatchItem(slot,certh,CKO_PRIVATE_KEY); |
michael@0 | 1089 | if ((keyh == CK_INVALID_HANDLE) && needLogin && |
michael@0 | 1090 | (SSL_ERROR_NO_CERTIFICATE == (err = PORT_GetError()) || |
michael@0 | 1091 | SEC_ERROR_TOKEN_NOT_LOGGED_IN == err )) { |
michael@0 | 1092 | /* try it again authenticated */ |
michael@0 | 1093 | rv = PK11_Authenticate(slot, PR_TRUE, wincx); |
michael@0 | 1094 | if (rv != SECSuccess) { |
michael@0 | 1095 | return NULL; |
michael@0 | 1096 | } |
michael@0 | 1097 | keyh = PK11_MatchItem(slot,certh,CKO_PRIVATE_KEY); |
michael@0 | 1098 | } |
michael@0 | 1099 | if (keyh == CK_INVALID_HANDLE) { |
michael@0 | 1100 | return NULL; |
michael@0 | 1101 | } |
michael@0 | 1102 | return PK11_MakePrivKey(slot, nullKey, PR_TRUE, keyh, wincx); |
michael@0 | 1103 | } |
michael@0 | 1104 | |
michael@0 | 1105 | /* |
michael@0 | 1106 | * import a cert for a private key we have already generated. Set the label |
michael@0 | 1107 | * on both to be the nickname. This is for the Key Gen, orphaned key case. |
michael@0 | 1108 | */ |
michael@0 | 1109 | PK11SlotInfo * |
michael@0 | 1110 | PK11_KeyForCertExists(CERTCertificate *cert, CK_OBJECT_HANDLE *keyPtr, |
michael@0 | 1111 | void *wincx) |
michael@0 | 1112 | { |
michael@0 | 1113 | PK11SlotList *list; |
michael@0 | 1114 | PK11SlotListElement *le; |
michael@0 | 1115 | SECItem *keyID; |
michael@0 | 1116 | CK_OBJECT_HANDLE key; |
michael@0 | 1117 | PK11SlotInfo *slot = NULL; |
michael@0 | 1118 | SECStatus rv; |
michael@0 | 1119 | int err; |
michael@0 | 1120 | |
michael@0 | 1121 | keyID = pk11_mkcertKeyID(cert); |
michael@0 | 1122 | /* get them all! */ |
michael@0 | 1123 | list = PK11_GetAllTokens(CKM_INVALID_MECHANISM,PR_FALSE,PR_TRUE,wincx); |
michael@0 | 1124 | if ((keyID == NULL) || (list == NULL)) { |
michael@0 | 1125 | if (keyID) SECITEM_FreeItem(keyID,PR_TRUE); |
michael@0 | 1126 | if (list) PK11_FreeSlotList(list); |
michael@0 | 1127 | return NULL; |
michael@0 | 1128 | } |
michael@0 | 1129 | |
michael@0 | 1130 | /* Look for the slot that holds the Key */ |
michael@0 | 1131 | for (le = list->head ; le; le = le->next) { |
michael@0 | 1132 | /* |
michael@0 | 1133 | * prevent a login race condition. If le->slot is logged in between |
michael@0 | 1134 | * our call to pk11_LoginStillRequired and the |
michael@0 | 1135 | * pk11_FindPrivateKeyFromCertID, the find will either succeed, or |
michael@0 | 1136 | * we will call it one more time after calling PK11_Authenticate |
michael@0 | 1137 | * (which is a noop on an authenticated token). |
michael@0 | 1138 | */ |
michael@0 | 1139 | PRBool needLogin = pk11_LoginStillRequired(le->slot,wincx); |
michael@0 | 1140 | key = pk11_FindPrivateKeyFromCertID(le->slot,keyID); |
michael@0 | 1141 | if ((key == CK_INVALID_HANDLE) && needLogin && |
michael@0 | 1142 | (SSL_ERROR_NO_CERTIFICATE == (err = PORT_GetError()) || |
michael@0 | 1143 | SEC_ERROR_TOKEN_NOT_LOGGED_IN == err )) { |
michael@0 | 1144 | /* authenticate and try again */ |
michael@0 | 1145 | rv = PK11_Authenticate(le->slot, PR_TRUE, wincx); |
michael@0 | 1146 | if (rv != SECSuccess) continue; |
michael@0 | 1147 | key = pk11_FindPrivateKeyFromCertID(le->slot,keyID); |
michael@0 | 1148 | } |
michael@0 | 1149 | if (key != CK_INVALID_HANDLE) { |
michael@0 | 1150 | slot = PK11_ReferenceSlot(le->slot); |
michael@0 | 1151 | if (keyPtr) *keyPtr = key; |
michael@0 | 1152 | break; |
michael@0 | 1153 | } |
michael@0 | 1154 | } |
michael@0 | 1155 | |
michael@0 | 1156 | SECITEM_FreeItem(keyID,PR_TRUE); |
michael@0 | 1157 | PK11_FreeSlotList(list); |
michael@0 | 1158 | return slot; |
michael@0 | 1159 | |
michael@0 | 1160 | } |
michael@0 | 1161 | /* |
michael@0 | 1162 | * import a cert for a private key we have already generated. Set the label |
michael@0 | 1163 | * on both to be the nickname. This is for the Key Gen, orphaned key case. |
michael@0 | 1164 | */ |
michael@0 | 1165 | PK11SlotInfo * |
michael@0 | 1166 | PK11_KeyForDERCertExists(SECItem *derCert, CK_OBJECT_HANDLE *keyPtr, |
michael@0 | 1167 | void *wincx) |
michael@0 | 1168 | { |
michael@0 | 1169 | CERTCertificate *cert; |
michael@0 | 1170 | PK11SlotInfo *slot = NULL; |
michael@0 | 1171 | |
michael@0 | 1172 | /* letting this use go -- the only thing that the cert is used for is |
michael@0 | 1173 | * to get the ID attribute. |
michael@0 | 1174 | */ |
michael@0 | 1175 | cert = CERT_DecodeDERCertificate(derCert, PR_FALSE, NULL); |
michael@0 | 1176 | if (cert == NULL) return NULL; |
michael@0 | 1177 | |
michael@0 | 1178 | slot = PK11_KeyForCertExists(cert, keyPtr, wincx); |
michael@0 | 1179 | CERT_DestroyCertificate (cert); |
michael@0 | 1180 | return slot; |
michael@0 | 1181 | } |
michael@0 | 1182 | |
michael@0 | 1183 | PK11SlotInfo * |
michael@0 | 1184 | PK11_ImportCertForKey(CERTCertificate *cert, const char *nickname, |
michael@0 | 1185 | void *wincx) |
michael@0 | 1186 | { |
michael@0 | 1187 | PK11SlotInfo *slot = NULL; |
michael@0 | 1188 | CK_OBJECT_HANDLE key; |
michael@0 | 1189 | |
michael@0 | 1190 | slot = PK11_KeyForCertExists(cert,&key,wincx); |
michael@0 | 1191 | |
michael@0 | 1192 | if (slot) { |
michael@0 | 1193 | if (PK11_ImportCert(slot,cert,key,nickname,PR_FALSE) != SECSuccess) { |
michael@0 | 1194 | PK11_FreeSlot(slot); |
michael@0 | 1195 | slot = NULL; |
michael@0 | 1196 | } |
michael@0 | 1197 | } else { |
michael@0 | 1198 | PORT_SetError(SEC_ERROR_ADDING_CERT); |
michael@0 | 1199 | } |
michael@0 | 1200 | |
michael@0 | 1201 | return slot; |
michael@0 | 1202 | } |
michael@0 | 1203 | |
michael@0 | 1204 | PK11SlotInfo * |
michael@0 | 1205 | PK11_ImportDERCertForKey(SECItem *derCert, char *nickname,void *wincx) |
michael@0 | 1206 | { |
michael@0 | 1207 | CERTCertificate *cert; |
michael@0 | 1208 | PK11SlotInfo *slot = NULL; |
michael@0 | 1209 | |
michael@0 | 1210 | cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), |
michael@0 | 1211 | derCert, NULL, PR_FALSE, PR_TRUE); |
michael@0 | 1212 | if (cert == NULL) return NULL; |
michael@0 | 1213 | |
michael@0 | 1214 | slot = PK11_ImportCertForKey(cert, nickname, wincx); |
michael@0 | 1215 | CERT_DestroyCertificate (cert); |
michael@0 | 1216 | return slot; |
michael@0 | 1217 | } |
michael@0 | 1218 | |
michael@0 | 1219 | static CK_OBJECT_HANDLE |
michael@0 | 1220 | pk11_FindCertObjectByTemplate(PK11SlotInfo **slotPtr, |
michael@0 | 1221 | CK_ATTRIBUTE *searchTemplate, int count, void *wincx) |
michael@0 | 1222 | { |
michael@0 | 1223 | PK11SlotList *list; |
michael@0 | 1224 | PK11SlotListElement *le; |
michael@0 | 1225 | CK_OBJECT_HANDLE certHandle = CK_INVALID_HANDLE; |
michael@0 | 1226 | PK11SlotInfo *slot = NULL; |
michael@0 | 1227 | SECStatus rv; |
michael@0 | 1228 | |
michael@0 | 1229 | *slotPtr = NULL; |
michael@0 | 1230 | |
michael@0 | 1231 | /* get them all! */ |
michael@0 | 1232 | list = PK11_GetAllTokens(CKM_INVALID_MECHANISM,PR_FALSE,PR_TRUE,wincx); |
michael@0 | 1233 | if (list == NULL) { |
michael@0 | 1234 | return CK_INVALID_HANDLE; |
michael@0 | 1235 | } |
michael@0 | 1236 | |
michael@0 | 1237 | |
michael@0 | 1238 | /* Look for the slot that holds the Key */ |
michael@0 | 1239 | for (le = list->head ; le; le = le->next) { |
michael@0 | 1240 | rv = pk11_AuthenticateUnfriendly(le->slot, PR_TRUE, wincx); |
michael@0 | 1241 | if (rv != SECSuccess) continue; |
michael@0 | 1242 | |
michael@0 | 1243 | certHandle = pk11_FindObjectByTemplate(le->slot,searchTemplate,count); |
michael@0 | 1244 | if (certHandle != CK_INVALID_HANDLE) { |
michael@0 | 1245 | slot = PK11_ReferenceSlot(le->slot); |
michael@0 | 1246 | break; |
michael@0 | 1247 | } |
michael@0 | 1248 | } |
michael@0 | 1249 | |
michael@0 | 1250 | PK11_FreeSlotList(list); |
michael@0 | 1251 | |
michael@0 | 1252 | if (slot == NULL) { |
michael@0 | 1253 | return CK_INVALID_HANDLE; |
michael@0 | 1254 | } |
michael@0 | 1255 | *slotPtr = slot; |
michael@0 | 1256 | return certHandle; |
michael@0 | 1257 | } |
michael@0 | 1258 | |
michael@0 | 1259 | CERTCertificate * |
michael@0 | 1260 | PK11_FindCertByIssuerAndSNOnToken(PK11SlotInfo *slot, |
michael@0 | 1261 | CERTIssuerAndSN *issuerSN, void *wincx) |
michael@0 | 1262 | { |
michael@0 | 1263 | CERTCertificate *rvCert = NULL; |
michael@0 | 1264 | NSSCertificate *cert = NULL; |
michael@0 | 1265 | NSSDER issuer, serial; |
michael@0 | 1266 | NSSTrustDomain *td = STAN_GetDefaultTrustDomain(); |
michael@0 | 1267 | NSSToken *token = slot->nssToken; |
michael@0 | 1268 | nssSession *session; |
michael@0 | 1269 | nssCryptokiObject *instance = NULL; |
michael@0 | 1270 | nssPKIObject *object = NULL; |
michael@0 | 1271 | SECItem *derSerial; |
michael@0 | 1272 | PRStatus status; |
michael@0 | 1273 | |
michael@0 | 1274 | if (!issuerSN || !issuerSN->derIssuer.data || !issuerSN->derIssuer.len || |
michael@0 | 1275 | !issuerSN->serialNumber.data || !issuerSN->serialNumber.len || |
michael@0 | 1276 | issuerSN->derIssuer.len > CERT_MAX_DN_BYTES || |
michael@0 | 1277 | issuerSN->serialNumber.len > CERT_MAX_SERIAL_NUMBER_BYTES ) { |
michael@0 | 1278 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
michael@0 | 1279 | return NULL; |
michael@0 | 1280 | } |
michael@0 | 1281 | |
michael@0 | 1282 | /* Paranoia */ |
michael@0 | 1283 | if (token == NULL) { |
michael@0 | 1284 | PORT_SetError(SEC_ERROR_NO_TOKEN); |
michael@0 | 1285 | return NULL; |
michael@0 | 1286 | } |
michael@0 | 1287 | |
michael@0 | 1288 | |
michael@0 | 1289 | /* PKCS#11 needs to use DER-encoded serial numbers. Create a |
michael@0 | 1290 | * CERTIssuerAndSN that actually has the encoded value and pass that |
michael@0 | 1291 | * to PKCS#11 (and the crypto context). |
michael@0 | 1292 | */ |
michael@0 | 1293 | derSerial = SEC_ASN1EncodeItem(NULL, NULL, |
michael@0 | 1294 | &issuerSN->serialNumber, |
michael@0 | 1295 | SEC_ASN1_GET(SEC_IntegerTemplate)); |
michael@0 | 1296 | if (!derSerial) { |
michael@0 | 1297 | return NULL; |
michael@0 | 1298 | } |
michael@0 | 1299 | |
michael@0 | 1300 | NSSITEM_FROM_SECITEM(&issuer, &issuerSN->derIssuer); |
michael@0 | 1301 | NSSITEM_FROM_SECITEM(&serial, derSerial); |
michael@0 | 1302 | |
michael@0 | 1303 | session = nssToken_GetDefaultSession(token); |
michael@0 | 1304 | if (!session) { |
michael@0 | 1305 | goto loser; |
michael@0 | 1306 | } |
michael@0 | 1307 | |
michael@0 | 1308 | instance = nssToken_FindCertificateByIssuerAndSerialNumber(token,session, |
michael@0 | 1309 | &issuer, &serial, nssTokenSearchType_TokenForced, &status); |
michael@0 | 1310 | |
michael@0 | 1311 | SECITEM_FreeItem(derSerial, PR_TRUE); |
michael@0 | 1312 | |
michael@0 | 1313 | if (!instance) { |
michael@0 | 1314 | goto loser; |
michael@0 | 1315 | } |
michael@0 | 1316 | object = nssPKIObject_Create(NULL, instance, td, NULL, nssPKIMonitor); |
michael@0 | 1317 | if (!object) { |
michael@0 | 1318 | goto loser; |
michael@0 | 1319 | } |
michael@0 | 1320 | instance = NULL; /* adopted by the previous call */ |
michael@0 | 1321 | cert = nssCertificate_Create(object); |
michael@0 | 1322 | if (!cert) { |
michael@0 | 1323 | goto loser; |
michael@0 | 1324 | } |
michael@0 | 1325 | object = NULL; /* adopted by the previous call */ |
michael@0 | 1326 | nssTrustDomain_AddCertsToCache(td, &cert,1); |
michael@0 | 1327 | /* on failure, cert is freed below */ |
michael@0 | 1328 | rvCert = STAN_GetCERTCertificate(cert); |
michael@0 | 1329 | if (!rvCert) { |
michael@0 | 1330 | goto loser; |
michael@0 | 1331 | } |
michael@0 | 1332 | return rvCert; |
michael@0 | 1333 | |
michael@0 | 1334 | loser: |
michael@0 | 1335 | if (instance) { |
michael@0 | 1336 | nssCryptokiObject_Destroy(instance); |
michael@0 | 1337 | } |
michael@0 | 1338 | if (object) { |
michael@0 | 1339 | nssPKIObject_Destroy(object); |
michael@0 | 1340 | } |
michael@0 | 1341 | if (cert) { |
michael@0 | 1342 | nssCertificate_Destroy(cert); |
michael@0 | 1343 | } |
michael@0 | 1344 | return NULL; |
michael@0 | 1345 | } |
michael@0 | 1346 | |
michael@0 | 1347 | static PRCallOnceType keyIDHashCallOnce; |
michael@0 | 1348 | |
michael@0 | 1349 | static PRStatus PR_CALLBACK |
michael@0 | 1350 | pk11_keyIDHash_populate(void *wincx) |
michael@0 | 1351 | { |
michael@0 | 1352 | CERTCertList *certList; |
michael@0 | 1353 | CERTCertListNode *node = NULL; |
michael@0 | 1354 | SECItem subjKeyID = {siBuffer, NULL, 0}; |
michael@0 | 1355 | SECItem *slotid = NULL; |
michael@0 | 1356 | SECMODModuleList *modules, *mlp; |
michael@0 | 1357 | SECMODListLock *moduleLock; |
michael@0 | 1358 | int i; |
michael@0 | 1359 | |
michael@0 | 1360 | certList = PK11_ListCerts(PK11CertListUser, wincx); |
michael@0 | 1361 | if (!certList) { |
michael@0 | 1362 | return PR_FAILURE; |
michael@0 | 1363 | } |
michael@0 | 1364 | |
michael@0 | 1365 | for (node = CERT_LIST_HEAD(certList); |
michael@0 | 1366 | !CERT_LIST_END(node, certList); |
michael@0 | 1367 | node = CERT_LIST_NEXT(node)) { |
michael@0 | 1368 | if (CERT_FindSubjectKeyIDExtension(node->cert, |
michael@0 | 1369 | &subjKeyID) == SECSuccess && |
michael@0 | 1370 | subjKeyID.data != NULL) { |
michael@0 | 1371 | cert_AddSubjectKeyIDMapping(&subjKeyID, node->cert); |
michael@0 | 1372 | SECITEM_FreeItem(&subjKeyID, PR_FALSE); |
michael@0 | 1373 | } |
michael@0 | 1374 | } |
michael@0 | 1375 | CERT_DestroyCertList(certList); |
michael@0 | 1376 | |
michael@0 | 1377 | /* |
michael@0 | 1378 | * Record the state of each slot in a hash. The concatenation of slotID |
michael@0 | 1379 | * and moduleID is used as its key, with the slot series as its value. |
michael@0 | 1380 | */ |
michael@0 | 1381 | slotid = SECITEM_AllocItem(NULL, NULL, |
michael@0 | 1382 | sizeof(CK_SLOT_ID) + sizeof(SECMODModuleID)); |
michael@0 | 1383 | if (!slotid) { |
michael@0 | 1384 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
michael@0 | 1385 | return PR_FAILURE; |
michael@0 | 1386 | } |
michael@0 | 1387 | moduleLock = SECMOD_GetDefaultModuleListLock(); |
michael@0 | 1388 | if (!moduleLock) { |
michael@0 | 1389 | PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
michael@0 | 1390 | return PR_FAILURE; |
michael@0 | 1391 | } |
michael@0 | 1392 | SECMOD_GetReadLock(moduleLock); |
michael@0 | 1393 | modules = SECMOD_GetDefaultModuleList(); |
michael@0 | 1394 | for (mlp = modules; mlp; mlp = mlp->next) { |
michael@0 | 1395 | for (i = 0; i < mlp->module->slotCount; i++) { |
michael@0 | 1396 | memcpy(slotid->data, &mlp->module->slots[i]->slotID, |
michael@0 | 1397 | sizeof(CK_SLOT_ID)); |
michael@0 | 1398 | memcpy(&slotid->data[sizeof(CK_SLOT_ID)], &mlp->module->moduleID, |
michael@0 | 1399 | sizeof(SECMODModuleID)); |
michael@0 | 1400 | cert_UpdateSubjectKeyIDSlotCheck(slotid, |
michael@0 | 1401 | mlp->module->slots[i]->series); |
michael@0 | 1402 | } |
michael@0 | 1403 | } |
michael@0 | 1404 | SECMOD_ReleaseReadLock(moduleLock); |
michael@0 | 1405 | SECITEM_FreeItem(slotid, PR_TRUE); |
michael@0 | 1406 | |
michael@0 | 1407 | return PR_SUCCESS; |
michael@0 | 1408 | } |
michael@0 | 1409 | |
michael@0 | 1410 | /* |
michael@0 | 1411 | * We're looking for a cert which we have the private key for that's on the |
michael@0 | 1412 | * list of recipients. This searches one slot. |
michael@0 | 1413 | * this is the new version for NSS SMIME code |
michael@0 | 1414 | * this stuff should REALLY be in the SMIME code, but some things in here are not public |
michael@0 | 1415 | * (they should be!) |
michael@0 | 1416 | */ |
michael@0 | 1417 | static CERTCertificate * |
michael@0 | 1418 | pk11_FindCertObjectByRecipientNew(PK11SlotInfo *slot, NSSCMSRecipient **recipientlist, int *rlIndex, void *pwarg) |
michael@0 | 1419 | { |
michael@0 | 1420 | NSSCMSRecipient *ri = NULL; |
michael@0 | 1421 | int i; |
michael@0 | 1422 | PRBool tokenRescanDone = PR_FALSE; |
michael@0 | 1423 | CERTCertTrust trust; |
michael@0 | 1424 | |
michael@0 | 1425 | for (i=0; (ri = recipientlist[i]) != NULL; i++) { |
michael@0 | 1426 | CERTCertificate *cert = NULL; |
michael@0 | 1427 | if (ri->kind == RLSubjKeyID) { |
michael@0 | 1428 | SECItem *derCert = cert_FindDERCertBySubjectKeyID(ri->id.subjectKeyID); |
michael@0 | 1429 | if (!derCert && !tokenRescanDone) { |
michael@0 | 1430 | /* |
michael@0 | 1431 | * We didn't find the cert by its key ID. If we have slots |
michael@0 | 1432 | * with removable tokens, a failure from |
michael@0 | 1433 | * cert_FindDERCertBySubjectKeyID doesn't necessarily imply |
michael@0 | 1434 | * that the cert is unavailable - the token might simply |
michael@0 | 1435 | * have been inserted after the initial run of |
michael@0 | 1436 | * pk11_keyIDHash_populate (wrapped by PR_CallOnceWithArg), |
michael@0 | 1437 | * or a different token might have been present in that |
michael@0 | 1438 | * slot, initially. Let's check for new tokens... |
michael@0 | 1439 | */ |
michael@0 | 1440 | PK11SlotList *sl = PK11_GetAllTokens(CKM_INVALID_MECHANISM, |
michael@0 | 1441 | PR_FALSE, PR_FALSE, pwarg); |
michael@0 | 1442 | if (sl) { |
michael@0 | 1443 | PK11SlotListElement *le; |
michael@0 | 1444 | SECItem *slotid = SECITEM_AllocItem(NULL, NULL, |
michael@0 | 1445 | sizeof(CK_SLOT_ID) + sizeof(SECMODModuleID)); |
michael@0 | 1446 | if (!slotid) { |
michael@0 | 1447 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
michael@0 | 1448 | return NULL; |
michael@0 | 1449 | } |
michael@0 | 1450 | for (le = sl->head; le; le = le->next) { |
michael@0 | 1451 | memcpy(slotid->data, &le->slot->slotID, |
michael@0 | 1452 | sizeof(CK_SLOT_ID)); |
michael@0 | 1453 | memcpy(&slotid->data[sizeof(CK_SLOT_ID)], |
michael@0 | 1454 | &le->slot->module->moduleID, |
michael@0 | 1455 | sizeof(SECMODModuleID)); |
michael@0 | 1456 | /* |
michael@0 | 1457 | * Any changes with the slot since our last check? |
michael@0 | 1458 | * If so, re-read the certs in that specific slot. |
michael@0 | 1459 | */ |
michael@0 | 1460 | if (cert_SubjectKeyIDSlotCheckSeries(slotid) |
michael@0 | 1461 | != PK11_GetSlotSeries(le->slot)) { |
michael@0 | 1462 | CERTCertListNode *node = NULL; |
michael@0 | 1463 | SECItem subjKeyID = {siBuffer, NULL, 0}; |
michael@0 | 1464 | CERTCertList *cl = PK11_ListCertsInSlot(le->slot); |
michael@0 | 1465 | if (!cl) { |
michael@0 | 1466 | continue; |
michael@0 | 1467 | } |
michael@0 | 1468 | for (node = CERT_LIST_HEAD(cl); |
michael@0 | 1469 | !CERT_LIST_END(node, cl); |
michael@0 | 1470 | node = CERT_LIST_NEXT(node)) { |
michael@0 | 1471 | if (CERT_IsUserCert(node->cert) && |
michael@0 | 1472 | CERT_FindSubjectKeyIDExtension(node->cert, |
michael@0 | 1473 | &subjKeyID) == SECSuccess) { |
michael@0 | 1474 | if (subjKeyID.data) { |
michael@0 | 1475 | cert_AddSubjectKeyIDMapping(&subjKeyID, |
michael@0 | 1476 | node->cert); |
michael@0 | 1477 | cert_UpdateSubjectKeyIDSlotCheck(slotid, |
michael@0 | 1478 | PK11_GetSlotSeries(le->slot)); |
michael@0 | 1479 | } |
michael@0 | 1480 | SECITEM_FreeItem(&subjKeyID, PR_FALSE); |
michael@0 | 1481 | } |
michael@0 | 1482 | } |
michael@0 | 1483 | CERT_DestroyCertList(cl); |
michael@0 | 1484 | } |
michael@0 | 1485 | } |
michael@0 | 1486 | PK11_FreeSlotList(sl); |
michael@0 | 1487 | SECITEM_FreeItem(slotid, PR_TRUE); |
michael@0 | 1488 | } |
michael@0 | 1489 | /* only check once per message/recipientlist */ |
michael@0 | 1490 | tokenRescanDone = PR_TRUE; |
michael@0 | 1491 | /* do another lookup (hopefully we found that cert...) */ |
michael@0 | 1492 | derCert = cert_FindDERCertBySubjectKeyID(ri->id.subjectKeyID); |
michael@0 | 1493 | } |
michael@0 | 1494 | if (derCert) { |
michael@0 | 1495 | cert = PK11_FindCertFromDERCertItem(slot, derCert, pwarg); |
michael@0 | 1496 | SECITEM_FreeItem(derCert, PR_TRUE); |
michael@0 | 1497 | } |
michael@0 | 1498 | } else { |
michael@0 | 1499 | cert = PK11_FindCertByIssuerAndSNOnToken(slot, ri->id.issuerAndSN, |
michael@0 | 1500 | pwarg); |
michael@0 | 1501 | } |
michael@0 | 1502 | if (cert) { |
michael@0 | 1503 | /* this isn't our cert */ |
michael@0 | 1504 | if (CERT_GetCertTrust(cert, &trust) != SECSuccess || |
michael@0 | 1505 | ((trust.emailFlags & CERTDB_USER) != CERTDB_USER)) { |
michael@0 | 1506 | CERT_DestroyCertificate(cert); |
michael@0 | 1507 | continue; |
michael@0 | 1508 | } |
michael@0 | 1509 | ri->slot = PK11_ReferenceSlot(slot); |
michael@0 | 1510 | *rlIndex = i; |
michael@0 | 1511 | return cert; |
michael@0 | 1512 | } |
michael@0 | 1513 | } |
michael@0 | 1514 | *rlIndex = -1; |
michael@0 | 1515 | return NULL; |
michael@0 | 1516 | } |
michael@0 | 1517 | |
michael@0 | 1518 | /* |
michael@0 | 1519 | * This function is the same as above, but it searches all the slots. |
michael@0 | 1520 | * this is the new version for NSS SMIME code |
michael@0 | 1521 | * this stuff should REALLY be in the SMIME code, but some things in here are not public |
michael@0 | 1522 | * (they should be!) |
michael@0 | 1523 | */ |
michael@0 | 1524 | static CERTCertificate * |
michael@0 | 1525 | pk11_AllFindCertObjectByRecipientNew(NSSCMSRecipient **recipientlist, void *wincx, int *rlIndex) |
michael@0 | 1526 | { |
michael@0 | 1527 | PK11SlotList *list; |
michael@0 | 1528 | PK11SlotListElement *le; |
michael@0 | 1529 | CERTCertificate *cert = NULL; |
michael@0 | 1530 | SECStatus rv; |
michael@0 | 1531 | |
michael@0 | 1532 | /* get them all! */ |
michael@0 | 1533 | list = PK11_GetAllTokens(CKM_INVALID_MECHANISM,PR_FALSE,PR_TRUE,wincx); |
michael@0 | 1534 | if (list == NULL) { |
michael@0 | 1535 | return CK_INVALID_HANDLE; |
michael@0 | 1536 | } |
michael@0 | 1537 | |
michael@0 | 1538 | /* Look for the slot that holds the Key */ |
michael@0 | 1539 | for (le = list->head ; le; le = le->next) { |
michael@0 | 1540 | rv = pk11_AuthenticateUnfriendly(le->slot, PR_TRUE, wincx); |
michael@0 | 1541 | if (rv != SECSuccess) continue; |
michael@0 | 1542 | |
michael@0 | 1543 | cert = pk11_FindCertObjectByRecipientNew(le->slot, |
michael@0 | 1544 | recipientlist, rlIndex, wincx); |
michael@0 | 1545 | if (cert) |
michael@0 | 1546 | break; |
michael@0 | 1547 | } |
michael@0 | 1548 | |
michael@0 | 1549 | PK11_FreeSlotList(list); |
michael@0 | 1550 | |
michael@0 | 1551 | return cert; |
michael@0 | 1552 | } |
michael@0 | 1553 | |
michael@0 | 1554 | /* |
michael@0 | 1555 | * We're looking for a cert which we have the private key for that's on the |
michael@0 | 1556 | * list of recipients. This searches one slot. |
michael@0 | 1557 | */ |
michael@0 | 1558 | static CERTCertificate * |
michael@0 | 1559 | pk11_FindCertObjectByRecipient(PK11SlotInfo *slot, |
michael@0 | 1560 | SEC_PKCS7RecipientInfo **recipientArray, |
michael@0 | 1561 | SEC_PKCS7RecipientInfo **rip, void *pwarg) |
michael@0 | 1562 | { |
michael@0 | 1563 | SEC_PKCS7RecipientInfo *ri = NULL; |
michael@0 | 1564 | CERTCertTrust trust; |
michael@0 | 1565 | int i; |
michael@0 | 1566 | |
michael@0 | 1567 | for (i=0; (ri = recipientArray[i]) != NULL; i++) { |
michael@0 | 1568 | CERTCertificate *cert; |
michael@0 | 1569 | |
michael@0 | 1570 | cert = PK11_FindCertByIssuerAndSNOnToken(slot, ri->issuerAndSN, |
michael@0 | 1571 | pwarg); |
michael@0 | 1572 | if (cert) { |
michael@0 | 1573 | /* this isn't our cert */ |
michael@0 | 1574 | if (CERT_GetCertTrust(cert, &trust) != SECSuccess || |
michael@0 | 1575 | ((trust.emailFlags & CERTDB_USER) != CERTDB_USER)) { |
michael@0 | 1576 | CERT_DestroyCertificate(cert); |
michael@0 | 1577 | continue; |
michael@0 | 1578 | } |
michael@0 | 1579 | *rip = ri; |
michael@0 | 1580 | return cert; |
michael@0 | 1581 | } |
michael@0 | 1582 | |
michael@0 | 1583 | } |
michael@0 | 1584 | *rip = NULL; |
michael@0 | 1585 | return NULL; |
michael@0 | 1586 | } |
michael@0 | 1587 | |
michael@0 | 1588 | /* |
michael@0 | 1589 | * This function is the same as above, but it searches all the slots. |
michael@0 | 1590 | */ |
michael@0 | 1591 | static CERTCertificate * |
michael@0 | 1592 | pk11_AllFindCertObjectByRecipient(PK11SlotInfo **slotPtr, |
michael@0 | 1593 | SEC_PKCS7RecipientInfo **recipientArray,SEC_PKCS7RecipientInfo **rip, |
michael@0 | 1594 | void *wincx) |
michael@0 | 1595 | { |
michael@0 | 1596 | PK11SlotList *list; |
michael@0 | 1597 | PK11SlotListElement *le; |
michael@0 | 1598 | CERTCertificate * cert = NULL; |
michael@0 | 1599 | PK11SlotInfo *slot = NULL; |
michael@0 | 1600 | SECStatus rv; |
michael@0 | 1601 | |
michael@0 | 1602 | *slotPtr = NULL; |
michael@0 | 1603 | |
michael@0 | 1604 | /* get them all! */ |
michael@0 | 1605 | list = PK11_GetAllTokens(CKM_INVALID_MECHANISM,PR_FALSE,PR_TRUE,wincx); |
michael@0 | 1606 | if (list == NULL) { |
michael@0 | 1607 | return CK_INVALID_HANDLE; |
michael@0 | 1608 | } |
michael@0 | 1609 | |
michael@0 | 1610 | *rip = NULL; |
michael@0 | 1611 | |
michael@0 | 1612 | /* Look for the slot that holds the Key */ |
michael@0 | 1613 | for (le = list->head ; le; le = le->next) { |
michael@0 | 1614 | rv = pk11_AuthenticateUnfriendly(le->slot, PR_TRUE, wincx); |
michael@0 | 1615 | if (rv != SECSuccess) continue; |
michael@0 | 1616 | |
michael@0 | 1617 | cert = pk11_FindCertObjectByRecipient(le->slot, recipientArray, |
michael@0 | 1618 | rip, wincx); |
michael@0 | 1619 | if (cert) { |
michael@0 | 1620 | slot = PK11_ReferenceSlot(le->slot); |
michael@0 | 1621 | break; |
michael@0 | 1622 | } |
michael@0 | 1623 | } |
michael@0 | 1624 | |
michael@0 | 1625 | PK11_FreeSlotList(list); |
michael@0 | 1626 | |
michael@0 | 1627 | if (slot == NULL) { |
michael@0 | 1628 | return NULL; |
michael@0 | 1629 | } |
michael@0 | 1630 | *slotPtr = slot; |
michael@0 | 1631 | PORT_Assert(cert != NULL); |
michael@0 | 1632 | return cert; |
michael@0 | 1633 | } |
michael@0 | 1634 | |
michael@0 | 1635 | /* |
michael@0 | 1636 | * We need to invert the search logic for PKCS 7 because if we search for |
michael@0 | 1637 | * each cert on the list over all the slots, we wind up with lots of spurious |
michael@0 | 1638 | * password prompts. This way we get only one password prompt per slot, at |
michael@0 | 1639 | * the max, and most of the time we can find the cert, and only prompt for |
michael@0 | 1640 | * the key... |
michael@0 | 1641 | */ |
michael@0 | 1642 | CERTCertificate * |
michael@0 | 1643 | PK11_FindCertAndKeyByRecipientList(PK11SlotInfo **slotPtr, |
michael@0 | 1644 | SEC_PKCS7RecipientInfo **array, SEC_PKCS7RecipientInfo **rip, |
michael@0 | 1645 | SECKEYPrivateKey**privKey, void *wincx) |
michael@0 | 1646 | { |
michael@0 | 1647 | CERTCertificate *cert = NULL; |
michael@0 | 1648 | |
michael@0 | 1649 | *privKey = NULL; |
michael@0 | 1650 | *slotPtr = NULL; |
michael@0 | 1651 | cert = pk11_AllFindCertObjectByRecipient(slotPtr,array,rip,wincx); |
michael@0 | 1652 | if (!cert) { |
michael@0 | 1653 | return NULL; |
michael@0 | 1654 | } |
michael@0 | 1655 | |
michael@0 | 1656 | *privKey = PK11_FindKeyByAnyCert(cert, wincx); |
michael@0 | 1657 | if (*privKey == NULL) { |
michael@0 | 1658 | goto loser; |
michael@0 | 1659 | } |
michael@0 | 1660 | |
michael@0 | 1661 | return cert; |
michael@0 | 1662 | loser: |
michael@0 | 1663 | if (cert) CERT_DestroyCertificate(cert); |
michael@0 | 1664 | if (*slotPtr) PK11_FreeSlot(*slotPtr); |
michael@0 | 1665 | *slotPtr = NULL; |
michael@0 | 1666 | return NULL; |
michael@0 | 1667 | } |
michael@0 | 1668 | |
michael@0 | 1669 | /* |
michael@0 | 1670 | * This is the new version of the above function for NSS SMIME code |
michael@0 | 1671 | * this stuff should REALLY be in the SMIME code, but some things in here are not public |
michael@0 | 1672 | * (they should be!) |
michael@0 | 1673 | */ |
michael@0 | 1674 | int |
michael@0 | 1675 | PK11_FindCertAndKeyByRecipientListNew(NSSCMSRecipient **recipientlist, void *wincx) |
michael@0 | 1676 | { |
michael@0 | 1677 | CERTCertificate *cert; |
michael@0 | 1678 | NSSCMSRecipient *rl; |
michael@0 | 1679 | PRStatus rv; |
michael@0 | 1680 | int rlIndex; |
michael@0 | 1681 | |
michael@0 | 1682 | rv = PR_CallOnceWithArg(&keyIDHashCallOnce, pk11_keyIDHash_populate, wincx); |
michael@0 | 1683 | if (rv != PR_SUCCESS) |
michael@0 | 1684 | return -1; |
michael@0 | 1685 | |
michael@0 | 1686 | cert = pk11_AllFindCertObjectByRecipientNew(recipientlist, wincx, &rlIndex); |
michael@0 | 1687 | if (!cert) { |
michael@0 | 1688 | return -1; |
michael@0 | 1689 | } |
michael@0 | 1690 | |
michael@0 | 1691 | rl = recipientlist[rlIndex]; |
michael@0 | 1692 | |
michael@0 | 1693 | /* at this point, rl->slot is set */ |
michael@0 | 1694 | |
michael@0 | 1695 | rl->privkey = PK11_FindKeyByAnyCert(cert, wincx); |
michael@0 | 1696 | if (rl->privkey == NULL) { |
michael@0 | 1697 | goto loser; |
michael@0 | 1698 | } |
michael@0 | 1699 | |
michael@0 | 1700 | /* make a cert from the cert handle */ |
michael@0 | 1701 | rl->cert = cert; |
michael@0 | 1702 | return rlIndex; |
michael@0 | 1703 | |
michael@0 | 1704 | loser: |
michael@0 | 1705 | if (cert) CERT_DestroyCertificate(cert); |
michael@0 | 1706 | if (rl->slot) PK11_FreeSlot(rl->slot); |
michael@0 | 1707 | rl->slot = NULL; |
michael@0 | 1708 | return -1; |
michael@0 | 1709 | } |
michael@0 | 1710 | |
michael@0 | 1711 | CERTCertificate * |
michael@0 | 1712 | PK11_FindCertByIssuerAndSN(PK11SlotInfo **slotPtr, CERTIssuerAndSN *issuerSN, |
michael@0 | 1713 | void *wincx) |
michael@0 | 1714 | { |
michael@0 | 1715 | CERTCertificate *rvCert = NULL; |
michael@0 | 1716 | NSSCertificate *cert; |
michael@0 | 1717 | NSSDER issuer, serial; |
michael@0 | 1718 | NSSCryptoContext *cc; |
michael@0 | 1719 | SECItem *derSerial; |
michael@0 | 1720 | |
michael@0 | 1721 | if (!issuerSN || !issuerSN->derIssuer.data || !issuerSN->derIssuer.len || |
michael@0 | 1722 | !issuerSN->serialNumber.data || !issuerSN->serialNumber.len || |
michael@0 | 1723 | issuerSN->derIssuer.len > CERT_MAX_DN_BYTES || |
michael@0 | 1724 | issuerSN->serialNumber.len > CERT_MAX_SERIAL_NUMBER_BYTES ) { |
michael@0 | 1725 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
michael@0 | 1726 | return NULL; |
michael@0 | 1727 | } |
michael@0 | 1728 | |
michael@0 | 1729 | if (slotPtr) *slotPtr = NULL; |
michael@0 | 1730 | |
michael@0 | 1731 | /* PKCS#11 needs to use DER-encoded serial numbers. Create a |
michael@0 | 1732 | * CERTIssuerAndSN that actually has the encoded value and pass that |
michael@0 | 1733 | * to PKCS#11 (and the crypto context). |
michael@0 | 1734 | */ |
michael@0 | 1735 | derSerial = SEC_ASN1EncodeItem(NULL, NULL, |
michael@0 | 1736 | &issuerSN->serialNumber, |
michael@0 | 1737 | SEC_ASN1_GET(SEC_IntegerTemplate)); |
michael@0 | 1738 | if (!derSerial) { |
michael@0 | 1739 | return NULL; |
michael@0 | 1740 | } |
michael@0 | 1741 | |
michael@0 | 1742 | NSSITEM_FROM_SECITEM(&issuer, &issuerSN->derIssuer); |
michael@0 | 1743 | NSSITEM_FROM_SECITEM(&serial, derSerial); |
michael@0 | 1744 | |
michael@0 | 1745 | cc = STAN_GetDefaultCryptoContext(); |
michael@0 | 1746 | cert = NSSCryptoContext_FindCertificateByIssuerAndSerialNumber(cc, |
michael@0 | 1747 | &issuer, |
michael@0 | 1748 | &serial); |
michael@0 | 1749 | if (cert) { |
michael@0 | 1750 | SECITEM_FreeItem(derSerial, PR_TRUE); |
michael@0 | 1751 | return STAN_GetCERTCertificateOrRelease(cert); |
michael@0 | 1752 | } |
michael@0 | 1753 | |
michael@0 | 1754 | do { |
michael@0 | 1755 | /* free the old cert on retry. Associated slot was not present */ |
michael@0 | 1756 | if (rvCert) { |
michael@0 | 1757 | CERT_DestroyCertificate(rvCert); |
michael@0 | 1758 | rvCert = NULL; |
michael@0 | 1759 | } |
michael@0 | 1760 | |
michael@0 | 1761 | cert = NSSTrustDomain_FindCertificateByIssuerAndSerialNumber( |
michael@0 | 1762 | STAN_GetDefaultTrustDomain(), |
michael@0 | 1763 | &issuer, |
michael@0 | 1764 | &serial); |
michael@0 | 1765 | if (!cert) { |
michael@0 | 1766 | break; |
michael@0 | 1767 | } |
michael@0 | 1768 | |
michael@0 | 1769 | rvCert = STAN_GetCERTCertificateOrRelease(cert); |
michael@0 | 1770 | if (rvCert == NULL) { |
michael@0 | 1771 | break; |
michael@0 | 1772 | } |
michael@0 | 1773 | |
michael@0 | 1774 | /* Check to see if the cert's token is still there */ |
michael@0 | 1775 | } while (!PK11_IsPresent(rvCert->slot)); |
michael@0 | 1776 | |
michael@0 | 1777 | if (rvCert && slotPtr) *slotPtr = PK11_ReferenceSlot(rvCert->slot); |
michael@0 | 1778 | |
michael@0 | 1779 | SECITEM_FreeItem(derSerial, PR_TRUE); |
michael@0 | 1780 | return rvCert; |
michael@0 | 1781 | } |
michael@0 | 1782 | |
michael@0 | 1783 | CK_OBJECT_HANDLE |
michael@0 | 1784 | PK11_FindObjectForCert(CERTCertificate *cert, void *wincx, PK11SlotInfo **pSlot) |
michael@0 | 1785 | { |
michael@0 | 1786 | CK_OBJECT_HANDLE certHandle; |
michael@0 | 1787 | CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; |
michael@0 | 1788 | CK_ATTRIBUTE *attr; |
michael@0 | 1789 | CK_ATTRIBUTE searchTemplate[]= { |
michael@0 | 1790 | { CKA_CLASS, NULL, 0 }, |
michael@0 | 1791 | { CKA_VALUE, NULL, 0 }, |
michael@0 | 1792 | }; |
michael@0 | 1793 | int templateSize = sizeof(searchTemplate)/sizeof(searchTemplate[0]); |
michael@0 | 1794 | |
michael@0 | 1795 | attr = searchTemplate; |
michael@0 | 1796 | PK11_SETATTRS(attr, CKA_CLASS, &certClass, sizeof(certClass)); attr++; |
michael@0 | 1797 | PK11_SETATTRS(attr, CKA_VALUE, cert->derCert.data, cert->derCert.len); |
michael@0 | 1798 | |
michael@0 | 1799 | if (cert->slot) { |
michael@0 | 1800 | certHandle = pk11_getcerthandle(cert->slot, cert, searchTemplate, |
michael@0 | 1801 | templateSize); |
michael@0 | 1802 | if (certHandle != CK_INVALID_HANDLE) { |
michael@0 | 1803 | *pSlot = PK11_ReferenceSlot(cert->slot); |
michael@0 | 1804 | return certHandle; |
michael@0 | 1805 | } |
michael@0 | 1806 | } |
michael@0 | 1807 | |
michael@0 | 1808 | certHandle = pk11_FindCertObjectByTemplate(pSlot, searchTemplate, |
michael@0 | 1809 | templateSize, wincx); |
michael@0 | 1810 | if (certHandle != CK_INVALID_HANDLE) { |
michael@0 | 1811 | if (cert->slot == NULL) { |
michael@0 | 1812 | cert->slot = PK11_ReferenceSlot(*pSlot); |
michael@0 | 1813 | cert->pkcs11ID = certHandle; |
michael@0 | 1814 | cert->ownSlot = PR_TRUE; |
michael@0 | 1815 | cert->series = cert->slot->series; |
michael@0 | 1816 | } |
michael@0 | 1817 | } |
michael@0 | 1818 | |
michael@0 | 1819 | return(certHandle); |
michael@0 | 1820 | } |
michael@0 | 1821 | |
michael@0 | 1822 | SECKEYPrivateKey * |
michael@0 | 1823 | PK11_FindKeyByAnyCert(CERTCertificate *cert, void *wincx) |
michael@0 | 1824 | { |
michael@0 | 1825 | CK_OBJECT_HANDLE certHandle; |
michael@0 | 1826 | CK_OBJECT_HANDLE keyHandle; |
michael@0 | 1827 | PK11SlotInfo *slot = NULL; |
michael@0 | 1828 | SECKEYPrivateKey *privKey = NULL; |
michael@0 | 1829 | PRBool needLogin; |
michael@0 | 1830 | SECStatus rv; |
michael@0 | 1831 | int err; |
michael@0 | 1832 | |
michael@0 | 1833 | certHandle = PK11_FindObjectForCert(cert, wincx, &slot); |
michael@0 | 1834 | if (certHandle == CK_INVALID_HANDLE) { |
michael@0 | 1835 | return NULL; |
michael@0 | 1836 | } |
michael@0 | 1837 | /* |
michael@0 | 1838 | * prevent a login race condition. If slot is logged in between |
michael@0 | 1839 | * our call to pk11_LoginStillRequired and the |
michael@0 | 1840 | * PK11_MatchItem. The matchItem call will either succeed, or |
michael@0 | 1841 | * we will call it one more time after calling PK11_Authenticate |
michael@0 | 1842 | * (which is a noop on an authenticated token). |
michael@0 | 1843 | */ |
michael@0 | 1844 | needLogin = pk11_LoginStillRequired(slot,wincx); |
michael@0 | 1845 | keyHandle = PK11_MatchItem(slot,certHandle,CKO_PRIVATE_KEY); |
michael@0 | 1846 | if ((keyHandle == CK_INVALID_HANDLE) && needLogin && |
michael@0 | 1847 | (SSL_ERROR_NO_CERTIFICATE == (err = PORT_GetError()) || |
michael@0 | 1848 | SEC_ERROR_TOKEN_NOT_LOGGED_IN == err ) ) { |
michael@0 | 1849 | /* authenticate and try again */ |
michael@0 | 1850 | rv = PK11_Authenticate(slot, PR_TRUE, wincx); |
michael@0 | 1851 | if (rv == SECSuccess) { |
michael@0 | 1852 | keyHandle = PK11_MatchItem(slot,certHandle,CKO_PRIVATE_KEY); |
michael@0 | 1853 | } |
michael@0 | 1854 | } |
michael@0 | 1855 | if (keyHandle != CK_INVALID_HANDLE) { |
michael@0 | 1856 | privKey = PK11_MakePrivKey(slot, nullKey, PR_TRUE, keyHandle, wincx); |
michael@0 | 1857 | } |
michael@0 | 1858 | if (slot) { |
michael@0 | 1859 | PK11_FreeSlot(slot); |
michael@0 | 1860 | } |
michael@0 | 1861 | return privKey; |
michael@0 | 1862 | } |
michael@0 | 1863 | |
michael@0 | 1864 | CK_OBJECT_HANDLE |
michael@0 | 1865 | pk11_FindPubKeyByAnyCert(CERTCertificate *cert, PK11SlotInfo **slot, void *wincx) |
michael@0 | 1866 | { |
michael@0 | 1867 | CK_OBJECT_HANDLE certHandle; |
michael@0 | 1868 | CK_OBJECT_HANDLE keyHandle; |
michael@0 | 1869 | |
michael@0 | 1870 | certHandle = PK11_FindObjectForCert(cert, wincx, slot); |
michael@0 | 1871 | if (certHandle == CK_INVALID_HANDLE) { |
michael@0 | 1872 | return CK_INVALID_HANDLE; |
michael@0 | 1873 | } |
michael@0 | 1874 | keyHandle = PK11_MatchItem(*slot,certHandle,CKO_PUBLIC_KEY); |
michael@0 | 1875 | if (keyHandle == CK_INVALID_HANDLE) { |
michael@0 | 1876 | PK11_FreeSlot(*slot); |
michael@0 | 1877 | return CK_INVALID_HANDLE; |
michael@0 | 1878 | } |
michael@0 | 1879 | return keyHandle; |
michael@0 | 1880 | } |
michael@0 | 1881 | |
michael@0 | 1882 | /* |
michael@0 | 1883 | * find the number of certs in the slot with the same subject name |
michael@0 | 1884 | */ |
michael@0 | 1885 | int |
michael@0 | 1886 | PK11_NumberCertsForCertSubject(CERTCertificate *cert) |
michael@0 | 1887 | { |
michael@0 | 1888 | CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; |
michael@0 | 1889 | CK_ATTRIBUTE theTemplate[] = { |
michael@0 | 1890 | { CKA_CLASS, NULL, 0 }, |
michael@0 | 1891 | { CKA_SUBJECT, NULL, 0 }, |
michael@0 | 1892 | }; |
michael@0 | 1893 | CK_ATTRIBUTE *attr = theTemplate; |
michael@0 | 1894 | int templateSize = sizeof(theTemplate)/sizeof(theTemplate[0]); |
michael@0 | 1895 | |
michael@0 | 1896 | PK11_SETATTRS(attr,CKA_CLASS, &certClass, sizeof(certClass)); attr++; |
michael@0 | 1897 | PK11_SETATTRS(attr,CKA_SUBJECT,cert->derSubject.data,cert->derSubject.len); |
michael@0 | 1898 | |
michael@0 | 1899 | if (cert->slot == NULL) { |
michael@0 | 1900 | PK11SlotList *list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, |
michael@0 | 1901 | PR_FALSE,PR_TRUE,NULL); |
michael@0 | 1902 | PK11SlotListElement *le; |
michael@0 | 1903 | int count = 0; |
michael@0 | 1904 | |
michael@0 | 1905 | if (!list) { |
michael@0 | 1906 | /* error code is set */ |
michael@0 | 1907 | return 0; |
michael@0 | 1908 | } |
michael@0 | 1909 | |
michael@0 | 1910 | /* loop through all the fortezza tokens */ |
michael@0 | 1911 | for (le = list->head; le; le = le->next) { |
michael@0 | 1912 | count += PK11_NumberObjectsFor(le->slot,theTemplate,templateSize); |
michael@0 | 1913 | } |
michael@0 | 1914 | PK11_FreeSlotList(list); |
michael@0 | 1915 | return count; |
michael@0 | 1916 | } |
michael@0 | 1917 | |
michael@0 | 1918 | return PK11_NumberObjectsFor(cert->slot,theTemplate,templateSize); |
michael@0 | 1919 | } |
michael@0 | 1920 | |
michael@0 | 1921 | /* |
michael@0 | 1922 | * Walk all the certs with the same subject |
michael@0 | 1923 | */ |
michael@0 | 1924 | SECStatus |
michael@0 | 1925 | PK11_TraverseCertsForSubject(CERTCertificate *cert, |
michael@0 | 1926 | SECStatus(* callback)(CERTCertificate*, void *), void *arg) |
michael@0 | 1927 | { |
michael@0 | 1928 | if(!cert) { |
michael@0 | 1929 | return SECFailure; |
michael@0 | 1930 | } |
michael@0 | 1931 | if (cert->slot == NULL) { |
michael@0 | 1932 | PK11SlotList *list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, |
michael@0 | 1933 | PR_FALSE,PR_TRUE,NULL); |
michael@0 | 1934 | PK11SlotListElement *le; |
michael@0 | 1935 | |
michael@0 | 1936 | if (!list) { |
michael@0 | 1937 | /* error code is set */ |
michael@0 | 1938 | return SECFailure; |
michael@0 | 1939 | } |
michael@0 | 1940 | /* loop through all the tokens */ |
michael@0 | 1941 | for (le = list->head; le; le = le->next) { |
michael@0 | 1942 | PK11_TraverseCertsForSubjectInSlot(cert,le->slot,callback,arg); |
michael@0 | 1943 | } |
michael@0 | 1944 | PK11_FreeSlotList(list); |
michael@0 | 1945 | return SECSuccess; |
michael@0 | 1946 | |
michael@0 | 1947 | } |
michael@0 | 1948 | |
michael@0 | 1949 | return PK11_TraverseCertsForSubjectInSlot(cert, cert->slot, callback, arg); |
michael@0 | 1950 | } |
michael@0 | 1951 | |
michael@0 | 1952 | SECStatus |
michael@0 | 1953 | PK11_TraverseCertsForSubjectInSlot(CERTCertificate *cert, PK11SlotInfo *slot, |
michael@0 | 1954 | SECStatus(* callback)(CERTCertificate*, void *), void *arg) |
michael@0 | 1955 | { |
michael@0 | 1956 | PRStatus nssrv = PR_SUCCESS; |
michael@0 | 1957 | NSSToken *token; |
michael@0 | 1958 | NSSDER subject; |
michael@0 | 1959 | NSSTrustDomain *td; |
michael@0 | 1960 | nssList *subjectList; |
michael@0 | 1961 | nssPKIObjectCollection *collection; |
michael@0 | 1962 | nssCryptokiObject **instances; |
michael@0 | 1963 | NSSCertificate **certs; |
michael@0 | 1964 | nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly; |
michael@0 | 1965 | td = STAN_GetDefaultTrustDomain(); |
michael@0 | 1966 | NSSITEM_FROM_SECITEM(&subject, &cert->derSubject); |
michael@0 | 1967 | token = PK11Slot_GetNSSToken(slot); |
michael@0 | 1968 | if (!nssToken_IsPresent(token)) { |
michael@0 | 1969 | return SECSuccess; |
michael@0 | 1970 | } |
michael@0 | 1971 | collection = nssCertificateCollection_Create(td, NULL); |
michael@0 | 1972 | if (!collection) { |
michael@0 | 1973 | return SECFailure; |
michael@0 | 1974 | } |
michael@0 | 1975 | subjectList = nssList_Create(NULL, PR_FALSE); |
michael@0 | 1976 | if (!subjectList) { |
michael@0 | 1977 | nssPKIObjectCollection_Destroy(collection); |
michael@0 | 1978 | return SECFailure; |
michael@0 | 1979 | } |
michael@0 | 1980 | (void)nssTrustDomain_GetCertsForSubjectFromCache(td, &subject, |
michael@0 | 1981 | subjectList); |
michael@0 | 1982 | transfer_token_certs_to_collection(subjectList, token, collection); |
michael@0 | 1983 | instances = nssToken_FindCertificatesBySubject(token, NULL, |
michael@0 | 1984 | &subject, |
michael@0 | 1985 | tokenOnly, 0, &nssrv); |
michael@0 | 1986 | nssPKIObjectCollection_AddInstances(collection, instances, 0); |
michael@0 | 1987 | nss_ZFreeIf(instances); |
michael@0 | 1988 | nssList_Destroy(subjectList); |
michael@0 | 1989 | certs = nssPKIObjectCollection_GetCertificates(collection, |
michael@0 | 1990 | NULL, 0, NULL); |
michael@0 | 1991 | nssPKIObjectCollection_Destroy(collection); |
michael@0 | 1992 | if (certs) { |
michael@0 | 1993 | CERTCertificate *oldie; |
michael@0 | 1994 | NSSCertificate **cp; |
michael@0 | 1995 | for (cp = certs; *cp; cp++) { |
michael@0 | 1996 | oldie = STAN_GetCERTCertificate(*cp); |
michael@0 | 1997 | if (!oldie) { |
michael@0 | 1998 | continue; |
michael@0 | 1999 | } |
michael@0 | 2000 | if ((*callback)(oldie, arg) != SECSuccess) { |
michael@0 | 2001 | nssrv = PR_FAILURE; |
michael@0 | 2002 | break; |
michael@0 | 2003 | } |
michael@0 | 2004 | } |
michael@0 | 2005 | nssCertificateArray_Destroy(certs); |
michael@0 | 2006 | } |
michael@0 | 2007 | return (nssrv == PR_SUCCESS) ? SECSuccess : SECFailure; |
michael@0 | 2008 | } |
michael@0 | 2009 | |
michael@0 | 2010 | SECStatus |
michael@0 | 2011 | PK11_TraverseCertsForNicknameInSlot(SECItem *nickname, PK11SlotInfo *slot, |
michael@0 | 2012 | SECStatus(* callback)(CERTCertificate*, void *), void *arg) |
michael@0 | 2013 | { |
michael@0 | 2014 | struct nss3_cert_cbstr pk11cb; |
michael@0 | 2015 | PRStatus nssrv = PR_SUCCESS; |
michael@0 | 2016 | NSSToken *token; |
michael@0 | 2017 | NSSTrustDomain *td; |
michael@0 | 2018 | NSSUTF8 *nick; |
michael@0 | 2019 | PRBool created = PR_FALSE; |
michael@0 | 2020 | nssCryptokiObject **instances; |
michael@0 | 2021 | nssPKIObjectCollection *collection = NULL; |
michael@0 | 2022 | NSSCertificate **certs; |
michael@0 | 2023 | nssList *nameList = NULL; |
michael@0 | 2024 | nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly; |
michael@0 | 2025 | pk11cb.callback = callback; |
michael@0 | 2026 | pk11cb.arg = arg; |
michael@0 | 2027 | token = PK11Slot_GetNSSToken(slot); |
michael@0 | 2028 | if (!nssToken_IsPresent(token)) { |
michael@0 | 2029 | return SECSuccess; |
michael@0 | 2030 | } |
michael@0 | 2031 | if (nickname->data[nickname->len-1] != '\0') { |
michael@0 | 2032 | nick = nssUTF8_Create(NULL, nssStringType_UTF8String, |
michael@0 | 2033 | nickname->data, nickname->len); |
michael@0 | 2034 | created = PR_TRUE; |
michael@0 | 2035 | } else { |
michael@0 | 2036 | nick = (NSSUTF8 *)nickname->data; |
michael@0 | 2037 | } |
michael@0 | 2038 | td = STAN_GetDefaultTrustDomain(); |
michael@0 | 2039 | collection = nssCertificateCollection_Create(td, NULL); |
michael@0 | 2040 | if (!collection) { |
michael@0 | 2041 | goto loser; |
michael@0 | 2042 | } |
michael@0 | 2043 | nameList = nssList_Create(NULL, PR_FALSE); |
michael@0 | 2044 | if (!nameList) { |
michael@0 | 2045 | goto loser; |
michael@0 | 2046 | } |
michael@0 | 2047 | (void)nssTrustDomain_GetCertsForNicknameFromCache(td, nick, nameList); |
michael@0 | 2048 | transfer_token_certs_to_collection(nameList, token, collection); |
michael@0 | 2049 | instances = nssToken_FindCertificatesByNickname(token, NULL, |
michael@0 | 2050 | nick, |
michael@0 | 2051 | tokenOnly, 0, &nssrv); |
michael@0 | 2052 | nssPKIObjectCollection_AddInstances(collection, instances, 0); |
michael@0 | 2053 | nss_ZFreeIf(instances); |
michael@0 | 2054 | nssList_Destroy(nameList); |
michael@0 | 2055 | certs = nssPKIObjectCollection_GetCertificates(collection, |
michael@0 | 2056 | NULL, 0, NULL); |
michael@0 | 2057 | nssPKIObjectCollection_Destroy(collection); |
michael@0 | 2058 | if (certs) { |
michael@0 | 2059 | CERTCertificate *oldie; |
michael@0 | 2060 | NSSCertificate **cp; |
michael@0 | 2061 | for (cp = certs; *cp; cp++) { |
michael@0 | 2062 | oldie = STAN_GetCERTCertificate(*cp); |
michael@0 | 2063 | if (!oldie) { |
michael@0 | 2064 | continue; |
michael@0 | 2065 | } |
michael@0 | 2066 | if ((*callback)(oldie, arg) != SECSuccess) { |
michael@0 | 2067 | nssrv = PR_FAILURE; |
michael@0 | 2068 | break; |
michael@0 | 2069 | } |
michael@0 | 2070 | } |
michael@0 | 2071 | nssCertificateArray_Destroy(certs); |
michael@0 | 2072 | } |
michael@0 | 2073 | if (created) nss_ZFreeIf(nick); |
michael@0 | 2074 | return (nssrv == PR_SUCCESS) ? SECSuccess : SECFailure; |
michael@0 | 2075 | loser: |
michael@0 | 2076 | if (created) { |
michael@0 | 2077 | nss_ZFreeIf(nick); |
michael@0 | 2078 | } |
michael@0 | 2079 | if (collection) { |
michael@0 | 2080 | nssPKIObjectCollection_Destroy(collection); |
michael@0 | 2081 | } |
michael@0 | 2082 | if (nameList) { |
michael@0 | 2083 | nssList_Destroy(nameList); |
michael@0 | 2084 | } |
michael@0 | 2085 | return SECFailure; |
michael@0 | 2086 | } |
michael@0 | 2087 | |
michael@0 | 2088 | SECStatus |
michael@0 | 2089 | PK11_TraverseCertsInSlot(PK11SlotInfo *slot, |
michael@0 | 2090 | SECStatus(* callback)(CERTCertificate*, void *), void *arg) |
michael@0 | 2091 | { |
michael@0 | 2092 | PRStatus nssrv; |
michael@0 | 2093 | NSSTrustDomain *td = STAN_GetDefaultTrustDomain(); |
michael@0 | 2094 | NSSToken *tok; |
michael@0 | 2095 | nssList *certList = NULL; |
michael@0 | 2096 | nssCryptokiObject **instances; |
michael@0 | 2097 | nssPKIObjectCollection *collection; |
michael@0 | 2098 | NSSCertificate **certs; |
michael@0 | 2099 | nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly; |
michael@0 | 2100 | tok = PK11Slot_GetNSSToken(slot); |
michael@0 | 2101 | if (!nssToken_IsPresent(tok)) { |
michael@0 | 2102 | return SECSuccess; |
michael@0 | 2103 | } |
michael@0 | 2104 | collection = nssCertificateCollection_Create(td, NULL); |
michael@0 | 2105 | if (!collection) { |
michael@0 | 2106 | return SECFailure; |
michael@0 | 2107 | } |
michael@0 | 2108 | certList = nssList_Create(NULL, PR_FALSE); |
michael@0 | 2109 | if (!certList) { |
michael@0 | 2110 | nssPKIObjectCollection_Destroy(collection); |
michael@0 | 2111 | return SECFailure; |
michael@0 | 2112 | } |
michael@0 | 2113 | (void)nssTrustDomain_GetCertsFromCache(td, certList); |
michael@0 | 2114 | transfer_token_certs_to_collection(certList, tok, collection); |
michael@0 | 2115 | instances = nssToken_FindObjects(tok, NULL, CKO_CERTIFICATE, |
michael@0 | 2116 | tokenOnly, 0, &nssrv); |
michael@0 | 2117 | nssPKIObjectCollection_AddInstances(collection, instances, 0); |
michael@0 | 2118 | nss_ZFreeIf(instances); |
michael@0 | 2119 | nssList_Destroy(certList); |
michael@0 | 2120 | certs = nssPKIObjectCollection_GetCertificates(collection, |
michael@0 | 2121 | NULL, 0, NULL); |
michael@0 | 2122 | nssPKIObjectCollection_Destroy(collection); |
michael@0 | 2123 | if (certs) { |
michael@0 | 2124 | CERTCertificate *oldie; |
michael@0 | 2125 | NSSCertificate **cp; |
michael@0 | 2126 | for (cp = certs; *cp; cp++) { |
michael@0 | 2127 | oldie = STAN_GetCERTCertificate(*cp); |
michael@0 | 2128 | if (!oldie) { |
michael@0 | 2129 | continue; |
michael@0 | 2130 | } |
michael@0 | 2131 | if ((*callback)(oldie, arg) != SECSuccess) { |
michael@0 | 2132 | nssrv = PR_FAILURE; |
michael@0 | 2133 | break; |
michael@0 | 2134 | } |
michael@0 | 2135 | } |
michael@0 | 2136 | nssCertificateArray_Destroy(certs); |
michael@0 | 2137 | } |
michael@0 | 2138 | return (nssrv == PR_SUCCESS) ? SECSuccess : SECFailure; |
michael@0 | 2139 | } |
michael@0 | 2140 | |
michael@0 | 2141 | /* |
michael@0 | 2142 | * return the certificate associated with a derCert |
michael@0 | 2143 | */ |
michael@0 | 2144 | CERTCertificate * |
michael@0 | 2145 | PK11_FindCertFromDERCert(PK11SlotInfo *slot, CERTCertificate *cert, |
michael@0 | 2146 | void *wincx) |
michael@0 | 2147 | { |
michael@0 | 2148 | return PK11_FindCertFromDERCertItem(slot, &cert->derCert, wincx); |
michael@0 | 2149 | } |
michael@0 | 2150 | |
michael@0 | 2151 | CERTCertificate * |
michael@0 | 2152 | PK11_FindCertFromDERCertItem(PK11SlotInfo *slot, const SECItem *inDerCert, |
michael@0 | 2153 | void *wincx) |
michael@0 | 2154 | |
michael@0 | 2155 | { |
michael@0 | 2156 | NSSDER derCert; |
michael@0 | 2157 | NSSToken *tok; |
michael@0 | 2158 | NSSTrustDomain *td = STAN_GetDefaultTrustDomain(); |
michael@0 | 2159 | nssCryptokiObject *co = NULL; |
michael@0 | 2160 | SECStatus rv; |
michael@0 | 2161 | |
michael@0 | 2162 | tok = PK11Slot_GetNSSToken(slot); |
michael@0 | 2163 | NSSITEM_FROM_SECITEM(&derCert, inDerCert); |
michael@0 | 2164 | rv = pk11_AuthenticateUnfriendly(slot, PR_TRUE, wincx); |
michael@0 | 2165 | if (rv != SECSuccess) { |
michael@0 | 2166 | PK11_FreeSlot(slot); |
michael@0 | 2167 | return NULL; |
michael@0 | 2168 | } |
michael@0 | 2169 | |
michael@0 | 2170 | co = nssToken_FindCertificateByEncodedCertificate(tok, NULL, &derCert, |
michael@0 | 2171 | nssTokenSearchType_TokenOnly, NULL); |
michael@0 | 2172 | |
michael@0 | 2173 | return co ? PK11_MakeCertFromHandle(slot, co->handle, NULL) : NULL; |
michael@0 | 2174 | |
michael@0 | 2175 | } |
michael@0 | 2176 | |
michael@0 | 2177 | /* |
michael@0 | 2178 | * import a cert for a private key we have already generated. Set the label |
michael@0 | 2179 | * on both to be the nickname. |
michael@0 | 2180 | */ |
michael@0 | 2181 | static CK_OBJECT_HANDLE |
michael@0 | 2182 | pk11_findKeyObjectByDERCert(PK11SlotInfo *slot, CERTCertificate *cert, |
michael@0 | 2183 | void *wincx) |
michael@0 | 2184 | { |
michael@0 | 2185 | SECItem *keyID; |
michael@0 | 2186 | CK_OBJECT_HANDLE key; |
michael@0 | 2187 | SECStatus rv; |
michael@0 | 2188 | PRBool needLogin; |
michael@0 | 2189 | int err; |
michael@0 | 2190 | |
michael@0 | 2191 | if((slot == NULL) || (cert == NULL)) { |
michael@0 | 2192 | return CK_INVALID_HANDLE; |
michael@0 | 2193 | } |
michael@0 | 2194 | |
michael@0 | 2195 | keyID = pk11_mkcertKeyID(cert); |
michael@0 | 2196 | if(keyID == NULL) { |
michael@0 | 2197 | return CK_INVALID_HANDLE; |
michael@0 | 2198 | } |
michael@0 | 2199 | |
michael@0 | 2200 | /* |
michael@0 | 2201 | * prevent a login race condition. If slot is logged in between |
michael@0 | 2202 | * our call to pk11_LoginStillRequired and the |
michael@0 | 2203 | * pk11_FindPrivateKeyFromCerID. The matchItem call will either succeed, or |
michael@0 | 2204 | * we will call it one more time after calling PK11_Authenticate |
michael@0 | 2205 | * (which is a noop on an authenticated token). |
michael@0 | 2206 | */ |
michael@0 | 2207 | needLogin = pk11_LoginStillRequired(slot,wincx); |
michael@0 | 2208 | key = pk11_FindPrivateKeyFromCertID(slot, keyID); |
michael@0 | 2209 | if ((key == CK_INVALID_HANDLE) && needLogin && |
michael@0 | 2210 | (SSL_ERROR_NO_CERTIFICATE == (err = PORT_GetError()) || |
michael@0 | 2211 | SEC_ERROR_TOKEN_NOT_LOGGED_IN == err )) { |
michael@0 | 2212 | /* authenticate and try again */ |
michael@0 | 2213 | rv = PK11_Authenticate(slot, PR_TRUE, wincx); |
michael@0 | 2214 | if (rv != SECSuccess) goto loser; |
michael@0 | 2215 | key = pk11_FindPrivateKeyFromCertID(slot, keyID); |
michael@0 | 2216 | } |
michael@0 | 2217 | |
michael@0 | 2218 | loser: |
michael@0 | 2219 | SECITEM_ZfreeItem(keyID, PR_TRUE); |
michael@0 | 2220 | return key; |
michael@0 | 2221 | } |
michael@0 | 2222 | |
michael@0 | 2223 | SECKEYPrivateKey * |
michael@0 | 2224 | PK11_FindKeyByDERCert(PK11SlotInfo *slot, CERTCertificate *cert, |
michael@0 | 2225 | void *wincx) |
michael@0 | 2226 | { |
michael@0 | 2227 | CK_OBJECT_HANDLE keyHandle; |
michael@0 | 2228 | |
michael@0 | 2229 | if((slot == NULL) || (cert == NULL)) { |
michael@0 | 2230 | return NULL; |
michael@0 | 2231 | } |
michael@0 | 2232 | |
michael@0 | 2233 | keyHandle = pk11_findKeyObjectByDERCert(slot, cert, wincx); |
michael@0 | 2234 | if (keyHandle == CK_INVALID_HANDLE) { |
michael@0 | 2235 | return NULL; |
michael@0 | 2236 | } |
michael@0 | 2237 | |
michael@0 | 2238 | return PK11_MakePrivKey(slot,nullKey,PR_TRUE,keyHandle,wincx); |
michael@0 | 2239 | } |
michael@0 | 2240 | |
michael@0 | 2241 | SECStatus |
michael@0 | 2242 | PK11_ImportCertForKeyToSlot(PK11SlotInfo *slot, CERTCertificate *cert, |
michael@0 | 2243 | char *nickname, |
michael@0 | 2244 | PRBool addCertUsage,void *wincx) |
michael@0 | 2245 | { |
michael@0 | 2246 | CK_OBJECT_HANDLE keyHandle; |
michael@0 | 2247 | |
michael@0 | 2248 | if((slot == NULL) || (cert == NULL) || (nickname == NULL)) { |
michael@0 | 2249 | return SECFailure; |
michael@0 | 2250 | } |
michael@0 | 2251 | |
michael@0 | 2252 | keyHandle = pk11_findKeyObjectByDERCert(slot, cert, wincx); |
michael@0 | 2253 | if (keyHandle == CK_INVALID_HANDLE) { |
michael@0 | 2254 | return SECFailure; |
michael@0 | 2255 | } |
michael@0 | 2256 | |
michael@0 | 2257 | return PK11_ImportCert(slot, cert, keyHandle, nickname, addCertUsage); |
michael@0 | 2258 | } |
michael@0 | 2259 | |
michael@0 | 2260 | |
michael@0 | 2261 | /* remove when the real version comes out */ |
michael@0 | 2262 | #define SEC_OID_MISSI_KEA 300 /* until we have v3 stuff merged */ |
michael@0 | 2263 | PRBool |
michael@0 | 2264 | KEAPQGCompare(CERTCertificate *server,CERTCertificate *cert) { |
michael@0 | 2265 | |
michael@0 | 2266 | /* not implemented */ |
michael@0 | 2267 | return PR_FALSE; |
michael@0 | 2268 | } |
michael@0 | 2269 | |
michael@0 | 2270 | PRBool |
michael@0 | 2271 | PK11_FortezzaHasKEA(CERTCertificate *cert) |
michael@0 | 2272 | { |
michael@0 | 2273 | /* look at the subject and see if it is a KEA for MISSI key */ |
michael@0 | 2274 | SECOidData *oid; |
michael@0 | 2275 | CERTCertTrust trust; |
michael@0 | 2276 | |
michael@0 | 2277 | if (CERT_GetCertTrust(cert, &trust) != SECSuccess || |
michael@0 | 2278 | ((trust.sslFlags & CERTDB_USER) != CERTDB_USER)) { |
michael@0 | 2279 | return PR_FALSE; |
michael@0 | 2280 | } |
michael@0 | 2281 | |
michael@0 | 2282 | oid = SECOID_FindOID(&cert->subjectPublicKeyInfo.algorithm.algorithm); |
michael@0 | 2283 | if (!oid) { |
michael@0 | 2284 | return PR_FALSE; |
michael@0 | 2285 | } |
michael@0 | 2286 | |
michael@0 | 2287 | return (PRBool)((oid->offset == SEC_OID_MISSI_KEA_DSS_OLD) || |
michael@0 | 2288 | (oid->offset == SEC_OID_MISSI_KEA_DSS) || |
michael@0 | 2289 | (oid->offset == SEC_OID_MISSI_KEA)) ; |
michael@0 | 2290 | } |
michael@0 | 2291 | |
michael@0 | 2292 | /* |
michael@0 | 2293 | * Find a kea cert on this slot that matches the domain of it's peer |
michael@0 | 2294 | */ |
michael@0 | 2295 | static CERTCertificate |
michael@0 | 2296 | *pk11_GetKEAMate(PK11SlotInfo *slot,CERTCertificate *peer) |
michael@0 | 2297 | { |
michael@0 | 2298 | int i; |
michael@0 | 2299 | CERTCertificate *returnedCert = NULL; |
michael@0 | 2300 | |
michael@0 | 2301 | for (i=0; i < slot->cert_count; i++) { |
michael@0 | 2302 | CERTCertificate *cert = slot->cert_array[i]; |
michael@0 | 2303 | |
michael@0 | 2304 | if (PK11_FortezzaHasKEA(cert) && KEAPQGCompare(peer,cert)) { |
michael@0 | 2305 | returnedCert = CERT_DupCertificate(cert); |
michael@0 | 2306 | break; |
michael@0 | 2307 | } |
michael@0 | 2308 | } |
michael@0 | 2309 | return returnedCert; |
michael@0 | 2310 | } |
michael@0 | 2311 | |
michael@0 | 2312 | /* |
michael@0 | 2313 | * The following is a FORTEZZA only Certificate request. We call this when we |
michael@0 | 2314 | * are doing a non-client auth SSL connection. We are only interested in the |
michael@0 | 2315 | * fortezza slots, and we are only interested in certs that share the same root |
michael@0 | 2316 | * key as the server. |
michael@0 | 2317 | */ |
michael@0 | 2318 | CERTCertificate * |
michael@0 | 2319 | PK11_FindBestKEAMatch(CERTCertificate *server, void *wincx) |
michael@0 | 2320 | { |
michael@0 | 2321 | PK11SlotList *keaList = PK11_GetAllTokens(CKM_KEA_KEY_DERIVE, |
michael@0 | 2322 | PR_FALSE,PR_TRUE,wincx); |
michael@0 | 2323 | PK11SlotListElement *le; |
michael@0 | 2324 | CERTCertificate *returnedCert = NULL; |
michael@0 | 2325 | SECStatus rv; |
michael@0 | 2326 | |
michael@0 | 2327 | if (!keaList) { |
michael@0 | 2328 | /* error code is set */ |
michael@0 | 2329 | return NULL; |
michael@0 | 2330 | } |
michael@0 | 2331 | |
michael@0 | 2332 | /* loop through all the fortezza tokens */ |
michael@0 | 2333 | for (le = keaList->head; le; le = le->next) { |
michael@0 | 2334 | rv = PK11_Authenticate(le->slot, PR_TRUE, wincx); |
michael@0 | 2335 | if (rv != SECSuccess) continue; |
michael@0 | 2336 | if (le->slot->session == CK_INVALID_SESSION) { |
michael@0 | 2337 | continue; |
michael@0 | 2338 | } |
michael@0 | 2339 | returnedCert = pk11_GetKEAMate(le->slot,server); |
michael@0 | 2340 | if (returnedCert) break; |
michael@0 | 2341 | } |
michael@0 | 2342 | PK11_FreeSlotList(keaList); |
michael@0 | 2343 | |
michael@0 | 2344 | return returnedCert; |
michael@0 | 2345 | } |
michael@0 | 2346 | |
michael@0 | 2347 | /* |
michael@0 | 2348 | * find a matched pair of kea certs to key exchange parameters from one |
michael@0 | 2349 | * fortezza card to another as necessary. |
michael@0 | 2350 | */ |
michael@0 | 2351 | SECStatus |
michael@0 | 2352 | PK11_GetKEAMatchedCerts(PK11SlotInfo *slot1, PK11SlotInfo *slot2, |
michael@0 | 2353 | CERTCertificate **cert1, CERTCertificate **cert2) |
michael@0 | 2354 | { |
michael@0 | 2355 | CERTCertificate *returnedCert = NULL; |
michael@0 | 2356 | int i; |
michael@0 | 2357 | |
michael@0 | 2358 | for (i=0; i < slot1->cert_count; i++) { |
michael@0 | 2359 | CERTCertificate *cert = slot1->cert_array[i]; |
michael@0 | 2360 | |
michael@0 | 2361 | if (PK11_FortezzaHasKEA(cert)) { |
michael@0 | 2362 | returnedCert = pk11_GetKEAMate(slot2,cert); |
michael@0 | 2363 | if (returnedCert != NULL) { |
michael@0 | 2364 | *cert2 = returnedCert; |
michael@0 | 2365 | *cert1 = CERT_DupCertificate(cert); |
michael@0 | 2366 | return SECSuccess; |
michael@0 | 2367 | } |
michael@0 | 2368 | } |
michael@0 | 2369 | } |
michael@0 | 2370 | return SECFailure; |
michael@0 | 2371 | } |
michael@0 | 2372 | |
michael@0 | 2373 | /* |
michael@0 | 2374 | * return the private key From a given Cert |
michael@0 | 2375 | */ |
michael@0 | 2376 | CK_OBJECT_HANDLE |
michael@0 | 2377 | PK11_FindCertInSlot(PK11SlotInfo *slot, CERTCertificate *cert, void *wincx) |
michael@0 | 2378 | { |
michael@0 | 2379 | CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; |
michael@0 | 2380 | CK_ATTRIBUTE theTemplate[] = { |
michael@0 | 2381 | { CKA_VALUE, NULL, 0 }, |
michael@0 | 2382 | { CKA_CLASS, NULL, 0 } |
michael@0 | 2383 | }; |
michael@0 | 2384 | /* if you change the array, change the variable below as well */ |
michael@0 | 2385 | int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]); |
michael@0 | 2386 | CK_ATTRIBUTE *attrs = theTemplate; |
michael@0 | 2387 | SECStatus rv; |
michael@0 | 2388 | |
michael@0 | 2389 | PK11_SETATTRS(attrs, CKA_VALUE, cert->derCert.data, |
michael@0 | 2390 | cert->derCert.len); attrs++; |
michael@0 | 2391 | PK11_SETATTRS(attrs, CKA_CLASS, &certClass, sizeof(certClass)); |
michael@0 | 2392 | |
michael@0 | 2393 | /* |
michael@0 | 2394 | * issue the find |
michael@0 | 2395 | */ |
michael@0 | 2396 | rv = pk11_AuthenticateUnfriendly(slot, PR_TRUE, wincx); |
michael@0 | 2397 | if (rv != SECSuccess) { |
michael@0 | 2398 | return CK_INVALID_HANDLE; |
michael@0 | 2399 | } |
michael@0 | 2400 | |
michael@0 | 2401 | return pk11_getcerthandle(slot,cert,theTemplate,tsize); |
michael@0 | 2402 | } |
michael@0 | 2403 | |
michael@0 | 2404 | /* Looking for PK11_GetKeyIDFromCert? |
michael@0 | 2405 | * Use PK11_GetLowLevelKeyIDForCert instead. |
michael@0 | 2406 | */ |
michael@0 | 2407 | |
michael@0 | 2408 | |
michael@0 | 2409 | struct listCertsStr { |
michael@0 | 2410 | PK11CertListType type; |
michael@0 | 2411 | CERTCertList *certList; |
michael@0 | 2412 | }; |
michael@0 | 2413 | |
michael@0 | 2414 | static PRStatus |
michael@0 | 2415 | pk11ListCertCallback(NSSCertificate *c, void *arg) |
michael@0 | 2416 | { |
michael@0 | 2417 | struct listCertsStr *listCertP = (struct listCertsStr *)arg; |
michael@0 | 2418 | CERTCertificate *newCert = NULL; |
michael@0 | 2419 | PK11CertListType type = listCertP->type; |
michael@0 | 2420 | CERTCertList *certList = listCertP->certList; |
michael@0 | 2421 | PRBool isUnique = PR_FALSE; |
michael@0 | 2422 | PRBool isCA = PR_FALSE; |
michael@0 | 2423 | char *nickname = NULL; |
michael@0 | 2424 | unsigned int certType; |
michael@0 | 2425 | SECStatus rv; |
michael@0 | 2426 | |
michael@0 | 2427 | if ((type == PK11CertListUnique) || (type == PK11CertListRootUnique) || |
michael@0 | 2428 | (type == PK11CertListCAUnique) || (type == PK11CertListUserUnique) ) { |
michael@0 | 2429 | /* only list one instance of each certificate, even if several exist */ |
michael@0 | 2430 | isUnique = PR_TRUE; |
michael@0 | 2431 | } |
michael@0 | 2432 | if ((type == PK11CertListCA) || (type == PK11CertListRootUnique) || |
michael@0 | 2433 | (type == PK11CertListCAUnique)) { |
michael@0 | 2434 | isCA = PR_TRUE; |
michael@0 | 2435 | } |
michael@0 | 2436 | |
michael@0 | 2437 | /* if we want user certs and we don't have one skip this cert */ |
michael@0 | 2438 | if ( ( (type == PK11CertListUser) || (type == PK11CertListUserUnique) ) && |
michael@0 | 2439 | !NSSCertificate_IsPrivateKeyAvailable(c, NULL,NULL)) { |
michael@0 | 2440 | return PR_SUCCESS; |
michael@0 | 2441 | } |
michael@0 | 2442 | |
michael@0 | 2443 | /* PK11CertListRootUnique means we want CA certs without a private key. |
michael@0 | 2444 | * This is for legacy app support . PK11CertListCAUnique should be used |
michael@0 | 2445 | * instead to get all CA certs, regardless of private key |
michael@0 | 2446 | */ |
michael@0 | 2447 | if ((type == PK11CertListRootUnique) && |
michael@0 | 2448 | NSSCertificate_IsPrivateKeyAvailable(c, NULL,NULL)) { |
michael@0 | 2449 | return PR_SUCCESS; |
michael@0 | 2450 | } |
michael@0 | 2451 | |
michael@0 | 2452 | /* caller still owns the reference to 'c' */ |
michael@0 | 2453 | newCert = STAN_GetCERTCertificate(c); |
michael@0 | 2454 | if (!newCert) { |
michael@0 | 2455 | return PR_SUCCESS; |
michael@0 | 2456 | } |
michael@0 | 2457 | /* if we want CA certs and it ain't one, skip it */ |
michael@0 | 2458 | if( isCA && (!CERT_IsCACert(newCert, &certType)) ) { |
michael@0 | 2459 | return PR_SUCCESS; |
michael@0 | 2460 | } |
michael@0 | 2461 | if (isUnique) { |
michael@0 | 2462 | CERT_DupCertificate(newCert); |
michael@0 | 2463 | |
michael@0 | 2464 | nickname = STAN_GetCERTCertificateName(certList->arena, c); |
michael@0 | 2465 | |
michael@0 | 2466 | /* put slot certs at the end */ |
michael@0 | 2467 | if (newCert->slot && !PK11_IsInternal(newCert->slot)) { |
michael@0 | 2468 | rv = CERT_AddCertToListTailWithData(certList,newCert,nickname); |
michael@0 | 2469 | } else { |
michael@0 | 2470 | rv = CERT_AddCertToListHeadWithData(certList,newCert,nickname); |
michael@0 | 2471 | } |
michael@0 | 2472 | /* if we didn't add the cert to the list, don't leak it */ |
michael@0 | 2473 | if (rv != SECSuccess) { |
michael@0 | 2474 | CERT_DestroyCertificate(newCert); |
michael@0 | 2475 | } |
michael@0 | 2476 | } else { |
michael@0 | 2477 | /* add multiple instances to the cert list */ |
michael@0 | 2478 | nssCryptokiObject **ip; |
michael@0 | 2479 | nssCryptokiObject **instances = nssPKIObject_GetInstances(&c->object); |
michael@0 | 2480 | if (!instances) { |
michael@0 | 2481 | return PR_SUCCESS; |
michael@0 | 2482 | } |
michael@0 | 2483 | for (ip = instances; *ip; ip++) { |
michael@0 | 2484 | nssCryptokiObject *instance = *ip; |
michael@0 | 2485 | PK11SlotInfo *slot = instance->token->pk11slot; |
michael@0 | 2486 | |
michael@0 | 2487 | /* put the same CERTCertificate in the list for all instances */ |
michael@0 | 2488 | CERT_DupCertificate(newCert); |
michael@0 | 2489 | |
michael@0 | 2490 | nickname = STAN_GetCERTCertificateNameForInstance( |
michael@0 | 2491 | certList->arena, c, instance); |
michael@0 | 2492 | |
michael@0 | 2493 | /* put slot certs at the end */ |
michael@0 | 2494 | if (slot && !PK11_IsInternal(slot)) { |
michael@0 | 2495 | rv = CERT_AddCertToListTailWithData(certList,newCert,nickname); |
michael@0 | 2496 | } else { |
michael@0 | 2497 | rv = CERT_AddCertToListHeadWithData(certList,newCert,nickname); |
michael@0 | 2498 | } |
michael@0 | 2499 | /* if we didn't add the cert to the list, don't leak it */ |
michael@0 | 2500 | if (rv != SECSuccess) { |
michael@0 | 2501 | CERT_DestroyCertificate(newCert); |
michael@0 | 2502 | } |
michael@0 | 2503 | } |
michael@0 | 2504 | nssCryptokiObjectArray_Destroy(instances); |
michael@0 | 2505 | } |
michael@0 | 2506 | return PR_SUCCESS; |
michael@0 | 2507 | } |
michael@0 | 2508 | |
michael@0 | 2509 | |
michael@0 | 2510 | CERTCertList * |
michael@0 | 2511 | PK11_ListCerts(PK11CertListType type, void *pwarg) |
michael@0 | 2512 | { |
michael@0 | 2513 | NSSTrustDomain *defaultTD = STAN_GetDefaultTrustDomain(); |
michael@0 | 2514 | CERTCertList *certList = NULL; |
michael@0 | 2515 | struct listCertsStr listCerts; |
michael@0 | 2516 | certList = CERT_NewCertList(); |
michael@0 | 2517 | listCerts.type = type; |
michael@0 | 2518 | listCerts.certList = certList; |
michael@0 | 2519 | |
michael@0 | 2520 | /* authenticate to the slots */ |
michael@0 | 2521 | (void) pk11_TraverseAllSlots( NULL, NULL, PR_TRUE, pwarg); |
michael@0 | 2522 | NSSTrustDomain_TraverseCertificates(defaultTD, pk11ListCertCallback, |
michael@0 | 2523 | &listCerts); |
michael@0 | 2524 | return certList; |
michael@0 | 2525 | } |
michael@0 | 2526 | |
michael@0 | 2527 | SECItem * |
michael@0 | 2528 | PK11_GetLowLevelKeyIDForCert(PK11SlotInfo *slot, |
michael@0 | 2529 | CERTCertificate *cert, void *wincx) |
michael@0 | 2530 | { |
michael@0 | 2531 | CK_OBJECT_CLASS certClass = CKO_CERTIFICATE; |
michael@0 | 2532 | CK_ATTRIBUTE theTemplate[] = { |
michael@0 | 2533 | { CKA_VALUE, NULL, 0 }, |
michael@0 | 2534 | { CKA_CLASS, NULL, 0 } |
michael@0 | 2535 | }; |
michael@0 | 2536 | /* if you change the array, change the variable below as well */ |
michael@0 | 2537 | int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]); |
michael@0 | 2538 | CK_OBJECT_HANDLE certHandle; |
michael@0 | 2539 | CK_ATTRIBUTE *attrs = theTemplate; |
michael@0 | 2540 | PK11SlotInfo *slotRef = NULL; |
michael@0 | 2541 | SECItem *item; |
michael@0 | 2542 | SECStatus rv; |
michael@0 | 2543 | |
michael@0 | 2544 | if (slot) { |
michael@0 | 2545 | PK11_SETATTRS(attrs, CKA_VALUE, cert->derCert.data, |
michael@0 | 2546 | cert->derCert.len); attrs++; |
michael@0 | 2547 | PK11_SETATTRS(attrs, CKA_CLASS, &certClass, sizeof(certClass)); |
michael@0 | 2548 | |
michael@0 | 2549 | rv = pk11_AuthenticateUnfriendly(slot, PR_TRUE, wincx); |
michael@0 | 2550 | if (rv != SECSuccess) { |
michael@0 | 2551 | return NULL; |
michael@0 | 2552 | } |
michael@0 | 2553 | certHandle = pk11_getcerthandle(slot,cert,theTemplate,tsize); |
michael@0 | 2554 | } else { |
michael@0 | 2555 | certHandle = PK11_FindObjectForCert(cert, wincx, &slotRef); |
michael@0 | 2556 | if (certHandle == CK_INVALID_HANDLE) { |
michael@0 | 2557 | return pk11_mkcertKeyID(cert); |
michael@0 | 2558 | } |
michael@0 | 2559 | slot = slotRef; |
michael@0 | 2560 | } |
michael@0 | 2561 | |
michael@0 | 2562 | if (certHandle == CK_INVALID_HANDLE) { |
michael@0 | 2563 | return NULL; |
michael@0 | 2564 | } |
michael@0 | 2565 | |
michael@0 | 2566 | item = pk11_GetLowLevelKeyFromHandle(slot,certHandle); |
michael@0 | 2567 | if (slotRef) PK11_FreeSlot(slotRef); |
michael@0 | 2568 | return item; |
michael@0 | 2569 | } |
michael@0 | 2570 | |
michael@0 | 2571 | /* argument type for listCertsCallback */ |
michael@0 | 2572 | typedef struct { |
michael@0 | 2573 | CERTCertList *list; |
michael@0 | 2574 | PK11SlotInfo *slot; |
michael@0 | 2575 | } ListCertsArg; |
michael@0 | 2576 | |
michael@0 | 2577 | static SECStatus |
michael@0 | 2578 | listCertsCallback(CERTCertificate* cert, void*arg) |
michael@0 | 2579 | { |
michael@0 | 2580 | ListCertsArg *cdata = (ListCertsArg*)arg; |
michael@0 | 2581 | char *nickname = NULL; |
michael@0 | 2582 | nssCryptokiObject *instance, **ci; |
michael@0 | 2583 | nssCryptokiObject **instances; |
michael@0 | 2584 | NSSCertificate *c = STAN_GetNSSCertificate(cert); |
michael@0 | 2585 | SECStatus rv; |
michael@0 | 2586 | |
michael@0 | 2587 | if (c == NULL) { |
michael@0 | 2588 | return SECFailure; |
michael@0 | 2589 | } |
michael@0 | 2590 | instances = nssPKIObject_GetInstances(&c->object); |
michael@0 | 2591 | if (!instances) { |
michael@0 | 2592 | return SECFailure; |
michael@0 | 2593 | } |
michael@0 | 2594 | instance = NULL; |
michael@0 | 2595 | for (ci = instances; *ci; ci++) { |
michael@0 | 2596 | if ((*ci)->token->pk11slot == cdata->slot) { |
michael@0 | 2597 | instance = *ci; |
michael@0 | 2598 | break; |
michael@0 | 2599 | } |
michael@0 | 2600 | } |
michael@0 | 2601 | PORT_Assert(instance != NULL); |
michael@0 | 2602 | if (!instance) { |
michael@0 | 2603 | nssCryptokiObjectArray_Destroy(instances); |
michael@0 | 2604 | PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
michael@0 | 2605 | return SECFailure; |
michael@0 | 2606 | } |
michael@0 | 2607 | nickname = STAN_GetCERTCertificateNameForInstance(cdata->list->arena, |
michael@0 | 2608 | c, instance); |
michael@0 | 2609 | nssCryptokiObjectArray_Destroy(instances); |
michael@0 | 2610 | |
michael@0 | 2611 | CERT_DupCertificate(cert); |
michael@0 | 2612 | rv = CERT_AddCertToListTailWithData(cdata->list, cert, nickname); |
michael@0 | 2613 | if (rv != SECSuccess) { |
michael@0 | 2614 | CERT_DestroyCertificate(cert); |
michael@0 | 2615 | } |
michael@0 | 2616 | return rv; |
michael@0 | 2617 | } |
michael@0 | 2618 | |
michael@0 | 2619 | CERTCertList * |
michael@0 | 2620 | PK11_ListCertsInSlot(PK11SlotInfo *slot) |
michael@0 | 2621 | { |
michael@0 | 2622 | SECStatus status; |
michael@0 | 2623 | CERTCertList *certs; |
michael@0 | 2624 | ListCertsArg cdata; |
michael@0 | 2625 | |
michael@0 | 2626 | certs = CERT_NewCertList(); |
michael@0 | 2627 | if(certs == NULL) return NULL; |
michael@0 | 2628 | cdata.list = certs; |
michael@0 | 2629 | cdata.slot = slot; |
michael@0 | 2630 | |
michael@0 | 2631 | status = PK11_TraverseCertsInSlot(slot, listCertsCallback, |
michael@0 | 2632 | &cdata); |
michael@0 | 2633 | |
michael@0 | 2634 | if( status != SECSuccess ) { |
michael@0 | 2635 | CERT_DestroyCertList(certs); |
michael@0 | 2636 | certs = NULL; |
michael@0 | 2637 | } |
michael@0 | 2638 | |
michael@0 | 2639 | return certs; |
michael@0 | 2640 | } |
michael@0 | 2641 | |
michael@0 | 2642 | PK11SlotList * |
michael@0 | 2643 | PK11_GetAllSlotsForCert(CERTCertificate *cert, void *arg) |
michael@0 | 2644 | { |
michael@0 | 2645 | nssCryptokiObject **ip; |
michael@0 | 2646 | PK11SlotList *slotList; |
michael@0 | 2647 | NSSCertificate *c; |
michael@0 | 2648 | nssCryptokiObject **instances; |
michael@0 | 2649 | PRBool found = PR_FALSE; |
michael@0 | 2650 | |
michael@0 | 2651 | if (!cert) { |
michael@0 | 2652 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
michael@0 | 2653 | return NULL; |
michael@0 | 2654 | } |
michael@0 | 2655 | |
michael@0 | 2656 | c = STAN_GetNSSCertificate(cert); |
michael@0 | 2657 | if (!c) { |
michael@0 | 2658 | CERT_MapStanError(); |
michael@0 | 2659 | return NULL; |
michael@0 | 2660 | } |
michael@0 | 2661 | |
michael@0 | 2662 | /* add multiple instances to the cert list */ |
michael@0 | 2663 | instances = nssPKIObject_GetInstances(&c->object); |
michael@0 | 2664 | if (!instances) { |
michael@0 | 2665 | PORT_SetError(SEC_ERROR_NO_TOKEN); |
michael@0 | 2666 | return NULL; |
michael@0 | 2667 | } |
michael@0 | 2668 | |
michael@0 | 2669 | slotList = PK11_NewSlotList(); |
michael@0 | 2670 | if (!slotList) { |
michael@0 | 2671 | nssCryptokiObjectArray_Destroy(instances); |
michael@0 | 2672 | return NULL; |
michael@0 | 2673 | } |
michael@0 | 2674 | |
michael@0 | 2675 | for (ip = instances; *ip; ip++) { |
michael@0 | 2676 | nssCryptokiObject *instance = *ip; |
michael@0 | 2677 | PK11SlotInfo *slot = instance->token->pk11slot; |
michael@0 | 2678 | if (slot) { |
michael@0 | 2679 | PK11_AddSlotToList(slotList, slot, PR_TRUE); |
michael@0 | 2680 | found = PR_TRUE; |
michael@0 | 2681 | } |
michael@0 | 2682 | } |
michael@0 | 2683 | if (!found) { |
michael@0 | 2684 | PK11_FreeSlotList(slotList); |
michael@0 | 2685 | PORT_SetError(SEC_ERROR_NO_TOKEN); |
michael@0 | 2686 | slotList = NULL; |
michael@0 | 2687 | } |
michael@0 | 2688 | |
michael@0 | 2689 | nssCryptokiObjectArray_Destroy(instances); |
michael@0 | 2690 | return slotList; |
michael@0 | 2691 | } |