tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }