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 *
pkix_OcspChecker_Destroy(PKIX_PL_Object * object,void * plContext)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 *
pkix_OcspChecker_RegisterSelf(void * plContext)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 *
pkix_OcspChecker_Create(PKIX_RevocationMethodType methodType,PKIX_UInt32 flags,PKIX_UInt32 priority,pkix_LocalRevocationCheckFn localRevChecker,pkix_ExternalRevocationCheckFn externalRevChecker,PKIX_PL_VerifyCallback verifyFn,pkix_RevocationMethod ** pChecker,void * plContext)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
pkix_OcspChecker_MapResultCodeToRevStatus(SECErrorCodes resultCode)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 *
pkix_OcspChecker_CheckLocal(PKIX_PL_Cert * cert,PKIX_PL_Cert * issuer,PKIX_PL_Date * date,pkix_RevocationMethod * checkerObject,PKIX_ProcessingParams * procParams,PKIX_UInt32 methodFlags,PKIX_Boolean chainVerificationState,PKIX_RevocationStatus * pRevStatus,CERTCRLEntryReasonCode * pReasonCode,void * plContext)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 *
pkix_OcspChecker_CheckExternal(PKIX_PL_Cert * cert,PKIX_PL_Cert * issuer,PKIX_PL_Date * date,pkix_RevocationMethod * checkerObject,PKIX_ProcessingParams * procParams,PKIX_UInt32 methodFlags,PKIX_RevocationStatus * pRevStatus,CERTCRLEntryReasonCode * pReasonCode,void ** pNBIOContext,void * plContext)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 }
418 
419 
420