pkix_ocspchecker.c (15778B)
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_ocspchecker.c 6 * 7 * OcspChecker Object Functions 8 * 9 */ 10 11 #include "pkix_ocspchecker.h" 12 #include "pkix_pl_ocspcertid.h" 13 #include "pkix_error.h" 14 15 16 /* --Private-Data-and-Types--------------------------------------- */ 17 18 typedef struct pkix_OcspCheckerStruct { 19 /* RevocationMethod is the super class of OcspChecker. */ 20 pkix_RevocationMethod method; 21 PKIX_PL_VerifyCallback certVerifyFcn; 22 } pkix_OcspChecker; 23 24 /* --Private-Functions-------------------------------------------- */ 25 26 /* 27 * FUNCTION: pkix_OcspChecker_Destroy 28 * (see comments for PKIX_PL_DestructorCallback in pkix_pl_system.h) 29 */ 30 static PKIX_Error * 31 pkix_OcspChecker_Destroy( 32 PKIX_PL_Object *object, 33 void *plContext) 34 { 35 return NULL; 36 } 37 38 /* 39 * FUNCTION: pkix_OcspChecker_RegisterSelf 40 * DESCRIPTION: 41 * Registers PKIX_OCSPCHECKER_TYPE and its related functions with 42 * systemClasses[] 43 * THREAD SAFETY: 44 * Not Thread Safe - for performance and complexity reasons 45 * 46 * Since this function is only called by PKIX_PL_Initialize, which should 47 * only be called once, it is acceptable that this function is not 48 * thread-safe. 49 */ 50 PKIX_Error * 51 pkix_OcspChecker_RegisterSelf(void *plContext) 52 { 53 extern pkix_ClassTable_Entry systemClasses[PKIX_NUMTYPES]; 54 pkix_ClassTable_Entry* entry = &systemClasses[PKIX_OCSPCHECKER_TYPE]; 55 56 PKIX_ENTER(OCSPCHECKER, "pkix_OcspChecker_RegisterSelf"); 57 58 entry->description = "OcspChecker"; 59 entry->typeObjectSize = sizeof(pkix_OcspChecker); 60 entry->destructor = pkix_OcspChecker_Destroy; 61 62 PKIX_RETURN(OCSPCHECKER); 63 } 64 65 66 /* 67 * FUNCTION: pkix_OcspChecker_Create 68 */ 69 PKIX_Error * 70 pkix_OcspChecker_Create(PKIX_RevocationMethodType methodType, 71 PKIX_UInt32 flags, 72 PKIX_UInt32 priority, 73 pkix_LocalRevocationCheckFn localRevChecker, 74 pkix_ExternalRevocationCheckFn externalRevChecker, 75 PKIX_PL_VerifyCallback verifyFn, 76 pkix_RevocationMethod **pChecker, 77 void *plContext) 78 { 79 pkix_OcspChecker *method = NULL; 80 81 PKIX_ENTER(OCSPCHECKER, "pkix_OcspChecker_Create"); 82 PKIX_NULLCHECK_ONE(pChecker); 83 84 PKIX_CHECK(PKIX_PL_Object_Alloc 85 (PKIX_OCSPCHECKER_TYPE, 86 sizeof (pkix_OcspChecker), 87 (PKIX_PL_Object **)&method, 88 plContext), 89 PKIX_COULDNOTCREATECERTCHAINCHECKEROBJECT); 90 91 pkixErrorResult = pkix_RevocationMethod_Init( 92 (pkix_RevocationMethod*)method, methodType, flags, priority, 93 localRevChecker, externalRevChecker, plContext); 94 if (pkixErrorResult) { 95 goto cleanup; 96 } 97 method->certVerifyFcn = (PKIX_PL_VerifyCallback)verifyFn; 98 99 *pChecker = (pkix_RevocationMethod*)method; 100 method = NULL; 101 102 cleanup: 103 PKIX_DECREF(method); 104 105 PKIX_RETURN(OCSPCHECKER); 106 } 107 108 /* 109 * FUNCTION: pkix_OcspChecker_MapResultCodeToRevStatus 110 */ 111 PKIX_RevocationStatus 112 pkix_OcspChecker_MapResultCodeToRevStatus(SECErrorCodes resultCode) 113 { 114 switch (resultCode) { 115 case SEC_ERROR_REVOKED_CERTIFICATE: 116 return PKIX_RevStatus_Revoked; 117 default: 118 return PKIX_RevStatus_NoInfo; 119 } 120 } 121 122 /* --Public-Functions--------------------------------------------- */ 123 124 /* 125 * FUNCTION: pkix_OcspChecker_Check (see comments in pkix_checker.h) 126 */ 127 128 /* 129 * The OCSPChecker is created in an idle state, and remains in this state until 130 * either (a) the default Responder has been set and enabled, and a Check 131 * request is received with no responder specified, or (b) a Check request is 132 * received with a specified responder. A request message is constructed and 133 * given to the HttpClient. If non-blocking I/O is used the client may return 134 * with WOULDBLOCK, in which case the OCSPChecker returns the WOULDBLOCK 135 * condition to its caller in turn. On a subsequent call the I/O is resumed. 136 * When a response is received it is decoded and the results provided to the 137 * caller. 138 * 139 */ 140 PKIX_Error * 141 pkix_OcspChecker_CheckLocal( 142 PKIX_PL_Cert *cert, 143 PKIX_PL_Cert *issuer, 144 PKIX_PL_Date *date, 145 pkix_RevocationMethod *checkerObject, 146 PKIX_ProcessingParams *procParams, 147 PKIX_UInt32 methodFlags, 148 PKIX_Boolean chainVerificationState, 149 PKIX_RevocationStatus *pRevStatus, 150 CERTCRLEntryReasonCode *pReasonCode, 151 void *plContext) 152 { 153 PKIX_PL_OcspCertID *cid = NULL; 154 PKIX_Boolean hasFreshStatus = PKIX_FALSE; 155 PKIX_Boolean statusIsGood = PKIX_FALSE; 156 SECErrorCodes resultCode = SEC_ERROR_REVOKED_CERTIFICATE_OCSP; 157 PKIX_RevocationStatus revStatus = PKIX_RevStatus_NoInfo; 158 159 PKIX_ENTER(OCSPCHECKER, "pkix_OcspChecker_CheckLocal"); 160 161 PKIX_CHECK( 162 PKIX_PL_OcspCertID_Create(cert, NULL, &cid, 163 plContext), 164 PKIX_OCSPCERTIDCREATEFAILED); 165 if (!cid) { 166 goto cleanup; 167 } 168 169 PKIX_CHECK( 170 PKIX_PL_OcspCertID_GetFreshCacheStatus(cid, date, 171 &hasFreshStatus, 172 &statusIsGood, 173 &resultCode, 174 plContext), 175 PKIX_OCSPCERTIDGETFRESHCACHESTATUSFAILED); 176 if (hasFreshStatus) { 177 if (statusIsGood) { 178 revStatus = PKIX_RevStatus_Success; 179 resultCode = 0; 180 } else { 181 revStatus = pkix_OcspChecker_MapResultCodeToRevStatus(resultCode); 182 } 183 } 184 185 cleanup: 186 *pRevStatus = revStatus; 187 188 /* ocsp carries only tree statuses: good, bad, and unknown. 189 * revStatus is used to pass them. reasonCode is always set 190 * to be unknown. */ 191 *pReasonCode = crlEntryReasonUnspecified; 192 PKIX_DECREF(cid); 193 194 PKIX_RETURN(OCSPCHECKER); 195 } 196 197 198 /* 199 * The OCSPChecker is created in an idle state, and remains in this state until 200 * either (a) the default Responder has been set and enabled, and a Check 201 * request is received with no responder specified, or (b) a Check request is 202 * received with a specified responder. A request message is constructed and 203 * given to the HttpClient. When a response is received it is decoded and the 204 * results provided to the caller. 205 * 206 * During the most recent enhancement of this function, it has been found that 207 * it doesn't correctly implement non-blocking I/O. 208 * 209 * The nbioContext is used in two places, for "response-obtaining" and 210 * for "response-verification". 211 * 212 * However, if this function gets called to resume, it always 213 * repeats the "request creation" and "response fetching" steps! 214 * As a result, the earlier operation is never resumed. 215 */ 216 PKIX_Error * 217 pkix_OcspChecker_CheckExternal( 218 PKIX_PL_Cert *cert, 219 PKIX_PL_Cert *issuer, 220 PKIX_PL_Date *date, 221 pkix_RevocationMethod *checkerObject, 222 PKIX_ProcessingParams *procParams, 223 PKIX_UInt32 methodFlags, 224 PKIX_RevocationStatus *pRevStatus, 225 CERTCRLEntryReasonCode *pReasonCode, 226 void **pNBIOContext, 227 void *plContext) 228 { 229 SECErrorCodes resultCode = SEC_ERROR_REVOKED_CERTIFICATE_OCSP; 230 PKIX_Boolean uriFound = PKIX_FALSE; 231 PKIX_Boolean passed = PKIX_TRUE; 232 pkix_OcspChecker *checker = NULL; 233 PKIX_PL_OcspCertID *cid = NULL; 234 PKIX_PL_OcspRequest *request = NULL; 235 PKIX_PL_OcspResponse *response = NULL; 236 PKIX_PL_Date *validity = NULL; 237 PKIX_RevocationStatus revStatus = PKIX_RevStatus_NoInfo; 238 void *nbioContext = NULL; 239 enum { stageGET, stagePOST } currentStage; 240 PRBool retry = PR_FALSE; 241 242 PKIX_ENTER(OCSPCHECKER, "pkix_OcspChecker_CheckExternal"); 243 244 PKIX_CHECK( 245 pkix_CheckType((PKIX_PL_Object*)checkerObject, 246 PKIX_OCSPCHECKER_TYPE, plContext), 247 PKIX_OBJECTNOTOCSPCHECKER); 248 249 checker = (pkix_OcspChecker *)checkerObject; 250 251 PKIX_CHECK( 252 PKIX_PL_OcspCertID_Create(cert, NULL, &cid, 253 plContext), 254 PKIX_OCSPCERTIDCREATEFAILED); 255 256 /* create request */ 257 PKIX_CHECK( 258 pkix_pl_OcspRequest_Create(cert, cid, validity, NULL, 259 methodFlags, &uriFound, &request, 260 plContext), 261 PKIX_OCSPREQUESTCREATEFAILED); 262 263 if (uriFound == PKIX_FALSE) { 264 /* no caching for certs lacking URI */ 265 resultCode = 0; 266 goto cleanup; 267 } 268 269 if (methodFlags & CERT_REV_M_FORCE_POST_METHOD_FOR_OCSP) { 270 /* Do not try HTTP GET, only HTTP POST */ 271 currentStage = stagePOST; 272 } else { 273 /* Try HTTP GET first, falling back to POST */ 274 currentStage = stageGET; 275 } 276 277 do { 278 const char *method; 279 passed = PKIX_TRUE; 280 281 retry = PR_FALSE; 282 if (currentStage == stageGET) { 283 method = "GET"; 284 } else { 285 PORT_Assert(currentStage == stagePOST); 286 method = "POST"; 287 } 288 289 /* send request and create a response object */ 290 PKIX_CHECK_NO_GOTO( 291 pkix_pl_OcspResponse_Create(request, method, NULL, 292 checker->certVerifyFcn, 293 &nbioContext, 294 &response, 295 plContext), 296 PKIX_OCSPRESPONSECREATEFAILED); 297 298 if (pkixErrorResult) { 299 passed = PKIX_FALSE; 300 } 301 302 if (passed && nbioContext != 0) { 303 *pNBIOContext = nbioContext; 304 goto cleanup; 305 } 306 307 if (passed){ 308 PKIX_CHECK_NO_GOTO( 309 pkix_pl_OcspResponse_Decode(response, &passed, 310 &resultCode, plContext), 311 PKIX_OCSPRESPONSEDECODEFAILED); 312 if (pkixErrorResult) { 313 passed = PKIX_FALSE; 314 } 315 } 316 317 if (passed){ 318 PKIX_CHECK_NO_GOTO( 319 pkix_pl_OcspResponse_GetStatus(response, &passed, 320 &resultCode, plContext), 321 PKIX_OCSPRESPONSEGETSTATUSRETURNEDANERROR); 322 if (pkixErrorResult) { 323 passed = PKIX_FALSE; 324 } 325 } 326 327 if (passed){ 328 PKIX_CHECK_NO_GOTO( 329 pkix_pl_OcspResponse_VerifySignature(response, cert, 330 procParams, &passed, 331 &nbioContext, plContext), 332 PKIX_OCSPRESPONSEVERIFYSIGNATUREFAILED); 333 if (pkixErrorResult) { 334 passed = PKIX_FALSE; 335 } else { 336 if (nbioContext != 0) { 337 *pNBIOContext = nbioContext; 338 goto cleanup; 339 } 340 } 341 } 342 343 if (!passed && currentStage == stagePOST) { 344 /* We won't retry a POST failure, so it's final. 345 * Because the following block with its call to 346 * pkix_pl_OcspResponse_GetStatusForCert 347 * will take care of caching good or bad state, 348 * but we only execute that next block if there hasn't 349 * been a failure yet, we must cache the POST 350 * failure now. 351 */ 352 353 if (cid && cid->certID) { 354 /* Caching MIGHT consume the cid. */ 355 PKIX_Error *err; 356 err = PKIX_PL_OcspCertID_RememberOCSPProcessingFailure( 357 cid, plContext); 358 if (err) { 359 PKIX_PL_Object_DecRef((PKIX_PL_Object*)err, plContext); 360 } 361 } 362 } 363 364 if (passed){ 365 PKIX_Boolean allowCachingOfFailures = 366 (currentStage == stagePOST) ? PKIX_TRUE : PKIX_FALSE; 367 368 PKIX_CHECK_NO_GOTO( 369 pkix_pl_OcspResponse_GetStatusForCert(cid, response, 370 allowCachingOfFailures, 371 date, 372 &passed, &resultCode, 373 plContext), 374 PKIX_OCSPRESPONSEGETSTATUSFORCERTFAILED); 375 if (pkixErrorResult) { 376 passed = PKIX_FALSE; 377 } else if (passed == PKIX_FALSE) { 378 revStatus = pkix_OcspChecker_MapResultCodeToRevStatus(resultCode); 379 } else { 380 revStatus = PKIX_RevStatus_Success; 381 } 382 } 383 384 if (currentStage == stageGET && revStatus != PKIX_RevStatus_Success && 385 revStatus != PKIX_RevStatus_Revoked) { 386 /* we'll retry */ 387 PKIX_DECREF(response); 388 retry = PR_TRUE; 389 currentStage = stagePOST; 390 revStatus = PKIX_RevStatus_NoInfo; 391 if (pkixErrorResult) { 392 PKIX_PL_Object_DecRef((PKIX_PL_Object*)pkixErrorResult, 393 plContext); 394 pkixErrorResult = NULL; 395 } 396 } 397 } while (retry); 398 399 cleanup: 400 if (revStatus == PKIX_RevStatus_NoInfo && (uriFound || 401 methodFlags & PKIX_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE) && 402 methodFlags & PKIX_REV_M_FAIL_ON_MISSING_FRESH_INFO) { 403 revStatus = PKIX_RevStatus_Revoked; 404 } 405 *pRevStatus = revStatus; 406 407 /* ocsp carries only three statuses: good, bad, and unknown. 408 * revStatus is used to pass them. reasonCode is always set 409 * to be unknown. */ 410 *pReasonCode = crlEntryReasonUnspecified; 411 412 PKIX_DECREF(cid); 413 PKIX_DECREF(request); 414 PKIX_DECREF(response); 415 416 PKIX_RETURN(OCSPCHECKER); 417 }