tor-browser

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

pkix_revocationchecker.c (17195B)


      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_revocationchecker.c
      6 *
      7 * RevocationChecker Object Functions
      8 *
      9 */
     10 
     11 #include "pkix_revocationchecker.h"
     12 #include "pkix_tools.h"
     13 
     14 /* --Private-Functions-------------------------------------------- */
     15 
     16 /*
     17 * FUNCTION: pkix_RevocationChecker_Destroy
     18 *      (see comments for PKIX_PL_DestructorCallback in pkix_pl_system.h)
     19 */
     20 static PKIX_Error *
     21 pkix_RevocationChecker_Destroy(
     22        PKIX_PL_Object *object,
     23        void *plContext)
     24 {
     25        PKIX_RevocationChecker *checker = NULL;
     26 
     27        PKIX_ENTER(REVOCATIONCHECKER, "pkix_RevocationChecker_Destroy");
     28        PKIX_NULLCHECK_ONE(object);
     29 
     30        /* Check that this object is a revocation checker */
     31        PKIX_CHECK(pkix_CheckType
     32                    (object, PKIX_REVOCATIONCHECKER_TYPE, plContext),
     33                    PKIX_OBJECTNOTREVOCATIONCHECKER);
     34 
     35        checker = (PKIX_RevocationChecker *)object;
     36 
     37        PKIX_DECREF(checker->leafMethodList);
     38        PKIX_DECREF(checker->chainMethodList);
     39        
     40 cleanup:
     41 
     42        PKIX_RETURN(REVOCATIONCHECKER);
     43 }
     44 
     45 /*
     46 * FUNCTION: pkix_RevocationChecker_Duplicate
     47 * (see comments for PKIX_PL_DuplicateCallback in pkix_pl_system.h)
     48 */
     49 static PKIX_Error *
     50 pkix_RevocationChecker_Duplicate(
     51        PKIX_PL_Object *object,
     52        PKIX_PL_Object **pNewObject,
     53        void *plContext)
     54 {
     55        PKIX_RevocationChecker *checker = NULL;
     56        PKIX_RevocationChecker *checkerDuplicate = NULL;
     57        PKIX_List *dupLeafList = NULL;
     58        PKIX_List *dupChainList = NULL;
     59 
     60        PKIX_ENTER(REVOCATIONCHECKER, "pkix_RevocationChecker_Duplicate");
     61        PKIX_NULLCHECK_TWO(object, pNewObject);
     62 
     63        PKIX_CHECK(pkix_CheckType
     64                    (object, PKIX_REVOCATIONCHECKER_TYPE, plContext),
     65                    PKIX_OBJECTNOTCERTCHAINCHECKER);
     66 
     67        checker = (PKIX_RevocationChecker *)object;
     68 
     69        if (checker->leafMethodList){
     70                PKIX_CHECK(PKIX_PL_Object_Duplicate
     71                            ((PKIX_PL_Object *)checker->leafMethodList,
     72                            (PKIX_PL_Object **)&dupLeafList,
     73                            plContext),
     74                            PKIX_OBJECTDUPLICATEFAILED);
     75        }
     76        if (checker->chainMethodList){
     77                PKIX_CHECK(PKIX_PL_Object_Duplicate
     78                            ((PKIX_PL_Object *)checker->chainMethodList,
     79                            (PKIX_PL_Object **)&dupChainList,
     80                            plContext),
     81                            PKIX_OBJECTDUPLICATEFAILED);
     82        }
     83 
     84        PKIX_CHECK(
     85            PKIX_RevocationChecker_Create(checker->leafMethodListFlags,
     86                                          checker->chainMethodListFlags,
     87                                          &checkerDuplicate,
     88                                          plContext),
     89            PKIX_REVOCATIONCHECKERCREATEFAILED);
     90 
     91        checkerDuplicate->leafMethodList = dupLeafList;
     92        checkerDuplicate->chainMethodList = dupChainList;
     93        dupLeafList = NULL;
     94        dupChainList = NULL;
     95 
     96        *pNewObject = (PKIX_PL_Object *)checkerDuplicate;
     97 
     98 cleanup:
     99        PKIX_DECREF(dupLeafList);
    100        PKIX_DECREF(dupChainList);
    101 
    102        PKIX_RETURN(REVOCATIONCHECKER);
    103 }
    104 
    105 /*
    106 * FUNCTION: pkix_RevocationChecker_RegisterSelf
    107 * DESCRIPTION:
    108 *  Registers PKIX_REVOCATIONCHECKER_TYPE and its related functions with
    109 *  systemClasses[]
    110 * THREAD SAFETY:
    111 *  Not Thread Safe - for performance and complexity reasons
    112 *
    113 *  Since this function is only called by PKIX_PL_Initialize, which should
    114 *  only be called once, it is acceptable that this function is not
    115 *  thread-safe.
    116 */
    117 PKIX_Error *
    118 pkix_RevocationChecker_RegisterSelf(void *plContext)
    119 {
    120        extern pkix_ClassTable_Entry systemClasses[PKIX_NUMTYPES];
    121        pkix_ClassTable_Entry entry;
    122 
    123        PKIX_ENTER(REVOCATIONCHECKER, "pkix_RevocationChecker_RegisterSelf");
    124 
    125        entry.description = "RevocationChecker";
    126        entry.objCounter = 0;
    127        entry.typeObjectSize = sizeof(PKIX_RevocationChecker);
    128        entry.destructor = pkix_RevocationChecker_Destroy;
    129        entry.equalsFunction = NULL;
    130        entry.hashcodeFunction = NULL;
    131        entry.toStringFunction = NULL;
    132        entry.comparator = NULL;
    133        entry.duplicateFunction = pkix_RevocationChecker_Duplicate;
    134 
    135        systemClasses[PKIX_REVOCATIONCHECKER_TYPE] = entry;
    136 
    137        PKIX_RETURN(REVOCATIONCHECKER);
    138 }
    139 
    140 /* Sort methods by their priorities (lower priority = higher preference) */
    141 static PKIX_Error *
    142 pkix_RevocationChecker_SortComparator(
    143        PKIX_PL_Object *obj1,
    144        PKIX_PL_Object *obj2,
    145        PKIX_Int32 *pResult,
    146        void *plContext)
    147 {
    148    pkix_RevocationMethod *method1 = NULL, *method2 = NULL;
    149    
    150    PKIX_ENTER(BUILD, "pkix_RevocationChecker_SortComparator");
    151    
    152    method1 = (pkix_RevocationMethod *)obj1;
    153    method2 = (pkix_RevocationMethod *)obj2;
    154    
    155    if (method1->priority < method2->priority) {
    156      *pResult = -1;
    157    } else if (method1->priority > method2->priority) {
    158      *pResult = 1;
    159    } else {
    160      *pResult = 0;
    161    }
    162    
    163    PKIX_RETURN(BUILD);
    164 }
    165 
    166 
    167 /* --Public-Functions--------------------------------------------- */
    168 
    169 
    170 /*
    171 * FUNCTION: PKIX_RevocationChecker_Create (see comments in pkix_revchecker.h)
    172 */
    173 PKIX_Error *
    174 PKIX_RevocationChecker_Create(
    175    PKIX_UInt32 leafMethodListFlags,
    176    PKIX_UInt32 chainMethodListFlags,
    177    PKIX_RevocationChecker **pChecker,
    178    void *plContext)
    179 {
    180    PKIX_RevocationChecker *checker = NULL;
    181    
    182    PKIX_ENTER(REVOCATIONCHECKER, "PKIX_RevocationChecker_Create");
    183    PKIX_NULLCHECK_ONE(pChecker);
    184    
    185    PKIX_CHECK(
    186        PKIX_PL_Object_Alloc(PKIX_REVOCATIONCHECKER_TYPE,
    187                             sizeof (PKIX_RevocationChecker),
    188                             (PKIX_PL_Object **)&checker,
    189                             plContext),
    190        PKIX_COULDNOTCREATECERTCHAINCHECKEROBJECT);
    191    
    192    checker->leafMethodListFlags = leafMethodListFlags;
    193    checker->chainMethodListFlags = chainMethodListFlags;
    194    checker->leafMethodList = NULL;
    195    checker->chainMethodList = NULL;
    196    
    197    *pChecker = checker;
    198    checker = NULL;
    199    
    200 cleanup:
    201    PKIX_DECREF(checker);
    202    
    203    PKIX_RETURN(REVOCATIONCHECKER);
    204 }
    205 
    206 /*
    207 * FUNCTION: PKIX_RevocationChecker_CreateAndAddMethod
    208 */
    209 PKIX_Error *
    210 PKIX_RevocationChecker_CreateAndAddMethod(
    211    PKIX_RevocationChecker *revChecker,
    212    PKIX_ProcessingParams *params,
    213    PKIX_RevocationMethodType methodType,
    214    PKIX_UInt32 flags,
    215    PKIX_UInt32 priority,
    216    PKIX_PL_VerifyCallback verificationFn,
    217    PKIX_Boolean isLeafMethod,
    218    void *plContext)
    219 {
    220    PKIX_List **methodList = NULL;
    221    PKIX_List  *unsortedList = NULL;
    222    PKIX_List  *certStores = NULL;
    223    pkix_RevocationMethod *method = NULL;
    224    pkix_LocalRevocationCheckFn *localRevChecker = NULL;
    225    pkix_ExternalRevocationCheckFn *externRevChecker = NULL;
    226    PKIX_UInt32 miFlags;
    227    
    228    PKIX_ENTER(REVOCATIONCHECKER, "PKIX_RevocationChecker_CreateAndAddMethod");
    229    PKIX_NULLCHECK_ONE(revChecker);
    230 
    231    /* If the caller has said "Either one is sufficient, then don't let the 
    232     * absence of any one method's info lead to an overall failure.
    233     */
    234    miFlags = isLeafMethod ? revChecker->leafMethodListFlags 
    235                    : revChecker->chainMethodListFlags;
    236    if (miFlags & PKIX_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE)
    237 flags &= ~PKIX_REV_M_FAIL_ON_MISSING_FRESH_INFO;
    238 
    239    switch (methodType) {
    240    case PKIX_RevocationMethod_CRL:
    241        localRevChecker = pkix_CrlChecker_CheckLocal;
    242        externRevChecker = pkix_CrlChecker_CheckExternal;
    243        PKIX_CHECK(
    244            PKIX_ProcessingParams_GetCertStores(params, &certStores,
    245                                                plContext),
    246            PKIX_PROCESSINGPARAMSGETCERTSTORESFAILED);
    247        PKIX_CHECK(
    248            pkix_CrlChecker_Create(methodType, flags, priority,
    249                                   localRevChecker, externRevChecker,
    250                                   certStores, verificationFn,
    251                                   &method,
    252                                   plContext),
    253            PKIX_COULDNOTCREATECRLCHECKEROBJECT);
    254        break;
    255    case PKIX_RevocationMethod_OCSP:
    256        localRevChecker = pkix_OcspChecker_CheckLocal;
    257        externRevChecker = pkix_OcspChecker_CheckExternal;
    258        PKIX_CHECK(
    259            pkix_OcspChecker_Create(methodType, flags, priority,
    260                                    localRevChecker, externRevChecker,
    261                                    verificationFn,
    262                                    &method,
    263                                    plContext),
    264            PKIX_COULDNOTCREATEOCSPCHECKEROBJECT);
    265        break;
    266    default:
    267        PKIX_ERROR(PKIX_INVALIDREVOCATIONMETHOD);
    268    }
    269 
    270    if (isLeafMethod) {
    271        methodList = &revChecker->leafMethodList;
    272    } else {
    273        methodList = &revChecker->chainMethodList;
    274    }
    275    
    276    if (*methodList == NULL) {
    277        PKIX_CHECK(
    278            PKIX_List_Create(methodList, plContext),
    279            PKIX_LISTCREATEFAILED);
    280    }
    281    unsortedList = *methodList;
    282    PKIX_CHECK(
    283        PKIX_List_AppendItem(unsortedList, (PKIX_PL_Object*)method, plContext),
    284        PKIX_LISTAPPENDITEMFAILED);
    285    PKIX_CHECK(
    286        pkix_List_BubbleSort(unsortedList, 
    287                             pkix_RevocationChecker_SortComparator,
    288                             methodList, plContext),
    289        PKIX_LISTBUBBLESORTFAILED);
    290 
    291 cleanup:
    292    PKIX_DECREF(method);
    293    PKIX_DECREF(unsortedList);
    294    PKIX_DECREF(certStores);
    295    
    296    PKIX_RETURN(REVOCATIONCHECKER);
    297 }
    298 
    299 /*
    300 * FUNCTION: PKIX_RevocationChecker_Check
    301 */
    302 PKIX_Error *
    303 PKIX_RevocationChecker_Check(
    304    PKIX_PL_Cert *cert,
    305    PKIX_PL_Cert *issuer,
    306    PKIX_RevocationChecker *revChecker,
    307    PKIX_ProcessingParams *procParams,
    308    PKIX_Boolean chainVerificationState,
    309    PKIX_Boolean testingLeafCert,
    310    PKIX_RevocationStatus *pRevStatus,
    311    PKIX_UInt32 *pReasonCode,
    312    void **pNbioContext,
    313    void *plContext)
    314 {
    315    PKIX_RevocationStatus overallStatus = PKIX_RevStatus_NoInfo;
    316    PKIX_RevocationStatus methodStatus[PKIX_RevocationMethod_MAX];
    317    PKIX_Boolean onlyUseRemoteMethods = PKIX_FALSE;
    318    PKIX_UInt32 revFlags = 0;
    319    PKIX_List *revList = NULL;
    320    PKIX_PL_Date *date = NULL;
    321    pkix_RevocationMethod *method = NULL;
    322    void *nbioContext;
    323    int tries;
    324    
    325    PKIX_ENTER(REVOCATIONCHECKER, "PKIX_RevocationChecker_Check");
    326    PKIX_NULLCHECK_TWO(revChecker, procParams);
    327 
    328    nbioContext = *pNbioContext;
    329    *pNbioContext = NULL;
    330    
    331    if (testingLeafCert) {
    332        revList = revChecker->leafMethodList;
    333        revFlags = revChecker->leafMethodListFlags;        
    334    } else {
    335        revList = revChecker->chainMethodList;
    336        revFlags = revChecker->chainMethodListFlags;
    337    }
    338    if (!revList) {
    339        /* Return NoInfo status */
    340        goto cleanup;
    341    }
    342 
    343    PORT_Memset(methodStatus, PKIX_RevStatus_NoInfo,
    344                sizeof(PKIX_RevocationStatus) * PKIX_RevocationMethod_MAX);
    345 
    346    date = procParams->date;
    347 
    348    /* Need to have two loops if we testing all local info first:
    349     *    first we are going to test all local(cached) info
    350     *    second, all remote info(fetching) */
    351    for (tries = 0;tries < 2;tries++) {
    352        unsigned int methodNum = 0;
    353        for (;methodNum < revList->length;methodNum++) {
    354            PKIX_UInt32 methodFlags = 0;
    355 
    356            PKIX_DECREF(method);
    357            PKIX_CHECK(
    358                PKIX_List_GetItem(revList, methodNum,
    359                                  (PKIX_PL_Object**)&method, plContext),
    360                PKIX_LISTGETITEMFAILED);
    361            methodFlags = method->flags;
    362            if (!(methodFlags & PKIX_REV_M_TEST_USING_THIS_METHOD)) {
    363                /* Will not check with this method. Skipping... */
    364                continue;
    365            }
    366            if (!onlyUseRemoteMethods &&
    367                methodStatus[methodNum] == PKIX_RevStatus_NoInfo) {
    368                PKIX_RevocationStatus revStatus = PKIX_RevStatus_NoInfo;
    369                PKIX_CHECK_NO_GOTO(
    370                    (*method->localRevChecker)(cert, issuer, date,
    371                                               method, procParams,
    372                                               methodFlags, 
    373                                               chainVerificationState,
    374                                               &revStatus,
    375                                               (CERTCRLEntryReasonCode *)pReasonCode,
    376                                               plContext),
    377                    PKIX_REVCHECKERCHECKFAILED);
    378                methodStatus[methodNum] = revStatus;
    379                if (revStatus == PKIX_RevStatus_Revoked) {
    380                    /* if error was generated use it as final error. */
    381                    overallStatus = PKIX_RevStatus_Revoked;
    382                    goto cleanup;
    383                }
    384                if (pkixErrorResult) {
    385                    /* Disregard errors. Only returned revStatus matters. */
    386                    PKIX_PL_Object_DecRef((PKIX_PL_Object*)pkixErrorResult,
    387                                          plContext);
    388                    pkixErrorResult = NULL;
    389                }
    390            }
    391            if ((!(revFlags & PKIX_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST) ||
    392                 onlyUseRemoteMethods) &&
    393                chainVerificationState &&
    394                methodStatus[methodNum] == PKIX_RevStatus_NoInfo) {
    395                if (!(methodFlags & PKIX_REV_M_FORBID_NETWORK_FETCHING)) {
    396                    PKIX_RevocationStatus revStatus = PKIX_RevStatus_NoInfo;
    397                    PKIX_CHECK_NO_GOTO(
    398                        (*method->externalRevChecker)(cert, issuer, date,
    399                                                      method,
    400                                                      procParams, methodFlags,
    401                                                      &revStatus,
    402                                                      (CERTCRLEntryReasonCode *)pReasonCode,
    403                                                      &nbioContext, plContext),
    404                        PKIX_REVCHECKERCHECKFAILED);
    405                    methodStatus[methodNum] = revStatus;
    406                    if (revStatus == PKIX_RevStatus_Revoked) {
    407                        /* if error was generated use it as final error. */
    408                        overallStatus = PKIX_RevStatus_Revoked;
    409                        goto cleanup;
    410                    }
    411                    if (pkixErrorResult) {
    412                        /* Disregard errors. Only returned revStatus matters. */
    413                        PKIX_PL_Object_DecRef((PKIX_PL_Object*)pkixErrorResult,
    414                                              plContext);
    415                        pkixErrorResult = NULL;
    416                    }
    417                } else if (methodFlags &
    418                           PKIX_REV_M_FAIL_ON_MISSING_FRESH_INFO) {
    419                    /* Info is not in the local cache. Network fetching is not
    420                     * allowed. If need to fail on missing fresh info for the
    421                     * the method, then we should fail right here.*/
    422                    overallStatus = PKIX_RevStatus_Revoked;
    423                    goto cleanup;
    424                }
    425            }
    426            /* If success and we should not check the next method, then
    427             * return a success. */
    428            if (methodStatus[methodNum] == PKIX_RevStatus_Success &&
    429                !(methodFlags & PKIX_REV_M_CONTINUE_TESTING_ON_FRESH_INFO)) {
    430                overallStatus = PKIX_RevStatus_Success;
    431                goto cleanup;
    432            }
    433        } /* inner loop */
    434        if (!onlyUseRemoteMethods &&
    435            revFlags & PKIX_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST &&
    436            chainVerificationState) {
    437            onlyUseRemoteMethods = PKIX_TRUE;
    438            continue;
    439        }
    440        break;
    441    } /* outer loop */
    442    
    443    if (overallStatus == PKIX_RevStatus_NoInfo &&
    444        chainVerificationState) {
    445        /* The following check makes sence only for chain
    446         * validation step, sinse we do not fetch info while
    447         * in the process of finding trusted anchor. 
    448         * For chain building step it is enough to know, that
    449         * the cert was not directly revoked by any of the
    450         * methods. */
    451 
    452        /* Still have no info. But one of the method could
    453         * have returned success status(possible if CONTINUE
    454         * TESTING ON FRESH INFO flag was used).
    455         * If any of the methods have returned Success status,
    456         * the overallStatus should be success. */
    457        int methodNum = 0;
    458        for (;methodNum < PKIX_RevocationMethod_MAX;methodNum++) {
    459            if (methodStatus[methodNum] == PKIX_RevStatus_Success) {
    460                overallStatus = PKIX_RevStatus_Success;
    461                goto cleanup;
    462            }
    463        }
    464        if (revFlags & PKIX_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE) {
    465            overallStatus = PKIX_RevStatus_Revoked;
    466        }
    467    }
    468 
    469 cleanup:
    470    *pRevStatus = overallStatus;
    471    PKIX_DECREF(method);
    472 
    473    PKIX_RETURN(REVOCATIONCHECKER);
    474 }