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 *
pkix_RevocationChecker_Destroy(PKIX_PL_Object * object,void * plContext)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 *
pkix_RevocationChecker_Duplicate(PKIX_PL_Object * object,PKIX_PL_Object ** pNewObject,void * plContext)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 *
pkix_RevocationChecker_RegisterSelf(void * plContext)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 *
pkix_RevocationChecker_SortComparator(PKIX_PL_Object * obj1,PKIX_PL_Object * obj2,PKIX_Int32 * pResult,void * plContext)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 *
PKIX_RevocationChecker_Create(PKIX_UInt32 leafMethodListFlags,PKIX_UInt32 chainMethodListFlags,PKIX_RevocationChecker ** pChecker,void * plContext)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 *
PKIX_RevocationChecker_CreateAndAddMethod(PKIX_RevocationChecker * revChecker,PKIX_ProcessingParams * params,PKIX_RevocationMethodType methodType,PKIX_UInt32 flags,PKIX_UInt32 priority,PKIX_PL_VerifyCallback verificationFn,PKIX_Boolean isLeafMethod,void * plContext)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 *
PKIX_RevocationChecker_Check(PKIX_PL_Cert * cert,PKIX_PL_Cert * issuer,PKIX_RevocationChecker * revChecker,PKIX_ProcessingParams * procParams,PKIX_Boolean chainVerificationState,PKIX_Boolean testingLeafCert,PKIX_RevocationStatus * pRevStatus,PKIX_UInt32 * pReasonCode,void ** pNbioContext,void * plContext)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 }
475 
476