security/nss/lib/certdb/certdb.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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 /*
michael@0 6 * Certificate handling code
michael@0 7 */
michael@0 8
michael@0 9 #include "nssilock.h"
michael@0 10 #include "prmon.h"
michael@0 11 #include "prtime.h"
michael@0 12 #include "cert.h"
michael@0 13 #include "certi.h"
michael@0 14 #include "secder.h"
michael@0 15 #include "secoid.h"
michael@0 16 #include "secasn1.h"
michael@0 17 #include "genname.h"
michael@0 18 #include "keyhi.h"
michael@0 19 #include "secitem.h"
michael@0 20 #include "certdb.h"
michael@0 21 #include "prprf.h"
michael@0 22 #include "sechash.h"
michael@0 23 #include "prlong.h"
michael@0 24 #include "certxutl.h"
michael@0 25 #include "portreg.h"
michael@0 26 #include "secerr.h"
michael@0 27 #include "sslerr.h"
michael@0 28 #include "pk11func.h"
michael@0 29 #include "xconst.h" /* for CERT_DecodeAltNameExtension */
michael@0 30
michael@0 31 #include "pki.h"
michael@0 32 #include "pki3hack.h"
michael@0 33
michael@0 34 SEC_ASN1_MKSUB(CERT_TimeChoiceTemplate)
michael@0 35 SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
michael@0 36 SEC_ASN1_MKSUB(SEC_BitStringTemplate)
michael@0 37 SEC_ASN1_MKSUB(SEC_IntegerTemplate)
michael@0 38 SEC_ASN1_MKSUB(SEC_SkipTemplate)
michael@0 39
michael@0 40 /*
michael@0 41 * Certificate database handling code
michael@0 42 */
michael@0 43
michael@0 44
michael@0 45 const SEC_ASN1Template CERT_CertExtensionTemplate[] = {
michael@0 46 { SEC_ASN1_SEQUENCE,
michael@0 47 0, NULL, sizeof(CERTCertExtension) },
michael@0 48 { SEC_ASN1_OBJECT_ID,
michael@0 49 offsetof(CERTCertExtension,id) },
michael@0 50 { SEC_ASN1_OPTIONAL | SEC_ASN1_BOOLEAN, /* XXX DER_DEFAULT */
michael@0 51 offsetof(CERTCertExtension,critical) },
michael@0 52 { SEC_ASN1_OCTET_STRING,
michael@0 53 offsetof(CERTCertExtension,value) },
michael@0 54 { 0, }
michael@0 55 };
michael@0 56
michael@0 57 const SEC_ASN1Template CERT_SequenceOfCertExtensionTemplate[] = {
michael@0 58 { SEC_ASN1_SEQUENCE_OF, 0, CERT_CertExtensionTemplate }
michael@0 59 };
michael@0 60
michael@0 61 const SEC_ASN1Template CERT_TimeChoiceTemplate[] = {
michael@0 62 { SEC_ASN1_CHOICE, offsetof(SECItem, type), 0, sizeof(SECItem) },
michael@0 63 { SEC_ASN1_UTC_TIME, 0, 0, siUTCTime },
michael@0 64 { SEC_ASN1_GENERALIZED_TIME, 0, 0, siGeneralizedTime },
michael@0 65 { 0 }
michael@0 66 };
michael@0 67
michael@0 68 const SEC_ASN1Template CERT_ValidityTemplate[] = {
michael@0 69 { SEC_ASN1_SEQUENCE,
michael@0 70 0, NULL, sizeof(CERTValidity) },
michael@0 71 { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
michael@0 72 offsetof(CERTValidity,notBefore),
michael@0 73 SEC_ASN1_SUB(CERT_TimeChoiceTemplate), 0 },
michael@0 74 { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
michael@0 75 offsetof(CERTValidity,notAfter),
michael@0 76 SEC_ASN1_SUB(CERT_TimeChoiceTemplate), 0 },
michael@0 77 { 0 }
michael@0 78 };
michael@0 79
michael@0 80 const SEC_ASN1Template CERT_CertificateTemplate[] = {
michael@0 81 { SEC_ASN1_SEQUENCE,
michael@0 82 0, NULL, sizeof(CERTCertificate) },
michael@0 83 { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
michael@0 84 SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, /* XXX DER_DEFAULT */
michael@0 85 offsetof(CERTCertificate,version),
michael@0 86 SEC_ASN1_SUB(SEC_IntegerTemplate) },
michael@0 87 { SEC_ASN1_INTEGER,
michael@0 88 offsetof(CERTCertificate,serialNumber) },
michael@0 89 { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
michael@0 90 offsetof(CERTCertificate,signature),
michael@0 91 SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
michael@0 92 { SEC_ASN1_SAVE,
michael@0 93 offsetof(CERTCertificate,derIssuer) },
michael@0 94 { SEC_ASN1_INLINE,
michael@0 95 offsetof(CERTCertificate,issuer),
michael@0 96 CERT_NameTemplate },
michael@0 97 { SEC_ASN1_INLINE,
michael@0 98 offsetof(CERTCertificate,validity),
michael@0 99 CERT_ValidityTemplate },
michael@0 100 { SEC_ASN1_SAVE,
michael@0 101 offsetof(CERTCertificate,derSubject) },
michael@0 102 { SEC_ASN1_INLINE,
michael@0 103 offsetof(CERTCertificate,subject),
michael@0 104 CERT_NameTemplate },
michael@0 105 { SEC_ASN1_SAVE,
michael@0 106 offsetof(CERTCertificate,derPublicKey) },
michael@0 107 { SEC_ASN1_INLINE,
michael@0 108 offsetof(CERTCertificate,subjectPublicKeyInfo),
michael@0 109 CERT_SubjectPublicKeyInfoTemplate },
michael@0 110 { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 1,
michael@0 111 offsetof(CERTCertificate,issuerID),
michael@0 112 SEC_ASN1_SUB(SEC_BitStringTemplate) },
michael@0 113 { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 2,
michael@0 114 offsetof(CERTCertificate,subjectID),
michael@0 115 SEC_ASN1_SUB(SEC_BitStringTemplate) },
michael@0 116 { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
michael@0 117 SEC_ASN1_CONTEXT_SPECIFIC | 3,
michael@0 118 offsetof(CERTCertificate,extensions),
michael@0 119 CERT_SequenceOfCertExtensionTemplate },
michael@0 120 { 0 }
michael@0 121 };
michael@0 122
michael@0 123 const SEC_ASN1Template SEC_SignedCertificateTemplate[] =
michael@0 124 {
michael@0 125 { SEC_ASN1_SEQUENCE,
michael@0 126 0, NULL, sizeof(CERTCertificate) },
michael@0 127 { SEC_ASN1_SAVE,
michael@0 128 offsetof(CERTCertificate,signatureWrap.data) },
michael@0 129 { SEC_ASN1_INLINE,
michael@0 130 0, CERT_CertificateTemplate },
michael@0 131 { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
michael@0 132 offsetof(CERTCertificate,signatureWrap.signatureAlgorithm),
michael@0 133 SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
michael@0 134 { SEC_ASN1_BIT_STRING,
michael@0 135 offsetof(CERTCertificate,signatureWrap.signature) },
michael@0 136 { 0 }
michael@0 137 };
michael@0 138
michael@0 139 /*
michael@0 140 * Find the subjectName in a DER encoded certificate
michael@0 141 */
michael@0 142 const SEC_ASN1Template SEC_CertSubjectTemplate[] = {
michael@0 143 { SEC_ASN1_SEQUENCE,
michael@0 144 0, NULL, sizeof(SECItem) },
michael@0 145 { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
michael@0 146 SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
michael@0 147 0, SEC_ASN1_SUB(SEC_SkipTemplate) }, /* version */
michael@0 148 { SEC_ASN1_SKIP }, /* serial number */
michael@0 149 { SEC_ASN1_SKIP }, /* signature algorithm */
michael@0 150 { SEC_ASN1_SKIP }, /* issuer */
michael@0 151 { SEC_ASN1_SKIP }, /* validity */
michael@0 152 { SEC_ASN1_ANY, 0, NULL }, /* subject */
michael@0 153 { SEC_ASN1_SKIP_REST },
michael@0 154 { 0 }
michael@0 155 };
michael@0 156
michael@0 157 /*
michael@0 158 * Find the issuerName in a DER encoded certificate
michael@0 159 */
michael@0 160 const SEC_ASN1Template SEC_CertIssuerTemplate[] = {
michael@0 161 { SEC_ASN1_SEQUENCE,
michael@0 162 0, NULL, sizeof(SECItem) },
michael@0 163 { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
michael@0 164 SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
michael@0 165 0, SEC_ASN1_SUB(SEC_SkipTemplate) }, /* version */
michael@0 166 { SEC_ASN1_SKIP }, /* serial number */
michael@0 167 { SEC_ASN1_SKIP }, /* signature algorithm */
michael@0 168 { SEC_ASN1_ANY, 0, NULL }, /* issuer */
michael@0 169 { SEC_ASN1_SKIP_REST },
michael@0 170 { 0 }
michael@0 171 };
michael@0 172 /*
michael@0 173 * Find the subjectName in a DER encoded certificate
michael@0 174 */
michael@0 175 const SEC_ASN1Template SEC_CertSerialNumberTemplate[] = {
michael@0 176 { SEC_ASN1_SEQUENCE,
michael@0 177 0, NULL, sizeof(SECItem) },
michael@0 178 { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
michael@0 179 SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
michael@0 180 0, SEC_ASN1_SUB(SEC_SkipTemplate) }, /* version */
michael@0 181 { SEC_ASN1_ANY, 0, NULL }, /* serial number */
michael@0 182 { SEC_ASN1_SKIP_REST },
michael@0 183 { 0 }
michael@0 184 };
michael@0 185
michael@0 186 /*
michael@0 187 * Find the issuer and serialNumber in a DER encoded certificate.
michael@0 188 * This data is used as the database lookup key since its the unique
michael@0 189 * identifier of a certificate.
michael@0 190 */
michael@0 191 const SEC_ASN1Template CERT_CertKeyTemplate[] = {
michael@0 192 { SEC_ASN1_SEQUENCE,
michael@0 193 0, NULL, sizeof(CERTCertKey) },
michael@0 194 { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
michael@0 195 SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
michael@0 196 0, SEC_ASN1_SUB(SEC_SkipTemplate) }, /* version */
michael@0 197 { SEC_ASN1_INTEGER,
michael@0 198 offsetof(CERTCertKey,serialNumber) },
michael@0 199 { SEC_ASN1_SKIP }, /* signature algorithm */
michael@0 200 { SEC_ASN1_ANY,
michael@0 201 offsetof(CERTCertKey,derIssuer) },
michael@0 202 { SEC_ASN1_SKIP_REST },
michael@0 203 { 0 }
michael@0 204 };
michael@0 205
michael@0 206 SEC_ASN1_CHOOSER_IMPLEMENT(CERT_TimeChoiceTemplate)
michael@0 207 SEC_ASN1_CHOOSER_IMPLEMENT(CERT_CertificateTemplate)
michael@0 208 SEC_ASN1_CHOOSER_IMPLEMENT(SEC_SignedCertificateTemplate)
michael@0 209 SEC_ASN1_CHOOSER_IMPLEMENT(CERT_SequenceOfCertExtensionTemplate)
michael@0 210
michael@0 211 SECStatus
michael@0 212 CERT_KeyFromIssuerAndSN(PLArenaPool *arena, SECItem *issuer, SECItem *sn,
michael@0 213 SECItem *key)
michael@0 214 {
michael@0 215 key->len = sn->len + issuer->len;
michael@0 216
michael@0 217 if ((sn->data == NULL) || (issuer->data == NULL)) {
michael@0 218 goto loser;
michael@0 219 }
michael@0 220
michael@0 221 key->data = (unsigned char*)PORT_ArenaAlloc(arena, key->len);
michael@0 222 if ( !key->data ) {
michael@0 223 goto loser;
michael@0 224 }
michael@0 225
michael@0 226 /* copy the serialNumber */
michael@0 227 PORT_Memcpy(key->data, sn->data, sn->len);
michael@0 228
michael@0 229 /* copy the issuer */
michael@0 230 PORT_Memcpy(&key->data[sn->len], issuer->data, issuer->len);
michael@0 231
michael@0 232 return(SECSuccess);
michael@0 233
michael@0 234 loser:
michael@0 235 return(SECFailure);
michael@0 236 }
michael@0 237
michael@0 238
michael@0 239 /*
michael@0 240 * Extract the subject name from a DER certificate
michael@0 241 */
michael@0 242 SECStatus
michael@0 243 CERT_NameFromDERCert(SECItem *derCert, SECItem *derName)
michael@0 244 {
michael@0 245 int rv;
michael@0 246 PLArenaPool *arena;
michael@0 247 CERTSignedData sd;
michael@0 248 void *tmpptr;
michael@0 249
michael@0 250 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
michael@0 251
michael@0 252 if ( ! arena ) {
michael@0 253 return(SECFailure);
michael@0 254 }
michael@0 255
michael@0 256 PORT_Memset(&sd, 0, sizeof(CERTSignedData));
michael@0 257 rv = SEC_QuickDERDecodeItem(arena, &sd, CERT_SignedDataTemplate, derCert);
michael@0 258
michael@0 259 if ( rv ) {
michael@0 260 goto loser;
michael@0 261 }
michael@0 262
michael@0 263 PORT_Memset(derName, 0, sizeof(SECItem));
michael@0 264 rv = SEC_QuickDERDecodeItem(arena, derName, SEC_CertSubjectTemplate, &sd.data);
michael@0 265
michael@0 266 if ( rv ) {
michael@0 267 goto loser;
michael@0 268 }
michael@0 269
michael@0 270 tmpptr = derName->data;
michael@0 271 derName->data = (unsigned char*)PORT_Alloc(derName->len);
michael@0 272 if ( derName->data == NULL ) {
michael@0 273 goto loser;
michael@0 274 }
michael@0 275
michael@0 276 PORT_Memcpy(derName->data, tmpptr, derName->len);
michael@0 277
michael@0 278 PORT_FreeArena(arena, PR_FALSE);
michael@0 279 return(SECSuccess);
michael@0 280
michael@0 281 loser:
michael@0 282 PORT_FreeArena(arena, PR_FALSE);
michael@0 283 return(SECFailure);
michael@0 284 }
michael@0 285
michael@0 286 SECStatus
michael@0 287 CERT_IssuerNameFromDERCert(SECItem *derCert, SECItem *derName)
michael@0 288 {
michael@0 289 int rv;
michael@0 290 PLArenaPool *arena;
michael@0 291 CERTSignedData sd;
michael@0 292 void *tmpptr;
michael@0 293
michael@0 294 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
michael@0 295
michael@0 296 if ( ! arena ) {
michael@0 297 return(SECFailure);
michael@0 298 }
michael@0 299
michael@0 300 PORT_Memset(&sd, 0, sizeof(CERTSignedData));
michael@0 301 rv = SEC_QuickDERDecodeItem(arena, &sd, CERT_SignedDataTemplate, derCert);
michael@0 302
michael@0 303 if ( rv ) {
michael@0 304 goto loser;
michael@0 305 }
michael@0 306
michael@0 307 PORT_Memset(derName, 0, sizeof(SECItem));
michael@0 308 rv = SEC_QuickDERDecodeItem(arena, derName, SEC_CertIssuerTemplate, &sd.data);
michael@0 309
michael@0 310 if ( rv ) {
michael@0 311 goto loser;
michael@0 312 }
michael@0 313
michael@0 314 tmpptr = derName->data;
michael@0 315 derName->data = (unsigned char*)PORT_Alloc(derName->len);
michael@0 316 if ( derName->data == NULL ) {
michael@0 317 goto loser;
michael@0 318 }
michael@0 319
michael@0 320 PORT_Memcpy(derName->data, tmpptr, derName->len);
michael@0 321
michael@0 322 PORT_FreeArena(arena, PR_FALSE);
michael@0 323 return(SECSuccess);
michael@0 324
michael@0 325 loser:
michael@0 326 PORT_FreeArena(arena, PR_FALSE);
michael@0 327 return(SECFailure);
michael@0 328 }
michael@0 329
michael@0 330 SECStatus
michael@0 331 CERT_SerialNumberFromDERCert(SECItem *derCert, SECItem *derName)
michael@0 332 {
michael@0 333 int rv;
michael@0 334 PLArenaPool *arena;
michael@0 335 CERTSignedData sd;
michael@0 336 void *tmpptr;
michael@0 337
michael@0 338 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
michael@0 339
michael@0 340 if ( ! arena ) {
michael@0 341 return(SECFailure);
michael@0 342 }
michael@0 343
michael@0 344 PORT_Memset(&sd, 0, sizeof(CERTSignedData));
michael@0 345 rv = SEC_QuickDERDecodeItem(arena, &sd, CERT_SignedDataTemplate, derCert);
michael@0 346
michael@0 347 if ( rv ) {
michael@0 348 goto loser;
michael@0 349 }
michael@0 350
michael@0 351 PORT_Memset(derName, 0, sizeof(SECItem));
michael@0 352 rv = SEC_QuickDERDecodeItem(arena, derName, SEC_CertSerialNumberTemplate, &sd.data);
michael@0 353
michael@0 354 if ( rv ) {
michael@0 355 goto loser;
michael@0 356 }
michael@0 357
michael@0 358 tmpptr = derName->data;
michael@0 359 derName->data = (unsigned char*)PORT_Alloc(derName->len);
michael@0 360 if ( derName->data == NULL ) {
michael@0 361 goto loser;
michael@0 362 }
michael@0 363
michael@0 364 PORT_Memcpy(derName->data, tmpptr, derName->len);
michael@0 365
michael@0 366 PORT_FreeArena(arena, PR_FALSE);
michael@0 367 return(SECSuccess);
michael@0 368
michael@0 369 loser:
michael@0 370 PORT_FreeArena(arena, PR_FALSE);
michael@0 371 return(SECFailure);
michael@0 372 }
michael@0 373
michael@0 374 /*
michael@0 375 * Generate a database key, based on serial number and issuer, from a
michael@0 376 * DER certificate.
michael@0 377 */
michael@0 378 SECStatus
michael@0 379 CERT_KeyFromDERCert(PLArenaPool *reqArena, SECItem *derCert, SECItem *key)
michael@0 380 {
michael@0 381 int rv;
michael@0 382 CERTSignedData sd;
michael@0 383 CERTCertKey certkey;
michael@0 384
michael@0 385 if (!reqArena) {
michael@0 386 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 387 return SECFailure;
michael@0 388 }
michael@0 389
michael@0 390 PORT_Memset(&sd, 0, sizeof(CERTSignedData));
michael@0 391 rv = SEC_QuickDERDecodeItem(reqArena, &sd, CERT_SignedDataTemplate,
michael@0 392 derCert);
michael@0 393
michael@0 394 if ( rv ) {
michael@0 395 goto loser;
michael@0 396 }
michael@0 397
michael@0 398 PORT_Memset(&certkey, 0, sizeof(CERTCertKey));
michael@0 399 rv = SEC_QuickDERDecodeItem(reqArena, &certkey, CERT_CertKeyTemplate,
michael@0 400 &sd.data);
michael@0 401
michael@0 402 if ( rv ) {
michael@0 403 goto loser;
michael@0 404 }
michael@0 405
michael@0 406 return(CERT_KeyFromIssuerAndSN(reqArena, &certkey.derIssuer,
michael@0 407 &certkey.serialNumber, key));
michael@0 408 loser:
michael@0 409 return(SECFailure);
michael@0 410 }
michael@0 411
michael@0 412 /*
michael@0 413 * fill in keyUsage field of the cert based on the cert extension
michael@0 414 * if the extension is not critical, then we allow all uses
michael@0 415 */
michael@0 416 static SECStatus
michael@0 417 GetKeyUsage(CERTCertificate *cert)
michael@0 418 {
michael@0 419 SECStatus rv;
michael@0 420 SECItem tmpitem;
michael@0 421
michael@0 422 rv = CERT_FindKeyUsageExtension(cert, &tmpitem);
michael@0 423 if ( rv == SECSuccess ) {
michael@0 424 /* remember the actual value of the extension */
michael@0 425 cert->rawKeyUsage = tmpitem.data[0];
michael@0 426 cert->keyUsagePresent = PR_TRUE;
michael@0 427 cert->keyUsage = tmpitem.data[0];
michael@0 428
michael@0 429 PORT_Free(tmpitem.data);
michael@0 430 tmpitem.data = NULL;
michael@0 431
michael@0 432 } else {
michael@0 433 /* if the extension is not present, then we allow all uses */
michael@0 434 cert->keyUsage = KU_ALL;
michael@0 435 cert->rawKeyUsage = KU_ALL;
michael@0 436 cert->keyUsagePresent = PR_FALSE;
michael@0 437 }
michael@0 438
michael@0 439 if ( CERT_GovtApprovedBitSet(cert) ) {
michael@0 440 cert->keyUsage |= KU_NS_GOVT_APPROVED;
michael@0 441 cert->rawKeyUsage |= KU_NS_GOVT_APPROVED;
michael@0 442 }
michael@0 443
michael@0 444 return(SECSuccess);
michael@0 445 }
michael@0 446
michael@0 447
michael@0 448 static SECStatus
michael@0 449 findOIDinOIDSeqByTagNum(CERTOidSequence *seq, SECOidTag tagnum)
michael@0 450 {
michael@0 451 SECItem **oids;
michael@0 452 SECItem *oid;
michael@0 453 SECStatus rv = SECFailure;
michael@0 454
michael@0 455 if (seq != NULL) {
michael@0 456 oids = seq->oids;
michael@0 457 while (oids != NULL && *oids != NULL) {
michael@0 458 oid = *oids;
michael@0 459 if (SECOID_FindOIDTag(oid) == tagnum) {
michael@0 460 rv = SECSuccess;
michael@0 461 break;
michael@0 462 }
michael@0 463 oids++;
michael@0 464 }
michael@0 465 }
michael@0 466 return rv;
michael@0 467 }
michael@0 468
michael@0 469 /*
michael@0 470 * fill in nsCertType field of the cert based on the cert extension
michael@0 471 */
michael@0 472 SECStatus
michael@0 473 cert_GetCertType(CERTCertificate *cert)
michael@0 474 {
michael@0 475 PRUint32 nsCertType;
michael@0 476
michael@0 477 if (cert->nsCertType) {
michael@0 478 /* once set, no need to recalculate */
michael@0 479 return SECSuccess;
michael@0 480 }
michael@0 481 nsCertType = cert_ComputeCertType(cert);
michael@0 482
michael@0 483 /* Assert that it is safe to cast &cert->nsCertType to "PRInt32 *" */
michael@0 484 PORT_Assert(sizeof(cert->nsCertType) == sizeof(PRInt32));
michael@0 485 PR_ATOMIC_SET((PRInt32 *)&cert->nsCertType, nsCertType);
michael@0 486 return SECSuccess;
michael@0 487 }
michael@0 488
michael@0 489 PRUint32
michael@0 490 cert_ComputeCertType(CERTCertificate *cert)
michael@0 491 {
michael@0 492 SECStatus rv;
michael@0 493 SECItem tmpitem;
michael@0 494 SECItem encodedExtKeyUsage;
michael@0 495 CERTOidSequence *extKeyUsage = NULL;
michael@0 496 PRBool basicConstraintPresent = PR_FALSE;
michael@0 497 CERTBasicConstraints basicConstraint;
michael@0 498 PRUint32 nsCertType = 0;
michael@0 499
michael@0 500 tmpitem.data = NULL;
michael@0 501 CERT_FindNSCertTypeExtension(cert, &tmpitem);
michael@0 502 encodedExtKeyUsage.data = NULL;
michael@0 503 rv = CERT_FindCertExtension(cert, SEC_OID_X509_EXT_KEY_USAGE,
michael@0 504 &encodedExtKeyUsage);
michael@0 505 if (rv == SECSuccess) {
michael@0 506 extKeyUsage = CERT_DecodeOidSequence(&encodedExtKeyUsage);
michael@0 507 }
michael@0 508 rv = CERT_FindBasicConstraintExten(cert, &basicConstraint);
michael@0 509 if (rv == SECSuccess) {
michael@0 510 basicConstraintPresent = PR_TRUE;
michael@0 511 }
michael@0 512 if (tmpitem.data != NULL || extKeyUsage != NULL) {
michael@0 513 if (tmpitem.data == NULL) {
michael@0 514 nsCertType = 0;
michael@0 515 } else {
michael@0 516 nsCertType = tmpitem.data[0];
michael@0 517 }
michael@0 518
michael@0 519 /* free tmpitem data pointer to avoid memory leak */
michael@0 520 PORT_Free(tmpitem.data);
michael@0 521 tmpitem.data = NULL;
michael@0 522
michael@0 523 /*
michael@0 524 * for this release, we will allow SSL certs with an email address
michael@0 525 * to be used for email
michael@0 526 */
michael@0 527 if ( ( nsCertType & NS_CERT_TYPE_SSL_CLIENT ) &&
michael@0 528 cert->emailAddr && cert->emailAddr[0]) {
michael@0 529 nsCertType |= NS_CERT_TYPE_EMAIL;
michael@0 530 }
michael@0 531 /*
michael@0 532 * for this release, we will allow SSL intermediate CAs to be
michael@0 533 * email intermediate CAs too.
michael@0 534 */
michael@0 535 if ( nsCertType & NS_CERT_TYPE_SSL_CA ) {
michael@0 536 nsCertType |= NS_CERT_TYPE_EMAIL_CA;
michael@0 537 }
michael@0 538 /*
michael@0 539 * allow a cert with the extended key usage of EMail Protect
michael@0 540 * to be used for email or as an email CA, if basic constraints
michael@0 541 * indicates that it is a CA.
michael@0 542 */
michael@0 543 if (findOIDinOIDSeqByTagNum(extKeyUsage,
michael@0 544 SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT) ==
michael@0 545 SECSuccess) {
michael@0 546 if (basicConstraintPresent == PR_TRUE &&
michael@0 547 (basicConstraint.isCA)) {
michael@0 548 nsCertType |= NS_CERT_TYPE_EMAIL_CA;
michael@0 549 } else {
michael@0 550 nsCertType |= NS_CERT_TYPE_EMAIL;
michael@0 551 }
michael@0 552 }
michael@0 553 if (findOIDinOIDSeqByTagNum(extKeyUsage,
michael@0 554 SEC_OID_EXT_KEY_USAGE_SERVER_AUTH) ==
michael@0 555 SECSuccess){
michael@0 556 if (basicConstraintPresent == PR_TRUE &&
michael@0 557 (basicConstraint.isCA)) {
michael@0 558 nsCertType |= NS_CERT_TYPE_SSL_CA;
michael@0 559 } else {
michael@0 560 nsCertType |= NS_CERT_TYPE_SSL_SERVER;
michael@0 561 }
michael@0 562 }
michael@0 563 /*
michael@0 564 * Treat certs with step-up OID as also having SSL server type.
michael@0 565 * COMODO needs this behaviour until June 2020. See Bug 737802.
michael@0 566 */
michael@0 567 if (findOIDinOIDSeqByTagNum(extKeyUsage,
michael@0 568 SEC_OID_NS_KEY_USAGE_GOVT_APPROVED) ==
michael@0 569 SECSuccess){
michael@0 570 if (basicConstraintPresent == PR_TRUE &&
michael@0 571 (basicConstraint.isCA)) {
michael@0 572 nsCertType |= NS_CERT_TYPE_SSL_CA;
michael@0 573 } else {
michael@0 574 nsCertType |= NS_CERT_TYPE_SSL_SERVER;
michael@0 575 }
michael@0 576 }
michael@0 577 if (findOIDinOIDSeqByTagNum(extKeyUsage,
michael@0 578 SEC_OID_EXT_KEY_USAGE_CLIENT_AUTH) ==
michael@0 579 SECSuccess){
michael@0 580 if (basicConstraintPresent == PR_TRUE &&
michael@0 581 (basicConstraint.isCA)) {
michael@0 582 nsCertType |= NS_CERT_TYPE_SSL_CA;
michael@0 583 } else {
michael@0 584 nsCertType |= NS_CERT_TYPE_SSL_CLIENT;
michael@0 585 }
michael@0 586 }
michael@0 587 if (findOIDinOIDSeqByTagNum(extKeyUsage,
michael@0 588 SEC_OID_EXT_KEY_USAGE_CODE_SIGN) ==
michael@0 589 SECSuccess) {
michael@0 590 if (basicConstraintPresent == PR_TRUE &&
michael@0 591 (basicConstraint.isCA)) {
michael@0 592 nsCertType |= NS_CERT_TYPE_OBJECT_SIGNING_CA;
michael@0 593 } else {
michael@0 594 nsCertType |= NS_CERT_TYPE_OBJECT_SIGNING;
michael@0 595 }
michael@0 596 }
michael@0 597 if (findOIDinOIDSeqByTagNum(extKeyUsage,
michael@0 598 SEC_OID_EXT_KEY_USAGE_TIME_STAMP) ==
michael@0 599 SECSuccess) {
michael@0 600 nsCertType |= EXT_KEY_USAGE_TIME_STAMP;
michael@0 601 }
michael@0 602 if (findOIDinOIDSeqByTagNum(extKeyUsage,
michael@0 603 SEC_OID_OCSP_RESPONDER) ==
michael@0 604 SECSuccess) {
michael@0 605 nsCertType |= EXT_KEY_USAGE_STATUS_RESPONDER;
michael@0 606 }
michael@0 607 } else {
michael@0 608 /* If no NS Cert Type extension and no EKU extension, then */
michael@0 609 nsCertType = 0;
michael@0 610 if (CERT_IsCACert(cert, &nsCertType))
michael@0 611 nsCertType |= EXT_KEY_USAGE_STATUS_RESPONDER;
michael@0 612 /* if the basic constraint extension says the cert is a CA, then
michael@0 613 allow SSL CA and EMAIL CA and Status Responder */
michael@0 614 if (basicConstraintPresent && basicConstraint.isCA ) {
michael@0 615 nsCertType |= (NS_CERT_TYPE_SSL_CA |
michael@0 616 NS_CERT_TYPE_EMAIL_CA |
michael@0 617 EXT_KEY_USAGE_STATUS_RESPONDER);
michael@0 618 }
michael@0 619 /* allow any ssl or email (no ca or object signing. */
michael@0 620 nsCertType |= NS_CERT_TYPE_SSL_CLIENT | NS_CERT_TYPE_SSL_SERVER |
michael@0 621 NS_CERT_TYPE_EMAIL;
michael@0 622 }
michael@0 623
michael@0 624 if (encodedExtKeyUsage.data != NULL) {
michael@0 625 PORT_Free(encodedExtKeyUsage.data);
michael@0 626 }
michael@0 627 if (extKeyUsage != NULL) {
michael@0 628 CERT_DestroyOidSequence(extKeyUsage);
michael@0 629 }
michael@0 630 return nsCertType;
michael@0 631 }
michael@0 632
michael@0 633 /*
michael@0 634 * cert_GetKeyID() - extract or generate the subjectKeyID from a certificate
michael@0 635 */
michael@0 636 SECStatus
michael@0 637 cert_GetKeyID(CERTCertificate *cert)
michael@0 638 {
michael@0 639 SECItem tmpitem;
michael@0 640 SECStatus rv;
michael@0 641
michael@0 642 cert->subjectKeyID.len = 0;
michael@0 643
michael@0 644 /* see of the cert has a key identifier extension */
michael@0 645 rv = CERT_FindSubjectKeyIDExtension(cert, &tmpitem);
michael@0 646 if ( rv == SECSuccess ) {
michael@0 647 cert->subjectKeyID.data = (unsigned char*) PORT_ArenaAlloc(cert->arena, tmpitem.len);
michael@0 648 if ( cert->subjectKeyID.data != NULL ) {
michael@0 649 PORT_Memcpy(cert->subjectKeyID.data, tmpitem.data, tmpitem.len);
michael@0 650 cert->subjectKeyID.len = tmpitem.len;
michael@0 651 cert->keyIDGenerated = PR_FALSE;
michael@0 652 }
michael@0 653
michael@0 654 PORT_Free(tmpitem.data);
michael@0 655 }
michael@0 656
michael@0 657 /* if the cert doesn't have a key identifier extension, then generate one*/
michael@0 658 if ( cert->subjectKeyID.len == 0 ) {
michael@0 659 /*
michael@0 660 * pkix says that if the subjectKeyID is not present, then we should
michael@0 661 * use the SHA-1 hash of the DER-encoded publicKeyInfo from the cert
michael@0 662 */
michael@0 663 cert->subjectKeyID.data = (unsigned char *)PORT_ArenaAlloc(cert->arena, SHA1_LENGTH);
michael@0 664 if ( cert->subjectKeyID.data != NULL ) {
michael@0 665 rv = PK11_HashBuf(SEC_OID_SHA1,cert->subjectKeyID.data,
michael@0 666 cert->derPublicKey.data,
michael@0 667 cert->derPublicKey.len);
michael@0 668 if ( rv == SECSuccess ) {
michael@0 669 cert->subjectKeyID.len = SHA1_LENGTH;
michael@0 670 }
michael@0 671 }
michael@0 672 }
michael@0 673
michael@0 674 if ( cert->subjectKeyID.len == 0 ) {
michael@0 675 return(SECFailure);
michael@0 676 }
michael@0 677 return(SECSuccess);
michael@0 678
michael@0 679 }
michael@0 680
michael@0 681 static PRBool
michael@0 682 cert_IsRootCert(CERTCertificate *cert)
michael@0 683 {
michael@0 684 SECStatus rv;
michael@0 685 SECItem tmpitem;
michael@0 686
michael@0 687 /* cache the authKeyID extension, if present */
michael@0 688 cert->authKeyID = CERT_FindAuthKeyIDExten(cert->arena, cert);
michael@0 689
michael@0 690 /* it MUST be self-issued to be a root */
michael@0 691 if (cert->derIssuer.len == 0 ||
michael@0 692 !SECITEM_ItemsAreEqual(&cert->derIssuer, &cert->derSubject))
michael@0 693 {
michael@0 694 return PR_FALSE;
michael@0 695 }
michael@0 696
michael@0 697 /* check the authKeyID extension */
michael@0 698 if (cert->authKeyID) {
michael@0 699 /* authority key identifier is present */
michael@0 700 if (cert->authKeyID->keyID.len > 0) {
michael@0 701 /* the keyIdentifier field is set, look for subjectKeyID */
michael@0 702 rv = CERT_FindSubjectKeyIDExtension(cert, &tmpitem);
michael@0 703 if (rv == SECSuccess) {
michael@0 704 PRBool match;
michael@0 705 /* also present, they MUST match for it to be a root */
michael@0 706 match = SECITEM_ItemsAreEqual(&cert->authKeyID->keyID,
michael@0 707 &tmpitem);
michael@0 708 PORT_Free(tmpitem.data);
michael@0 709 if (!match) return PR_FALSE; /* else fall through */
michael@0 710 } else {
michael@0 711 /* the subject key ID is required when AKI is present */
michael@0 712 return PR_FALSE;
michael@0 713 }
michael@0 714 }
michael@0 715 if (cert->authKeyID->authCertIssuer) {
michael@0 716 SECItem *caName;
michael@0 717 caName = (SECItem *)CERT_GetGeneralNameByType(
michael@0 718 cert->authKeyID->authCertIssuer,
michael@0 719 certDirectoryName, PR_TRUE);
michael@0 720 if (caName) {
michael@0 721 if (!SECITEM_ItemsAreEqual(&cert->derIssuer, caName)) {
michael@0 722 return PR_FALSE;
michael@0 723 } /* else fall through */
michael@0 724 } /* else ??? could not get general name as directory name? */
michael@0 725 }
michael@0 726 if (cert->authKeyID->authCertSerialNumber.len > 0) {
michael@0 727 if (!SECITEM_ItemsAreEqual(&cert->serialNumber,
michael@0 728 &cert->authKeyID->authCertSerialNumber)) {
michael@0 729 return PR_FALSE;
michael@0 730 } /* else fall through */
michael@0 731 }
michael@0 732 /* all of the AKI fields that were present passed the test */
michael@0 733 return PR_TRUE;
michael@0 734 }
michael@0 735 /* else the AKI was not present, so this is a root */
michael@0 736 return PR_TRUE;
michael@0 737 }
michael@0 738
michael@0 739 /*
michael@0 740 * take a DER certificate and decode it into a certificate structure
michael@0 741 */
michael@0 742 CERTCertificate *
michael@0 743 CERT_DecodeDERCertificate(SECItem *derSignedCert, PRBool copyDER,
michael@0 744 char *nickname)
michael@0 745 {
michael@0 746 CERTCertificate *cert;
michael@0 747 PLArenaPool *arena;
michael@0 748 void *data;
michael@0 749 int rv;
michael@0 750 int len;
michael@0 751 char *tmpname;
michael@0 752
michael@0 753 /* make a new arena */
michael@0 754 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
michael@0 755
michael@0 756 if ( !arena ) {
michael@0 757 return 0;
michael@0 758 }
michael@0 759
michael@0 760 /* allocate the certificate structure */
michael@0 761 cert = (CERTCertificate *)PORT_ArenaZAlloc(arena, sizeof(CERTCertificate));
michael@0 762
michael@0 763 if ( !cert ) {
michael@0 764 goto loser;
michael@0 765 }
michael@0 766
michael@0 767 cert->arena = arena;
michael@0 768
michael@0 769 if ( copyDER ) {
michael@0 770 /* copy the DER data for the cert into this arena */
michael@0 771 data = (void *)PORT_ArenaAlloc(arena, derSignedCert->len);
michael@0 772 if ( !data ) {
michael@0 773 goto loser;
michael@0 774 }
michael@0 775 cert->derCert.data = (unsigned char *)data;
michael@0 776 cert->derCert.len = derSignedCert->len;
michael@0 777 PORT_Memcpy(data, derSignedCert->data, derSignedCert->len);
michael@0 778 } else {
michael@0 779 /* point to passed in DER data */
michael@0 780 cert->derCert = *derSignedCert;
michael@0 781 }
michael@0 782
michael@0 783 /* decode the certificate info */
michael@0 784 rv = SEC_QuickDERDecodeItem(arena, cert, SEC_SignedCertificateTemplate,
michael@0 785 &cert->derCert);
michael@0 786
michael@0 787 if ( rv ) {
michael@0 788 goto loser;
michael@0 789 }
michael@0 790
michael@0 791 if (cert_HasUnknownCriticalExten (cert->extensions) == PR_TRUE) {
michael@0 792 cert->options.bits.hasUnsupportedCriticalExt = PR_TRUE;
michael@0 793 }
michael@0 794
michael@0 795 /* generate and save the database key for the cert */
michael@0 796 rv = CERT_KeyFromIssuerAndSN(arena, &cert->derIssuer, &cert->serialNumber,
michael@0 797 &cert->certKey);
michael@0 798 if ( rv ) {
michael@0 799 goto loser;
michael@0 800 }
michael@0 801
michael@0 802 /* set the nickname */
michael@0 803 if ( nickname == NULL ) {
michael@0 804 cert->nickname = NULL;
michael@0 805 } else {
michael@0 806 /* copy and install the nickname */
michael@0 807 len = PORT_Strlen(nickname) + 1;
michael@0 808 cert->nickname = (char*)PORT_ArenaAlloc(arena, len);
michael@0 809 if ( cert->nickname == NULL ) {
michael@0 810 goto loser;
michael@0 811 }
michael@0 812
michael@0 813 PORT_Memcpy(cert->nickname, nickname, len);
michael@0 814 }
michael@0 815
michael@0 816 /* set the email address */
michael@0 817 cert->emailAddr = cert_GetCertificateEmailAddresses(cert);
michael@0 818
michael@0 819 /* initialize the subjectKeyID */
michael@0 820 rv = cert_GetKeyID(cert);
michael@0 821 if ( rv != SECSuccess ) {
michael@0 822 goto loser;
michael@0 823 }
michael@0 824
michael@0 825 /* initialize keyUsage */
michael@0 826 rv = GetKeyUsage(cert);
michael@0 827 if ( rv != SECSuccess ) {
michael@0 828 goto loser;
michael@0 829 }
michael@0 830
michael@0 831 /* determine if this is a root cert */
michael@0 832 cert->isRoot = cert_IsRootCert(cert);
michael@0 833
michael@0 834 /* initialize the certType */
michael@0 835 rv = cert_GetCertType(cert);
michael@0 836 if ( rv != SECSuccess ) {
michael@0 837 goto loser;
michael@0 838 }
michael@0 839
michael@0 840 tmpname = CERT_NameToAscii(&cert->subject);
michael@0 841 if ( tmpname != NULL ) {
michael@0 842 cert->subjectName = PORT_ArenaStrdup(cert->arena, tmpname);
michael@0 843 PORT_Free(tmpname);
michael@0 844 }
michael@0 845
michael@0 846 tmpname = CERT_NameToAscii(&cert->issuer);
michael@0 847 if ( tmpname != NULL ) {
michael@0 848 cert->issuerName = PORT_ArenaStrdup(cert->arena, tmpname);
michael@0 849 PORT_Free(tmpname);
michael@0 850 }
michael@0 851
michael@0 852 cert->referenceCount = 1;
michael@0 853 cert->slot = NULL;
michael@0 854 cert->pkcs11ID = CK_INVALID_HANDLE;
michael@0 855 cert->dbnickname = NULL;
michael@0 856
michael@0 857 return(cert);
michael@0 858
michael@0 859 loser:
michael@0 860
michael@0 861 if ( arena ) {
michael@0 862 PORT_FreeArena(arena, PR_FALSE);
michael@0 863 }
michael@0 864
michael@0 865 return(0);
michael@0 866 }
michael@0 867
michael@0 868 CERTCertificate *
michael@0 869 __CERT_DecodeDERCertificate(SECItem *derSignedCert, PRBool copyDER,
michael@0 870 char *nickname)
michael@0 871 {
michael@0 872 return CERT_DecodeDERCertificate(derSignedCert, copyDER, nickname);
michael@0 873 }
michael@0 874
michael@0 875
michael@0 876 CERTValidity *
michael@0 877 CERT_CreateValidity(PRTime notBefore, PRTime notAfter)
michael@0 878 {
michael@0 879 CERTValidity *v;
michael@0 880 int rv;
michael@0 881 PLArenaPool *arena;
michael@0 882
michael@0 883 if (notBefore > notAfter) {
michael@0 884 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 885 return NULL;
michael@0 886 }
michael@0 887 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
michael@0 888
michael@0 889 if ( !arena ) {
michael@0 890 return(0);
michael@0 891 }
michael@0 892
michael@0 893 v = (CERTValidity*) PORT_ArenaZAlloc(arena, sizeof(CERTValidity));
michael@0 894 if (v) {
michael@0 895 v->arena = arena;
michael@0 896 rv = DER_EncodeTimeChoice(arena, &v->notBefore, notBefore);
michael@0 897 if (rv) goto loser;
michael@0 898 rv = DER_EncodeTimeChoice(arena, &v->notAfter, notAfter);
michael@0 899 if (rv) goto loser;
michael@0 900 }
michael@0 901 return v;
michael@0 902
michael@0 903 loser:
michael@0 904 CERT_DestroyValidity(v);
michael@0 905 return 0;
michael@0 906 }
michael@0 907
michael@0 908 SECStatus
michael@0 909 CERT_CopyValidity(PLArenaPool *arena, CERTValidity *to, CERTValidity *from)
michael@0 910 {
michael@0 911 SECStatus rv;
michael@0 912
michael@0 913 CERT_DestroyValidity(to);
michael@0 914 to->arena = arena;
michael@0 915
michael@0 916 rv = SECITEM_CopyItem(arena, &to->notBefore, &from->notBefore);
michael@0 917 if (rv) return rv;
michael@0 918 rv = SECITEM_CopyItem(arena, &to->notAfter, &from->notAfter);
michael@0 919 return rv;
michael@0 920 }
michael@0 921
michael@0 922 void
michael@0 923 CERT_DestroyValidity(CERTValidity *v)
michael@0 924 {
michael@0 925 if (v && v->arena) {
michael@0 926 PORT_FreeArena(v->arena, PR_FALSE);
michael@0 927 }
michael@0 928 return;
michael@0 929 }
michael@0 930
michael@0 931 /*
michael@0 932 ** Amount of time that a certifiate is allowed good before it is actually
michael@0 933 ** good. This is used for pending certificates, ones that are about to be
michael@0 934 ** valid. The slop is designed to allow for some variance in the clocks
michael@0 935 ** of the machine checking the certificate.
michael@0 936 */
michael@0 937 #define PENDING_SLOP (24L*60L*60L) /* seconds per day */
michael@0 938 static PRInt32 pendingSlop = PENDING_SLOP; /* seconds */
michael@0 939
michael@0 940 PRInt32
michael@0 941 CERT_GetSlopTime(void)
michael@0 942 {
michael@0 943 return pendingSlop; /* seconds */
michael@0 944 }
michael@0 945
michael@0 946 SECStatus
michael@0 947 CERT_SetSlopTime(PRInt32 slop) /* seconds */
michael@0 948 {
michael@0 949 if (slop < 0)
michael@0 950 return SECFailure;
michael@0 951 pendingSlop = slop;
michael@0 952 return SECSuccess;
michael@0 953 }
michael@0 954
michael@0 955 SECStatus
michael@0 956 CERT_GetCertTimes(const CERTCertificate *c, PRTime *notBefore, PRTime *notAfter)
michael@0 957 {
michael@0 958 SECStatus rv;
michael@0 959
michael@0 960 if (!c || !notBefore || !notAfter) {
michael@0 961 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 962 return SECFailure;
michael@0 963 }
michael@0 964
michael@0 965 /* convert DER not-before time */
michael@0 966 rv = DER_DecodeTimeChoice(notBefore, &c->validity.notBefore);
michael@0 967 if (rv) {
michael@0 968 return(SECFailure);
michael@0 969 }
michael@0 970
michael@0 971 /* convert DER not-after time */
michael@0 972 rv = DER_DecodeTimeChoice(notAfter, &c->validity.notAfter);
michael@0 973 if (rv) {
michael@0 974 return(SECFailure);
michael@0 975 }
michael@0 976
michael@0 977 return(SECSuccess);
michael@0 978 }
michael@0 979
michael@0 980 /*
michael@0 981 * Check the validity times of a certificate
michael@0 982 */
michael@0 983 SECCertTimeValidity
michael@0 984 CERT_CheckCertValidTimes(const CERTCertificate *c, PRTime t,
michael@0 985 PRBool allowOverride)
michael@0 986 {
michael@0 987 PRTime notBefore, notAfter, llPendingSlop, tmp1;
michael@0 988 SECStatus rv;
michael@0 989
michael@0 990 if (!c) {
michael@0 991 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 992 return(secCertTimeUndetermined);
michael@0 993 }
michael@0 994 /* if cert is already marked OK, then don't bother to check */
michael@0 995 if ( allowOverride && c->timeOK ) {
michael@0 996 return(secCertTimeValid);
michael@0 997 }
michael@0 998
michael@0 999 rv = CERT_GetCertTimes(c, &notBefore, &notAfter);
michael@0 1000
michael@0 1001 if (rv) {
michael@0 1002 return(secCertTimeExpired); /*XXX is this the right thing to do here?*/
michael@0 1003 }
michael@0 1004
michael@0 1005 LL_I2L(llPendingSlop, pendingSlop);
michael@0 1006 /* convert to micro seconds */
michael@0 1007 LL_UI2L(tmp1, PR_USEC_PER_SEC);
michael@0 1008 LL_MUL(llPendingSlop, llPendingSlop, tmp1);
michael@0 1009 LL_SUB(notBefore, notBefore, llPendingSlop);
michael@0 1010 if ( LL_CMP( t, <, notBefore ) ) {
michael@0 1011 PORT_SetError(SEC_ERROR_EXPIRED_CERTIFICATE);
michael@0 1012 return(secCertTimeNotValidYet);
michael@0 1013 }
michael@0 1014 if ( LL_CMP( t, >, notAfter) ) {
michael@0 1015 PORT_SetError(SEC_ERROR_EXPIRED_CERTIFICATE);
michael@0 1016 return(secCertTimeExpired);
michael@0 1017 }
michael@0 1018
michael@0 1019 return(secCertTimeValid);
michael@0 1020 }
michael@0 1021
michael@0 1022 SECStatus
michael@0 1023 SEC_GetCrlTimes(CERTCrl *date, PRTime *notBefore, PRTime *notAfter)
michael@0 1024 {
michael@0 1025 int rv;
michael@0 1026
michael@0 1027 /* convert DER not-before time */
michael@0 1028 rv = DER_DecodeTimeChoice(notBefore, &date->lastUpdate);
michael@0 1029 if (rv) {
michael@0 1030 return(SECFailure);
michael@0 1031 }
michael@0 1032
michael@0 1033 /* convert DER not-after time */
michael@0 1034 if (date->nextUpdate.data) {
michael@0 1035 rv = DER_DecodeTimeChoice(notAfter, &date->nextUpdate);
michael@0 1036 if (rv) {
michael@0 1037 return(SECFailure);
michael@0 1038 }
michael@0 1039 }
michael@0 1040 else {
michael@0 1041 LL_I2L(*notAfter, 0L);
michael@0 1042 }
michael@0 1043 return(SECSuccess);
michael@0 1044 }
michael@0 1045
michael@0 1046 /* These routines should probably be combined with the cert
michael@0 1047 * routines using an common extraction routine.
michael@0 1048 */
michael@0 1049 SECCertTimeValidity
michael@0 1050 SEC_CheckCrlTimes(CERTCrl *crl, PRTime t) {
michael@0 1051 PRTime notBefore, notAfter, llPendingSlop, tmp1;
michael@0 1052 SECStatus rv;
michael@0 1053
michael@0 1054 rv = SEC_GetCrlTimes(crl, &notBefore, &notAfter);
michael@0 1055
michael@0 1056 if (rv) {
michael@0 1057 return(secCertTimeExpired);
michael@0 1058 }
michael@0 1059
michael@0 1060 LL_I2L(llPendingSlop, pendingSlop);
michael@0 1061 /* convert to micro seconds */
michael@0 1062 LL_I2L(tmp1, PR_USEC_PER_SEC);
michael@0 1063 LL_MUL(llPendingSlop, llPendingSlop, tmp1);
michael@0 1064 LL_SUB(notBefore, notBefore, llPendingSlop);
michael@0 1065 if ( LL_CMP( t, <, notBefore ) ) {
michael@0 1066 return(secCertTimeNotValidYet);
michael@0 1067 }
michael@0 1068
michael@0 1069 /* If next update is omitted and the test for notBefore passes, then
michael@0 1070 we assume that the crl is up to date.
michael@0 1071 */
michael@0 1072 if ( LL_IS_ZERO(notAfter) ) {
michael@0 1073 return(secCertTimeValid);
michael@0 1074 }
michael@0 1075
michael@0 1076 if ( LL_CMP( t, >, notAfter) ) {
michael@0 1077 return(secCertTimeExpired);
michael@0 1078 }
michael@0 1079
michael@0 1080 return(secCertTimeValid);
michael@0 1081 }
michael@0 1082
michael@0 1083 PRBool
michael@0 1084 SEC_CrlIsNewer(CERTCrl *inNew, CERTCrl *old) {
michael@0 1085 PRTime newNotBefore, newNotAfter;
michael@0 1086 PRTime oldNotBefore, oldNotAfter;
michael@0 1087 SECStatus rv;
michael@0 1088
michael@0 1089 /* problems with the new CRL? reject it */
michael@0 1090 rv = SEC_GetCrlTimes(inNew, &newNotBefore, &newNotAfter);
michael@0 1091 if (rv) return PR_FALSE;
michael@0 1092
michael@0 1093 /* problems with the old CRL? replace it */
michael@0 1094 rv = SEC_GetCrlTimes(old, &oldNotBefore, &oldNotAfter);
michael@0 1095 if (rv) return PR_TRUE;
michael@0 1096
michael@0 1097 /* Question: what about the notAfter's? */
michael@0 1098 return ((PRBool)LL_CMP(oldNotBefore, <, newNotBefore));
michael@0 1099 }
michael@0 1100
michael@0 1101 /*
michael@0 1102 * return required key usage and cert type based on cert usage
michael@0 1103 */
michael@0 1104 SECStatus
michael@0 1105 CERT_KeyUsageAndTypeForCertUsage(SECCertUsage usage,
michael@0 1106 PRBool ca,
michael@0 1107 unsigned int *retKeyUsage,
michael@0 1108 unsigned int *retCertType)
michael@0 1109 {
michael@0 1110 unsigned int requiredKeyUsage = 0;
michael@0 1111 unsigned int requiredCertType = 0;
michael@0 1112
michael@0 1113 if ( ca ) {
michael@0 1114 switch ( usage ) {
michael@0 1115 case certUsageSSLServerWithStepUp:
michael@0 1116 requiredKeyUsage = KU_NS_GOVT_APPROVED | KU_KEY_CERT_SIGN;
michael@0 1117 requiredCertType = NS_CERT_TYPE_SSL_CA;
michael@0 1118 break;
michael@0 1119 case certUsageSSLClient:
michael@0 1120 requiredKeyUsage = KU_KEY_CERT_SIGN;
michael@0 1121 requiredCertType = NS_CERT_TYPE_SSL_CA;
michael@0 1122 break;
michael@0 1123 case certUsageSSLServer:
michael@0 1124 requiredKeyUsage = KU_KEY_CERT_SIGN;
michael@0 1125 requiredCertType = NS_CERT_TYPE_SSL_CA;
michael@0 1126 break;
michael@0 1127 case certUsageSSLCA:
michael@0 1128 requiredKeyUsage = KU_KEY_CERT_SIGN;
michael@0 1129 requiredCertType = NS_CERT_TYPE_SSL_CA;
michael@0 1130 break;
michael@0 1131 case certUsageEmailSigner:
michael@0 1132 requiredKeyUsage = KU_KEY_CERT_SIGN;
michael@0 1133 requiredCertType = NS_CERT_TYPE_EMAIL_CA;
michael@0 1134 break;
michael@0 1135 case certUsageEmailRecipient:
michael@0 1136 requiredKeyUsage = KU_KEY_CERT_SIGN;
michael@0 1137 requiredCertType = NS_CERT_TYPE_EMAIL_CA;
michael@0 1138 break;
michael@0 1139 case certUsageObjectSigner:
michael@0 1140 requiredKeyUsage = KU_KEY_CERT_SIGN;
michael@0 1141 requiredCertType = NS_CERT_TYPE_OBJECT_SIGNING_CA;
michael@0 1142 break;
michael@0 1143 case certUsageAnyCA:
michael@0 1144 case certUsageVerifyCA:
michael@0 1145 case certUsageStatusResponder:
michael@0 1146 requiredKeyUsage = KU_KEY_CERT_SIGN;
michael@0 1147 requiredCertType = NS_CERT_TYPE_OBJECT_SIGNING_CA |
michael@0 1148 NS_CERT_TYPE_EMAIL_CA |
michael@0 1149 NS_CERT_TYPE_SSL_CA;
michael@0 1150 break;
michael@0 1151 default:
michael@0 1152 PORT_Assert(0);
michael@0 1153 goto loser;
michael@0 1154 }
michael@0 1155 } else {
michael@0 1156 switch ( usage ) {
michael@0 1157 case certUsageSSLClient:
michael@0 1158 /*
michael@0 1159 * RFC 5280 lists digitalSignature and keyAgreement for
michael@0 1160 * id-kp-clientAuth. NSS does not support the *_fixed_dh and
michael@0 1161 * *_fixed_ecdh client certificate types.
michael@0 1162 */
michael@0 1163 requiredKeyUsage = KU_DIGITAL_SIGNATURE;
michael@0 1164 requiredCertType = NS_CERT_TYPE_SSL_CLIENT;
michael@0 1165 break;
michael@0 1166 case certUsageSSLServer:
michael@0 1167 requiredKeyUsage = KU_KEY_AGREEMENT_OR_ENCIPHERMENT;
michael@0 1168 requiredCertType = NS_CERT_TYPE_SSL_SERVER;
michael@0 1169 break;
michael@0 1170 case certUsageSSLServerWithStepUp:
michael@0 1171 requiredKeyUsage = KU_KEY_AGREEMENT_OR_ENCIPHERMENT |
michael@0 1172 KU_NS_GOVT_APPROVED;
michael@0 1173 requiredCertType = NS_CERT_TYPE_SSL_SERVER;
michael@0 1174 break;
michael@0 1175 case certUsageSSLCA:
michael@0 1176 requiredKeyUsage = KU_KEY_CERT_SIGN;
michael@0 1177 requiredCertType = NS_CERT_TYPE_SSL_CA;
michael@0 1178 break;
michael@0 1179 case certUsageEmailSigner:
michael@0 1180 requiredKeyUsage = KU_DIGITAL_SIGNATURE_OR_NON_REPUDIATION;
michael@0 1181 requiredCertType = NS_CERT_TYPE_EMAIL;
michael@0 1182 break;
michael@0 1183 case certUsageEmailRecipient:
michael@0 1184 requiredKeyUsage = KU_KEY_AGREEMENT_OR_ENCIPHERMENT;
michael@0 1185 requiredCertType = NS_CERT_TYPE_EMAIL;
michael@0 1186 break;
michael@0 1187 case certUsageObjectSigner:
michael@0 1188 /* RFC 5280 lists only digitalSignature for id-kp-codeSigning. */
michael@0 1189 requiredKeyUsage = KU_DIGITAL_SIGNATURE;
michael@0 1190 requiredCertType = NS_CERT_TYPE_OBJECT_SIGNING;
michael@0 1191 break;
michael@0 1192 case certUsageStatusResponder:
michael@0 1193 requiredKeyUsage = KU_DIGITAL_SIGNATURE_OR_NON_REPUDIATION;
michael@0 1194 requiredCertType = EXT_KEY_USAGE_STATUS_RESPONDER;
michael@0 1195 break;
michael@0 1196 default:
michael@0 1197 PORT_Assert(0);
michael@0 1198 goto loser;
michael@0 1199 }
michael@0 1200 }
michael@0 1201
michael@0 1202 if ( retKeyUsage != NULL ) {
michael@0 1203 *retKeyUsage = requiredKeyUsage;
michael@0 1204 }
michael@0 1205 if ( retCertType != NULL ) {
michael@0 1206 *retCertType = requiredCertType;
michael@0 1207 }
michael@0 1208
michael@0 1209 return(SECSuccess);
michael@0 1210 loser:
michael@0 1211 return(SECFailure);
michael@0 1212 }
michael@0 1213
michael@0 1214 /*
michael@0 1215 * check the key usage of a cert against a set of required values
michael@0 1216 */
michael@0 1217 SECStatus
michael@0 1218 CERT_CheckKeyUsage(CERTCertificate *cert, unsigned int requiredUsage)
michael@0 1219 {
michael@0 1220 if (!cert) {
michael@0 1221 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 1222 return SECFailure;
michael@0 1223 }
michael@0 1224 /* choose between key agreement or key encipherment based on key
michael@0 1225 * type in cert
michael@0 1226 */
michael@0 1227 if ( requiredUsage & KU_KEY_AGREEMENT_OR_ENCIPHERMENT ) {
michael@0 1228 KeyType keyType = CERT_GetCertKeyType(&cert->subjectPublicKeyInfo);
michael@0 1229 /* turn off the special bit */
michael@0 1230 requiredUsage &= (~KU_KEY_AGREEMENT_OR_ENCIPHERMENT);
michael@0 1231
michael@0 1232 switch (keyType) {
michael@0 1233 case rsaKey:
michael@0 1234 requiredUsage |= KU_KEY_ENCIPHERMENT;
michael@0 1235 break;
michael@0 1236 case dsaKey:
michael@0 1237 requiredUsage |= KU_DIGITAL_SIGNATURE;
michael@0 1238 break;
michael@0 1239 case dhKey:
michael@0 1240 requiredUsage |= KU_KEY_AGREEMENT;
michael@0 1241 break;
michael@0 1242 case ecKey:
michael@0 1243 /* Accept either signature or agreement. */
michael@0 1244 if (!(cert->keyUsage & (KU_DIGITAL_SIGNATURE | KU_KEY_AGREEMENT)))
michael@0 1245 goto loser;
michael@0 1246 break;
michael@0 1247 default:
michael@0 1248 goto loser;
michael@0 1249 }
michael@0 1250 }
michael@0 1251
michael@0 1252 /* Allow either digital signature or non-repudiation */
michael@0 1253 if ( requiredUsage & KU_DIGITAL_SIGNATURE_OR_NON_REPUDIATION ) {
michael@0 1254 /* turn off the special bit */
michael@0 1255 requiredUsage &= (~KU_DIGITAL_SIGNATURE_OR_NON_REPUDIATION);
michael@0 1256
michael@0 1257 if (!(cert->keyUsage & (KU_DIGITAL_SIGNATURE | KU_NON_REPUDIATION)))
michael@0 1258 goto loser;
michael@0 1259 }
michael@0 1260
michael@0 1261 if ( (cert->keyUsage & requiredUsage) == requiredUsage )
michael@0 1262 return SECSuccess;
michael@0 1263
michael@0 1264 loser:
michael@0 1265 PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE);
michael@0 1266 return SECFailure;
michael@0 1267 }
michael@0 1268
michael@0 1269
michael@0 1270 CERTCertificate *
michael@0 1271 CERT_DupCertificate(CERTCertificate *c)
michael@0 1272 {
michael@0 1273 if (c) {
michael@0 1274 NSSCertificate *tmp = STAN_GetNSSCertificate(c);
michael@0 1275 nssCertificate_AddRef(tmp);
michael@0 1276 }
michael@0 1277 return c;
michael@0 1278 }
michael@0 1279
michael@0 1280 /*
michael@0 1281 * Allow use of default cert database, so that apps(such as mozilla) don't
michael@0 1282 * have to pass the handle all over the place.
michael@0 1283 */
michael@0 1284 static CERTCertDBHandle *default_cert_db_handle = 0;
michael@0 1285
michael@0 1286 void
michael@0 1287 CERT_SetDefaultCertDB(CERTCertDBHandle *handle)
michael@0 1288 {
michael@0 1289 default_cert_db_handle = handle;
michael@0 1290
michael@0 1291 return;
michael@0 1292 }
michael@0 1293
michael@0 1294 CERTCertDBHandle *
michael@0 1295 CERT_GetDefaultCertDB(void)
michael@0 1296 {
michael@0 1297 return(default_cert_db_handle);
michael@0 1298 }
michael@0 1299
michael@0 1300 /* XXX this would probably be okay/better as an xp routine? */
michael@0 1301 static void
michael@0 1302 sec_lower_string(char *s)
michael@0 1303 {
michael@0 1304 if ( s == NULL ) {
michael@0 1305 return;
michael@0 1306 }
michael@0 1307
michael@0 1308 while ( *s ) {
michael@0 1309 *s = PORT_Tolower(*s);
michael@0 1310 s++;
michael@0 1311 }
michael@0 1312
michael@0 1313 return;
michael@0 1314 }
michael@0 1315
michael@0 1316 static PRBool
michael@0 1317 cert_IsIPAddr(const char *hn)
michael@0 1318 {
michael@0 1319 PRBool isIPaddr = PR_FALSE;
michael@0 1320 PRNetAddr netAddr;
michael@0 1321 isIPaddr = (PR_SUCCESS == PR_StringToNetAddr(hn, &netAddr));
michael@0 1322 return isIPaddr;
michael@0 1323 }
michael@0 1324
michael@0 1325 /*
michael@0 1326 ** Add a domain name to the list of names that the user has explicitly
michael@0 1327 ** allowed (despite cert name mismatches) for use with a server cert.
michael@0 1328 */
michael@0 1329 SECStatus
michael@0 1330 CERT_AddOKDomainName(CERTCertificate *cert, const char *hn)
michael@0 1331 {
michael@0 1332 CERTOKDomainName *domainOK;
michael@0 1333 int newNameLen;
michael@0 1334
michael@0 1335 if (!hn || !(newNameLen = strlen(hn))) {
michael@0 1336 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 1337 return SECFailure;
michael@0 1338 }
michael@0 1339 domainOK = (CERTOKDomainName *)PORT_ArenaZAlloc(cert->arena,
michael@0 1340 (sizeof *domainOK) + newNameLen);
michael@0 1341 if (!domainOK)
michael@0 1342 return SECFailure; /* error code is already set. */
michael@0 1343
michael@0 1344 PORT_Strcpy(domainOK->name, hn);
michael@0 1345 sec_lower_string(domainOK->name);
michael@0 1346
michael@0 1347 /* put at head of list. */
michael@0 1348 domainOK->next = cert->domainOK;
michael@0 1349 cert->domainOK = domainOK;
michael@0 1350 return SECSuccess;
michael@0 1351 }
michael@0 1352
michael@0 1353 /* returns SECSuccess if hn matches pattern cn,
michael@0 1354 ** returns SECFailure with SSL_ERROR_BAD_CERT_DOMAIN if no match,
michael@0 1355 ** returns SECFailure with some other error code if another error occurs.
michael@0 1356 **
michael@0 1357 ** This function may modify string cn, so caller must pass a modifiable copy.
michael@0 1358 */
michael@0 1359 static SECStatus
michael@0 1360 cert_TestHostName(char * cn, const char * hn)
michael@0 1361 {
michael@0 1362 static int useShellExp = -1;
michael@0 1363
michael@0 1364 if (useShellExp < 0) {
michael@0 1365 useShellExp = (NULL != PR_GetEnv("NSS_USE_SHEXP_IN_CERT_NAME"));
michael@0 1366 }
michael@0 1367 if (useShellExp) {
michael@0 1368 /* Backward compatible code, uses Shell Expressions (SHEXP). */
michael@0 1369 int regvalid = PORT_RegExpValid(cn);
michael@0 1370 if (regvalid != NON_SXP) {
michael@0 1371 SECStatus rv;
michael@0 1372 /* cn is a regular expression, try to match the shexp */
michael@0 1373 int match = PORT_RegExpCaseSearch(hn, cn);
michael@0 1374
michael@0 1375 if ( match == 0 ) {
michael@0 1376 rv = SECSuccess;
michael@0 1377 } else {
michael@0 1378 PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
michael@0 1379 rv = SECFailure;
michael@0 1380 }
michael@0 1381 return rv;
michael@0 1382 }
michael@0 1383 } else {
michael@0 1384 /* New approach conforms to RFC 6125. */
michael@0 1385 char *wildcard = PORT_Strchr(cn, '*');
michael@0 1386 char *firstcndot = PORT_Strchr(cn, '.');
michael@0 1387 char *secondcndot = firstcndot ? PORT_Strchr(firstcndot+1, '.') : NULL;
michael@0 1388 char *firsthndot = PORT_Strchr(hn, '.');
michael@0 1389
michael@0 1390 /* For a cn pattern to be considered valid, the wildcard character...
michael@0 1391 * - may occur only in a DNS name with at least 3 components, and
michael@0 1392 * - may occur only as last character in the first component, and
michael@0 1393 * - may be preceded by additional characters, and
michael@0 1394 * - must not be preceded by an IDNA ACE prefix (xn--)
michael@0 1395 */
michael@0 1396 if (wildcard && secondcndot && secondcndot[1] && firsthndot
michael@0 1397 && firstcndot - wildcard == 1 /* wildcard is last char in first component */
michael@0 1398 && secondcndot - firstcndot > 1 /* second component is non-empty */
michael@0 1399 && PORT_Strrchr(cn, '*') == wildcard /* only one wildcard in cn */
michael@0 1400 && !PORT_Strncasecmp(cn, hn, wildcard - cn)
michael@0 1401 && !PORT_Strcasecmp(firstcndot, firsthndot)
michael@0 1402 /* If hn starts with xn--, then cn must start with wildcard */
michael@0 1403 && (PORT_Strncasecmp(hn, "xn--", 4) || wildcard == cn)) {
michael@0 1404 /* valid wildcard pattern match */
michael@0 1405 return SECSuccess;
michael@0 1406 }
michael@0 1407 }
michael@0 1408 /* String cn has no wildcard or shell expression.
michael@0 1409 * Compare entire string hn with cert name.
michael@0 1410 */
michael@0 1411 if (PORT_Strcasecmp(hn, cn) == 0) {
michael@0 1412 return SECSuccess;
michael@0 1413 }
michael@0 1414
michael@0 1415 PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
michael@0 1416 return SECFailure;
michael@0 1417 }
michael@0 1418
michael@0 1419
michael@0 1420 SECStatus
michael@0 1421 cert_VerifySubjectAltName(const CERTCertificate *cert, const char *hn)
michael@0 1422 {
michael@0 1423 PLArenaPool * arena = NULL;
michael@0 1424 CERTGeneralName * nameList = NULL;
michael@0 1425 CERTGeneralName * current;
michael@0 1426 char * cn;
michael@0 1427 int cnBufLen;
michael@0 1428 unsigned int hnLen;
michael@0 1429 int DNSextCount = 0;
michael@0 1430 int IPextCount = 0;
michael@0 1431 PRBool isIPaddr = PR_FALSE;
michael@0 1432 SECStatus rv = SECFailure;
michael@0 1433 SECItem subAltName;
michael@0 1434 PRNetAddr netAddr;
michael@0 1435 char cnbuf[128];
michael@0 1436
michael@0 1437 subAltName.data = NULL;
michael@0 1438 hnLen = strlen(hn);
michael@0 1439 cn = cnbuf;
michael@0 1440 cnBufLen = sizeof cnbuf;
michael@0 1441
michael@0 1442 rv = CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME,
michael@0 1443 &subAltName);
michael@0 1444 if (rv != SECSuccess) {
michael@0 1445 goto fail;
michael@0 1446 }
michael@0 1447 isIPaddr = (PR_SUCCESS == PR_StringToNetAddr(hn, &netAddr));
michael@0 1448 rv = SECFailure;
michael@0 1449 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
michael@0 1450 if (!arena)
michael@0 1451 goto fail;
michael@0 1452
michael@0 1453 nameList = current = CERT_DecodeAltNameExtension(arena, &subAltName);
michael@0 1454 if (!current)
michael@0 1455 goto fail;
michael@0 1456
michael@0 1457 do {
michael@0 1458 switch (current->type) {
michael@0 1459 case certDNSName:
michael@0 1460 if (!isIPaddr) {
michael@0 1461 /* DNS name current->name.other.data is not null terminated.
michael@0 1462 ** so must copy it.
michael@0 1463 */
michael@0 1464 int cnLen = current->name.other.len;
michael@0 1465 rv = CERT_RFC1485_EscapeAndQuote(cn, cnBufLen,
michael@0 1466 (char *)current->name.other.data,
michael@0 1467 cnLen);
michael@0 1468 if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_OUTPUT_LEN) {
michael@0 1469 cnBufLen = cnLen * 3 + 3; /* big enough for worst case */
michael@0 1470 cn = (char *)PORT_ArenaAlloc(arena, cnBufLen);
michael@0 1471 if (!cn)
michael@0 1472 goto fail;
michael@0 1473 rv = CERT_RFC1485_EscapeAndQuote(cn, cnBufLen,
michael@0 1474 (char *)current->name.other.data,
michael@0 1475 cnLen);
michael@0 1476 }
michael@0 1477 if (rv == SECSuccess)
michael@0 1478 rv = cert_TestHostName(cn ,hn);
michael@0 1479 if (rv == SECSuccess)
michael@0 1480 goto finish;
michael@0 1481 }
michael@0 1482 DNSextCount++;
michael@0 1483 break;
michael@0 1484 case certIPAddress:
michael@0 1485 if (isIPaddr) {
michael@0 1486 int match = 0;
michael@0 1487 PRIPv6Addr v6Addr;
michael@0 1488 if (current->name.other.len == 4 && /* IP v4 address */
michael@0 1489 netAddr.inet.family == PR_AF_INET) {
michael@0 1490 match = !memcmp(&netAddr.inet.ip,
michael@0 1491 current->name.other.data, 4);
michael@0 1492 } else if (current->name.other.len == 16 && /* IP v6 address */
michael@0 1493 netAddr.ipv6.family == PR_AF_INET6) {
michael@0 1494 match = !memcmp(&netAddr.ipv6.ip,
michael@0 1495 current->name.other.data, 16);
michael@0 1496 } else if (current->name.other.len == 16 && /* IP v6 address */
michael@0 1497 netAddr.inet.family == PR_AF_INET) {
michael@0 1498 /* convert netAddr to ipv6, then compare. */
michael@0 1499 /* ipv4 must be in Network Byte Order on input. */
michael@0 1500 PR_ConvertIPv4AddrToIPv6(netAddr.inet.ip, &v6Addr);
michael@0 1501 match = !memcmp(&v6Addr, current->name.other.data, 16);
michael@0 1502 } else if (current->name.other.len == 4 && /* IP v4 address */
michael@0 1503 netAddr.inet.family == PR_AF_INET6) {
michael@0 1504 /* convert netAddr to ipv6, then compare. */
michael@0 1505 PRUint32 ipv4 = (current->name.other.data[0] << 24) |
michael@0 1506 (current->name.other.data[1] << 16) |
michael@0 1507 (current->name.other.data[2] << 8) |
michael@0 1508 current->name.other.data[3];
michael@0 1509 /* ipv4 must be in Network Byte Order on input. */
michael@0 1510 PR_ConvertIPv4AddrToIPv6(PR_htonl(ipv4), &v6Addr);
michael@0 1511 match = !memcmp(&netAddr.ipv6.ip, &v6Addr, 16);
michael@0 1512 }
michael@0 1513 if (match) {
michael@0 1514 rv = SECSuccess;
michael@0 1515 goto finish;
michael@0 1516 }
michael@0 1517 }
michael@0 1518 IPextCount++;
michael@0 1519 break;
michael@0 1520 default:
michael@0 1521 break;
michael@0 1522 }
michael@0 1523 current = CERT_GetNextGeneralName(current);
michael@0 1524 } while (current != nameList);
michael@0 1525
michael@0 1526 fail:
michael@0 1527
michael@0 1528 if (!(isIPaddr ? IPextCount : DNSextCount)) {
michael@0 1529 /* no relevant value in the extension was found. */
michael@0 1530 PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND);
michael@0 1531 } else {
michael@0 1532 PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
michael@0 1533 }
michael@0 1534 rv = SECFailure;
michael@0 1535
michael@0 1536 finish:
michael@0 1537
michael@0 1538 /* Don't free nameList, it's part of the arena. */
michael@0 1539 if (arena) {
michael@0 1540 PORT_FreeArena(arena, PR_FALSE);
michael@0 1541 }
michael@0 1542
michael@0 1543 if (subAltName.data) {
michael@0 1544 SECITEM_FreeItem(&subAltName, PR_FALSE);
michael@0 1545 }
michael@0 1546
michael@0 1547 return rv;
michael@0 1548 }
michael@0 1549
michael@0 1550 /*
michael@0 1551 * If found:
michael@0 1552 * - subAltName contains the extension (caller must free)
michael@0 1553 * - return value is the decoded namelist (allocated off arena)
michael@0 1554 * if not found, or if failure to decode:
michael@0 1555 * - return value is NULL
michael@0 1556 */
michael@0 1557 CERTGeneralName *
michael@0 1558 cert_GetSubjectAltNameList(const CERTCertificate *cert, PLArenaPool *arena)
michael@0 1559 {
michael@0 1560 CERTGeneralName * nameList = NULL;
michael@0 1561 SECStatus rv = SECFailure;
michael@0 1562 SECItem subAltName;
michael@0 1563
michael@0 1564 if (!cert || !arena)
michael@0 1565 return NULL;
michael@0 1566
michael@0 1567 subAltName.data = NULL;
michael@0 1568
michael@0 1569 rv = CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME,
michael@0 1570 &subAltName);
michael@0 1571 if (rv != SECSuccess)
michael@0 1572 return NULL;
michael@0 1573
michael@0 1574 nameList = CERT_DecodeAltNameExtension(arena, &subAltName);
michael@0 1575 SECITEM_FreeItem(&subAltName, PR_FALSE);
michael@0 1576 return nameList;
michael@0 1577 }
michael@0 1578
michael@0 1579 PRUint32
michael@0 1580 cert_CountDNSPatterns(CERTGeneralName *firstName)
michael@0 1581 {
michael@0 1582 CERTGeneralName * current;
michael@0 1583 PRUint32 count = 0;
michael@0 1584
michael@0 1585 if (!firstName)
michael@0 1586 return 0;
michael@0 1587
michael@0 1588 current = firstName;
michael@0 1589 do {
michael@0 1590 switch (current->type) {
michael@0 1591 case certDNSName:
michael@0 1592 case certIPAddress:
michael@0 1593 ++count;
michael@0 1594 break;
michael@0 1595 default:
michael@0 1596 break;
michael@0 1597 }
michael@0 1598 current = CERT_GetNextGeneralName(current);
michael@0 1599 } while (current != firstName);
michael@0 1600
michael@0 1601 return count;
michael@0 1602 }
michael@0 1603
michael@0 1604 #ifndef INET6_ADDRSTRLEN
michael@0 1605 #define INET6_ADDRSTRLEN 46
michael@0 1606 #endif
michael@0 1607
michael@0 1608 /* will fill nickNames,
michael@0 1609 * will allocate all data from nickNames->arena,
michael@0 1610 * numberOfGeneralNames should have been obtained from cert_CountDNSPatterns,
michael@0 1611 * will ensure the numberOfGeneralNames matches the number of output entries.
michael@0 1612 */
michael@0 1613 SECStatus
michael@0 1614 cert_GetDNSPatternsFromGeneralNames(CERTGeneralName *firstName,
michael@0 1615 PRUint32 numberOfGeneralNames,
michael@0 1616 CERTCertNicknames *nickNames)
michael@0 1617 {
michael@0 1618 CERTGeneralName *currentInput;
michael@0 1619 char **currentOutput;
michael@0 1620
michael@0 1621 if (!firstName || !nickNames || !numberOfGeneralNames)
michael@0 1622 return SECFailure;
michael@0 1623
michael@0 1624 nickNames->numnicknames = numberOfGeneralNames;
michael@0 1625 nickNames->nicknames = PORT_ArenaAlloc(nickNames->arena,
michael@0 1626 sizeof(char *) * numberOfGeneralNames);
michael@0 1627 if (!nickNames->nicknames)
michael@0 1628 return SECFailure;
michael@0 1629
michael@0 1630 currentInput = firstName;
michael@0 1631 currentOutput = nickNames->nicknames;
michael@0 1632 do {
michael@0 1633 char *cn = NULL;
michael@0 1634 char ipbuf[INET6_ADDRSTRLEN];
michael@0 1635 PRNetAddr addr;
michael@0 1636
michael@0 1637 if (numberOfGeneralNames < 1) {
michael@0 1638 /* internal consistency error */
michael@0 1639 return SECFailure;
michael@0 1640 }
michael@0 1641
michael@0 1642 switch (currentInput->type) {
michael@0 1643 case certDNSName:
michael@0 1644 /* DNS name currentInput->name.other.data is not null terminated.
michael@0 1645 ** so must copy it.
michael@0 1646 */
michael@0 1647 cn = (char *)PORT_ArenaAlloc(nickNames->arena,
michael@0 1648 currentInput->name.other.len + 1);
michael@0 1649 if (!cn)
michael@0 1650 return SECFailure;
michael@0 1651 PORT_Memcpy(cn, currentInput->name.other.data,
michael@0 1652 currentInput->name.other.len);
michael@0 1653 cn[currentInput->name.other.len] = 0;
michael@0 1654 break;
michael@0 1655 case certIPAddress:
michael@0 1656 if (currentInput->name.other.len == 4) {
michael@0 1657 addr.inet.family = PR_AF_INET;
michael@0 1658 memcpy(&addr.inet.ip, currentInput->name.other.data,
michael@0 1659 currentInput->name.other.len);
michael@0 1660 } else if (currentInput->name.other.len == 16) {
michael@0 1661 addr.ipv6.family = PR_AF_INET6;
michael@0 1662 memcpy(&addr.ipv6.ip, currentInput->name.other.data,
michael@0 1663 currentInput->name.other.len);
michael@0 1664 }
michael@0 1665 if (PR_NetAddrToString(&addr, ipbuf, sizeof(ipbuf)) == PR_FAILURE)
michael@0 1666 return SECFailure;
michael@0 1667 cn = PORT_ArenaStrdup(nickNames->arena, ipbuf);
michael@0 1668 if (!cn)
michael@0 1669 return SECFailure;
michael@0 1670 break;
michael@0 1671 default:
michael@0 1672 break;
michael@0 1673 }
michael@0 1674 if (cn) {
michael@0 1675 *currentOutput = cn;
michael@0 1676 nickNames->totallen += PORT_Strlen(cn);
michael@0 1677 ++currentOutput;
michael@0 1678 --numberOfGeneralNames;
michael@0 1679 }
michael@0 1680 currentInput = CERT_GetNextGeneralName(currentInput);
michael@0 1681 } while (currentInput != firstName);
michael@0 1682
michael@0 1683 return (numberOfGeneralNames == 0) ? SECSuccess : SECFailure;
michael@0 1684 }
michael@0 1685
michael@0 1686 /*
michael@0 1687 * Collect all valid DNS names from the given cert.
michael@0 1688 * The output arena will reference some temporaray data,
michael@0 1689 * but this saves us from dealing with two arenas.
michael@0 1690 * The caller may free all data by freeing CERTCertNicknames->arena.
michael@0 1691 */
michael@0 1692 CERTCertNicknames *
michael@0 1693 CERT_GetValidDNSPatternsFromCert(CERTCertificate *cert)
michael@0 1694 {
michael@0 1695 CERTGeneralName *generalNames;
michael@0 1696 CERTCertNicknames *nickNames;
michael@0 1697 PLArenaPool *arena;
michael@0 1698 char *singleName;
michael@0 1699
michael@0 1700 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
michael@0 1701 if (!arena) {
michael@0 1702 return NULL;
michael@0 1703 }
michael@0 1704
michael@0 1705 nickNames = PORT_ArenaAlloc(arena, sizeof(CERTCertNicknames));
michael@0 1706 if (!nickNames) {
michael@0 1707 PORT_FreeArena(arena, PR_FALSE);
michael@0 1708 return NULL;
michael@0 1709 }
michael@0 1710
michael@0 1711 /* init the structure */
michael@0 1712 nickNames->arena = arena;
michael@0 1713 nickNames->head = NULL;
michael@0 1714 nickNames->numnicknames = 0;
michael@0 1715 nickNames->nicknames = NULL;
michael@0 1716 nickNames->totallen = 0;
michael@0 1717
michael@0 1718 generalNames = cert_GetSubjectAltNameList(cert, arena);
michael@0 1719 if (generalNames) {
michael@0 1720 SECStatus rv_getnames = SECFailure;
michael@0 1721 PRUint32 numNames = cert_CountDNSPatterns(generalNames);
michael@0 1722
michael@0 1723 if (numNames) {
michael@0 1724 rv_getnames = cert_GetDNSPatternsFromGeneralNames(generalNames,
michael@0 1725 numNames, nickNames);
michael@0 1726 }
michael@0 1727
michael@0 1728 /* if there were names, we'll exit now, either with success or failure */
michael@0 1729 if (numNames) {
michael@0 1730 if (rv_getnames == SECSuccess) {
michael@0 1731 return nickNames;
michael@0 1732 }
michael@0 1733
michael@0 1734 /* failure to produce output */
michael@0 1735 PORT_FreeArena(arena, PR_FALSE);
michael@0 1736 return NULL;
michael@0 1737 }
michael@0 1738 }
michael@0 1739
michael@0 1740 /* no SAN extension or no names found in extension */
michael@0 1741 singleName = CERT_GetCommonName(&cert->subject);
michael@0 1742 if (singleName) {
michael@0 1743 nickNames->numnicknames = 1;
michael@0 1744 nickNames->nicknames = PORT_ArenaAlloc(arena, sizeof(char *));
michael@0 1745 if (nickNames->nicknames) {
michael@0 1746 *nickNames->nicknames = PORT_ArenaStrdup(arena, singleName);
michael@0 1747 }
michael@0 1748 PORT_Free(singleName);
michael@0 1749
michael@0 1750 /* Did we allocate both the buffer of pointers and the string? */
michael@0 1751 if (nickNames->nicknames && *nickNames->nicknames) {
michael@0 1752 return nickNames;
michael@0 1753 }
michael@0 1754 }
michael@0 1755
michael@0 1756 PORT_FreeArena(arena, PR_FALSE);
michael@0 1757 return NULL;
michael@0 1758 }
michael@0 1759
michael@0 1760 /* Make sure that the name of the host we are connecting to matches the
michael@0 1761 * name that is incoded in the common-name component of the certificate
michael@0 1762 * that they are using.
michael@0 1763 */
michael@0 1764 SECStatus
michael@0 1765 CERT_VerifyCertName(const CERTCertificate *cert, const char *hn)
michael@0 1766 {
michael@0 1767 char * cn;
michael@0 1768 SECStatus rv;
michael@0 1769 CERTOKDomainName *domainOK;
michael@0 1770
michael@0 1771 if (!hn || !strlen(hn)) {
michael@0 1772 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 1773 return SECFailure;
michael@0 1774 }
michael@0 1775
michael@0 1776 /* if the name is one that the user has already approved, it's OK. */
michael@0 1777 for (domainOK = cert->domainOK; domainOK; domainOK = domainOK->next) {
michael@0 1778 if (0 == PORT_Strcasecmp(hn, domainOK->name)) {
michael@0 1779 return SECSuccess;
michael@0 1780 }
michael@0 1781 }
michael@0 1782
michael@0 1783 /* Per RFC 2818, if the SubjectAltName extension is present, it must
michael@0 1784 ** be used as the cert's identity.
michael@0 1785 */
michael@0 1786 rv = cert_VerifySubjectAltName(cert, hn);
michael@0 1787 if (rv == SECSuccess || PORT_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND)
michael@0 1788 return rv;
michael@0 1789
michael@0 1790 cn = CERT_GetCommonName(&cert->subject);
michael@0 1791 if ( cn ) {
michael@0 1792 PRBool isIPaddr = cert_IsIPAddr(hn);
michael@0 1793 if (isIPaddr) {
michael@0 1794 if (PORT_Strcasecmp(hn, cn) == 0) {
michael@0 1795 rv = SECSuccess;
michael@0 1796 } else {
michael@0 1797 PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
michael@0 1798 rv = SECFailure;
michael@0 1799 }
michael@0 1800 } else {
michael@0 1801 rv = cert_TestHostName(cn, hn);
michael@0 1802 }
michael@0 1803 PORT_Free(cn);
michael@0 1804 } else
michael@0 1805 PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
michael@0 1806 return rv;
michael@0 1807 }
michael@0 1808
michael@0 1809 PRBool
michael@0 1810 CERT_CompareCerts(const CERTCertificate *c1, const CERTCertificate *c2)
michael@0 1811 {
michael@0 1812 SECComparison comp;
michael@0 1813
michael@0 1814 comp = SECITEM_CompareItem(&c1->derCert, &c2->derCert);
michael@0 1815 if ( comp == SECEqual ) { /* certs are the same */
michael@0 1816 return(PR_TRUE);
michael@0 1817 } else {
michael@0 1818 return(PR_FALSE);
michael@0 1819 }
michael@0 1820 }
michael@0 1821
michael@0 1822 static SECStatus
michael@0 1823 StringsEqual(char *s1, char *s2) {
michael@0 1824 if ( ( s1 == NULL ) || ( s2 == NULL ) ) {
michael@0 1825 if ( s1 != s2 ) { /* only one is null */
michael@0 1826 return(SECFailure);
michael@0 1827 }
michael@0 1828 return(SECSuccess); /* both are null */
michael@0 1829 }
michael@0 1830
michael@0 1831 if ( PORT_Strcmp( s1, s2 ) != 0 ) {
michael@0 1832 return(SECFailure); /* not equal */
michael@0 1833 }
michael@0 1834
michael@0 1835 return(SECSuccess); /* strings are equal */
michael@0 1836 }
michael@0 1837
michael@0 1838
michael@0 1839 PRBool
michael@0 1840 CERT_CompareCertsForRedirection(CERTCertificate *c1, CERTCertificate *c2)
michael@0 1841 {
michael@0 1842 SECComparison comp;
michael@0 1843 char *c1str, *c2str;
michael@0 1844 SECStatus eq;
michael@0 1845
michael@0 1846 comp = SECITEM_CompareItem(&c1->derCert, &c2->derCert);
michael@0 1847 if ( comp == SECEqual ) { /* certs are the same */
michael@0 1848 return(PR_TRUE);
michael@0 1849 }
michael@0 1850
michael@0 1851 /* check if they are issued by the same CA */
michael@0 1852 comp = SECITEM_CompareItem(&c1->derIssuer, &c2->derIssuer);
michael@0 1853 if ( comp != SECEqual ) { /* different issuer */
michael@0 1854 return(PR_FALSE);
michael@0 1855 }
michael@0 1856
michael@0 1857 /* check country name */
michael@0 1858 c1str = CERT_GetCountryName(&c1->subject);
michael@0 1859 c2str = CERT_GetCountryName(&c2->subject);
michael@0 1860 eq = StringsEqual(c1str, c2str);
michael@0 1861 PORT_Free(c1str);
michael@0 1862 PORT_Free(c2str);
michael@0 1863 if ( eq != SECSuccess ) {
michael@0 1864 return(PR_FALSE);
michael@0 1865 }
michael@0 1866
michael@0 1867 /* check locality name */
michael@0 1868 c1str = CERT_GetLocalityName(&c1->subject);
michael@0 1869 c2str = CERT_GetLocalityName(&c2->subject);
michael@0 1870 eq = StringsEqual(c1str, c2str);
michael@0 1871 PORT_Free(c1str);
michael@0 1872 PORT_Free(c2str);
michael@0 1873 if ( eq != SECSuccess ) {
michael@0 1874 return(PR_FALSE);
michael@0 1875 }
michael@0 1876
michael@0 1877 /* check state name */
michael@0 1878 c1str = CERT_GetStateName(&c1->subject);
michael@0 1879 c2str = CERT_GetStateName(&c2->subject);
michael@0 1880 eq = StringsEqual(c1str, c2str);
michael@0 1881 PORT_Free(c1str);
michael@0 1882 PORT_Free(c2str);
michael@0 1883 if ( eq != SECSuccess ) {
michael@0 1884 return(PR_FALSE);
michael@0 1885 }
michael@0 1886
michael@0 1887 /* check org name */
michael@0 1888 c1str = CERT_GetOrgName(&c1->subject);
michael@0 1889 c2str = CERT_GetOrgName(&c2->subject);
michael@0 1890 eq = StringsEqual(c1str, c2str);
michael@0 1891 PORT_Free(c1str);
michael@0 1892 PORT_Free(c2str);
michael@0 1893 if ( eq != SECSuccess ) {
michael@0 1894 return(PR_FALSE);
michael@0 1895 }
michael@0 1896
michael@0 1897 #ifdef NOTDEF
michael@0 1898 /* check orgUnit name */
michael@0 1899 /*
michael@0 1900 * We need to revisit this and decide which fields should be allowed to be
michael@0 1901 * different
michael@0 1902 */
michael@0 1903 c1str = CERT_GetOrgUnitName(&c1->subject);
michael@0 1904 c2str = CERT_GetOrgUnitName(&c2->subject);
michael@0 1905 eq = StringsEqual(c1str, c2str);
michael@0 1906 PORT_Free(c1str);
michael@0 1907 PORT_Free(c2str);
michael@0 1908 if ( eq != SECSuccess ) {
michael@0 1909 return(PR_FALSE);
michael@0 1910 }
michael@0 1911 #endif
michael@0 1912
michael@0 1913 return(PR_TRUE); /* all fields but common name are the same */
michael@0 1914 }
michael@0 1915
michael@0 1916
michael@0 1917 /* CERT_CertChainFromCert and CERT_DestroyCertificateList moved
michael@0 1918 to certhigh.c */
michael@0 1919
michael@0 1920
michael@0 1921 CERTIssuerAndSN *
michael@0 1922 CERT_GetCertIssuerAndSN(PLArenaPool *arena, CERTCertificate *cert)
michael@0 1923 {
michael@0 1924 CERTIssuerAndSN *result;
michael@0 1925 SECStatus rv;
michael@0 1926
michael@0 1927 if ( arena == NULL ) {
michael@0 1928 arena = cert->arena;
michael@0 1929 }
michael@0 1930
michael@0 1931 result = (CERTIssuerAndSN*)PORT_ArenaZAlloc(arena, sizeof(*result));
michael@0 1932 if (result == NULL) {
michael@0 1933 PORT_SetError (SEC_ERROR_NO_MEMORY);
michael@0 1934 return NULL;
michael@0 1935 }
michael@0 1936
michael@0 1937 rv = SECITEM_CopyItem(arena, &result->derIssuer, &cert->derIssuer);
michael@0 1938 if (rv != SECSuccess)
michael@0 1939 return NULL;
michael@0 1940
michael@0 1941 rv = CERT_CopyName(arena, &result->issuer, &cert->issuer);
michael@0 1942 if (rv != SECSuccess)
michael@0 1943 return NULL;
michael@0 1944
michael@0 1945 rv = SECITEM_CopyItem(arena, &result->serialNumber, &cert->serialNumber);
michael@0 1946 if (rv != SECSuccess)
michael@0 1947 return NULL;
michael@0 1948
michael@0 1949 return result;
michael@0 1950 }
michael@0 1951
michael@0 1952 char *
michael@0 1953 CERT_MakeCANickname(CERTCertificate *cert)
michael@0 1954 {
michael@0 1955 char *firstname = NULL;
michael@0 1956 char *org = NULL;
michael@0 1957 char *nickname = NULL;
michael@0 1958 int count;
michael@0 1959 CERTCertificate *dummycert;
michael@0 1960
michael@0 1961 firstname = CERT_GetCommonName(&cert->subject);
michael@0 1962 if ( firstname == NULL ) {
michael@0 1963 firstname = CERT_GetOrgUnitName(&cert->subject);
michael@0 1964 }
michael@0 1965
michael@0 1966 org = CERT_GetOrgName(&cert->issuer);
michael@0 1967 if (org == NULL) {
michael@0 1968 org = CERT_GetDomainComponentName(&cert->issuer);
michael@0 1969 if (org == NULL) {
michael@0 1970 if (firstname) {
michael@0 1971 org = firstname;
michael@0 1972 firstname = NULL;
michael@0 1973 } else {
michael@0 1974 org = PORT_Strdup("Unknown CA");
michael@0 1975 }
michael@0 1976 }
michael@0 1977 }
michael@0 1978
michael@0 1979 /* can only fail if PORT_Strdup fails, in which case
michael@0 1980 * we're having memory problems. */
michael@0 1981 if (org == NULL) {
michael@0 1982 goto done;
michael@0 1983 }
michael@0 1984
michael@0 1985
michael@0 1986 count = 1;
michael@0 1987 while ( 1 ) {
michael@0 1988
michael@0 1989 if ( firstname ) {
michael@0 1990 if ( count == 1 ) {
michael@0 1991 nickname = PR_smprintf("%s - %s", firstname, org);
michael@0 1992 } else {
michael@0 1993 nickname = PR_smprintf("%s - %s #%d", firstname, org, count);
michael@0 1994 }
michael@0 1995 } else {
michael@0 1996 if ( count == 1 ) {
michael@0 1997 nickname = PR_smprintf("%s", org);
michael@0 1998 } else {
michael@0 1999 nickname = PR_smprintf("%s #%d", org, count);
michael@0 2000 }
michael@0 2001 }
michael@0 2002 if ( nickname == NULL ) {
michael@0 2003 goto done;
michael@0 2004 }
michael@0 2005
michael@0 2006 /* look up the nickname to make sure it isn't in use already */
michael@0 2007 dummycert = CERT_FindCertByNickname(cert->dbhandle, nickname);
michael@0 2008
michael@0 2009 if ( dummycert == NULL ) {
michael@0 2010 goto done;
michael@0 2011 }
michael@0 2012
michael@0 2013 /* found a cert, destroy it and loop */
michael@0 2014 CERT_DestroyCertificate(dummycert);
michael@0 2015
michael@0 2016 /* free the nickname */
michael@0 2017 PORT_Free(nickname);
michael@0 2018
michael@0 2019 count++;
michael@0 2020 }
michael@0 2021
michael@0 2022 done:
michael@0 2023 if ( firstname ) {
michael@0 2024 PORT_Free(firstname);
michael@0 2025 }
michael@0 2026 if ( org ) {
michael@0 2027 PORT_Free(org);
michael@0 2028 }
michael@0 2029
michael@0 2030 return(nickname);
michael@0 2031 }
michael@0 2032
michael@0 2033 /* CERT_Import_CAChain moved to certhigh.c */
michael@0 2034
michael@0 2035 void
michael@0 2036 CERT_DestroyCrl (CERTSignedCrl *crl)
michael@0 2037 {
michael@0 2038 SEC_DestroyCrl (crl);
michael@0 2039 }
michael@0 2040
michael@0 2041 static int
michael@0 2042 cert_Version(CERTCertificate *cert)
michael@0 2043 {
michael@0 2044 int version = 0;
michael@0 2045 if (cert && cert->version.data && cert->version.len) {
michael@0 2046 version = DER_GetInteger(&cert->version);
michael@0 2047 if (version < 0)
michael@0 2048 version = 0;
michael@0 2049 }
michael@0 2050 return version;
michael@0 2051 }
michael@0 2052
michael@0 2053 static unsigned int
michael@0 2054 cert_ComputeTrustOverrides(CERTCertificate *cert, unsigned int cType)
michael@0 2055 {
michael@0 2056 CERTCertTrust trust;
michael@0 2057 SECStatus rv = SECFailure;
michael@0 2058
michael@0 2059 rv = CERT_GetCertTrust(cert, &trust);
michael@0 2060
michael@0 2061 if (rv == SECSuccess && (trust.sslFlags |
michael@0 2062 trust.emailFlags |
michael@0 2063 trust.objectSigningFlags)) {
michael@0 2064
michael@0 2065 if (trust.sslFlags & (CERTDB_TERMINAL_RECORD|CERTDB_TRUSTED))
michael@0 2066 cType |= NS_CERT_TYPE_SSL_SERVER|NS_CERT_TYPE_SSL_CLIENT;
michael@0 2067 if (trust.sslFlags & (CERTDB_VALID_CA|CERTDB_TRUSTED_CA))
michael@0 2068 cType |= NS_CERT_TYPE_SSL_CA;
michael@0 2069 #if defined(CERTDB_NOT_TRUSTED)
michael@0 2070 if (trust.sslFlags & CERTDB_NOT_TRUSTED)
michael@0 2071 cType &= ~(NS_CERT_TYPE_SSL_SERVER|NS_CERT_TYPE_SSL_CLIENT|
michael@0 2072 NS_CERT_TYPE_SSL_CA);
michael@0 2073 #endif
michael@0 2074 if (trust.emailFlags & (CERTDB_TERMINAL_RECORD|CERTDB_TRUSTED))
michael@0 2075 cType |= NS_CERT_TYPE_EMAIL;
michael@0 2076 if (trust.emailFlags & (CERTDB_VALID_CA|CERTDB_TRUSTED_CA))
michael@0 2077 cType |= NS_CERT_TYPE_EMAIL_CA;
michael@0 2078 #if defined(CERTDB_NOT_TRUSTED)
michael@0 2079 if (trust.emailFlags & CERTDB_NOT_TRUSTED)
michael@0 2080 cType &= ~(NS_CERT_TYPE_EMAIL|NS_CERT_TYPE_EMAIL_CA);
michael@0 2081 #endif
michael@0 2082 if (trust.objectSigningFlags & (CERTDB_TERMINAL_RECORD|CERTDB_TRUSTED))
michael@0 2083 cType |= NS_CERT_TYPE_OBJECT_SIGNING;
michael@0 2084 if (trust.objectSigningFlags & (CERTDB_VALID_CA|CERTDB_TRUSTED_CA))
michael@0 2085 cType |= NS_CERT_TYPE_OBJECT_SIGNING_CA;
michael@0 2086 #if defined(CERTDB_NOT_TRUSTED)
michael@0 2087 if (trust.objectSigningFlags & CERTDB_NOT_TRUSTED)
michael@0 2088 cType &= ~(NS_CERT_TYPE_OBJECT_SIGNING|
michael@0 2089 NS_CERT_TYPE_OBJECT_SIGNING_CA);
michael@0 2090 #endif
michael@0 2091 }
michael@0 2092 return cType;
michael@0 2093 }
michael@0 2094
michael@0 2095 /*
michael@0 2096 * Does a cert belong to a CA? We decide based on perm database trust
michael@0 2097 * flags, Netscape Cert Type Extension, and KeyUsage Extension.
michael@0 2098 */
michael@0 2099 PRBool
michael@0 2100 CERT_IsCACert(CERTCertificate *cert, unsigned int *rettype)
michael@0 2101 {
michael@0 2102 unsigned int cType = cert->nsCertType;
michael@0 2103 PRBool ret = PR_FALSE;
michael@0 2104
michael@0 2105 if (cType & (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA |
michael@0 2106 NS_CERT_TYPE_OBJECT_SIGNING_CA)) {
michael@0 2107 ret = PR_TRUE;
michael@0 2108 } else {
michael@0 2109 SECStatus rv;
michael@0 2110 CERTBasicConstraints constraints;
michael@0 2111
michael@0 2112 rv = CERT_FindBasicConstraintExten(cert, &constraints);
michael@0 2113 if (rv == SECSuccess && constraints.isCA) {
michael@0 2114 ret = PR_TRUE;
michael@0 2115 cType |= (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA);
michael@0 2116 }
michael@0 2117 }
michael@0 2118
michael@0 2119 /* finally check if it's an X.509 v1 root CA */
michael@0 2120 if (!ret &&
michael@0 2121 (cert->isRoot && cert_Version(cert) < SEC_CERTIFICATE_VERSION_3)) {
michael@0 2122 ret = PR_TRUE;
michael@0 2123 cType |= (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA);
michael@0 2124 }
michael@0 2125 /* Now apply trust overrides, if any */
michael@0 2126 cType = cert_ComputeTrustOverrides(cert, cType);
michael@0 2127 ret = (cType & (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA |
michael@0 2128 NS_CERT_TYPE_OBJECT_SIGNING_CA)) ? PR_TRUE : PR_FALSE;
michael@0 2129
michael@0 2130 if (rettype != NULL) {
michael@0 2131 *rettype = cType;
michael@0 2132 }
michael@0 2133 return ret;
michael@0 2134 }
michael@0 2135
michael@0 2136 PRBool
michael@0 2137 CERT_IsCADERCert(SECItem *derCert, unsigned int *type) {
michael@0 2138 CERTCertificate *cert;
michael@0 2139 PRBool isCA;
michael@0 2140
michael@0 2141 /* This is okay -- only looks at extensions */
michael@0 2142 cert = CERT_DecodeDERCertificate(derCert, PR_FALSE, NULL);
michael@0 2143 if (cert == NULL) return PR_FALSE;
michael@0 2144
michael@0 2145 isCA = CERT_IsCACert(cert,type);
michael@0 2146 CERT_DestroyCertificate (cert);
michael@0 2147 return isCA;
michael@0 2148 }
michael@0 2149
michael@0 2150 PRBool
michael@0 2151 CERT_IsRootDERCert(SECItem *derCert)
michael@0 2152 {
michael@0 2153 CERTCertificate *cert;
michael@0 2154 PRBool isRoot;
michael@0 2155
michael@0 2156 /* This is okay -- only looks at extensions */
michael@0 2157 cert = CERT_DecodeDERCertificate(derCert, PR_FALSE, NULL);
michael@0 2158 if (cert == NULL) return PR_FALSE;
michael@0 2159
michael@0 2160 isRoot = cert->isRoot;
michael@0 2161 CERT_DestroyCertificate (cert);
michael@0 2162 return isRoot;
michael@0 2163 }
michael@0 2164
michael@0 2165 CERTCompareValidityStatus
michael@0 2166 CERT_CompareValidityTimes(CERTValidity* val_a, CERTValidity* val_b)
michael@0 2167 {
michael@0 2168 PRTime notBeforeA, notBeforeB, notAfterA, notAfterB;
michael@0 2169
michael@0 2170 if (!val_a || !val_b)
michael@0 2171 {
michael@0 2172 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 2173 return certValidityUndetermined;
michael@0 2174 }
michael@0 2175
michael@0 2176 if ( SECSuccess != DER_DecodeTimeChoice(&notBeforeA, &val_a->notBefore) ||
michael@0 2177 SECSuccess != DER_DecodeTimeChoice(&notBeforeB, &val_b->notBefore) ||
michael@0 2178 SECSuccess != DER_DecodeTimeChoice(&notAfterA, &val_a->notAfter) ||
michael@0 2179 SECSuccess != DER_DecodeTimeChoice(&notAfterB, &val_b->notAfter) ) {
michael@0 2180 return certValidityUndetermined;
michael@0 2181 }
michael@0 2182
michael@0 2183 /* sanity check */
michael@0 2184 if (LL_CMP(notBeforeA,>,notAfterA) || LL_CMP(notBeforeB,>,notAfterB)) {
michael@0 2185 PORT_SetError(SEC_ERROR_INVALID_TIME);
michael@0 2186 return certValidityUndetermined;
michael@0 2187 }
michael@0 2188
michael@0 2189 if (LL_CMP(notAfterA,!=,notAfterB)) {
michael@0 2190 /* one cert validity goes farther into the future, select it */
michael@0 2191 return LL_CMP(notAfterA,<,notAfterB) ?
michael@0 2192 certValidityChooseB : certValidityChooseA;
michael@0 2193 }
michael@0 2194 /* the two certs have the same expiration date */
michael@0 2195 PORT_Assert(LL_CMP(notAfterA, == , notAfterB));
michael@0 2196 /* do they also have the same start date ? */
michael@0 2197 if (LL_CMP(notBeforeA,==,notBeforeB)) {
michael@0 2198 return certValidityEqual;
michael@0 2199 }
michael@0 2200 /* choose cert with the later start date */
michael@0 2201 return LL_CMP(notBeforeA,<,notBeforeB) ?
michael@0 2202 certValidityChooseB : certValidityChooseA;
michael@0 2203 }
michael@0 2204
michael@0 2205 /*
michael@0 2206 * is certa newer than certb? If one is expired, pick the other one.
michael@0 2207 */
michael@0 2208 PRBool
michael@0 2209 CERT_IsNewer(CERTCertificate *certa, CERTCertificate *certb)
michael@0 2210 {
michael@0 2211 PRTime notBeforeA, notAfterA, notBeforeB, notAfterB, now;
michael@0 2212 SECStatus rv;
michael@0 2213 PRBool newerbefore, newerafter;
michael@0 2214
michael@0 2215 rv = CERT_GetCertTimes(certa, &notBeforeA, &notAfterA);
michael@0 2216 if ( rv != SECSuccess ) {
michael@0 2217 return(PR_FALSE);
michael@0 2218 }
michael@0 2219
michael@0 2220 rv = CERT_GetCertTimes(certb, &notBeforeB, &notAfterB);
michael@0 2221 if ( rv != SECSuccess ) {
michael@0 2222 return(PR_TRUE);
michael@0 2223 }
michael@0 2224
michael@0 2225 newerbefore = PR_FALSE;
michael@0 2226 if ( LL_CMP(notBeforeA, >, notBeforeB) ) {
michael@0 2227 newerbefore = PR_TRUE;
michael@0 2228 }
michael@0 2229
michael@0 2230 newerafter = PR_FALSE;
michael@0 2231 if ( LL_CMP(notAfterA, >, notAfterB) ) {
michael@0 2232 newerafter = PR_TRUE;
michael@0 2233 }
michael@0 2234
michael@0 2235 if ( newerbefore && newerafter ) {
michael@0 2236 return(PR_TRUE);
michael@0 2237 }
michael@0 2238
michael@0 2239 if ( ( !newerbefore ) && ( !newerafter ) ) {
michael@0 2240 return(PR_FALSE);
michael@0 2241 }
michael@0 2242
michael@0 2243 /* get current time */
michael@0 2244 now = PR_Now();
michael@0 2245
michael@0 2246 if ( newerbefore ) {
michael@0 2247 /* cert A was issued after cert B, but expires sooner */
michael@0 2248 /* if A is expired, then pick B */
michael@0 2249 if ( LL_CMP(notAfterA, <, now ) ) {
michael@0 2250 return(PR_FALSE);
michael@0 2251 }
michael@0 2252 return(PR_TRUE);
michael@0 2253 } else {
michael@0 2254 /* cert B was issued after cert A, but expires sooner */
michael@0 2255 /* if B is expired, then pick A */
michael@0 2256 if ( LL_CMP(notAfterB, <, now ) ) {
michael@0 2257 return(PR_TRUE);
michael@0 2258 }
michael@0 2259 return(PR_FALSE);
michael@0 2260 }
michael@0 2261 }
michael@0 2262
michael@0 2263 void
michael@0 2264 CERT_DestroyCertArray(CERTCertificate **certs, unsigned int ncerts)
michael@0 2265 {
michael@0 2266 unsigned int i;
michael@0 2267
michael@0 2268 if ( certs ) {
michael@0 2269 for ( i = 0; i < ncerts; i++ ) {
michael@0 2270 if ( certs[i] ) {
michael@0 2271 CERT_DestroyCertificate(certs[i]);
michael@0 2272 }
michael@0 2273 }
michael@0 2274
michael@0 2275 PORT_Free(certs);
michael@0 2276 }
michael@0 2277
michael@0 2278 return;
michael@0 2279 }
michael@0 2280
michael@0 2281 char *
michael@0 2282 CERT_FixupEmailAddr(const char *emailAddr)
michael@0 2283 {
michael@0 2284 char *retaddr;
michael@0 2285 char *str;
michael@0 2286
michael@0 2287 if ( emailAddr == NULL ) {
michael@0 2288 return(NULL);
michael@0 2289 }
michael@0 2290
michael@0 2291 /* copy the string */
michael@0 2292 str = retaddr = PORT_Strdup(emailAddr);
michael@0 2293 if ( str == NULL ) {
michael@0 2294 return(NULL);
michael@0 2295 }
michael@0 2296
michael@0 2297 /* make it lower case */
michael@0 2298 while ( *str ) {
michael@0 2299 *str = tolower( *str );
michael@0 2300 str++;
michael@0 2301 }
michael@0 2302
michael@0 2303 return(retaddr);
michael@0 2304 }
michael@0 2305
michael@0 2306 /*
michael@0 2307 * NOTE - don't allow encode of govt-approved or invisible bits
michael@0 2308 */
michael@0 2309 SECStatus
michael@0 2310 CERT_DecodeTrustString(CERTCertTrust *trust, const char *trusts)
michael@0 2311 {
michael@0 2312 unsigned int i;
michael@0 2313 unsigned int *pflags;
michael@0 2314
michael@0 2315 if (!trust) {
michael@0 2316 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 2317 return SECFailure;
michael@0 2318 }
michael@0 2319 trust->sslFlags = 0;
michael@0 2320 trust->emailFlags = 0;
michael@0 2321 trust->objectSigningFlags = 0;
michael@0 2322 if (!trusts) {
michael@0 2323 PORT_SetError(SEC_ERROR_INVALID_ARGS);
michael@0 2324 return SECFailure;
michael@0 2325 }
michael@0 2326
michael@0 2327 pflags = &trust->sslFlags;
michael@0 2328
michael@0 2329 for (i=0; i < PORT_Strlen(trusts); i++) {
michael@0 2330 switch (trusts[i]) {
michael@0 2331 case 'p':
michael@0 2332 *pflags = *pflags | CERTDB_TERMINAL_RECORD;
michael@0 2333 break;
michael@0 2334
michael@0 2335 case 'P':
michael@0 2336 *pflags = *pflags | CERTDB_TRUSTED | CERTDB_TERMINAL_RECORD;
michael@0 2337 break;
michael@0 2338
michael@0 2339 case 'w':
michael@0 2340 *pflags = *pflags | CERTDB_SEND_WARN;
michael@0 2341 break;
michael@0 2342
michael@0 2343 case 'c':
michael@0 2344 *pflags = *pflags | CERTDB_VALID_CA;
michael@0 2345 break;
michael@0 2346
michael@0 2347 case 'T':
michael@0 2348 *pflags = *pflags | CERTDB_TRUSTED_CLIENT_CA | CERTDB_VALID_CA;
michael@0 2349 break;
michael@0 2350
michael@0 2351 case 'C' :
michael@0 2352 *pflags = *pflags | CERTDB_TRUSTED_CA | CERTDB_VALID_CA;
michael@0 2353 break;
michael@0 2354
michael@0 2355 case 'u':
michael@0 2356 *pflags = *pflags | CERTDB_USER;
michael@0 2357 break;
michael@0 2358
michael@0 2359 case 'i':
michael@0 2360 *pflags = *pflags | CERTDB_INVISIBLE_CA;
michael@0 2361 break;
michael@0 2362 case 'g':
michael@0 2363 *pflags = *pflags | CERTDB_GOVT_APPROVED_CA;
michael@0 2364 break;
michael@0 2365
michael@0 2366 case ',':
michael@0 2367 if ( pflags == &trust->sslFlags ) {
michael@0 2368 pflags = &trust->emailFlags;
michael@0 2369 } else {
michael@0 2370 pflags = &trust->objectSigningFlags;
michael@0 2371 }
michael@0 2372 break;
michael@0 2373 default:
michael@0 2374 return SECFailure;
michael@0 2375 }
michael@0 2376 }
michael@0 2377
michael@0 2378 return SECSuccess;
michael@0 2379 }
michael@0 2380
michael@0 2381 static void
michael@0 2382 EncodeFlags(char *trusts, unsigned int flags)
michael@0 2383 {
michael@0 2384 if (flags & CERTDB_VALID_CA)
michael@0 2385 if (!(flags & CERTDB_TRUSTED_CA) &&
michael@0 2386 !(flags & CERTDB_TRUSTED_CLIENT_CA))
michael@0 2387 PORT_Strcat(trusts, "c");
michael@0 2388 if (flags & CERTDB_TERMINAL_RECORD)
michael@0 2389 if (!(flags & CERTDB_TRUSTED))
michael@0 2390 PORT_Strcat(trusts, "p");
michael@0 2391 if (flags & CERTDB_TRUSTED_CA)
michael@0 2392 PORT_Strcat(trusts, "C");
michael@0 2393 if (flags & CERTDB_TRUSTED_CLIENT_CA)
michael@0 2394 PORT_Strcat(trusts, "T");
michael@0 2395 if (flags & CERTDB_TRUSTED)
michael@0 2396 PORT_Strcat(trusts, "P");
michael@0 2397 if (flags & CERTDB_USER)
michael@0 2398 PORT_Strcat(trusts, "u");
michael@0 2399 if (flags & CERTDB_SEND_WARN)
michael@0 2400 PORT_Strcat(trusts, "w");
michael@0 2401 if (flags & CERTDB_INVISIBLE_CA)
michael@0 2402 PORT_Strcat(trusts, "I");
michael@0 2403 if (flags & CERTDB_GOVT_APPROVED_CA)
michael@0 2404 PORT_Strcat(trusts, "G");
michael@0 2405 return;
michael@0 2406 }
michael@0 2407
michael@0 2408 char *
michael@0 2409 CERT_EncodeTrustString(CERTCertTrust *trust)
michael@0 2410 {
michael@0 2411 char tmpTrustSSL[32];
michael@0 2412 char tmpTrustEmail[32];
michael@0 2413 char tmpTrustSigning[32];
michael@0 2414 char *retstr = NULL;
michael@0 2415
michael@0 2416 if ( trust ) {
michael@0 2417 tmpTrustSSL[0] = '\0';
michael@0 2418 tmpTrustEmail[0] = '\0';
michael@0 2419 tmpTrustSigning[0] = '\0';
michael@0 2420
michael@0 2421 EncodeFlags(tmpTrustSSL, trust->sslFlags);
michael@0 2422 EncodeFlags(tmpTrustEmail, trust->emailFlags);
michael@0 2423 EncodeFlags(tmpTrustSigning, trust->objectSigningFlags);
michael@0 2424
michael@0 2425 retstr = PR_smprintf("%s,%s,%s", tmpTrustSSL, tmpTrustEmail,
michael@0 2426 tmpTrustSigning);
michael@0 2427 }
michael@0 2428
michael@0 2429 return(retstr);
michael@0 2430 }
michael@0 2431
michael@0 2432 SECStatus
michael@0 2433 CERT_ImportCerts(CERTCertDBHandle *certdb, SECCertUsage usage,
michael@0 2434 unsigned int ncerts, SECItem **derCerts,
michael@0 2435 CERTCertificate ***retCerts, PRBool keepCerts,
michael@0 2436 PRBool caOnly, char *nickname)
michael@0 2437 {
michael@0 2438 unsigned int i;
michael@0 2439 CERTCertificate **certs = NULL;
michael@0 2440 SECStatus rv;
michael@0 2441 unsigned int fcerts = 0;
michael@0 2442
michael@0 2443 if ( ncerts ) {
michael@0 2444 certs = PORT_ZNewArray(CERTCertificate*, ncerts);
michael@0 2445 if ( certs == NULL ) {
michael@0 2446 return(SECFailure);
michael@0 2447 }
michael@0 2448
michael@0 2449 /* decode all of the certs into the temporary DB */
michael@0 2450 for ( i = 0, fcerts= 0; i < ncerts; i++) {
michael@0 2451 certs[fcerts] = CERT_NewTempCertificate(certdb,
michael@0 2452 derCerts[i],
michael@0 2453 NULL,
michael@0 2454 PR_FALSE,
michael@0 2455 PR_TRUE);
michael@0 2456 if (certs[fcerts]) {
michael@0 2457 SECItem subjKeyID = {siBuffer, NULL, 0};
michael@0 2458 if (CERT_FindSubjectKeyIDExtension(certs[fcerts],
michael@0 2459 &subjKeyID) == SECSuccess) {
michael@0 2460 if (subjKeyID.data) {
michael@0 2461 cert_AddSubjectKeyIDMapping(&subjKeyID, certs[fcerts]);
michael@0 2462 }
michael@0 2463 SECITEM_FreeItem(&subjKeyID, PR_FALSE);
michael@0 2464 }
michael@0 2465 fcerts++;
michael@0 2466 }
michael@0 2467 }
michael@0 2468
michael@0 2469 if ( keepCerts ) {
michael@0 2470 for ( i = 0; i < fcerts; i++ ) {
michael@0 2471 char* canickname = NULL;
michael@0 2472 PRBool isCA;
michael@0 2473
michael@0 2474 SECKEY_UpdateCertPQG(certs[i]);
michael@0 2475
michael@0 2476 isCA = CERT_IsCACert(certs[i], NULL);
michael@0 2477 if ( isCA ) {
michael@0 2478 canickname = CERT_MakeCANickname(certs[i]);
michael@0 2479 }
michael@0 2480
michael@0 2481 if(isCA && (fcerts > 1)) {
michael@0 2482 /* if we are importing only a single cert and specifying
michael@0 2483 * a nickname, we want to use that nickname if it a CA,
michael@0 2484 * otherwise if there are more than one cert, we don't
michael@0 2485 * know which cert it belongs to. But we still may try
michael@0 2486 * the individual canickname from the cert itself.
michael@0 2487 */
michael@0 2488 rv = CERT_AddTempCertToPerm(certs[i], canickname, NULL);
michael@0 2489 } else {
michael@0 2490 rv = CERT_AddTempCertToPerm(certs[i],
michael@0 2491 nickname?nickname:canickname, NULL);
michael@0 2492 }
michael@0 2493
michael@0 2494 PORT_Free(canickname);
michael@0 2495 /* don't care if it fails - keep going */
michael@0 2496 }
michael@0 2497 }
michael@0 2498 }
michael@0 2499
michael@0 2500 if ( retCerts ) {
michael@0 2501 *retCerts = certs;
michael@0 2502 } else {
michael@0 2503 if (certs) {
michael@0 2504 CERT_DestroyCertArray(certs, fcerts);
michael@0 2505 }
michael@0 2506 }
michael@0 2507
michael@0 2508 return ((fcerts || !ncerts) ? SECSuccess : SECFailure);
michael@0 2509 }
michael@0 2510
michael@0 2511 /*
michael@0 2512 * a real list of certificates - need to convert CERTCertificateList
michael@0 2513 * stuff and ASN 1 encoder/decoder over to using this...
michael@0 2514 */
michael@0 2515 CERTCertList *
michael@0 2516 CERT_NewCertList(void)
michael@0 2517 {
michael@0 2518 PLArenaPool *arena = NULL;
michael@0 2519 CERTCertList *ret = NULL;
michael@0 2520
michael@0 2521 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
michael@0 2522 if ( arena == NULL ) {
michael@0 2523 goto loser;
michael@0 2524 }
michael@0 2525
michael@0 2526 ret = (CERTCertList *)PORT_ArenaZAlloc(arena, sizeof(CERTCertList));
michael@0 2527 if ( ret == NULL ) {
michael@0 2528 goto loser;
michael@0 2529 }
michael@0 2530
michael@0 2531 ret->arena = arena;
michael@0 2532
michael@0 2533 PR_INIT_CLIST(&ret->list);
michael@0 2534
michael@0 2535 return(ret);
michael@0 2536
michael@0 2537 loser:
michael@0 2538 if ( arena != NULL ) {
michael@0 2539 PORT_FreeArena(arena, PR_FALSE);
michael@0 2540 }
michael@0 2541
michael@0 2542 return(NULL);
michael@0 2543 }
michael@0 2544
michael@0 2545 void
michael@0 2546 CERT_DestroyCertList(CERTCertList *certs)
michael@0 2547 {
michael@0 2548 PRCList *node;
michael@0 2549
michael@0 2550 while( !PR_CLIST_IS_EMPTY(&certs->list) ) {
michael@0 2551 node = PR_LIST_HEAD(&certs->list);
michael@0 2552 CERT_DestroyCertificate(((CERTCertListNode *)node)->cert);
michael@0 2553 PR_REMOVE_LINK(node);
michael@0 2554 }
michael@0 2555
michael@0 2556 PORT_FreeArena(certs->arena, PR_FALSE);
michael@0 2557
michael@0 2558 return;
michael@0 2559 }
michael@0 2560
michael@0 2561 void
michael@0 2562 CERT_RemoveCertListNode(CERTCertListNode *node)
michael@0 2563 {
michael@0 2564 CERT_DestroyCertificate(node->cert);
michael@0 2565 PR_REMOVE_LINK(&node->links);
michael@0 2566 return;
michael@0 2567 }
michael@0 2568
michael@0 2569
michael@0 2570 SECStatus
michael@0 2571 CERT_AddCertToListTailWithData(CERTCertList *certs,
michael@0 2572 CERTCertificate *cert, void *appData)
michael@0 2573 {
michael@0 2574 CERTCertListNode *node;
michael@0 2575
michael@0 2576 node = (CERTCertListNode *)PORT_ArenaZAlloc(certs->arena,
michael@0 2577 sizeof(CERTCertListNode));
michael@0 2578 if ( node == NULL ) {
michael@0 2579 goto loser;
michael@0 2580 }
michael@0 2581
michael@0 2582 PR_INSERT_BEFORE(&node->links, &certs->list);
michael@0 2583 /* certs->count++; */
michael@0 2584 node->cert = cert;
michael@0 2585 node->appData = appData;
michael@0 2586 return(SECSuccess);
michael@0 2587
michael@0 2588 loser:
michael@0 2589 return(SECFailure);
michael@0 2590 }
michael@0 2591
michael@0 2592 SECStatus
michael@0 2593 CERT_AddCertToListTail(CERTCertList *certs, CERTCertificate *cert)
michael@0 2594 {
michael@0 2595 return CERT_AddCertToListTailWithData(certs, cert, NULL);
michael@0 2596 }
michael@0 2597
michael@0 2598 SECStatus
michael@0 2599 CERT_AddCertToListHeadWithData(CERTCertList *certs,
michael@0 2600 CERTCertificate *cert, void *appData)
michael@0 2601 {
michael@0 2602 CERTCertListNode *node;
michael@0 2603 CERTCertListNode *head;
michael@0 2604
michael@0 2605 head = CERT_LIST_HEAD(certs);
michael@0 2606
michael@0 2607 if (head == NULL) return CERT_AddCertToListTail(certs,cert);
michael@0 2608
michael@0 2609 node = (CERTCertListNode *)PORT_ArenaZAlloc(certs->arena,
michael@0 2610 sizeof(CERTCertListNode));
michael@0 2611 if ( node == NULL ) {
michael@0 2612 goto loser;
michael@0 2613 }
michael@0 2614
michael@0 2615 PR_INSERT_BEFORE(&node->links, &head->links);
michael@0 2616 /* certs->count++; */
michael@0 2617 node->cert = cert;
michael@0 2618 node->appData = appData;
michael@0 2619 return(SECSuccess);
michael@0 2620
michael@0 2621 loser:
michael@0 2622 return(SECFailure);
michael@0 2623 }
michael@0 2624
michael@0 2625 SECStatus
michael@0 2626 CERT_AddCertToListHead(CERTCertList *certs, CERTCertificate *cert)
michael@0 2627 {
michael@0 2628 return CERT_AddCertToListHeadWithData(certs, cert, NULL);
michael@0 2629 }
michael@0 2630
michael@0 2631 /*
michael@0 2632 * Sort callback function to determine if cert a is newer than cert b.
michael@0 2633 * Not valid certs are considered older than valid certs.
michael@0 2634 */
michael@0 2635 PRBool
michael@0 2636 CERT_SortCBValidity(CERTCertificate *certa,
michael@0 2637 CERTCertificate *certb,
michael@0 2638 void *arg)
michael@0 2639 {
michael@0 2640 PRTime sorttime;
michael@0 2641 PRTime notBeforeA, notAfterA, notBeforeB, notAfterB;
michael@0 2642 SECStatus rv;
michael@0 2643 PRBool newerbefore, newerafter;
michael@0 2644 PRBool aNotValid = PR_FALSE, bNotValid = PR_FALSE;
michael@0 2645
michael@0 2646 sorttime = *(PRTime *)arg;
michael@0 2647
michael@0 2648 rv = CERT_GetCertTimes(certa, &notBeforeA, &notAfterA);
michael@0 2649 if ( rv != SECSuccess ) {
michael@0 2650 return(PR_FALSE);
michael@0 2651 }
michael@0 2652
michael@0 2653 rv = CERT_GetCertTimes(certb, &notBeforeB, &notAfterB);
michael@0 2654 if ( rv != SECSuccess ) {
michael@0 2655 return(PR_TRUE);
michael@0 2656 }
michael@0 2657 newerbefore = PR_FALSE;
michael@0 2658 if ( LL_CMP(notBeforeA, >, notBeforeB) ) {
michael@0 2659 newerbefore = PR_TRUE;
michael@0 2660 }
michael@0 2661 newerafter = PR_FALSE;
michael@0 2662 if ( LL_CMP(notAfterA, >, notAfterB) ) {
michael@0 2663 newerafter = PR_TRUE;
michael@0 2664 }
michael@0 2665
michael@0 2666 /* check if A is valid at sorttime */
michael@0 2667 if ( CERT_CheckCertValidTimes(certa, sorttime, PR_FALSE)
michael@0 2668 != secCertTimeValid ) {
michael@0 2669 aNotValid = PR_TRUE;
michael@0 2670 }
michael@0 2671
michael@0 2672 /* check if B is valid at sorttime */
michael@0 2673 if ( CERT_CheckCertValidTimes(certb, sorttime, PR_FALSE)
michael@0 2674 != secCertTimeValid ) {
michael@0 2675 bNotValid = PR_TRUE;
michael@0 2676 }
michael@0 2677
michael@0 2678 /* a is valid, b is not */
michael@0 2679 if ( bNotValid && ( ! aNotValid ) ) {
michael@0 2680 return(PR_TRUE);
michael@0 2681 }
michael@0 2682
michael@0 2683 /* b is valid, a is not */
michael@0 2684 if ( aNotValid && ( ! bNotValid ) ) {
michael@0 2685 return(PR_FALSE);
michael@0 2686 }
michael@0 2687
michael@0 2688 /* a and b are either valid or not valid */
michael@0 2689 if ( newerbefore && newerafter ) {
michael@0 2690 return(PR_TRUE);
michael@0 2691 }
michael@0 2692
michael@0 2693 if ( ( !newerbefore ) && ( !newerafter ) ) {
michael@0 2694 return(PR_FALSE);
michael@0 2695 }
michael@0 2696
michael@0 2697 if ( newerbefore ) {
michael@0 2698 /* cert A was issued after cert B, but expires sooner */
michael@0 2699 return(PR_TRUE);
michael@0 2700 } else {
michael@0 2701 /* cert B was issued after cert A, but expires sooner */
michael@0 2702 return(PR_FALSE);
michael@0 2703 }
michael@0 2704 }
michael@0 2705
michael@0 2706
michael@0 2707 SECStatus
michael@0 2708 CERT_AddCertToListSorted(CERTCertList *certs,
michael@0 2709 CERTCertificate *cert,
michael@0 2710 CERTSortCallback f,
michael@0 2711 void *arg)
michael@0 2712 {
michael@0 2713 CERTCertListNode *node;
michael@0 2714 CERTCertListNode *head;
michael@0 2715 PRBool ret;
michael@0 2716
michael@0 2717 node = (CERTCertListNode *)PORT_ArenaZAlloc(certs->arena,
michael@0 2718 sizeof(CERTCertListNode));
michael@0 2719 if ( node == NULL ) {
michael@0 2720 goto loser;
michael@0 2721 }
michael@0 2722
michael@0 2723 head = CERT_LIST_HEAD(certs);
michael@0 2724
michael@0 2725 while ( !CERT_LIST_END(head, certs) ) {
michael@0 2726
michael@0 2727 /* if cert is already in the list, then don't add it again */
michael@0 2728 if ( cert == head->cert ) {
michael@0 2729 /*XXX*/
michael@0 2730 /* don't keep a reference */
michael@0 2731 CERT_DestroyCertificate(cert);
michael@0 2732 goto done;
michael@0 2733 }
michael@0 2734
michael@0 2735 ret = (* f)(cert, head->cert, arg);
michael@0 2736 /* if sort function succeeds, then insert before current node */
michael@0 2737 if ( ret ) {
michael@0 2738 PR_INSERT_BEFORE(&node->links, &head->links);
michael@0 2739 goto done;
michael@0 2740 }
michael@0 2741
michael@0 2742 head = CERT_LIST_NEXT(head);
michael@0 2743 }
michael@0 2744 /* if we get to the end, then just insert it at the tail */
michael@0 2745 PR_INSERT_BEFORE(&node->links, &certs->list);
michael@0 2746
michael@0 2747 done:
michael@0 2748 /* certs->count++; */
michael@0 2749 node->cert = cert;
michael@0 2750 return(SECSuccess);
michael@0 2751
michael@0 2752 loser:
michael@0 2753 return(SECFailure);
michael@0 2754 }
michael@0 2755
michael@0 2756 /* This routine is here because pcertdb.c still has a call to it.
michael@0 2757 * The SMIME profile code in pcertdb.c should be split into high (find
michael@0 2758 * the email cert) and low (store the profile) code. At that point, we
michael@0 2759 * can move this to certhigh.c where it belongs.
michael@0 2760 *
michael@0 2761 * remove certs from a list that don't have keyUsage and certType
michael@0 2762 * that match the given usage.
michael@0 2763 */
michael@0 2764 SECStatus
michael@0 2765 CERT_FilterCertListByUsage(CERTCertList *certList, SECCertUsage usage,
michael@0 2766 PRBool ca)
michael@0 2767 {
michael@0 2768 unsigned int requiredKeyUsage;
michael@0 2769 unsigned int requiredCertType;
michael@0 2770 CERTCertListNode *node, *savenode;
michael@0 2771 SECStatus rv;
michael@0 2772
michael@0 2773 if (certList == NULL) goto loser;
michael@0 2774
michael@0 2775 rv = CERT_KeyUsageAndTypeForCertUsage(usage, ca, &requiredKeyUsage,
michael@0 2776 &requiredCertType);
michael@0 2777 if ( rv != SECSuccess ) {
michael@0 2778 goto loser;
michael@0 2779 }
michael@0 2780
michael@0 2781 node = CERT_LIST_HEAD(certList);
michael@0 2782
michael@0 2783 while ( !CERT_LIST_END(node, certList) ) {
michael@0 2784
michael@0 2785 PRBool bad = (PRBool)(!node->cert);
michael@0 2786
michael@0 2787 /* bad key usage ? */
michael@0 2788 if ( !bad &&
michael@0 2789 CERT_CheckKeyUsage(node->cert, requiredKeyUsage) != SECSuccess ) {
michael@0 2790 bad = PR_TRUE;
michael@0 2791 }
michael@0 2792 /* bad cert type ? */
michael@0 2793 if ( !bad ) {
michael@0 2794 unsigned int certType = 0;
michael@0 2795 if ( ca ) {
michael@0 2796 /* This function returns a more comprehensive cert type that
michael@0 2797 * takes trust flags into consideration. Should probably
michael@0 2798 * fix the cert decoding code to do this.
michael@0 2799 */
michael@0 2800 (void)CERT_IsCACert(node->cert, &certType);
michael@0 2801 } else {
michael@0 2802 certType = node->cert->nsCertType;
michael@0 2803 }
michael@0 2804 if ( !( certType & requiredCertType ) ) {
michael@0 2805 bad = PR_TRUE;
michael@0 2806 }
michael@0 2807 }
michael@0 2808
michael@0 2809 if ( bad ) {
michael@0 2810 /* remove the node if it is bad */
michael@0 2811 savenode = CERT_LIST_NEXT(node);
michael@0 2812 CERT_RemoveCertListNode(node);
michael@0 2813 node = savenode;
michael@0 2814 } else {
michael@0 2815 node = CERT_LIST_NEXT(node);
michael@0 2816 }
michael@0 2817 }
michael@0 2818 return(SECSuccess);
michael@0 2819
michael@0 2820 loser:
michael@0 2821 return(SECFailure);
michael@0 2822 }
michael@0 2823
michael@0 2824 PRBool CERT_IsUserCert(CERTCertificate* cert)
michael@0 2825 {
michael@0 2826 CERTCertTrust trust;
michael@0 2827 SECStatus rv = SECFailure;
michael@0 2828
michael@0 2829 rv = CERT_GetCertTrust(cert, &trust);
michael@0 2830 if (rv == SECSuccess &&
michael@0 2831 ((trust.sslFlags & CERTDB_USER ) ||
michael@0 2832 (trust.emailFlags & CERTDB_USER ) ||
michael@0 2833 (trust.objectSigningFlags & CERTDB_USER )) ) {
michael@0 2834 return PR_TRUE;
michael@0 2835 } else {
michael@0 2836 return PR_FALSE;
michael@0 2837 }
michael@0 2838 }
michael@0 2839
michael@0 2840 SECStatus
michael@0 2841 CERT_FilterCertListForUserCerts(CERTCertList *certList)
michael@0 2842 {
michael@0 2843 CERTCertListNode *node, *freenode;
michael@0 2844 CERTCertificate *cert;
michael@0 2845
michael@0 2846 if (!certList) {
michael@0 2847 return SECFailure;
michael@0 2848 }
michael@0 2849
michael@0 2850 node = CERT_LIST_HEAD(certList);
michael@0 2851
michael@0 2852 while ( ! CERT_LIST_END(node, certList) ) {
michael@0 2853 cert = node->cert;
michael@0 2854 if ( PR_TRUE != CERT_IsUserCert(cert) ) {
michael@0 2855 /* Not a User Cert, so remove this cert from the list */
michael@0 2856 freenode = node;
michael@0 2857 node = CERT_LIST_NEXT(node);
michael@0 2858 CERT_RemoveCertListNode(freenode);
michael@0 2859 } else {
michael@0 2860 /* Is a User cert, so leave it in the list */
michael@0 2861 node = CERT_LIST_NEXT(node);
michael@0 2862 }
michael@0 2863 }
michael@0 2864
michael@0 2865 return(SECSuccess);
michael@0 2866 }
michael@0 2867
michael@0 2868 static PZLock *certRefCountLock = NULL;
michael@0 2869
michael@0 2870 /*
michael@0 2871 * Acquire the cert reference count lock
michael@0 2872 * There is currently one global lock for all certs, but I'm putting a cert
michael@0 2873 * arg here so that it will be easy to make it per-cert in the future if
michael@0 2874 * that turns out to be necessary.
michael@0 2875 */
michael@0 2876 void
michael@0 2877 CERT_LockCertRefCount(CERTCertificate *cert)
michael@0 2878 {
michael@0 2879 PORT_Assert(certRefCountLock != NULL);
michael@0 2880 PZ_Lock(certRefCountLock);
michael@0 2881 return;
michael@0 2882 }
michael@0 2883
michael@0 2884 /*
michael@0 2885 * Free the cert reference count lock
michael@0 2886 */
michael@0 2887 void
michael@0 2888 CERT_UnlockCertRefCount(CERTCertificate *cert)
michael@0 2889 {
michael@0 2890 PRStatus prstat;
michael@0 2891
michael@0 2892 PORT_Assert(certRefCountLock != NULL);
michael@0 2893
michael@0 2894 prstat = PZ_Unlock(certRefCountLock);
michael@0 2895
michael@0 2896 PORT_Assert(prstat == PR_SUCCESS);
michael@0 2897
michael@0 2898 return;
michael@0 2899 }
michael@0 2900
michael@0 2901 static PZLock *certTrustLock = NULL;
michael@0 2902
michael@0 2903 /*
michael@0 2904 * Acquire the cert trust lock
michael@0 2905 * There is currently one global lock for all certs, but I'm putting a cert
michael@0 2906 * arg here so that it will be easy to make it per-cert in the future if
michael@0 2907 * that turns out to be necessary.
michael@0 2908 */
michael@0 2909 void
michael@0 2910 CERT_LockCertTrust(const CERTCertificate *cert)
michael@0 2911 {
michael@0 2912 PORT_Assert(certTrustLock != NULL);
michael@0 2913 PZ_Lock(certTrustLock);
michael@0 2914 return;
michael@0 2915 }
michael@0 2916
michael@0 2917 SECStatus
michael@0 2918 cert_InitLocks(void)
michael@0 2919 {
michael@0 2920 if ( certRefCountLock == NULL ) {
michael@0 2921 certRefCountLock = PZ_NewLock(nssILockRefLock);
michael@0 2922 PORT_Assert(certRefCountLock != NULL);
michael@0 2923 if (!certRefCountLock) {
michael@0 2924 return SECFailure;
michael@0 2925 }
michael@0 2926 }
michael@0 2927
michael@0 2928 if ( certTrustLock == NULL ) {
michael@0 2929 certTrustLock = PZ_NewLock(nssILockCertDB);
michael@0 2930 PORT_Assert(certTrustLock != NULL);
michael@0 2931 if (!certTrustLock) {
michael@0 2932 PZ_DestroyLock(certRefCountLock);
michael@0 2933 certRefCountLock = NULL;
michael@0 2934 return SECFailure;
michael@0 2935 }
michael@0 2936 }
michael@0 2937
michael@0 2938 return SECSuccess;
michael@0 2939 }
michael@0 2940
michael@0 2941 SECStatus
michael@0 2942 cert_DestroyLocks(void)
michael@0 2943 {
michael@0 2944 SECStatus rv = SECSuccess;
michael@0 2945
michael@0 2946 PORT_Assert(certRefCountLock != NULL);
michael@0 2947 if (certRefCountLock) {
michael@0 2948 PZ_DestroyLock(certRefCountLock);
michael@0 2949 certRefCountLock = NULL;
michael@0 2950 } else {
michael@0 2951 rv = SECFailure;
michael@0 2952 }
michael@0 2953
michael@0 2954 PORT_Assert(certTrustLock != NULL);
michael@0 2955 if (certTrustLock) {
michael@0 2956 PZ_DestroyLock(certTrustLock);
michael@0 2957 certTrustLock = NULL;
michael@0 2958 } else {
michael@0 2959 rv = SECFailure;
michael@0 2960 }
michael@0 2961 return rv;
michael@0 2962 }
michael@0 2963
michael@0 2964 /*
michael@0 2965 * Free the cert trust lock
michael@0 2966 */
michael@0 2967 void
michael@0 2968 CERT_UnlockCertTrust(const CERTCertificate *cert)
michael@0 2969 {
michael@0 2970 PRStatus prstat;
michael@0 2971
michael@0 2972 PORT_Assert(certTrustLock != NULL);
michael@0 2973
michael@0 2974 prstat = PZ_Unlock(certTrustLock);
michael@0 2975
michael@0 2976 PORT_Assert(prstat == PR_SUCCESS);
michael@0 2977
michael@0 2978 return;
michael@0 2979 }
michael@0 2980
michael@0 2981
michael@0 2982 /*
michael@0 2983 * Get the StatusConfig data for this handle
michael@0 2984 */
michael@0 2985 CERTStatusConfig *
michael@0 2986 CERT_GetStatusConfig(CERTCertDBHandle *handle)
michael@0 2987 {
michael@0 2988 return handle->statusConfig;
michael@0 2989 }
michael@0 2990
michael@0 2991 /*
michael@0 2992 * Set the StatusConfig data for this handle. There
michael@0 2993 * should not be another configuration set.
michael@0 2994 */
michael@0 2995 void
michael@0 2996 CERT_SetStatusConfig(CERTCertDBHandle *handle, CERTStatusConfig *statusConfig)
michael@0 2997 {
michael@0 2998 PORT_Assert(handle->statusConfig == NULL);
michael@0 2999 handle->statusConfig = statusConfig;
michael@0 3000 }
michael@0 3001
michael@0 3002 /*
michael@0 3003 * Code for dealing with subjKeyID to cert mappings.
michael@0 3004 */
michael@0 3005
michael@0 3006 static PLHashTable *gSubjKeyIDHash = NULL;
michael@0 3007 static PRLock *gSubjKeyIDLock = NULL;
michael@0 3008 static PLHashTable *gSubjKeyIDSlotCheckHash = NULL;
michael@0 3009 static PRLock *gSubjKeyIDSlotCheckLock = NULL;
michael@0 3010
michael@0 3011 static void *cert_AllocTable(void *pool, PRSize size)
michael@0 3012 {
michael@0 3013 return PORT_Alloc(size);
michael@0 3014 }
michael@0 3015
michael@0 3016 static void cert_FreeTable(void *pool, void *item)
michael@0 3017 {
michael@0 3018 PORT_Free(item);
michael@0 3019 }
michael@0 3020
michael@0 3021 static PLHashEntry* cert_AllocEntry(void *pool, const void *key)
michael@0 3022 {
michael@0 3023 return PORT_New(PLHashEntry);
michael@0 3024 }
michael@0 3025
michael@0 3026 static void cert_FreeEntry(void *pool, PLHashEntry *he, PRUintn flag)
michael@0 3027 {
michael@0 3028 SECITEM_FreeItem((SECItem*)(he->value), PR_TRUE);
michael@0 3029 if (flag == HT_FREE_ENTRY) {
michael@0 3030 SECITEM_FreeItem((SECItem*)(he->key), PR_TRUE);
michael@0 3031 PORT_Free(he);
michael@0 3032 }
michael@0 3033 }
michael@0 3034
michael@0 3035 static PLHashAllocOps cert_AllocOps = {
michael@0 3036 cert_AllocTable, cert_FreeTable, cert_AllocEntry, cert_FreeEntry
michael@0 3037 };
michael@0 3038
michael@0 3039 SECStatus
michael@0 3040 cert_CreateSubjectKeyIDSlotCheckHash(void)
michael@0 3041 {
michael@0 3042 /*
michael@0 3043 * This hash is used to remember the series of a slot
michael@0 3044 * when we last checked for user certs
michael@0 3045 */
michael@0 3046 gSubjKeyIDSlotCheckHash = PL_NewHashTable(0, SECITEM_Hash,
michael@0 3047 SECITEM_HashCompare,
michael@0 3048 SECITEM_HashCompare,
michael@0 3049 &cert_AllocOps, NULL);
michael@0 3050 if (!gSubjKeyIDSlotCheckHash) {
michael@0 3051 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 3052 return SECFailure;
michael@0 3053 }
michael@0 3054 gSubjKeyIDSlotCheckLock = PR_NewLock();
michael@0 3055 if (!gSubjKeyIDSlotCheckLock) {
michael@0 3056 PL_HashTableDestroy(gSubjKeyIDSlotCheckHash);
michael@0 3057 gSubjKeyIDSlotCheckHash = NULL;
michael@0 3058 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 3059 return SECFailure;
michael@0 3060 }
michael@0 3061 return SECSuccess;
michael@0 3062 }
michael@0 3063
michael@0 3064 SECStatus
michael@0 3065 cert_CreateSubjectKeyIDHashTable(void)
michael@0 3066 {
michael@0 3067 gSubjKeyIDHash = PL_NewHashTable(0, SECITEM_Hash, SECITEM_HashCompare,
michael@0 3068 SECITEM_HashCompare,
michael@0 3069 &cert_AllocOps, NULL);
michael@0 3070 if (!gSubjKeyIDHash) {
michael@0 3071 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 3072 return SECFailure;
michael@0 3073 }
michael@0 3074 gSubjKeyIDLock = PR_NewLock();
michael@0 3075 if (!gSubjKeyIDLock) {
michael@0 3076 PL_HashTableDestroy(gSubjKeyIDHash);
michael@0 3077 gSubjKeyIDHash = NULL;
michael@0 3078 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 3079 return SECFailure;
michael@0 3080 }
michael@0 3081 /* initialize the companion hash (for remembering slot series) */
michael@0 3082 if (cert_CreateSubjectKeyIDSlotCheckHash() != SECSuccess) {
michael@0 3083 cert_DestroySubjectKeyIDHashTable();
michael@0 3084 return SECFailure;
michael@0 3085 }
michael@0 3086 return SECSuccess;
michael@0 3087 }
michael@0 3088
michael@0 3089 SECStatus
michael@0 3090 cert_AddSubjectKeyIDMapping(SECItem *subjKeyID, CERTCertificate *cert)
michael@0 3091 {
michael@0 3092 SECItem *newKeyID, *oldVal, *newVal;
michael@0 3093 SECStatus rv = SECFailure;
michael@0 3094
michael@0 3095 if (!gSubjKeyIDLock) {
michael@0 3096 /* If one is created, then both are there. So only check for one. */
michael@0 3097 return SECFailure;
michael@0 3098 }
michael@0 3099
michael@0 3100 newVal = SECITEM_DupItem(&cert->derCert);
michael@0 3101 if (!newVal) {
michael@0 3102 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 3103 goto done;
michael@0 3104 }
michael@0 3105 newKeyID = SECITEM_DupItem(subjKeyID);
michael@0 3106 if (!newKeyID) {
michael@0 3107 SECITEM_FreeItem(newVal, PR_TRUE);
michael@0 3108 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 3109 goto done;
michael@0 3110 }
michael@0 3111
michael@0 3112 PR_Lock(gSubjKeyIDLock);
michael@0 3113 /* The hash table implementation does not free up the memory
michael@0 3114 * associated with the key of an already existing entry if we add a
michael@0 3115 * duplicate, so we would wind up leaking the previously allocated
michael@0 3116 * key if we don't remove before adding.
michael@0 3117 */
michael@0 3118 oldVal = (SECItem*)PL_HashTableLookup(gSubjKeyIDHash, subjKeyID);
michael@0 3119 if (oldVal) {
michael@0 3120 PL_HashTableRemove(gSubjKeyIDHash, subjKeyID);
michael@0 3121 }
michael@0 3122
michael@0 3123 rv = (PL_HashTableAdd(gSubjKeyIDHash, newKeyID, newVal)) ? SECSuccess :
michael@0 3124 SECFailure;
michael@0 3125 PR_Unlock(gSubjKeyIDLock);
michael@0 3126 done:
michael@0 3127 return rv;
michael@0 3128 }
michael@0 3129
michael@0 3130 SECStatus
michael@0 3131 cert_RemoveSubjectKeyIDMapping(SECItem *subjKeyID)
michael@0 3132 {
michael@0 3133 SECStatus rv;
michael@0 3134 if (!gSubjKeyIDLock)
michael@0 3135 return SECFailure;
michael@0 3136
michael@0 3137 PR_Lock(gSubjKeyIDLock);
michael@0 3138 rv = (PL_HashTableRemove(gSubjKeyIDHash, subjKeyID)) ? SECSuccess :
michael@0 3139 SECFailure;
michael@0 3140 PR_Unlock(gSubjKeyIDLock);
michael@0 3141 return rv;
michael@0 3142 }
michael@0 3143
michael@0 3144 SECStatus
michael@0 3145 cert_UpdateSubjectKeyIDSlotCheck(SECItem *slotid, int series)
michael@0 3146 {
michael@0 3147 SECItem *oldSeries, *newSlotid, *newSeries;
michael@0 3148 SECStatus rv = SECFailure;
michael@0 3149
michael@0 3150 if (!gSubjKeyIDSlotCheckLock) {
michael@0 3151 return rv;
michael@0 3152 }
michael@0 3153
michael@0 3154 newSlotid = SECITEM_DupItem(slotid);
michael@0 3155 newSeries = SECITEM_AllocItem(NULL, NULL, sizeof(int));
michael@0 3156 if (!newSlotid || !newSeries ) {
michael@0 3157 PORT_SetError(SEC_ERROR_NO_MEMORY);
michael@0 3158 goto loser;
michael@0 3159 }
michael@0 3160 PORT_Memcpy(newSeries->data, &series, sizeof(int));
michael@0 3161
michael@0 3162 PR_Lock(gSubjKeyIDSlotCheckLock);
michael@0 3163 oldSeries = (SECItem *)PL_HashTableLookup(gSubjKeyIDSlotCheckHash, slotid);
michael@0 3164 if (oldSeries) {
michael@0 3165 /*
michael@0 3166 * make sure we don't leak the key of an existing entry
michael@0 3167 * (similar to cert_AddSubjectKeyIDMapping, see comment there)
michael@0 3168 */
michael@0 3169 PL_HashTableRemove(gSubjKeyIDSlotCheckHash, slotid);
michael@0 3170 }
michael@0 3171 rv = (PL_HashTableAdd(gSubjKeyIDSlotCheckHash, newSlotid, newSeries)) ?
michael@0 3172 SECSuccess : SECFailure;
michael@0 3173 PR_Unlock(gSubjKeyIDSlotCheckLock);
michael@0 3174 if (rv == SECSuccess) {
michael@0 3175 return rv;
michael@0 3176 }
michael@0 3177
michael@0 3178 loser:
michael@0 3179 if (newSlotid) {
michael@0 3180 SECITEM_FreeItem(newSlotid, PR_TRUE);
michael@0 3181 }
michael@0 3182 if (newSeries) {
michael@0 3183 SECITEM_FreeItem(newSeries, PR_TRUE);
michael@0 3184 }
michael@0 3185 return rv;
michael@0 3186 }
michael@0 3187
michael@0 3188 int
michael@0 3189 cert_SubjectKeyIDSlotCheckSeries(SECItem *slotid)
michael@0 3190 {
michael@0 3191 SECItem *seriesItem = NULL;
michael@0 3192 int series;
michael@0 3193
michael@0 3194 if (!gSubjKeyIDSlotCheckLock) {
michael@0 3195 PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
michael@0 3196 return -1;
michael@0 3197 }
michael@0 3198
michael@0 3199 PR_Lock(gSubjKeyIDSlotCheckLock);
michael@0 3200 seriesItem = (SECItem *)PL_HashTableLookup(gSubjKeyIDSlotCheckHash, slotid);
michael@0 3201 PR_Unlock(gSubjKeyIDSlotCheckLock);
michael@0 3202 /* getting a null series just means we haven't registered one yet,
michael@0 3203 * just return 0 */
michael@0 3204 if (seriesItem == NULL) {
michael@0 3205 return 0;
michael@0 3206 }
michael@0 3207 /* if we got a series back, assert if it's not the proper length. */
michael@0 3208 PORT_Assert(seriesItem->len == sizeof(int));
michael@0 3209 if (seriesItem->len != sizeof(int)) {
michael@0 3210 PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
michael@0 3211 return -1;
michael@0 3212 }
michael@0 3213 PORT_Memcpy(&series, seriesItem->data, sizeof(int));
michael@0 3214 return series;
michael@0 3215 }
michael@0 3216
michael@0 3217 SECStatus
michael@0 3218 cert_DestroySubjectKeyIDSlotCheckHash(void)
michael@0 3219 {
michael@0 3220 if (gSubjKeyIDSlotCheckHash) {
michael@0 3221 PR_Lock(gSubjKeyIDSlotCheckLock);
michael@0 3222 PL_HashTableDestroy(gSubjKeyIDSlotCheckHash);
michael@0 3223 gSubjKeyIDSlotCheckHash = NULL;
michael@0 3224 PR_Unlock(gSubjKeyIDSlotCheckLock);
michael@0 3225 PR_DestroyLock(gSubjKeyIDSlotCheckLock);
michael@0 3226 gSubjKeyIDSlotCheckLock = NULL;
michael@0 3227 }
michael@0 3228 return SECSuccess;
michael@0 3229 }
michael@0 3230
michael@0 3231 SECStatus
michael@0 3232 cert_DestroySubjectKeyIDHashTable(void)
michael@0 3233 {
michael@0 3234 if (gSubjKeyIDHash) {
michael@0 3235 PR_Lock(gSubjKeyIDLock);
michael@0 3236 PL_HashTableDestroy(gSubjKeyIDHash);
michael@0 3237 gSubjKeyIDHash = NULL;
michael@0 3238 PR_Unlock(gSubjKeyIDLock);
michael@0 3239 PR_DestroyLock(gSubjKeyIDLock);
michael@0 3240 gSubjKeyIDLock = NULL;
michael@0 3241 }
michael@0 3242 cert_DestroySubjectKeyIDSlotCheckHash();
michael@0 3243 return SECSuccess;
michael@0 3244 }
michael@0 3245
michael@0 3246 SECItem*
michael@0 3247 cert_FindDERCertBySubjectKeyID(SECItem *subjKeyID)
michael@0 3248 {
michael@0 3249 SECItem *val;
michael@0 3250
michael@0 3251 if (!gSubjKeyIDLock)
michael@0 3252 return NULL;
michael@0 3253
michael@0 3254 PR_Lock(gSubjKeyIDLock);
michael@0 3255 val = (SECItem*)PL_HashTableLookup(gSubjKeyIDHash, subjKeyID);
michael@0 3256 if (val) {
michael@0 3257 val = SECITEM_DupItem(val);
michael@0 3258 }
michael@0 3259 PR_Unlock(gSubjKeyIDLock);
michael@0 3260 return val;
michael@0 3261 }
michael@0 3262
michael@0 3263 CERTCertificate*
michael@0 3264 CERT_FindCertBySubjectKeyID(CERTCertDBHandle *handle, SECItem *subjKeyID)
michael@0 3265 {
michael@0 3266 CERTCertificate *cert = NULL;
michael@0 3267 SECItem *derCert;
michael@0 3268
michael@0 3269 derCert = cert_FindDERCertBySubjectKeyID(subjKeyID);
michael@0 3270 if (derCert) {
michael@0 3271 cert = CERT_FindCertByDERCert(handle, derCert);
michael@0 3272 SECITEM_FreeItem(derCert, PR_TRUE);
michael@0 3273 }
michael@0 3274 return cert;
michael@0 3275 }

mercurial