Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | /* |
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, ¬Before, ¬After); |
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, ¬Before, ¬After); |
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(¬BeforeA, &val_a->notBefore) || |
michael@0 | 2177 | SECSuccess != DER_DecodeTimeChoice(¬BeforeB, &val_b->notBefore) || |
michael@0 | 2178 | SECSuccess != DER_DecodeTimeChoice(¬AfterA, &val_a->notAfter) || |
michael@0 | 2179 | SECSuccess != DER_DecodeTimeChoice(¬AfterB, &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, ¬BeforeA, ¬AfterA); |
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, ¬BeforeB, ¬AfterB); |
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, ¬BeforeA, ¬AfterA); |
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, ¬BeforeB, ¬AfterB); |
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 | } |