8108b76871b7840e2318619068ab05166acc8a29
[platform/upstream/nss.git] / nss / lib / libpkix / pkix / checker / pkix_ocspchecker.c
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         PKIX_UInt32 *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. If non-blocking I/O is used the client may return
204  * with WOULDBLOCK, in which case the OCSPChecker returns the WOULDBLOCK
205  * condition to its caller in turn. On a subsequent call the I/O is resumed.
206  * When a response is received it is decoded and the results provided to the
207  * caller.
208  *
209  */
210 PKIX_Error *
211 pkix_OcspChecker_CheckExternal(
212         PKIX_PL_Cert *cert,
213         PKIX_PL_Cert *issuer,
214         PKIX_PL_Date *date,
215         pkix_RevocationMethod *checkerObject,
216         PKIX_ProcessingParams *procParams,
217         PKIX_UInt32 methodFlags,
218         PKIX_RevocationStatus *pRevStatus,
219         PKIX_UInt32 *pReasonCode,
220         void **pNBIOContext,
221         void *plContext)
222 {
223         SECErrorCodes resultCode = SEC_ERROR_REVOKED_CERTIFICATE_OCSP;
224         PKIX_Boolean uriFound = PKIX_FALSE;
225         PKIX_Boolean passed = PKIX_TRUE;
226         pkix_OcspChecker *checker = NULL;
227         PKIX_PL_OcspCertID *cid = NULL;
228         PKIX_PL_OcspRequest *request = NULL;
229         PKIX_PL_OcspResponse *response = NULL;
230         PKIX_PL_Date *validity = NULL;
231         PKIX_RevocationStatus revStatus = PKIX_RevStatus_NoInfo;
232         void *nbioContext = NULL;
233
234         PKIX_ENTER(OCSPCHECKER, "pkix_OcspChecker_CheckExternal");
235
236         PKIX_CHECK(
237             pkix_CheckType((PKIX_PL_Object*)checkerObject,
238                            PKIX_OCSPCHECKER_TYPE, plContext),
239                 PKIX_OBJECTNOTOCSPCHECKER);
240
241         checker = (pkix_OcspChecker *)checkerObject;
242
243         PKIX_CHECK(
244             PKIX_PL_OcspCertID_Create(cert, NULL, &cid,
245                                       plContext),
246             PKIX_OCSPCERTIDCREATEFAILED);
247         
248         /* create request */
249         PKIX_CHECK(
250             pkix_pl_OcspRequest_Create(cert, cid, validity, NULL, 
251                                        methodFlags, &uriFound, &request,
252                                        plContext),
253             PKIX_OCSPREQUESTCREATEFAILED);
254         
255         if (uriFound == PKIX_FALSE) {
256             /* no caching for certs lacking URI */
257             resultCode = 0;
258             goto cleanup;
259         }
260
261         /* send request and create a response object */
262         PKIX_CHECK(
263             pkix_pl_OcspResponse_Create(request, NULL,
264                                         checker->certVerifyFcn,
265                                         &nbioContext,
266                                         &response,
267                                         plContext),
268             PKIX_OCSPRESPONSECREATEFAILED);
269         if (nbioContext != 0) {
270             *pNBIOContext = nbioContext;
271             goto cleanup;
272         }
273         
274         PKIX_CHECK(
275             pkix_pl_OcspResponse_Decode(response, &passed,
276                                         &resultCode, plContext),
277             PKIX_OCSPRESPONSEDECODEFAILED);
278         if (passed == PKIX_FALSE) {
279             goto cleanup;
280         }
281         
282         PKIX_CHECK(
283             pkix_pl_OcspResponse_GetStatus(response, &passed,
284                                            &resultCode, plContext),
285             PKIX_OCSPRESPONSEGETSTATUSRETURNEDANERROR);
286         if (passed == PKIX_FALSE) {
287             goto cleanup;
288         }
289
290         PKIX_CHECK(
291             pkix_pl_OcspResponse_VerifySignature(response, cert,
292                                                  procParams, &passed, 
293                                                  &nbioContext, plContext),
294             PKIX_OCSPRESPONSEVERIFYSIGNATUREFAILED);
295         if (nbioContext != 0) {
296                 *pNBIOContext = nbioContext;
297                 goto cleanup;
298         }
299         if (passed == PKIX_FALSE) {
300                 goto cleanup;
301         }
302
303         PKIX_CHECK(
304             pkix_pl_OcspResponse_GetStatusForCert(cid, response, date,
305                                                   &passed, &resultCode,
306                                                   plContext),
307             PKIX_OCSPRESPONSEGETSTATUSFORCERTFAILED);
308         if (passed == PKIX_FALSE) {
309             revStatus = pkix_OcspChecker_MapResultCodeToRevStatus(resultCode);
310         } else {
311             revStatus = PKIX_RevStatus_Success;
312         }
313
314 cleanup:
315         if (revStatus == PKIX_RevStatus_NoInfo && (uriFound || 
316             methodFlags & PKIX_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE) &&
317             methodFlags & PKIX_REV_M_FAIL_ON_MISSING_FRESH_INFO) {
318             revStatus = PKIX_RevStatus_Revoked;
319         }
320         *pRevStatus = revStatus;
321
322         /* ocsp carries only tree statuses: good, bad, and unknown.
323          * revStatus is used to pass them. reasonCode is always set
324          * to be unknown. */
325         *pReasonCode = crlEntryReasonUnspecified;
326
327         if (!passed && cid && cid->certID) {
328                 /* We still own the certID object, which means that 
329                  * it did not get consumed to create a cache entry.
330                  * Let's make sure there is one.
331                  */
332                 PKIX_Error *err;
333                 err = PKIX_PL_OcspCertID_RememberOCSPProcessingFailure(
334                         cid, plContext);
335                 if (err) {
336                         PKIX_PL_Object_DecRef((PKIX_PL_Object*)err, plContext);
337                 }
338         }
339         PKIX_DECREF(cid);
340         PKIX_DECREF(request);
341         PKIX_DECREF(response);
342
343         PKIX_RETURN(OCSPCHECKER);
344 }
345
346