security/nss/lib/libpkix/pkix/checker/pkix_ocspchecker.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial