1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/nss/lib/libpkix/pkix/checker/pkix_ocspchecker.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,419 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 +/* 1.8 + * pkix_ocspchecker.c 1.9 + * 1.10 + * OcspChecker Object Functions 1.11 + * 1.12 + */ 1.13 + 1.14 +#include "pkix_ocspchecker.h" 1.15 +#include "pkix_pl_ocspcertid.h" 1.16 +#include "pkix_error.h" 1.17 + 1.18 + 1.19 +/* --Private-Data-and-Types--------------------------------------- */ 1.20 + 1.21 +typedef struct pkix_OcspCheckerStruct { 1.22 + /* RevocationMethod is the super class of OcspChecker. */ 1.23 + pkix_RevocationMethod method; 1.24 + PKIX_PL_VerifyCallback certVerifyFcn; 1.25 +} pkix_OcspChecker; 1.26 + 1.27 +/* --Private-Functions-------------------------------------------- */ 1.28 + 1.29 +/* 1.30 + * FUNCTION: pkix_OcspChecker_Destroy 1.31 + * (see comments for PKIX_PL_DestructorCallback in pkix_pl_system.h) 1.32 + */ 1.33 +static PKIX_Error * 1.34 +pkix_OcspChecker_Destroy( 1.35 + PKIX_PL_Object *object, 1.36 + void *plContext) 1.37 +{ 1.38 + return NULL; 1.39 +} 1.40 + 1.41 +/* 1.42 + * FUNCTION: pkix_OcspChecker_RegisterSelf 1.43 + * DESCRIPTION: 1.44 + * Registers PKIX_OCSPCHECKER_TYPE and its related functions with 1.45 + * systemClasses[] 1.46 + * THREAD SAFETY: 1.47 + * Not Thread Safe - for performance and complexity reasons 1.48 + * 1.49 + * Since this function is only called by PKIX_PL_Initialize, which should 1.50 + * only be called once, it is acceptable that this function is not 1.51 + * thread-safe. 1.52 + */ 1.53 +PKIX_Error * 1.54 +pkix_OcspChecker_RegisterSelf(void *plContext) 1.55 +{ 1.56 + extern pkix_ClassTable_Entry systemClasses[PKIX_NUMTYPES]; 1.57 + pkix_ClassTable_Entry* entry = &systemClasses[PKIX_OCSPCHECKER_TYPE]; 1.58 + 1.59 + PKIX_ENTER(OCSPCHECKER, "pkix_OcspChecker_RegisterSelf"); 1.60 + 1.61 + entry->description = "OcspChecker"; 1.62 + entry->typeObjectSize = sizeof(pkix_OcspChecker); 1.63 + entry->destructor = pkix_OcspChecker_Destroy; 1.64 + 1.65 + PKIX_RETURN(OCSPCHECKER); 1.66 +} 1.67 + 1.68 + 1.69 +/* 1.70 + * FUNCTION: pkix_OcspChecker_Create 1.71 + */ 1.72 +PKIX_Error * 1.73 +pkix_OcspChecker_Create(PKIX_RevocationMethodType methodType, 1.74 + PKIX_UInt32 flags, 1.75 + PKIX_UInt32 priority, 1.76 + pkix_LocalRevocationCheckFn localRevChecker, 1.77 + pkix_ExternalRevocationCheckFn externalRevChecker, 1.78 + PKIX_PL_VerifyCallback verifyFn, 1.79 + pkix_RevocationMethod **pChecker, 1.80 + void *plContext) 1.81 +{ 1.82 + pkix_OcspChecker *method = NULL; 1.83 + 1.84 + PKIX_ENTER(OCSPCHECKER, "pkix_OcspChecker_Create"); 1.85 + PKIX_NULLCHECK_ONE(pChecker); 1.86 + 1.87 + PKIX_CHECK(PKIX_PL_Object_Alloc 1.88 + (PKIX_OCSPCHECKER_TYPE, 1.89 + sizeof (pkix_OcspChecker), 1.90 + (PKIX_PL_Object **)&method, 1.91 + plContext), 1.92 + PKIX_COULDNOTCREATECERTCHAINCHECKEROBJECT); 1.93 + 1.94 + pkixErrorResult = pkix_RevocationMethod_Init( 1.95 + (pkix_RevocationMethod*)method, methodType, flags, priority, 1.96 + localRevChecker, externalRevChecker, plContext); 1.97 + if (pkixErrorResult) { 1.98 + goto cleanup; 1.99 + } 1.100 + method->certVerifyFcn = (PKIX_PL_VerifyCallback)verifyFn; 1.101 + 1.102 + *pChecker = (pkix_RevocationMethod*)method; 1.103 + method = NULL; 1.104 + 1.105 +cleanup: 1.106 + PKIX_DECREF(method); 1.107 + 1.108 + PKIX_RETURN(OCSPCHECKER); 1.109 +} 1.110 + 1.111 +/* 1.112 + * FUNCTION: pkix_OcspChecker_MapResultCodeToRevStatus 1.113 + */ 1.114 +PKIX_RevocationStatus 1.115 +pkix_OcspChecker_MapResultCodeToRevStatus(SECErrorCodes resultCode) 1.116 +{ 1.117 + switch (resultCode) { 1.118 + case SEC_ERROR_REVOKED_CERTIFICATE: 1.119 + return PKIX_RevStatus_Revoked; 1.120 + default: 1.121 + return PKIX_RevStatus_NoInfo; 1.122 + } 1.123 +} 1.124 + 1.125 +/* --Public-Functions--------------------------------------------- */ 1.126 + 1.127 +/* 1.128 + * FUNCTION: pkix_OcspChecker_Check (see comments in pkix_checker.h) 1.129 + */ 1.130 + 1.131 +/* 1.132 + * The OCSPChecker is created in an idle state, and remains in this state until 1.133 + * either (a) the default Responder has been set and enabled, and a Check 1.134 + * request is received with no responder specified, or (b) a Check request is 1.135 + * received with a specified responder. A request message is constructed and 1.136 + * given to the HttpClient. If non-blocking I/O is used the client may return 1.137 + * with WOULDBLOCK, in which case the OCSPChecker returns the WOULDBLOCK 1.138 + * condition to its caller in turn. On a subsequent call the I/O is resumed. 1.139 + * When a response is received it is decoded and the results provided to the 1.140 + * caller. 1.141 + * 1.142 + */ 1.143 +PKIX_Error * 1.144 +pkix_OcspChecker_CheckLocal( 1.145 + PKIX_PL_Cert *cert, 1.146 + PKIX_PL_Cert *issuer, 1.147 + PKIX_PL_Date *date, 1.148 + pkix_RevocationMethod *checkerObject, 1.149 + PKIX_ProcessingParams *procParams, 1.150 + PKIX_UInt32 methodFlags, 1.151 + PKIX_Boolean chainVerificationState, 1.152 + PKIX_RevocationStatus *pRevStatus, 1.153 + PKIX_UInt32 *pReasonCode, 1.154 + void *plContext) 1.155 +{ 1.156 + PKIX_PL_OcspCertID *cid = NULL; 1.157 + PKIX_Boolean hasFreshStatus = PKIX_FALSE; 1.158 + PKIX_Boolean statusIsGood = PKIX_FALSE; 1.159 + SECErrorCodes resultCode = SEC_ERROR_REVOKED_CERTIFICATE_OCSP; 1.160 + PKIX_RevocationStatus revStatus = PKIX_RevStatus_NoInfo; 1.161 + 1.162 + PKIX_ENTER(OCSPCHECKER, "pkix_OcspChecker_CheckLocal"); 1.163 + 1.164 + PKIX_CHECK( 1.165 + PKIX_PL_OcspCertID_Create(cert, NULL, &cid, 1.166 + plContext), 1.167 + PKIX_OCSPCERTIDCREATEFAILED); 1.168 + if (!cid) { 1.169 + goto cleanup; 1.170 + } 1.171 + 1.172 + PKIX_CHECK( 1.173 + PKIX_PL_OcspCertID_GetFreshCacheStatus(cid, date, 1.174 + &hasFreshStatus, 1.175 + &statusIsGood, 1.176 + &resultCode, 1.177 + plContext), 1.178 + PKIX_OCSPCERTIDGETFRESHCACHESTATUSFAILED); 1.179 + if (hasFreshStatus) { 1.180 + if (statusIsGood) { 1.181 + revStatus = PKIX_RevStatus_Success; 1.182 + resultCode = 0; 1.183 + } else { 1.184 + revStatus = pkix_OcspChecker_MapResultCodeToRevStatus(resultCode); 1.185 + } 1.186 + } 1.187 + 1.188 +cleanup: 1.189 + *pRevStatus = revStatus; 1.190 + 1.191 + /* ocsp carries only tree statuses: good, bad, and unknown. 1.192 + * revStatus is used to pass them. reasonCode is always set 1.193 + * to be unknown. */ 1.194 + *pReasonCode = crlEntryReasonUnspecified; 1.195 + PKIX_DECREF(cid); 1.196 + 1.197 + PKIX_RETURN(OCSPCHECKER); 1.198 +} 1.199 + 1.200 + 1.201 +/* 1.202 + * The OCSPChecker is created in an idle state, and remains in this state until 1.203 + * either (a) the default Responder has been set and enabled, and a Check 1.204 + * request is received with no responder specified, or (b) a Check request is 1.205 + * received with a specified responder. A request message is constructed and 1.206 + * given to the HttpClient. When a response is received it is decoded and the 1.207 + * results provided to the caller. 1.208 + * 1.209 + * During the most recent enhancement of this function, it has been found that 1.210 + * it doesn't correctly implement non-blocking I/O. 1.211 + * 1.212 + * The nbioContext is used in two places, for "response-obtaining" and 1.213 + * for "response-verification". 1.214 + * 1.215 + * However, if this function gets called to resume, it always 1.216 + * repeats the "request creation" and "response fetching" steps! 1.217 + * As a result, the earlier operation is never resumed. 1.218 + */ 1.219 +PKIX_Error * 1.220 +pkix_OcspChecker_CheckExternal( 1.221 + PKIX_PL_Cert *cert, 1.222 + PKIX_PL_Cert *issuer, 1.223 + PKIX_PL_Date *date, 1.224 + pkix_RevocationMethod *checkerObject, 1.225 + PKIX_ProcessingParams *procParams, 1.226 + PKIX_UInt32 methodFlags, 1.227 + PKIX_RevocationStatus *pRevStatus, 1.228 + PKIX_UInt32 *pReasonCode, 1.229 + void **pNBIOContext, 1.230 + void *plContext) 1.231 +{ 1.232 + SECErrorCodes resultCode = SEC_ERROR_REVOKED_CERTIFICATE_OCSP; 1.233 + PKIX_Boolean uriFound = PKIX_FALSE; 1.234 + PKIX_Boolean passed = PKIX_TRUE; 1.235 + pkix_OcspChecker *checker = NULL; 1.236 + PKIX_PL_OcspCertID *cid = NULL; 1.237 + PKIX_PL_OcspRequest *request = NULL; 1.238 + PKIX_PL_OcspResponse *response = NULL; 1.239 + PKIX_PL_Date *validity = NULL; 1.240 + PKIX_RevocationStatus revStatus = PKIX_RevStatus_NoInfo; 1.241 + void *nbioContext = NULL; 1.242 + enum { stageGET, stagePOST } currentStage; 1.243 + PRBool retry = PR_FALSE; 1.244 + 1.245 + PKIX_ENTER(OCSPCHECKER, "pkix_OcspChecker_CheckExternal"); 1.246 + 1.247 + PKIX_CHECK( 1.248 + pkix_CheckType((PKIX_PL_Object*)checkerObject, 1.249 + PKIX_OCSPCHECKER_TYPE, plContext), 1.250 + PKIX_OBJECTNOTOCSPCHECKER); 1.251 + 1.252 + checker = (pkix_OcspChecker *)checkerObject; 1.253 + 1.254 + PKIX_CHECK( 1.255 + PKIX_PL_OcspCertID_Create(cert, NULL, &cid, 1.256 + plContext), 1.257 + PKIX_OCSPCERTIDCREATEFAILED); 1.258 + 1.259 + /* create request */ 1.260 + PKIX_CHECK( 1.261 + pkix_pl_OcspRequest_Create(cert, cid, validity, NULL, 1.262 + methodFlags, &uriFound, &request, 1.263 + plContext), 1.264 + PKIX_OCSPREQUESTCREATEFAILED); 1.265 + 1.266 + if (uriFound == PKIX_FALSE) { 1.267 + /* no caching for certs lacking URI */ 1.268 + resultCode = 0; 1.269 + goto cleanup; 1.270 + } 1.271 + 1.272 + if (methodFlags & CERT_REV_M_FORCE_POST_METHOD_FOR_OCSP) { 1.273 + /* Do not try HTTP GET, only HTTP POST */ 1.274 + currentStage = stagePOST; 1.275 + } else { 1.276 + /* Try HTTP GET first, falling back to POST */ 1.277 + currentStage = stageGET; 1.278 + } 1.279 + 1.280 + do { 1.281 + const char *method; 1.282 + passed = PKIX_TRUE; 1.283 + 1.284 + retry = PR_FALSE; 1.285 + if (currentStage == stageGET) { 1.286 + method = "GET"; 1.287 + } else { 1.288 + PORT_Assert(currentStage == stagePOST); 1.289 + method = "POST"; 1.290 + } 1.291 + 1.292 + /* send request and create a response object */ 1.293 + PKIX_CHECK_NO_GOTO( 1.294 + pkix_pl_OcspResponse_Create(request, method, NULL, 1.295 + checker->certVerifyFcn, 1.296 + &nbioContext, 1.297 + &response, 1.298 + plContext), 1.299 + PKIX_OCSPRESPONSECREATEFAILED); 1.300 + 1.301 + if (pkixErrorResult) { 1.302 + passed = PKIX_FALSE; 1.303 + } 1.304 + 1.305 + if (passed && nbioContext != 0) { 1.306 + *pNBIOContext = nbioContext; 1.307 + goto cleanup; 1.308 + } 1.309 + 1.310 + if (passed){ 1.311 + PKIX_CHECK_NO_GOTO( 1.312 + pkix_pl_OcspResponse_Decode(response, &passed, 1.313 + &resultCode, plContext), 1.314 + PKIX_OCSPRESPONSEDECODEFAILED); 1.315 + if (pkixErrorResult) { 1.316 + passed = PKIX_FALSE; 1.317 + } 1.318 + } 1.319 + 1.320 + if (passed){ 1.321 + PKIX_CHECK_NO_GOTO( 1.322 + pkix_pl_OcspResponse_GetStatus(response, &passed, 1.323 + &resultCode, plContext), 1.324 + PKIX_OCSPRESPONSEGETSTATUSRETURNEDANERROR); 1.325 + if (pkixErrorResult) { 1.326 + passed = PKIX_FALSE; 1.327 + } 1.328 + } 1.329 + 1.330 + if (passed){ 1.331 + PKIX_CHECK_NO_GOTO( 1.332 + pkix_pl_OcspResponse_VerifySignature(response, cert, 1.333 + procParams, &passed, 1.334 + &nbioContext, plContext), 1.335 + PKIX_OCSPRESPONSEVERIFYSIGNATUREFAILED); 1.336 + if (pkixErrorResult) { 1.337 + passed = PKIX_FALSE; 1.338 + } else { 1.339 + if (nbioContext != 0) { 1.340 + *pNBIOContext = nbioContext; 1.341 + goto cleanup; 1.342 + } 1.343 + } 1.344 + } 1.345 + 1.346 + if (!passed && currentStage == stagePOST) { 1.347 + /* We won't retry a POST failure, so it's final. 1.348 + * Because the following block with its call to 1.349 + * pkix_pl_OcspResponse_GetStatusForCert 1.350 + * will take care of caching good or bad state, 1.351 + * but we only execute that next block if there hasn't 1.352 + * been a failure yet, we must cache the POST 1.353 + * failure now. 1.354 + */ 1.355 + 1.356 + if (cid && cid->certID) { 1.357 + /* Caching MIGHT consume the cid. */ 1.358 + PKIX_Error *err; 1.359 + err = PKIX_PL_OcspCertID_RememberOCSPProcessingFailure( 1.360 + cid, plContext); 1.361 + if (err) { 1.362 + PKIX_PL_Object_DecRef((PKIX_PL_Object*)err, plContext); 1.363 + } 1.364 + } 1.365 + } 1.366 + 1.367 + if (passed){ 1.368 + PKIX_Boolean allowCachingOfFailures = 1.369 + (currentStage == stagePOST) ? PKIX_TRUE : PKIX_FALSE; 1.370 + 1.371 + PKIX_CHECK_NO_GOTO( 1.372 + pkix_pl_OcspResponse_GetStatusForCert(cid, response, 1.373 + allowCachingOfFailures, 1.374 + date, 1.375 + &passed, &resultCode, 1.376 + plContext), 1.377 + PKIX_OCSPRESPONSEGETSTATUSFORCERTFAILED); 1.378 + if (pkixErrorResult) { 1.379 + passed = PKIX_FALSE; 1.380 + } else if (passed == PKIX_FALSE) { 1.381 + revStatus = pkix_OcspChecker_MapResultCodeToRevStatus(resultCode); 1.382 + } else { 1.383 + revStatus = PKIX_RevStatus_Success; 1.384 + } 1.385 + } 1.386 + 1.387 + if (currentStage == stageGET && revStatus != PKIX_RevStatus_Success && 1.388 + revStatus != PKIX_RevStatus_Revoked) { 1.389 + /* we'll retry */ 1.390 + PKIX_DECREF(response); 1.391 + retry = PR_TRUE; 1.392 + currentStage = stagePOST; 1.393 + revStatus = PKIX_RevStatus_NoInfo; 1.394 + if (pkixErrorResult) { 1.395 + PKIX_PL_Object_DecRef((PKIX_PL_Object*)pkixErrorResult, 1.396 + plContext); 1.397 + pkixErrorResult = NULL; 1.398 + } 1.399 + } 1.400 + } while (retry); 1.401 + 1.402 +cleanup: 1.403 + if (revStatus == PKIX_RevStatus_NoInfo && (uriFound || 1.404 + methodFlags & PKIX_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE) && 1.405 + methodFlags & PKIX_REV_M_FAIL_ON_MISSING_FRESH_INFO) { 1.406 + revStatus = PKIX_RevStatus_Revoked; 1.407 + } 1.408 + *pRevStatus = revStatus; 1.409 + 1.410 + /* ocsp carries only three statuses: good, bad, and unknown. 1.411 + * revStatus is used to pass them. reasonCode is always set 1.412 + * to be unknown. */ 1.413 + *pReasonCode = crlEntryReasonUnspecified; 1.414 + 1.415 + PKIX_DECREF(cid); 1.416 + PKIX_DECREF(request); 1.417 + PKIX_DECREF(response); 1.418 + 1.419 + PKIX_RETURN(OCSPCHECKER); 1.420 +} 1.421 + 1.422 +