| OLD | NEW |
| 1 /* This Source Code Form is subject to the terms of the Mozilla Public | 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 | 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/. */ | 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| 4 /* | 4 /* |
| 5 * pkix_ocspchecker.c | 5 * pkix_ocspchecker.c |
| 6 * | 6 * |
| 7 * OcspChecker Object Functions | 7 * OcspChecker Object Functions |
| 8 * | 8 * |
| 9 */ | 9 */ |
| 10 | 10 |
| (...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 193 | 193 |
| 194 PKIX_RETURN(OCSPCHECKER); | 194 PKIX_RETURN(OCSPCHECKER); |
| 195 } | 195 } |
| 196 | 196 |
| 197 | 197 |
| 198 /* | 198 /* |
| 199 * The OCSPChecker is created in an idle state, and remains in this state until | 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 | 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 | 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 | 202 * received with a specified responder. A request message is constructed and |
| 203 * given to the HttpClient. If non-blocking I/O is used the client may return | 203 * given to the HttpClient. When a response is received it is decoded and the |
| 204 * with WOULDBLOCK, in which case the OCSPChecker returns the WOULDBLOCK | 204 * results provided to the caller. |
| 205 * condition to its caller in turn. On a subsequent call the I/O is resumed. | |
| 206 * When a response is received it is decoded and the results provided to the | |
| 207 * caller. | |
| 208 * | 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. |
| 209 */ | 215 */ |
| 210 PKIX_Error * | 216 PKIX_Error * |
| 211 pkix_OcspChecker_CheckExternal( | 217 pkix_OcspChecker_CheckExternal( |
| 212 PKIX_PL_Cert *cert, | 218 PKIX_PL_Cert *cert, |
| 213 PKIX_PL_Cert *issuer, | 219 PKIX_PL_Cert *issuer, |
| 214 PKIX_PL_Date *date, | 220 PKIX_PL_Date *date, |
| 215 pkix_RevocationMethod *checkerObject, | 221 pkix_RevocationMethod *checkerObject, |
| 216 PKIX_ProcessingParams *procParams, | 222 PKIX_ProcessingParams *procParams, |
| 217 PKIX_UInt32 methodFlags, | 223 PKIX_UInt32 methodFlags, |
| 218 PKIX_RevocationStatus *pRevStatus, | 224 PKIX_RevocationStatus *pRevStatus, |
| 219 PKIX_UInt32 *pReasonCode, | 225 PKIX_UInt32 *pReasonCode, |
| 220 void **pNBIOContext, | 226 void **pNBIOContext, |
| 221 void *plContext) | 227 void *plContext) |
| 222 { | 228 { |
| 223 SECErrorCodes resultCode = SEC_ERROR_REVOKED_CERTIFICATE_OCSP; | 229 SECErrorCodes resultCode = SEC_ERROR_REVOKED_CERTIFICATE_OCSP; |
| 224 PKIX_Boolean uriFound = PKIX_FALSE; | 230 PKIX_Boolean uriFound = PKIX_FALSE; |
| 225 PKIX_Boolean passed = PKIX_TRUE; | 231 PKIX_Boolean passed = PKIX_TRUE; |
| 226 pkix_OcspChecker *checker = NULL; | 232 pkix_OcspChecker *checker = NULL; |
| 227 PKIX_PL_OcspCertID *cid = NULL; | 233 PKIX_PL_OcspCertID *cid = NULL; |
| 228 PKIX_PL_OcspRequest *request = NULL; | 234 PKIX_PL_OcspRequest *request = NULL; |
| 229 PKIX_PL_OcspResponse *response = NULL; | 235 PKIX_PL_OcspResponse *response = NULL; |
| 230 PKIX_PL_Date *validity = NULL; | 236 PKIX_PL_Date *validity = NULL; |
| 231 PKIX_RevocationStatus revStatus = PKIX_RevStatus_NoInfo; | 237 PKIX_RevocationStatus revStatus = PKIX_RevStatus_NoInfo; |
| 232 void *nbioContext = NULL; | 238 void *nbioContext = NULL; |
| 239 enum { stageGET, stagePOST } currentStage; |
| 240 PRBool retry = PR_FALSE; |
| 233 | 241 |
| 234 PKIX_ENTER(OCSPCHECKER, "pkix_OcspChecker_CheckExternal"); | 242 PKIX_ENTER(OCSPCHECKER, "pkix_OcspChecker_CheckExternal"); |
| 235 | 243 |
| 236 PKIX_CHECK( | 244 PKIX_CHECK( |
| 237 pkix_CheckType((PKIX_PL_Object*)checkerObject, | 245 pkix_CheckType((PKIX_PL_Object*)checkerObject, |
| 238 PKIX_OCSPCHECKER_TYPE, plContext), | 246 PKIX_OCSPCHECKER_TYPE, plContext), |
| 239 PKIX_OBJECTNOTOCSPCHECKER); | 247 PKIX_OBJECTNOTOCSPCHECKER); |
| 240 | 248 |
| 241 checker = (pkix_OcspChecker *)checkerObject; | 249 checker = (pkix_OcspChecker *)checkerObject; |
| 242 | 250 |
| 243 PKIX_CHECK( | 251 PKIX_CHECK( |
| 244 PKIX_PL_OcspCertID_Create(cert, NULL, &cid, | 252 PKIX_PL_OcspCertID_Create(cert, NULL, &cid, |
| 245 plContext), | 253 plContext), |
| 246 PKIX_OCSPCERTIDCREATEFAILED); | 254 PKIX_OCSPCERTIDCREATEFAILED); |
| 247 | 255 |
| 248 /* create request */ | 256 /* create request */ |
| 249 PKIX_CHECK( | 257 PKIX_CHECK( |
| 250 pkix_pl_OcspRequest_Create(cert, cid, validity, NULL, | 258 pkix_pl_OcspRequest_Create(cert, cid, validity, NULL, |
| 251 methodFlags, &uriFound, &request, | 259 methodFlags, &uriFound, &request, |
| 252 plContext), | 260 plContext), |
| 253 PKIX_OCSPREQUESTCREATEFAILED); | 261 PKIX_OCSPREQUESTCREATEFAILED); |
| 254 | 262 |
| 255 if (uriFound == PKIX_FALSE) { | 263 if (uriFound == PKIX_FALSE) { |
| 256 /* no caching for certs lacking URI */ | 264 /* no caching for certs lacking URI */ |
| 257 resultCode = 0; | 265 resultCode = 0; |
| 258 goto cleanup; | 266 goto cleanup; |
| 259 } | 267 } |
| 260 | 268 |
| 261 /* send request and create a response object */ | 269 if (methodFlags & CERT_REV_M_FORCE_POST_METHOD_FOR_OCSP) { |
| 262 PKIX_CHECK( | 270 /* Do not try HTTP GET, only HTTP POST */ |
| 263 pkix_pl_OcspResponse_Create(request, NULL, | 271 currentStage = stagePOST; |
| 264 checker->certVerifyFcn, | 272 } else { |
| 265 &nbioContext, | 273 /* Try HTTP GET first, falling back to POST */ |
| 266 &response, | 274 currentStage = stageGET; |
| 267 plContext), | |
| 268 PKIX_OCSPRESPONSECREATEFAILED); | |
| 269 if (nbioContext != 0) { | |
| 270 *pNBIOContext = nbioContext; | |
| 271 goto cleanup; | |
| 272 } | |
| 273 | |
| 274 PKIX_CHECK( | |
| 275 pkix_pl_OcspResponse_Decode(response, &passed, | |
| 276 &resultCode, plContext), | |
| 277 PKIX_OCSPRESPONSEDECODEFAILED); | |
| 278 if (passed == PKIX_FALSE) { | |
| 279 goto cleanup; | |
| 280 } | |
| 281 | |
| 282 PKIX_CHECK( | |
| 283 pkix_pl_OcspResponse_GetStatus(response, &passed, | |
| 284 &resultCode, plContext), | |
| 285 PKIX_OCSPRESPONSEGETSTATUSRETURNEDANERROR); | |
| 286 if (passed == PKIX_FALSE) { | |
| 287 goto cleanup; | |
| 288 } | 275 } |
| 289 | 276 |
| 290 PKIX_CHECK( | 277 do { |
| 291 pkix_pl_OcspResponse_VerifySignature(response, cert, | 278 const char *method; |
| 292 procParams, &passed, | 279 passed = PKIX_TRUE; |
| 293 &nbioContext, plContext), | |
| 294 PKIX_OCSPRESPONSEVERIFYSIGNATUREFAILED); | |
| 295 »if (nbioContext != 0) { | |
| 296 »*pNBIOContext = nbioContext; | |
| 297 goto cleanup; | |
| 298 } | |
| 299 if (passed == PKIX_FALSE) { | |
| 300 goto cleanup; | |
| 301 } | |
| 302 | 280 |
| 303 PKIX_CHECK( | 281 retry = PR_FALSE; |
| 304 pkix_pl_OcspResponse_GetStatusForCert(cid, response, date, | 282 if (currentStage == stageGET) { |
| 305 &passed, &resultCode, | 283 method = "GET"; |
| 306 plContext), | 284 } else { |
| 307 PKIX_OCSPRESPONSEGETSTATUSFORCERTFAILED); | 285 PORT_Assert(currentStage == stagePOST); |
| 308 if (passed == PKIX_FALSE) { | 286 method = "POST"; |
| 309 revStatus = pkix_OcspChecker_MapResultCodeToRevStatus(resultCode); | 287 } |
| 310 } else { | 288 |
| 311 revStatus = PKIX_RevStatus_Success; | 289 /* send request and create a response object */ |
| 312 } | 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, plContex
t), |
| 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, &pa
ssed, |
| 331 &nbioContext, p
lContext), |
| 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_RememberOCSPProcessingF
ailure( |
| 357 cid, plContext); |
| 358 if (err) { |
| 359 PKIX_PL_Object_DecRef((PKIX_PL_Object*)e
rr, plContext); |
| 360 } |
| 361 } |
| 362 } |
| 363 |
| 364 if (passed){ |
| 365 PKIX_Boolean allowCachingOfFailures = |
| 366 (currentStage == stagePOST) ? PKIX_TRUE : PKIX_F
ALSE; |
| 367 |
| 368 PKIX_CHECK_NO_GOTO( |
| 369 pkix_pl_OcspResponse_GetStatusForCert(cid, response, |
| 370 allowCachingOf
Failures, |
| 371 date, |
| 372 &passed, &resu
ltCode, |
| 373 plContext), |
| 374 PKIX_OCSPRESPONSEGETSTATUSFORCERTFAILED); |
| 375 if (pkixErrorResult) { |
| 376 passed = PKIX_FALSE; |
| 377 } else if (passed == PKIX_FALSE) { |
| 378 revStatus = pkix_OcspChecker_MapResultCodeToRevS
tatus(resultCode); |
| 379 } else { |
| 380 revStatus = PKIX_RevStatus_Success; |
| 381 } |
| 382 } |
| 383 |
| 384 if (currentStage == stageGET && revStatus != PKIX_RevStatus_Succ
ess && |
| 385 revStatus != PKIX_RevStatus_Revo
ked) { |
| 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*)pkixError
Result, |
| 393 plContext); |
| 394 pkixErrorResult = NULL; |
| 395 } |
| 396 } |
| 397 } while (retry); |
| 313 | 398 |
| 314 cleanup: | 399 cleanup: |
| 315 if (revStatus == PKIX_RevStatus_NoInfo && (uriFound || | 400 if (revStatus == PKIX_RevStatus_NoInfo && (uriFound || |
| 316 methodFlags & PKIX_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE) && | 401 methodFlags & PKIX_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE) && |
| 317 methodFlags & PKIX_REV_M_FAIL_ON_MISSING_FRESH_INFO) { | 402 methodFlags & PKIX_REV_M_FAIL_ON_MISSING_FRESH_INFO) { |
| 318 revStatus = PKIX_RevStatus_Revoked; | 403 revStatus = PKIX_RevStatus_Revoked; |
| 319 } | 404 } |
| 320 *pRevStatus = revStatus; | 405 *pRevStatus = revStatus; |
| 321 | 406 |
| 322 /* ocsp carries only tree statuses: good, bad, and unknown. | 407 /* ocsp carries only three statuses: good, bad, and unknown. |
| 323 * revStatus is used to pass them. reasonCode is always set | 408 * revStatus is used to pass them. reasonCode is always set |
| 324 * to be unknown. */ | 409 * to be unknown. */ |
| 325 *pReasonCode = crlEntryReasonUnspecified; | 410 *pReasonCode = crlEntryReasonUnspecified; |
| 326 | 411 |
| 327 if (!passed && cid && cid->certID) { | |
| 328 /* We still own the certID object, which means that | |
| 329 * it did not get consumed to create a cache entry. | |
| 330 * Let's make sure there is one. | |
| 331 */ | |
| 332 PKIX_Error *err; | |
| 333 err = PKIX_PL_OcspCertID_RememberOCSPProcessingFailure( | |
| 334 cid, plContext); | |
| 335 if (err) { | |
| 336 PKIX_PL_Object_DecRef((PKIX_PL_Object*)err, plContext); | |
| 337 } | |
| 338 } | |
| 339 PKIX_DECREF(cid); | 412 PKIX_DECREF(cid); |
| 340 PKIX_DECREF(request); | 413 PKIX_DECREF(request); |
| 341 PKIX_DECREF(response); | 414 PKIX_DECREF(response); |
| 342 | 415 |
| 343 PKIX_RETURN(OCSPCHECKER); | 416 PKIX_RETURN(OCSPCHECKER); |
| 344 } | 417 } |
| 345 | 418 |
| 346 | 419 |
| OLD | NEW |