pkix_revocationchecker.c (17195B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 /* 5 * pkix_revocationchecker.c 6 * 7 * RevocationChecker Object Functions 8 * 9 */ 10 11 #include "pkix_revocationchecker.h" 12 #include "pkix_tools.h" 13 14 /* --Private-Functions-------------------------------------------- */ 15 16 /* 17 * FUNCTION: pkix_RevocationChecker_Destroy 18 * (see comments for PKIX_PL_DestructorCallback in pkix_pl_system.h) 19 */ 20 static PKIX_Error * 21 pkix_RevocationChecker_Destroy( 22 PKIX_PL_Object *object, 23 void *plContext) 24 { 25 PKIX_RevocationChecker *checker = NULL; 26 27 PKIX_ENTER(REVOCATIONCHECKER, "pkix_RevocationChecker_Destroy"); 28 PKIX_NULLCHECK_ONE(object); 29 30 /* Check that this object is a revocation checker */ 31 PKIX_CHECK(pkix_CheckType 32 (object, PKIX_REVOCATIONCHECKER_TYPE, plContext), 33 PKIX_OBJECTNOTREVOCATIONCHECKER); 34 35 checker = (PKIX_RevocationChecker *)object; 36 37 PKIX_DECREF(checker->leafMethodList); 38 PKIX_DECREF(checker->chainMethodList); 39 40 cleanup: 41 42 PKIX_RETURN(REVOCATIONCHECKER); 43 } 44 45 /* 46 * FUNCTION: pkix_RevocationChecker_Duplicate 47 * (see comments for PKIX_PL_DuplicateCallback in pkix_pl_system.h) 48 */ 49 static PKIX_Error * 50 pkix_RevocationChecker_Duplicate( 51 PKIX_PL_Object *object, 52 PKIX_PL_Object **pNewObject, 53 void *plContext) 54 { 55 PKIX_RevocationChecker *checker = NULL; 56 PKIX_RevocationChecker *checkerDuplicate = NULL; 57 PKIX_List *dupLeafList = NULL; 58 PKIX_List *dupChainList = NULL; 59 60 PKIX_ENTER(REVOCATIONCHECKER, "pkix_RevocationChecker_Duplicate"); 61 PKIX_NULLCHECK_TWO(object, pNewObject); 62 63 PKIX_CHECK(pkix_CheckType 64 (object, PKIX_REVOCATIONCHECKER_TYPE, plContext), 65 PKIX_OBJECTNOTCERTCHAINCHECKER); 66 67 checker = (PKIX_RevocationChecker *)object; 68 69 if (checker->leafMethodList){ 70 PKIX_CHECK(PKIX_PL_Object_Duplicate 71 ((PKIX_PL_Object *)checker->leafMethodList, 72 (PKIX_PL_Object **)&dupLeafList, 73 plContext), 74 PKIX_OBJECTDUPLICATEFAILED); 75 } 76 if (checker->chainMethodList){ 77 PKIX_CHECK(PKIX_PL_Object_Duplicate 78 ((PKIX_PL_Object *)checker->chainMethodList, 79 (PKIX_PL_Object **)&dupChainList, 80 plContext), 81 PKIX_OBJECTDUPLICATEFAILED); 82 } 83 84 PKIX_CHECK( 85 PKIX_RevocationChecker_Create(checker->leafMethodListFlags, 86 checker->chainMethodListFlags, 87 &checkerDuplicate, 88 plContext), 89 PKIX_REVOCATIONCHECKERCREATEFAILED); 90 91 checkerDuplicate->leafMethodList = dupLeafList; 92 checkerDuplicate->chainMethodList = dupChainList; 93 dupLeafList = NULL; 94 dupChainList = NULL; 95 96 *pNewObject = (PKIX_PL_Object *)checkerDuplicate; 97 98 cleanup: 99 PKIX_DECREF(dupLeafList); 100 PKIX_DECREF(dupChainList); 101 102 PKIX_RETURN(REVOCATIONCHECKER); 103 } 104 105 /* 106 * FUNCTION: pkix_RevocationChecker_RegisterSelf 107 * DESCRIPTION: 108 * Registers PKIX_REVOCATIONCHECKER_TYPE and its related functions with 109 * systemClasses[] 110 * THREAD SAFETY: 111 * Not Thread Safe - for performance and complexity reasons 112 * 113 * Since this function is only called by PKIX_PL_Initialize, which should 114 * only be called once, it is acceptable that this function is not 115 * thread-safe. 116 */ 117 PKIX_Error * 118 pkix_RevocationChecker_RegisterSelf(void *plContext) 119 { 120 extern pkix_ClassTable_Entry systemClasses[PKIX_NUMTYPES]; 121 pkix_ClassTable_Entry entry; 122 123 PKIX_ENTER(REVOCATIONCHECKER, "pkix_RevocationChecker_RegisterSelf"); 124 125 entry.description = "RevocationChecker"; 126 entry.objCounter = 0; 127 entry.typeObjectSize = sizeof(PKIX_RevocationChecker); 128 entry.destructor = pkix_RevocationChecker_Destroy; 129 entry.equalsFunction = NULL; 130 entry.hashcodeFunction = NULL; 131 entry.toStringFunction = NULL; 132 entry.comparator = NULL; 133 entry.duplicateFunction = pkix_RevocationChecker_Duplicate; 134 135 systemClasses[PKIX_REVOCATIONCHECKER_TYPE] = entry; 136 137 PKIX_RETURN(REVOCATIONCHECKER); 138 } 139 140 /* Sort methods by their priorities (lower priority = higher preference) */ 141 static PKIX_Error * 142 pkix_RevocationChecker_SortComparator( 143 PKIX_PL_Object *obj1, 144 PKIX_PL_Object *obj2, 145 PKIX_Int32 *pResult, 146 void *plContext) 147 { 148 pkix_RevocationMethod *method1 = NULL, *method2 = NULL; 149 150 PKIX_ENTER(BUILD, "pkix_RevocationChecker_SortComparator"); 151 152 method1 = (pkix_RevocationMethod *)obj1; 153 method2 = (pkix_RevocationMethod *)obj2; 154 155 if (method1->priority < method2->priority) { 156 *pResult = -1; 157 } else if (method1->priority > method2->priority) { 158 *pResult = 1; 159 } else { 160 *pResult = 0; 161 } 162 163 PKIX_RETURN(BUILD); 164 } 165 166 167 /* --Public-Functions--------------------------------------------- */ 168 169 170 /* 171 * FUNCTION: PKIX_RevocationChecker_Create (see comments in pkix_revchecker.h) 172 */ 173 PKIX_Error * 174 PKIX_RevocationChecker_Create( 175 PKIX_UInt32 leafMethodListFlags, 176 PKIX_UInt32 chainMethodListFlags, 177 PKIX_RevocationChecker **pChecker, 178 void *plContext) 179 { 180 PKIX_RevocationChecker *checker = NULL; 181 182 PKIX_ENTER(REVOCATIONCHECKER, "PKIX_RevocationChecker_Create"); 183 PKIX_NULLCHECK_ONE(pChecker); 184 185 PKIX_CHECK( 186 PKIX_PL_Object_Alloc(PKIX_REVOCATIONCHECKER_TYPE, 187 sizeof (PKIX_RevocationChecker), 188 (PKIX_PL_Object **)&checker, 189 plContext), 190 PKIX_COULDNOTCREATECERTCHAINCHECKEROBJECT); 191 192 checker->leafMethodListFlags = leafMethodListFlags; 193 checker->chainMethodListFlags = chainMethodListFlags; 194 checker->leafMethodList = NULL; 195 checker->chainMethodList = NULL; 196 197 *pChecker = checker; 198 checker = NULL; 199 200 cleanup: 201 PKIX_DECREF(checker); 202 203 PKIX_RETURN(REVOCATIONCHECKER); 204 } 205 206 /* 207 * FUNCTION: PKIX_RevocationChecker_CreateAndAddMethod 208 */ 209 PKIX_Error * 210 PKIX_RevocationChecker_CreateAndAddMethod( 211 PKIX_RevocationChecker *revChecker, 212 PKIX_ProcessingParams *params, 213 PKIX_RevocationMethodType methodType, 214 PKIX_UInt32 flags, 215 PKIX_UInt32 priority, 216 PKIX_PL_VerifyCallback verificationFn, 217 PKIX_Boolean isLeafMethod, 218 void *plContext) 219 { 220 PKIX_List **methodList = NULL; 221 PKIX_List *unsortedList = NULL; 222 PKIX_List *certStores = NULL; 223 pkix_RevocationMethod *method = NULL; 224 pkix_LocalRevocationCheckFn *localRevChecker = NULL; 225 pkix_ExternalRevocationCheckFn *externRevChecker = NULL; 226 PKIX_UInt32 miFlags; 227 228 PKIX_ENTER(REVOCATIONCHECKER, "PKIX_RevocationChecker_CreateAndAddMethod"); 229 PKIX_NULLCHECK_ONE(revChecker); 230 231 /* If the caller has said "Either one is sufficient, then don't let the 232 * absence of any one method's info lead to an overall failure. 233 */ 234 miFlags = isLeafMethod ? revChecker->leafMethodListFlags 235 : revChecker->chainMethodListFlags; 236 if (miFlags & PKIX_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE) 237 flags &= ~PKIX_REV_M_FAIL_ON_MISSING_FRESH_INFO; 238 239 switch (methodType) { 240 case PKIX_RevocationMethod_CRL: 241 localRevChecker = pkix_CrlChecker_CheckLocal; 242 externRevChecker = pkix_CrlChecker_CheckExternal; 243 PKIX_CHECK( 244 PKIX_ProcessingParams_GetCertStores(params, &certStores, 245 plContext), 246 PKIX_PROCESSINGPARAMSGETCERTSTORESFAILED); 247 PKIX_CHECK( 248 pkix_CrlChecker_Create(methodType, flags, priority, 249 localRevChecker, externRevChecker, 250 certStores, verificationFn, 251 &method, 252 plContext), 253 PKIX_COULDNOTCREATECRLCHECKEROBJECT); 254 break; 255 case PKIX_RevocationMethod_OCSP: 256 localRevChecker = pkix_OcspChecker_CheckLocal; 257 externRevChecker = pkix_OcspChecker_CheckExternal; 258 PKIX_CHECK( 259 pkix_OcspChecker_Create(methodType, flags, priority, 260 localRevChecker, externRevChecker, 261 verificationFn, 262 &method, 263 plContext), 264 PKIX_COULDNOTCREATEOCSPCHECKEROBJECT); 265 break; 266 default: 267 PKIX_ERROR(PKIX_INVALIDREVOCATIONMETHOD); 268 } 269 270 if (isLeafMethod) { 271 methodList = &revChecker->leafMethodList; 272 } else { 273 methodList = &revChecker->chainMethodList; 274 } 275 276 if (*methodList == NULL) { 277 PKIX_CHECK( 278 PKIX_List_Create(methodList, plContext), 279 PKIX_LISTCREATEFAILED); 280 } 281 unsortedList = *methodList; 282 PKIX_CHECK( 283 PKIX_List_AppendItem(unsortedList, (PKIX_PL_Object*)method, plContext), 284 PKIX_LISTAPPENDITEMFAILED); 285 PKIX_CHECK( 286 pkix_List_BubbleSort(unsortedList, 287 pkix_RevocationChecker_SortComparator, 288 methodList, plContext), 289 PKIX_LISTBUBBLESORTFAILED); 290 291 cleanup: 292 PKIX_DECREF(method); 293 PKIX_DECREF(unsortedList); 294 PKIX_DECREF(certStores); 295 296 PKIX_RETURN(REVOCATIONCHECKER); 297 } 298 299 /* 300 * FUNCTION: PKIX_RevocationChecker_Check 301 */ 302 PKIX_Error * 303 PKIX_RevocationChecker_Check( 304 PKIX_PL_Cert *cert, 305 PKIX_PL_Cert *issuer, 306 PKIX_RevocationChecker *revChecker, 307 PKIX_ProcessingParams *procParams, 308 PKIX_Boolean chainVerificationState, 309 PKIX_Boolean testingLeafCert, 310 PKIX_RevocationStatus *pRevStatus, 311 PKIX_UInt32 *pReasonCode, 312 void **pNbioContext, 313 void *plContext) 314 { 315 PKIX_RevocationStatus overallStatus = PKIX_RevStatus_NoInfo; 316 PKIX_RevocationStatus methodStatus[PKIX_RevocationMethod_MAX]; 317 PKIX_Boolean onlyUseRemoteMethods = PKIX_FALSE; 318 PKIX_UInt32 revFlags = 0; 319 PKIX_List *revList = NULL; 320 PKIX_PL_Date *date = NULL; 321 pkix_RevocationMethod *method = NULL; 322 void *nbioContext; 323 int tries; 324 325 PKIX_ENTER(REVOCATIONCHECKER, "PKIX_RevocationChecker_Check"); 326 PKIX_NULLCHECK_TWO(revChecker, procParams); 327 328 nbioContext = *pNbioContext; 329 *pNbioContext = NULL; 330 331 if (testingLeafCert) { 332 revList = revChecker->leafMethodList; 333 revFlags = revChecker->leafMethodListFlags; 334 } else { 335 revList = revChecker->chainMethodList; 336 revFlags = revChecker->chainMethodListFlags; 337 } 338 if (!revList) { 339 /* Return NoInfo status */ 340 goto cleanup; 341 } 342 343 PORT_Memset(methodStatus, PKIX_RevStatus_NoInfo, 344 sizeof(PKIX_RevocationStatus) * PKIX_RevocationMethod_MAX); 345 346 date = procParams->date; 347 348 /* Need to have two loops if we testing all local info first: 349 * first we are going to test all local(cached) info 350 * second, all remote info(fetching) */ 351 for (tries = 0;tries < 2;tries++) { 352 unsigned int methodNum = 0; 353 for (;methodNum < revList->length;methodNum++) { 354 PKIX_UInt32 methodFlags = 0; 355 356 PKIX_DECREF(method); 357 PKIX_CHECK( 358 PKIX_List_GetItem(revList, methodNum, 359 (PKIX_PL_Object**)&method, plContext), 360 PKIX_LISTGETITEMFAILED); 361 methodFlags = method->flags; 362 if (!(methodFlags & PKIX_REV_M_TEST_USING_THIS_METHOD)) { 363 /* Will not check with this method. Skipping... */ 364 continue; 365 } 366 if (!onlyUseRemoteMethods && 367 methodStatus[methodNum] == PKIX_RevStatus_NoInfo) { 368 PKIX_RevocationStatus revStatus = PKIX_RevStatus_NoInfo; 369 PKIX_CHECK_NO_GOTO( 370 (*method->localRevChecker)(cert, issuer, date, 371 method, procParams, 372 methodFlags, 373 chainVerificationState, 374 &revStatus, 375 (CERTCRLEntryReasonCode *)pReasonCode, 376 plContext), 377 PKIX_REVCHECKERCHECKFAILED); 378 methodStatus[methodNum] = revStatus; 379 if (revStatus == PKIX_RevStatus_Revoked) { 380 /* if error was generated use it as final error. */ 381 overallStatus = PKIX_RevStatus_Revoked; 382 goto cleanup; 383 } 384 if (pkixErrorResult) { 385 /* Disregard errors. Only returned revStatus matters. */ 386 PKIX_PL_Object_DecRef((PKIX_PL_Object*)pkixErrorResult, 387 plContext); 388 pkixErrorResult = NULL; 389 } 390 } 391 if ((!(revFlags & PKIX_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST) || 392 onlyUseRemoteMethods) && 393 chainVerificationState && 394 methodStatus[methodNum] == PKIX_RevStatus_NoInfo) { 395 if (!(methodFlags & PKIX_REV_M_FORBID_NETWORK_FETCHING)) { 396 PKIX_RevocationStatus revStatus = PKIX_RevStatus_NoInfo; 397 PKIX_CHECK_NO_GOTO( 398 (*method->externalRevChecker)(cert, issuer, date, 399 method, 400 procParams, methodFlags, 401 &revStatus, 402 (CERTCRLEntryReasonCode *)pReasonCode, 403 &nbioContext, plContext), 404 PKIX_REVCHECKERCHECKFAILED); 405 methodStatus[methodNum] = revStatus; 406 if (revStatus == PKIX_RevStatus_Revoked) { 407 /* if error was generated use it as final error. */ 408 overallStatus = PKIX_RevStatus_Revoked; 409 goto cleanup; 410 } 411 if (pkixErrorResult) { 412 /* Disregard errors. Only returned revStatus matters. */ 413 PKIX_PL_Object_DecRef((PKIX_PL_Object*)pkixErrorResult, 414 plContext); 415 pkixErrorResult = NULL; 416 } 417 } else if (methodFlags & 418 PKIX_REV_M_FAIL_ON_MISSING_FRESH_INFO) { 419 /* Info is not in the local cache. Network fetching is not 420 * allowed. If need to fail on missing fresh info for the 421 * the method, then we should fail right here.*/ 422 overallStatus = PKIX_RevStatus_Revoked; 423 goto cleanup; 424 } 425 } 426 /* If success and we should not check the next method, then 427 * return a success. */ 428 if (methodStatus[methodNum] == PKIX_RevStatus_Success && 429 !(methodFlags & PKIX_REV_M_CONTINUE_TESTING_ON_FRESH_INFO)) { 430 overallStatus = PKIX_RevStatus_Success; 431 goto cleanup; 432 } 433 } /* inner loop */ 434 if (!onlyUseRemoteMethods && 435 revFlags & PKIX_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST && 436 chainVerificationState) { 437 onlyUseRemoteMethods = PKIX_TRUE; 438 continue; 439 } 440 break; 441 } /* outer loop */ 442 443 if (overallStatus == PKIX_RevStatus_NoInfo && 444 chainVerificationState) { 445 /* The following check makes sence only for chain 446 * validation step, sinse we do not fetch info while 447 * in the process of finding trusted anchor. 448 * For chain building step it is enough to know, that 449 * the cert was not directly revoked by any of the 450 * methods. */ 451 452 /* Still have no info. But one of the method could 453 * have returned success status(possible if CONTINUE 454 * TESTING ON FRESH INFO flag was used). 455 * If any of the methods have returned Success status, 456 * the overallStatus should be success. */ 457 int methodNum = 0; 458 for (;methodNum < PKIX_RevocationMethod_MAX;methodNum++) { 459 if (methodStatus[methodNum] == PKIX_RevStatus_Success) { 460 overallStatus = PKIX_RevStatus_Success; 461 goto cleanup; 462 } 463 } 464 if (revFlags & PKIX_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE) { 465 overallStatus = PKIX_RevStatus_Revoked; 466 } 467 } 468 469 cleanup: 470 *pRevStatus = overallStatus; 471 PKIX_DECREF(method); 472 473 PKIX_RETURN(REVOCATIONCHECKER); 474 }