/src/mozilla-central/security/nss/lib/certhigh/ocsp.c
Line | Count | Source (jump to first uncovered line) |
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 | | /* |
6 | | * Implementation of OCSP services, for both client and server. |
7 | | * (XXX, really, mostly just for client right now, but intended to do both.) |
8 | | */ |
9 | | |
10 | | #include "prerror.h" |
11 | | #include "prprf.h" |
12 | | #include "plarena.h" |
13 | | #include "prnetdb.h" |
14 | | |
15 | | #include "seccomon.h" |
16 | | #include "secitem.h" |
17 | | #include "secoidt.h" |
18 | | #include "secasn1.h" |
19 | | #include "secder.h" |
20 | | #include "cert.h" |
21 | | #include "certi.h" |
22 | | #include "xconst.h" |
23 | | #include "secerr.h" |
24 | | #include "secoid.h" |
25 | | #include "hasht.h" |
26 | | #include "sechash.h" |
27 | | #include "secasn1.h" |
28 | | #include "plbase64.h" |
29 | | #include "keyhi.h" |
30 | | #include "cryptohi.h" |
31 | | #include "ocsp.h" |
32 | | #include "ocspti.h" |
33 | | #include "ocspi.h" |
34 | | #include "genname.h" |
35 | | #include "certxutl.h" |
36 | | #include "pk11func.h" /* for PK11_HashBuf */ |
37 | | #include <stdarg.h> |
38 | | #include <plhash.h> |
39 | | |
40 | 0 | #define DEFAULT_OCSP_CACHE_SIZE 1000 |
41 | 0 | #define DEFAULT_MINIMUM_SECONDS_TO_NEXT_OCSP_FETCH_ATTEMPT 1 * 60 * 60L |
42 | 0 | #define DEFAULT_MAXIMUM_SECONDS_TO_NEXT_OCSP_FETCH_ATTEMPT 24 * 60 * 60L |
43 | | #define DEFAULT_OSCP_TIMEOUT_SECONDS 60 |
44 | 0 | #define MICROSECONDS_PER_SECOND 1000000L |
45 | | |
46 | | typedef struct OCSPCacheItemStr OCSPCacheItem; |
47 | | typedef struct OCSPCacheDataStr OCSPCacheData; |
48 | | |
49 | | struct OCSPCacheItemStr { |
50 | | /* LRU linking */ |
51 | | OCSPCacheItem *moreRecent; |
52 | | OCSPCacheItem *lessRecent; |
53 | | |
54 | | /* key */ |
55 | | CERTOCSPCertID *certID; |
56 | | /* CertID's arena also used to allocate "this" cache item */ |
57 | | |
58 | | /* cache control information */ |
59 | | PRTime nextFetchAttemptTime; |
60 | | |
61 | | /* Cached contents. Use a separate arena, because lifetime is different */ |
62 | | PLArenaPool *certStatusArena; /* NULL means: no cert status cached */ |
63 | | ocspCertStatus certStatus; |
64 | | |
65 | | /* This may contain an error code when no OCSP response is available. */ |
66 | | SECErrorCodes missingResponseError; |
67 | | |
68 | | PRPackedBool haveThisUpdate; |
69 | | PRPackedBool haveNextUpdate; |
70 | | PRTime thisUpdate; |
71 | | PRTime nextUpdate; |
72 | | }; |
73 | | |
74 | | struct OCSPCacheDataStr { |
75 | | PLHashTable *entries; |
76 | | PRUint32 numberOfEntries; |
77 | | OCSPCacheItem *MRUitem; /* most recently used cache item */ |
78 | | OCSPCacheItem *LRUitem; /* least recently used cache item */ |
79 | | }; |
80 | | |
81 | | static struct OCSPGlobalStruct { |
82 | | PRMonitor *monitor; |
83 | | const SEC_HttpClientFcn *defaultHttpClientFcn; |
84 | | PRInt32 maxCacheEntries; |
85 | | PRUint32 minimumSecondsToNextFetchAttempt; |
86 | | PRUint32 maximumSecondsToNextFetchAttempt; |
87 | | PRUint32 timeoutSeconds; |
88 | | OCSPCacheData cache; |
89 | | SEC_OcspFailureMode ocspFailureMode; |
90 | | CERT_StringFromCertFcn alternateOCSPAIAFcn; |
91 | | PRBool forcePost; |
92 | | } OCSP_Global = { NULL, |
93 | | NULL, |
94 | | DEFAULT_OCSP_CACHE_SIZE, |
95 | | DEFAULT_MINIMUM_SECONDS_TO_NEXT_OCSP_FETCH_ATTEMPT, |
96 | | DEFAULT_MAXIMUM_SECONDS_TO_NEXT_OCSP_FETCH_ATTEMPT, |
97 | | DEFAULT_OSCP_TIMEOUT_SECONDS, |
98 | | { NULL, 0, NULL, NULL }, |
99 | | ocspMode_FailureIsVerificationFailure, |
100 | | NULL, |
101 | | PR_FALSE }; |
102 | | |
103 | | /* Forward declarations */ |
104 | | static SECItem * |
105 | | ocsp_GetEncodedOCSPResponseFromRequest(PLArenaPool *arena, |
106 | | CERTOCSPRequest *request, |
107 | | const char *location, |
108 | | const char *method, |
109 | | PRTime time, |
110 | | PRBool addServiceLocator, |
111 | | void *pwArg, |
112 | | CERTOCSPRequest **pRequest); |
113 | | static SECStatus |
114 | | ocsp_GetOCSPStatusFromNetwork(CERTCertDBHandle *handle, |
115 | | CERTOCSPCertID *certID, |
116 | | CERTCertificate *cert, |
117 | | PRTime time, |
118 | | void *pwArg, |
119 | | PRBool *certIDWasConsumed, |
120 | | SECStatus *rv_ocsp); |
121 | | |
122 | | static SECStatus |
123 | | ocsp_GetDecodedVerifiedSingleResponseForID(CERTCertDBHandle *handle, |
124 | | CERTOCSPCertID *certID, |
125 | | CERTCertificate *cert, |
126 | | PRTime time, |
127 | | void *pwArg, |
128 | | const SECItem *encodedResponse, |
129 | | CERTOCSPResponse **pDecodedResponse, |
130 | | CERTOCSPSingleResponse **pSingle); |
131 | | |
132 | | static SECStatus |
133 | | ocsp_CertRevokedAfter(ocspRevokedInfo *revokedInfo, PRTime time); |
134 | | |
135 | | static CERTOCSPCertID * |
136 | | cert_DupOCSPCertID(const CERTOCSPCertID *src); |
137 | | |
138 | | #ifndef DEBUG |
139 | | #define OCSP_TRACE(msg) |
140 | | #define OCSP_TRACE_TIME(msg, time) |
141 | | #define OCSP_TRACE_CERT(cert) |
142 | | #define OCSP_TRACE_CERTID(certid) |
143 | | #else |
144 | | #define OCSP_TRACE(msg) ocsp_Trace msg |
145 | | #define OCSP_TRACE_TIME(msg, time) ocsp_dumpStringWithTime(msg, time) |
146 | | #define OCSP_TRACE_CERT(cert) dumpCertificate(cert) |
147 | | #define OCSP_TRACE_CERTID(certid) dumpCertID(certid) |
148 | | |
149 | | #if defined(XP_UNIX) || defined(XP_WIN32) || defined(XP_BEOS) || \ |
150 | | defined(XP_MACOSX) |
151 | | #define NSS_HAVE_GETENV 1 |
152 | | #endif |
153 | | |
154 | | static PRBool |
155 | | wantOcspTrace(void) |
156 | | { |
157 | | static PRBool firstTime = PR_TRUE; |
158 | | static PRBool wantTrace = PR_FALSE; |
159 | | |
160 | | #ifdef NSS_HAVE_GETENV |
161 | | if (firstTime) { |
162 | | char *ev = PR_GetEnvSecure("NSS_TRACE_OCSP"); |
163 | | if (ev && ev[0]) { |
164 | | wantTrace = PR_TRUE; |
165 | | } |
166 | | firstTime = PR_FALSE; |
167 | | } |
168 | | #endif |
169 | | return wantTrace; |
170 | | } |
171 | | |
172 | | static void |
173 | | ocsp_Trace(const char *format, ...) |
174 | | { |
175 | | char buf[2000]; |
176 | | va_list args; |
177 | | |
178 | | if (!wantOcspTrace()) |
179 | | return; |
180 | | va_start(args, format); |
181 | | PR_vsnprintf(buf, sizeof(buf), format, args); |
182 | | va_end(args); |
183 | | PR_LogPrint("%s", buf); |
184 | | } |
185 | | |
186 | | static void |
187 | | ocsp_dumpStringWithTime(const char *str, PRTime time) |
188 | | { |
189 | | PRExplodedTime timePrintable; |
190 | | char timestr[256]; |
191 | | |
192 | | if (!wantOcspTrace()) |
193 | | return; |
194 | | PR_ExplodeTime(time, PR_GMTParameters, &timePrintable); |
195 | | if (PR_FormatTime(timestr, 256, "%a %b %d %H:%M:%S %Y", &timePrintable)) { |
196 | | ocsp_Trace("OCSP %s %s\n", str, timestr); |
197 | | } |
198 | | } |
199 | | |
200 | | static void |
201 | | printHexString(const char *prefix, SECItem *hexval) |
202 | | { |
203 | | unsigned int i; |
204 | | char *hexbuf = NULL; |
205 | | |
206 | | for (i = 0; i < hexval->len; i++) { |
207 | | if (i != hexval->len - 1) { |
208 | | hexbuf = PR_sprintf_append(hexbuf, "%02x:", hexval->data[i]); |
209 | | } else { |
210 | | hexbuf = PR_sprintf_append(hexbuf, "%02x", hexval->data[i]); |
211 | | } |
212 | | } |
213 | | if (hexbuf) { |
214 | | ocsp_Trace("%s %s\n", prefix, hexbuf); |
215 | | PR_smprintf_free(hexbuf); |
216 | | } |
217 | | } |
218 | | |
219 | | static void |
220 | | dumpCertificate(CERTCertificate *cert) |
221 | | { |
222 | | if (!wantOcspTrace()) |
223 | | return; |
224 | | |
225 | | ocsp_Trace("OCSP ----------------\n"); |
226 | | ocsp_Trace("OCSP ## SUBJECT: %s\n", cert->subjectName); |
227 | | { |
228 | | PRTime timeBefore, timeAfter; |
229 | | PRExplodedTime beforePrintable, afterPrintable; |
230 | | char beforestr[256], afterstr[256]; |
231 | | PRStatus rv1, rv2; |
232 | | DER_DecodeTimeChoice(&timeBefore, &cert->validity.notBefore); |
233 | | DER_DecodeTimeChoice(&timeAfter, &cert->validity.notAfter); |
234 | | PR_ExplodeTime(timeBefore, PR_GMTParameters, &beforePrintable); |
235 | | PR_ExplodeTime(timeAfter, PR_GMTParameters, &afterPrintable); |
236 | | rv1 = PR_FormatTime(beforestr, 256, "%a %b %d %H:%M:%S %Y", |
237 | | &beforePrintable); |
238 | | rv2 = PR_FormatTime(afterstr, 256, "%a %b %d %H:%M:%S %Y", |
239 | | &afterPrintable); |
240 | | ocsp_Trace("OCSP ## VALIDITY: %s to %s\n", rv1 ? beforestr : "", |
241 | | rv2 ? afterstr : ""); |
242 | | } |
243 | | ocsp_Trace("OCSP ## ISSUER: %s\n", cert->issuerName); |
244 | | printHexString("OCSP ## SERIAL NUMBER:", &cert->serialNumber); |
245 | | } |
246 | | |
247 | | static void |
248 | | dumpCertID(CERTOCSPCertID *certID) |
249 | | { |
250 | | if (!wantOcspTrace()) |
251 | | return; |
252 | | |
253 | | printHexString("OCSP certID issuer", &certID->issuerNameHash); |
254 | | printHexString("OCSP certID serial", &certID->serialNumber); |
255 | | } |
256 | | #endif |
257 | | |
258 | | SECStatus |
259 | | SEC_RegisterDefaultHttpClient(const SEC_HttpClientFcn *fcnTable) |
260 | 0 | { |
261 | 0 | if (!OCSP_Global.monitor) { |
262 | 0 | PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
263 | 0 | return SECFailure; |
264 | 0 | } |
265 | 0 |
|
266 | 0 | PR_EnterMonitor(OCSP_Global.monitor); |
267 | 0 | OCSP_Global.defaultHttpClientFcn = fcnTable; |
268 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
269 | 0 |
|
270 | 0 | return SECSuccess; |
271 | 0 | } |
272 | | |
273 | | SECStatus |
274 | | CERT_RegisterAlternateOCSPAIAInfoCallBack( |
275 | | CERT_StringFromCertFcn newCallback, |
276 | | CERT_StringFromCertFcn *oldCallback) |
277 | 0 | { |
278 | 0 | CERT_StringFromCertFcn old; |
279 | 0 |
|
280 | 0 | if (!OCSP_Global.monitor) { |
281 | 0 | PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
282 | 0 | return SECFailure; |
283 | 0 | } |
284 | 0 |
|
285 | 0 | PR_EnterMonitor(OCSP_Global.monitor); |
286 | 0 | old = OCSP_Global.alternateOCSPAIAFcn; |
287 | 0 | OCSP_Global.alternateOCSPAIAFcn = newCallback; |
288 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
289 | 0 | if (oldCallback) |
290 | 0 | *oldCallback = old; |
291 | 0 | return SECSuccess; |
292 | 0 | } |
293 | | |
294 | | static PLHashNumber PR_CALLBACK |
295 | | ocsp_CacheKeyHashFunction(const void *key) |
296 | 0 | { |
297 | 0 | CERTOCSPCertID *cid = (CERTOCSPCertID *)key; |
298 | 0 | PLHashNumber hash = 0; |
299 | 0 | unsigned int i; |
300 | 0 | unsigned char *walk; |
301 | 0 |
|
302 | 0 | /* a very simple hash calculation for the initial coding phase */ |
303 | 0 | walk = (unsigned char *)cid->issuerNameHash.data; |
304 | 0 | for (i = 0; i < cid->issuerNameHash.len; ++i, ++walk) { |
305 | 0 | hash += *walk; |
306 | 0 | } |
307 | 0 | walk = (unsigned char *)cid->issuerKeyHash.data; |
308 | 0 | for (i = 0; i < cid->issuerKeyHash.len; ++i, ++walk) { |
309 | 0 | hash += *walk; |
310 | 0 | } |
311 | 0 | walk = (unsigned char *)cid->serialNumber.data; |
312 | 0 | for (i = 0; i < cid->serialNumber.len; ++i, ++walk) { |
313 | 0 | hash += *walk; |
314 | 0 | } |
315 | 0 | return hash; |
316 | 0 | } |
317 | | |
318 | | static PRIntn PR_CALLBACK |
319 | | ocsp_CacheKeyCompareFunction(const void *v1, const void *v2) |
320 | 0 | { |
321 | 0 | CERTOCSPCertID *cid1 = (CERTOCSPCertID *)v1; |
322 | 0 | CERTOCSPCertID *cid2 = (CERTOCSPCertID *)v2; |
323 | 0 |
|
324 | 0 | return (SECEqual == SECITEM_CompareItem(&cid1->issuerNameHash, |
325 | 0 | &cid2->issuerNameHash) && |
326 | 0 | SECEqual == SECITEM_CompareItem(&cid1->issuerKeyHash, |
327 | 0 | &cid2->issuerKeyHash) && |
328 | 0 | SECEqual == SECITEM_CompareItem(&cid1->serialNumber, |
329 | 0 | &cid2->serialNumber)); |
330 | 0 | } |
331 | | |
332 | | static SECStatus |
333 | | ocsp_CopyRevokedInfo(PLArenaPool *arena, ocspCertStatus *dest, |
334 | | ocspRevokedInfo *src) |
335 | 0 | { |
336 | 0 | SECStatus rv = SECFailure; |
337 | 0 | void *mark; |
338 | 0 |
|
339 | 0 | mark = PORT_ArenaMark(arena); |
340 | 0 |
|
341 | 0 | dest->certStatusInfo.revokedInfo = |
342 | 0 | (ocspRevokedInfo *)PORT_ArenaZAlloc(arena, sizeof(ocspRevokedInfo)); |
343 | 0 | if (!dest->certStatusInfo.revokedInfo) { |
344 | 0 | goto loser; |
345 | 0 | } |
346 | 0 | |
347 | 0 | rv = SECITEM_CopyItem(arena, |
348 | 0 | &dest->certStatusInfo.revokedInfo->revocationTime, |
349 | 0 | &src->revocationTime); |
350 | 0 | if (rv != SECSuccess) { |
351 | 0 | goto loser; |
352 | 0 | } |
353 | 0 | |
354 | 0 | if (src->revocationReason) { |
355 | 0 | dest->certStatusInfo.revokedInfo->revocationReason = |
356 | 0 | SECITEM_ArenaDupItem(arena, src->revocationReason); |
357 | 0 | if (!dest->certStatusInfo.revokedInfo->revocationReason) { |
358 | 0 | goto loser; |
359 | 0 | } |
360 | 0 | } else { |
361 | 0 | dest->certStatusInfo.revokedInfo->revocationReason = NULL; |
362 | 0 | } |
363 | 0 |
|
364 | 0 | PORT_ArenaUnmark(arena, mark); |
365 | 0 | return SECSuccess; |
366 | 0 | |
367 | 0 | loser: |
368 | 0 | PORT_ArenaRelease(arena, mark); |
369 | 0 | return SECFailure; |
370 | 0 | } |
371 | | |
372 | | static SECStatus |
373 | | ocsp_CopyCertStatus(PLArenaPool *arena, ocspCertStatus *dest, |
374 | | ocspCertStatus *src) |
375 | 0 | { |
376 | 0 | SECStatus rv = SECFailure; |
377 | 0 | dest->certStatusType = src->certStatusType; |
378 | 0 |
|
379 | 0 | switch (src->certStatusType) { |
380 | 0 | case ocspCertStatus_good: |
381 | 0 | dest->certStatusInfo.goodInfo = |
382 | 0 | SECITEM_ArenaDupItem(arena, src->certStatusInfo.goodInfo); |
383 | 0 | if (dest->certStatusInfo.goodInfo != NULL) { |
384 | 0 | rv = SECSuccess; |
385 | 0 | } |
386 | 0 | break; |
387 | 0 | case ocspCertStatus_revoked: |
388 | 0 | rv = ocsp_CopyRevokedInfo(arena, dest, |
389 | 0 | src->certStatusInfo.revokedInfo); |
390 | 0 | break; |
391 | 0 | case ocspCertStatus_unknown: |
392 | 0 | dest->certStatusInfo.unknownInfo = |
393 | 0 | SECITEM_ArenaDupItem(arena, src->certStatusInfo.unknownInfo); |
394 | 0 | if (dest->certStatusInfo.unknownInfo != NULL) { |
395 | 0 | rv = SECSuccess; |
396 | 0 | } |
397 | 0 | break; |
398 | 0 | case ocspCertStatus_other: |
399 | 0 | default: |
400 | 0 | PORT_Assert(src->certStatusType == ocspCertStatus_other); |
401 | 0 | dest->certStatusInfo.otherInfo = |
402 | 0 | SECITEM_ArenaDupItem(arena, src->certStatusInfo.otherInfo); |
403 | 0 | if (dest->certStatusInfo.otherInfo != NULL) { |
404 | 0 | rv = SECSuccess; |
405 | 0 | } |
406 | 0 | break; |
407 | 0 | } |
408 | 0 | return rv; |
409 | 0 | } |
410 | | |
411 | | static void |
412 | | ocsp_AddCacheItemToLinkedList(OCSPCacheData *cache, OCSPCacheItem *new_most_recent) |
413 | 0 | { |
414 | 0 | PR_EnterMonitor(OCSP_Global.monitor); |
415 | 0 |
|
416 | 0 | if (!cache->LRUitem) { |
417 | 0 | cache->LRUitem = new_most_recent; |
418 | 0 | } |
419 | 0 | new_most_recent->lessRecent = cache->MRUitem; |
420 | 0 | new_most_recent->moreRecent = NULL; |
421 | 0 |
|
422 | 0 | if (cache->MRUitem) { |
423 | 0 | cache->MRUitem->moreRecent = new_most_recent; |
424 | 0 | } |
425 | 0 | cache->MRUitem = new_most_recent; |
426 | 0 |
|
427 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
428 | 0 | } |
429 | | |
430 | | static void |
431 | | ocsp_RemoveCacheItemFromLinkedList(OCSPCacheData *cache, OCSPCacheItem *item) |
432 | 0 | { |
433 | 0 | PR_EnterMonitor(OCSP_Global.monitor); |
434 | 0 |
|
435 | 0 | if (!item->lessRecent && !item->moreRecent) { |
436 | 0 | /* |
437 | 0 | * Fail gracefully on attempts to remove an item from the list, |
438 | 0 | * which is currently not part of the list. |
439 | 0 | * But check for the edge case it is the single entry in the list. |
440 | 0 | */ |
441 | 0 | if (item == cache->LRUitem && |
442 | 0 | item == cache->MRUitem) { |
443 | 0 | /* remove the single entry */ |
444 | 0 | PORT_Assert(cache->numberOfEntries == 1); |
445 | 0 | PORT_Assert(item->moreRecent == NULL); |
446 | 0 | cache->MRUitem = NULL; |
447 | 0 | cache->LRUitem = NULL; |
448 | 0 | } |
449 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
450 | 0 | return; |
451 | 0 | } |
452 | 0 |
|
453 | 0 | PORT_Assert(cache->numberOfEntries > 1); |
454 | 0 |
|
455 | 0 | if (item == cache->LRUitem) { |
456 | 0 | PORT_Assert(item != cache->MRUitem); |
457 | 0 | PORT_Assert(item->lessRecent == NULL); |
458 | 0 | PORT_Assert(item->moreRecent != NULL); |
459 | 0 | PORT_Assert(item->moreRecent->lessRecent == item); |
460 | 0 | cache->LRUitem = item->moreRecent; |
461 | 0 | cache->LRUitem->lessRecent = NULL; |
462 | 0 | } else if (item == cache->MRUitem) { |
463 | 0 | PORT_Assert(item->moreRecent == NULL); |
464 | 0 | PORT_Assert(item->lessRecent != NULL); |
465 | 0 | PORT_Assert(item->lessRecent->moreRecent == item); |
466 | 0 | cache->MRUitem = item->lessRecent; |
467 | 0 | cache->MRUitem->moreRecent = NULL; |
468 | 0 | } else { |
469 | 0 | /* remove an entry in the middle of the list */ |
470 | 0 | PORT_Assert(item->moreRecent != NULL); |
471 | 0 | PORT_Assert(item->lessRecent != NULL); |
472 | 0 | PORT_Assert(item->lessRecent->moreRecent == item); |
473 | 0 | PORT_Assert(item->moreRecent->lessRecent == item); |
474 | 0 | item->moreRecent->lessRecent = item->lessRecent; |
475 | 0 | item->lessRecent->moreRecent = item->moreRecent; |
476 | 0 | } |
477 | 0 |
|
478 | 0 | item->lessRecent = NULL; |
479 | 0 | item->moreRecent = NULL; |
480 | 0 |
|
481 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
482 | 0 | } |
483 | | |
484 | | static void |
485 | | ocsp_MakeCacheEntryMostRecent(OCSPCacheData *cache, OCSPCacheItem *new_most_recent) |
486 | 0 | { |
487 | 0 | OCSP_TRACE(("OCSP ocsp_MakeCacheEntryMostRecent THREADID %p\n", |
488 | 0 | PR_GetCurrentThread())); |
489 | 0 | PR_EnterMonitor(OCSP_Global.monitor); |
490 | 0 | if (cache->MRUitem == new_most_recent) { |
491 | 0 | OCSP_TRACE(("OCSP ocsp_MakeCacheEntryMostRecent ALREADY MOST\n")); |
492 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
493 | 0 | return; |
494 | 0 | } |
495 | 0 | OCSP_TRACE(("OCSP ocsp_MakeCacheEntryMostRecent NEW entry\n")); |
496 | 0 | ocsp_RemoveCacheItemFromLinkedList(cache, new_most_recent); |
497 | 0 | ocsp_AddCacheItemToLinkedList(cache, new_most_recent); |
498 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
499 | 0 | } |
500 | | |
501 | | static PRBool |
502 | | ocsp_IsCacheDisabled(void) |
503 | 0 | { |
504 | 0 | /* |
505 | 0 | * maxCacheEntries == 0 means unlimited cache entries |
506 | 0 | * maxCacheEntries < 0 means cache is disabled |
507 | 0 | */ |
508 | 0 | PRBool retval; |
509 | 0 | PR_EnterMonitor(OCSP_Global.monitor); |
510 | 0 | retval = (OCSP_Global.maxCacheEntries < 0); |
511 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
512 | 0 | return retval; |
513 | 0 | } |
514 | | |
515 | | static OCSPCacheItem * |
516 | | ocsp_FindCacheEntry(OCSPCacheData *cache, CERTOCSPCertID *certID) |
517 | 0 | { |
518 | 0 | OCSPCacheItem *found_ocsp_item = NULL; |
519 | 0 | OCSP_TRACE(("OCSP ocsp_FindCacheEntry\n")); |
520 | 0 | OCSP_TRACE_CERTID(certID); |
521 | 0 | PR_EnterMonitor(OCSP_Global.monitor); |
522 | 0 | if (ocsp_IsCacheDisabled()) |
523 | 0 | goto loser; |
524 | 0 | |
525 | 0 | found_ocsp_item = (OCSPCacheItem *)PL_HashTableLookup( |
526 | 0 | cache->entries, certID); |
527 | 0 | if (!found_ocsp_item) |
528 | 0 | goto loser; |
529 | 0 | |
530 | 0 | OCSP_TRACE(("OCSP ocsp_FindCacheEntry FOUND!\n")); |
531 | 0 | ocsp_MakeCacheEntryMostRecent(cache, found_ocsp_item); |
532 | 0 |
|
533 | 0 | loser: |
534 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
535 | 0 | return found_ocsp_item; |
536 | 0 | } |
537 | | |
538 | | static void |
539 | | ocsp_FreeCacheItem(OCSPCacheItem *item) |
540 | 0 | { |
541 | 0 | OCSP_TRACE(("OCSP ocsp_FreeCacheItem\n")); |
542 | 0 | if (item->certStatusArena) { |
543 | 0 | PORT_FreeArena(item->certStatusArena, PR_FALSE); |
544 | 0 | } |
545 | 0 | if (item->certID->poolp) { |
546 | 0 | /* freeing this poolp arena will also free item */ |
547 | 0 | PORT_FreeArena(item->certID->poolp, PR_FALSE); |
548 | 0 | } |
549 | 0 | } |
550 | | |
551 | | static void |
552 | | ocsp_RemoveCacheItem(OCSPCacheData *cache, OCSPCacheItem *item) |
553 | 0 | { |
554 | 0 | /* The item we're removing could be either the least recently used item, |
555 | 0 | * or it could be an item that couldn't get updated with newer status info |
556 | 0 | * because of an allocation failure, or it could get removed because we're |
557 | 0 | * cleaning up. |
558 | 0 | */ |
559 | 0 | OCSP_TRACE(("OCSP ocsp_RemoveCacheItem, THREADID %p\n", PR_GetCurrentThread())); |
560 | 0 | PR_EnterMonitor(OCSP_Global.monitor); |
561 | 0 |
|
562 | 0 | ocsp_RemoveCacheItemFromLinkedList(cache, item); |
563 | | #ifdef DEBUG |
564 | | { |
565 | | PRBool couldRemoveFromHashTable = PL_HashTableRemove(cache->entries, |
566 | | item->certID); |
567 | | PORT_Assert(couldRemoveFromHashTable); |
568 | | } |
569 | | #else |
570 | | PL_HashTableRemove(cache->entries, item->certID); |
571 | 0 | #endif |
572 | 0 | --cache->numberOfEntries; |
573 | 0 | ocsp_FreeCacheItem(item); |
574 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
575 | 0 | } |
576 | | |
577 | | static void |
578 | | ocsp_CheckCacheSize(OCSPCacheData *cache) |
579 | 0 | { |
580 | 0 | OCSP_TRACE(("OCSP ocsp_CheckCacheSize\n")); |
581 | 0 | PR_EnterMonitor(OCSP_Global.monitor); |
582 | 0 | if (OCSP_Global.maxCacheEntries > 0) { |
583 | 0 | /* Cache is not disabled. Number of cache entries is limited. |
584 | 0 | * The monitor ensures that maxCacheEntries remains positive. |
585 | 0 | */ |
586 | 0 | while (cache->numberOfEntries > |
587 | 0 | (PRUint32)OCSP_Global.maxCacheEntries) { |
588 | 0 | ocsp_RemoveCacheItem(cache, cache->LRUitem); |
589 | 0 | } |
590 | 0 | } |
591 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
592 | 0 | } |
593 | | |
594 | | SECStatus |
595 | | CERT_ClearOCSPCache(void) |
596 | 0 | { |
597 | 0 | OCSP_TRACE(("OCSP CERT_ClearOCSPCache\n")); |
598 | 0 | PR_EnterMonitor(OCSP_Global.monitor); |
599 | 0 | while (OCSP_Global.cache.numberOfEntries > 0) { |
600 | 0 | ocsp_RemoveCacheItem(&OCSP_Global.cache, |
601 | 0 | OCSP_Global.cache.LRUitem); |
602 | 0 | } |
603 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
604 | 0 | return SECSuccess; |
605 | 0 | } |
606 | | |
607 | | static SECStatus |
608 | | ocsp_CreateCacheItemAndConsumeCertID(OCSPCacheData *cache, |
609 | | CERTOCSPCertID *certID, |
610 | | OCSPCacheItem **pCacheItem) |
611 | 0 | { |
612 | 0 | PLArenaPool *arena; |
613 | 0 | void *mark; |
614 | 0 | PLHashEntry *new_hash_entry; |
615 | 0 | OCSPCacheItem *item; |
616 | 0 |
|
617 | 0 | PORT_Assert(pCacheItem != NULL); |
618 | 0 | *pCacheItem = NULL; |
619 | 0 |
|
620 | 0 | PR_EnterMonitor(OCSP_Global.monitor); |
621 | 0 | arena = certID->poolp; |
622 | 0 | mark = PORT_ArenaMark(arena); |
623 | 0 |
|
624 | 0 | /* ZAlloc will init all Bools to False and all Pointers to NULL |
625 | 0 | and all error codes to zero/good. */ |
626 | 0 | item = (OCSPCacheItem *)PORT_ArenaZAlloc(certID->poolp, |
627 | 0 | sizeof(OCSPCacheItem)); |
628 | 0 | if (!item) { |
629 | 0 | goto loser; |
630 | 0 | } |
631 | 0 | item->certID = certID; |
632 | 0 | new_hash_entry = PL_HashTableAdd(cache->entries, item->certID, |
633 | 0 | item); |
634 | 0 | if (!new_hash_entry) { |
635 | 0 | goto loser; |
636 | 0 | } |
637 | 0 | ++cache->numberOfEntries; |
638 | 0 | PORT_ArenaUnmark(arena, mark); |
639 | 0 | ocsp_AddCacheItemToLinkedList(cache, item); |
640 | 0 | *pCacheItem = item; |
641 | 0 |
|
642 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
643 | 0 | return SECSuccess; |
644 | 0 |
|
645 | 0 | loser: |
646 | 0 | PORT_ArenaRelease(arena, mark); |
647 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
648 | 0 | return SECFailure; |
649 | 0 | } |
650 | | |
651 | | static SECStatus |
652 | | ocsp_SetCacheItemResponse(OCSPCacheItem *item, |
653 | | const CERTOCSPSingleResponse *response) |
654 | 0 | { |
655 | 0 | if (item->certStatusArena) { |
656 | 0 | PORT_FreeArena(item->certStatusArena, PR_FALSE); |
657 | 0 | item->certStatusArena = NULL; |
658 | 0 | } |
659 | 0 | item->haveThisUpdate = item->haveNextUpdate = PR_FALSE; |
660 | 0 | if (response) { |
661 | 0 | SECStatus rv; |
662 | 0 | item->certStatusArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
663 | 0 | if (item->certStatusArena == NULL) { |
664 | 0 | return SECFailure; |
665 | 0 | } |
666 | 0 | rv = ocsp_CopyCertStatus(item->certStatusArena, &item->certStatus, |
667 | 0 | response->certStatus); |
668 | 0 | if (rv != SECSuccess) { |
669 | 0 | PORT_FreeArena(item->certStatusArena, PR_FALSE); |
670 | 0 | item->certStatusArena = NULL; |
671 | 0 | return rv; |
672 | 0 | } |
673 | 0 | item->missingResponseError = 0; |
674 | 0 | rv = DER_GeneralizedTimeToTime(&item->thisUpdate, |
675 | 0 | &response->thisUpdate); |
676 | 0 | item->haveThisUpdate = (rv == SECSuccess); |
677 | 0 | if (response->nextUpdate) { |
678 | 0 | rv = DER_GeneralizedTimeToTime(&item->nextUpdate, |
679 | 0 | response->nextUpdate); |
680 | 0 | item->haveNextUpdate = (rv == SECSuccess); |
681 | 0 | } else { |
682 | 0 | item->haveNextUpdate = PR_FALSE; |
683 | 0 | } |
684 | 0 | } |
685 | 0 | return SECSuccess; |
686 | 0 | } |
687 | | |
688 | | static void |
689 | | ocsp_FreshenCacheItemNextFetchAttemptTime(OCSPCacheItem *cacheItem) |
690 | 0 | { |
691 | 0 | PRTime now; |
692 | 0 | PRTime earliestAllowedNextFetchAttemptTime; |
693 | 0 | PRTime latestTimeWhenResponseIsConsideredFresh; |
694 | 0 |
|
695 | 0 | OCSP_TRACE(("OCSP ocsp_FreshenCacheItemNextFetchAttemptTime\n")); |
696 | 0 |
|
697 | 0 | PR_EnterMonitor(OCSP_Global.monitor); |
698 | 0 |
|
699 | 0 | now = PR_Now(); |
700 | 0 | OCSP_TRACE_TIME("now:", now); |
701 | 0 |
|
702 | 0 | if (cacheItem->haveThisUpdate) { |
703 | 0 | OCSP_TRACE_TIME("thisUpdate:", cacheItem->thisUpdate); |
704 | 0 | latestTimeWhenResponseIsConsideredFresh = cacheItem->thisUpdate + |
705 | 0 | OCSP_Global.maximumSecondsToNextFetchAttempt * |
706 | 0 | MICROSECONDS_PER_SECOND; |
707 | 0 | OCSP_TRACE_TIME("latestTimeWhenResponseIsConsideredFresh:", |
708 | 0 | latestTimeWhenResponseIsConsideredFresh); |
709 | 0 | } else { |
710 | 0 | latestTimeWhenResponseIsConsideredFresh = now + |
711 | 0 | OCSP_Global.minimumSecondsToNextFetchAttempt * |
712 | 0 | MICROSECONDS_PER_SECOND; |
713 | 0 | OCSP_TRACE_TIME("no thisUpdate, " |
714 | 0 | "latestTimeWhenResponseIsConsideredFresh:", |
715 | 0 | latestTimeWhenResponseIsConsideredFresh); |
716 | 0 | } |
717 | 0 |
|
718 | 0 | if (cacheItem->haveNextUpdate) { |
719 | 0 | OCSP_TRACE_TIME("have nextUpdate:", cacheItem->nextUpdate); |
720 | 0 | } |
721 | 0 |
|
722 | 0 | if (cacheItem->haveNextUpdate && |
723 | 0 | cacheItem->nextUpdate < latestTimeWhenResponseIsConsideredFresh) { |
724 | 0 | latestTimeWhenResponseIsConsideredFresh = cacheItem->nextUpdate; |
725 | 0 | OCSP_TRACE_TIME("nextUpdate is smaller than latestFresh, setting " |
726 | 0 | "latestTimeWhenResponseIsConsideredFresh:", |
727 | 0 | latestTimeWhenResponseIsConsideredFresh); |
728 | 0 | } |
729 | 0 |
|
730 | 0 | earliestAllowedNextFetchAttemptTime = now + |
731 | 0 | OCSP_Global.minimumSecondsToNextFetchAttempt * |
732 | 0 | MICROSECONDS_PER_SECOND; |
733 | 0 | OCSP_TRACE_TIME("earliestAllowedNextFetchAttemptTime:", |
734 | 0 | earliestAllowedNextFetchAttemptTime); |
735 | 0 |
|
736 | 0 | if (latestTimeWhenResponseIsConsideredFresh < |
737 | 0 | earliestAllowedNextFetchAttemptTime) { |
738 | 0 | latestTimeWhenResponseIsConsideredFresh = |
739 | 0 | earliestAllowedNextFetchAttemptTime; |
740 | 0 | OCSP_TRACE_TIME("latest < earliest, setting latest to:", |
741 | 0 | latestTimeWhenResponseIsConsideredFresh); |
742 | 0 | } |
743 | 0 |
|
744 | 0 | cacheItem->nextFetchAttemptTime = |
745 | 0 | latestTimeWhenResponseIsConsideredFresh; |
746 | 0 | OCSP_TRACE_TIME("nextFetchAttemptTime", |
747 | 0 | latestTimeWhenResponseIsConsideredFresh); |
748 | 0 |
|
749 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
750 | 0 | } |
751 | | |
752 | | static PRBool |
753 | | ocsp_IsCacheItemFresh(OCSPCacheItem *cacheItem) |
754 | 0 | { |
755 | 0 | PRTime now; |
756 | 0 | PRBool fresh; |
757 | 0 |
|
758 | 0 | now = PR_Now(); |
759 | 0 |
|
760 | 0 | fresh = cacheItem->nextFetchAttemptTime > now; |
761 | 0 |
|
762 | 0 | /* Work around broken OCSP responders that return unknown responses for |
763 | 0 | * certificates, especially certificates that were just recently issued. |
764 | 0 | */ |
765 | 0 | if (fresh && cacheItem->certStatusArena && |
766 | 0 | cacheItem->certStatus.certStatusType == ocspCertStatus_unknown) { |
767 | 0 | fresh = PR_FALSE; |
768 | 0 | } |
769 | 0 |
|
770 | 0 | OCSP_TRACE(("OCSP ocsp_IsCacheItemFresh: %d\n", fresh)); |
771 | 0 |
|
772 | 0 | return fresh; |
773 | 0 | } |
774 | | |
775 | | /* |
776 | | * Status in *certIDWasConsumed will always be correct, regardless of |
777 | | * return value. |
778 | | * If the caller is unable to transfer ownership of certID, |
779 | | * then the caller must set certIDWasConsumed to NULL, |
780 | | * and this function will potentially duplicate the certID object. |
781 | | */ |
782 | | static SECStatus |
783 | | ocsp_CreateOrUpdateCacheEntry(OCSPCacheData *cache, |
784 | | CERTOCSPCertID *certID, |
785 | | CERTOCSPSingleResponse *single, |
786 | | PRBool *certIDWasConsumed) |
787 | 0 | { |
788 | 0 | SECStatus rv; |
789 | 0 | OCSPCacheItem *cacheItem; |
790 | 0 | OCSP_TRACE(("OCSP ocsp_CreateOrUpdateCacheEntry\n")); |
791 | 0 |
|
792 | 0 | if (certIDWasConsumed) |
793 | 0 | *certIDWasConsumed = PR_FALSE; |
794 | 0 |
|
795 | 0 | PR_EnterMonitor(OCSP_Global.monitor); |
796 | 0 | PORT_Assert(OCSP_Global.maxCacheEntries >= 0); |
797 | 0 |
|
798 | 0 | cacheItem = ocsp_FindCacheEntry(cache, certID); |
799 | 0 |
|
800 | 0 | /* Don't replace an unknown or revoked entry with an error entry, even if |
801 | 0 | * the existing entry is expired. Instead, we'll continue to use the |
802 | 0 | * existing (possibly expired) cache entry until we receive a valid signed |
803 | 0 | * response to replace it. |
804 | 0 | */ |
805 | 0 | if (!single && cacheItem && cacheItem->certStatusArena && |
806 | 0 | (cacheItem->certStatus.certStatusType == ocspCertStatus_revoked || |
807 | 0 | cacheItem->certStatus.certStatusType == ocspCertStatus_unknown)) { |
808 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
809 | 0 | return SECSuccess; |
810 | 0 | } |
811 | 0 | |
812 | 0 | if (!cacheItem) { |
813 | 0 | CERTOCSPCertID *myCertID; |
814 | 0 | if (certIDWasConsumed) { |
815 | 0 | myCertID = certID; |
816 | 0 | *certIDWasConsumed = PR_TRUE; |
817 | 0 | } else { |
818 | 0 | myCertID = cert_DupOCSPCertID(certID); |
819 | 0 | if (!myCertID) { |
820 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
821 | 0 | PORT_SetError(PR_OUT_OF_MEMORY_ERROR); |
822 | 0 | return SECFailure; |
823 | 0 | } |
824 | 0 | } |
825 | 0 |
|
826 | 0 | rv = ocsp_CreateCacheItemAndConsumeCertID(cache, myCertID, |
827 | 0 | &cacheItem); |
828 | 0 | if (rv != SECSuccess) { |
829 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
830 | 0 | return rv; |
831 | 0 | } |
832 | 0 | } |
833 | 0 | if (single) { |
834 | 0 | PRTime thisUpdate; |
835 | 0 | rv = DER_GeneralizedTimeToTime(&thisUpdate, &single->thisUpdate); |
836 | 0 |
|
837 | 0 | if (!cacheItem->haveThisUpdate || |
838 | 0 | (rv == SECSuccess && cacheItem->thisUpdate < thisUpdate)) { |
839 | 0 | rv = ocsp_SetCacheItemResponse(cacheItem, single); |
840 | 0 | if (rv != SECSuccess) { |
841 | 0 | ocsp_RemoveCacheItem(cache, cacheItem); |
842 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
843 | 0 | return rv; |
844 | 0 | } |
845 | 0 | } else { |
846 | 0 | OCSP_TRACE(("Not caching response because the response is not " |
847 | 0 | "newer than the cache")); |
848 | 0 | } |
849 | 0 | } else { |
850 | 0 | cacheItem->missingResponseError = PORT_GetError(); |
851 | 0 | if (cacheItem->certStatusArena) { |
852 | 0 | PORT_FreeArena(cacheItem->certStatusArena, PR_FALSE); |
853 | 0 | cacheItem->certStatusArena = NULL; |
854 | 0 | } |
855 | 0 | } |
856 | 0 | ocsp_FreshenCacheItemNextFetchAttemptTime(cacheItem); |
857 | 0 | ocsp_CheckCacheSize(cache); |
858 | 0 |
|
859 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
860 | 0 | return SECSuccess; |
861 | 0 | } |
862 | | |
863 | | extern SECStatus |
864 | | CERT_SetOCSPFailureMode(SEC_OcspFailureMode ocspFailureMode) |
865 | 0 | { |
866 | 0 | switch (ocspFailureMode) { |
867 | 0 | case ocspMode_FailureIsVerificationFailure: |
868 | 0 | case ocspMode_FailureIsNotAVerificationFailure: |
869 | 0 | break; |
870 | 0 | default: |
871 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
872 | 0 | return SECFailure; |
873 | 0 | } |
874 | 0 |
|
875 | 0 | PR_EnterMonitor(OCSP_Global.monitor); |
876 | 0 | OCSP_Global.ocspFailureMode = ocspFailureMode; |
877 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
878 | 0 | return SECSuccess; |
879 | 0 | } |
880 | | |
881 | | SECStatus |
882 | | CERT_OCSPCacheSettings(PRInt32 maxCacheEntries, |
883 | | PRUint32 minimumSecondsToNextFetchAttempt, |
884 | | PRUint32 maximumSecondsToNextFetchAttempt) |
885 | 0 | { |
886 | 0 | if (minimumSecondsToNextFetchAttempt > maximumSecondsToNextFetchAttempt || |
887 | 0 | maxCacheEntries < -1) { |
888 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
889 | 0 | return SECFailure; |
890 | 0 | } |
891 | 0 |
|
892 | 0 | PR_EnterMonitor(OCSP_Global.monitor); |
893 | 0 |
|
894 | 0 | if (maxCacheEntries < 0) { |
895 | 0 | OCSP_Global.maxCacheEntries = -1; /* disable cache */ |
896 | 0 | } else if (maxCacheEntries == 0) { |
897 | 0 | OCSP_Global.maxCacheEntries = 0; /* unlimited cache entries */ |
898 | 0 | } else { |
899 | 0 | OCSP_Global.maxCacheEntries = maxCacheEntries; |
900 | 0 | } |
901 | 0 |
|
902 | 0 | if (minimumSecondsToNextFetchAttempt < |
903 | 0 | OCSP_Global.minimumSecondsToNextFetchAttempt || |
904 | 0 | maximumSecondsToNextFetchAttempt < |
905 | 0 | OCSP_Global.maximumSecondsToNextFetchAttempt) { |
906 | 0 | /* |
907 | 0 | * Ensure our existing cache entries are not used longer than the |
908 | 0 | * new settings allow, we're lazy and just clear the cache |
909 | 0 | */ |
910 | 0 | CERT_ClearOCSPCache(); |
911 | 0 | } |
912 | 0 |
|
913 | 0 | OCSP_Global.minimumSecondsToNextFetchAttempt = |
914 | 0 | minimumSecondsToNextFetchAttempt; |
915 | 0 | OCSP_Global.maximumSecondsToNextFetchAttempt = |
916 | 0 | maximumSecondsToNextFetchAttempt; |
917 | 0 | ocsp_CheckCacheSize(&OCSP_Global.cache); |
918 | 0 |
|
919 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
920 | 0 | return SECSuccess; |
921 | 0 | } |
922 | | |
923 | | SECStatus |
924 | | CERT_SetOCSPTimeout(PRUint32 seconds) |
925 | 0 | { |
926 | 0 | /* no locking, see bug 406120 */ |
927 | 0 | OCSP_Global.timeoutSeconds = seconds; |
928 | 0 | return SECSuccess; |
929 | 0 | } |
930 | | |
931 | | /* this function is called at NSS initialization time */ |
932 | | SECStatus |
933 | | OCSP_InitGlobal(void) |
934 | 0 | { |
935 | 0 | SECStatus rv = SECFailure; |
936 | 0 |
|
937 | 0 | if (OCSP_Global.monitor == NULL) { |
938 | 0 | OCSP_Global.monitor = PR_NewMonitor(); |
939 | 0 | } |
940 | 0 | if (!OCSP_Global.monitor) |
941 | 0 | return SECFailure; |
942 | 0 | |
943 | 0 | PR_EnterMonitor(OCSP_Global.monitor); |
944 | 0 | if (!OCSP_Global.cache.entries) { |
945 | 0 | OCSP_Global.cache.entries = |
946 | 0 | PL_NewHashTable(0, |
947 | 0 | ocsp_CacheKeyHashFunction, |
948 | 0 | ocsp_CacheKeyCompareFunction, |
949 | 0 | PL_CompareValues, |
950 | 0 | NULL, |
951 | 0 | NULL); |
952 | 0 | OCSP_Global.ocspFailureMode = ocspMode_FailureIsVerificationFailure; |
953 | 0 | OCSP_Global.cache.numberOfEntries = 0; |
954 | 0 | OCSP_Global.cache.MRUitem = NULL; |
955 | 0 | OCSP_Global.cache.LRUitem = NULL; |
956 | 0 | } else { |
957 | 0 | /* |
958 | 0 | * NSS might call this function twice while attempting to init. |
959 | 0 | * But it's not allowed to call this again after any activity. |
960 | 0 | */ |
961 | 0 | PORT_Assert(OCSP_Global.cache.numberOfEntries == 0); |
962 | 0 | PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); |
963 | 0 | } |
964 | 0 | if (OCSP_Global.cache.entries) |
965 | 0 | rv = SECSuccess; |
966 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
967 | 0 | return rv; |
968 | 0 | } |
969 | | |
970 | | SECStatus |
971 | | OCSP_ShutdownGlobal(void) |
972 | 0 | { |
973 | 0 | if (!OCSP_Global.monitor) |
974 | 0 | return SECSuccess; |
975 | 0 | |
976 | 0 | PR_EnterMonitor(OCSP_Global.monitor); |
977 | 0 | if (OCSP_Global.cache.entries) { |
978 | 0 | CERT_ClearOCSPCache(); |
979 | 0 | PL_HashTableDestroy(OCSP_Global.cache.entries); |
980 | 0 | OCSP_Global.cache.entries = NULL; |
981 | 0 | } |
982 | 0 | PORT_Assert(OCSP_Global.cache.numberOfEntries == 0); |
983 | 0 | OCSP_Global.cache.MRUitem = NULL; |
984 | 0 | OCSP_Global.cache.LRUitem = NULL; |
985 | 0 |
|
986 | 0 | OCSP_Global.defaultHttpClientFcn = NULL; |
987 | 0 | OCSP_Global.maxCacheEntries = DEFAULT_OCSP_CACHE_SIZE; |
988 | 0 | OCSP_Global.minimumSecondsToNextFetchAttempt = |
989 | 0 | DEFAULT_MINIMUM_SECONDS_TO_NEXT_OCSP_FETCH_ATTEMPT; |
990 | 0 | OCSP_Global.maximumSecondsToNextFetchAttempt = |
991 | 0 | DEFAULT_MAXIMUM_SECONDS_TO_NEXT_OCSP_FETCH_ATTEMPT; |
992 | 0 | OCSP_Global.ocspFailureMode = |
993 | 0 | ocspMode_FailureIsVerificationFailure; |
994 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
995 | 0 |
|
996 | 0 | PR_DestroyMonitor(OCSP_Global.monitor); |
997 | 0 | OCSP_Global.monitor = NULL; |
998 | 0 | return SECSuccess; |
999 | 0 | } |
1000 | | |
1001 | | /* |
1002 | | * A return value of NULL means: |
1003 | | * The application did not register it's own HTTP client. |
1004 | | */ |
1005 | | const SEC_HttpClientFcn * |
1006 | | SEC_GetRegisteredHttpClient(void) |
1007 | 0 | { |
1008 | 0 | const SEC_HttpClientFcn *retval; |
1009 | 0 |
|
1010 | 0 | if (!OCSP_Global.monitor) { |
1011 | 0 | PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
1012 | 0 | return NULL; |
1013 | 0 | } |
1014 | 0 |
|
1015 | 0 | PR_EnterMonitor(OCSP_Global.monitor); |
1016 | 0 | retval = OCSP_Global.defaultHttpClientFcn; |
1017 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
1018 | 0 |
|
1019 | 0 | return retval; |
1020 | 0 | } |
1021 | | |
1022 | | /* |
1023 | | * The following structure is only used internally. It is allocated when |
1024 | | * someone turns on OCSP checking, and hangs off of the status-configuration |
1025 | | * structure in the certdb structure. We use it to keep configuration |
1026 | | * information specific to OCSP checking. |
1027 | | */ |
1028 | | typedef struct ocspCheckingContextStr { |
1029 | | PRBool useDefaultResponder; |
1030 | | char *defaultResponderURI; |
1031 | | char *defaultResponderNickname; |
1032 | | CERTCertificate *defaultResponderCert; |
1033 | | } ocspCheckingContext; |
1034 | | |
1035 | | SEC_ASN1_MKSUB(SEC_AnyTemplate) |
1036 | | SEC_ASN1_MKSUB(SEC_IntegerTemplate) |
1037 | | SEC_ASN1_MKSUB(SEC_NullTemplate) |
1038 | | SEC_ASN1_MKSUB(SEC_OctetStringTemplate) |
1039 | | SEC_ASN1_MKSUB(SEC_PointerToAnyTemplate) |
1040 | | SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate) |
1041 | | SEC_ASN1_MKSUB(SEC_SequenceOfAnyTemplate) |
1042 | | SEC_ASN1_MKSUB(SEC_PointerToGeneralizedTimeTemplate) |
1043 | | SEC_ASN1_MKSUB(SEC_PointerToEnumeratedTemplate) |
1044 | | |
1045 | | /* |
1046 | | * Forward declarations of sub-types, so I can lay out the types in the |
1047 | | * same order as the ASN.1 is laid out in the OCSP spec itself. |
1048 | | * |
1049 | | * These are in alphabetical order (case-insensitive); please keep it that way! |
1050 | | */ |
1051 | | extern const SEC_ASN1Template ocsp_CertIDTemplate[]; |
1052 | | extern const SEC_ASN1Template ocsp_PointerToSignatureTemplate[]; |
1053 | | extern const SEC_ASN1Template ocsp_PointerToResponseBytesTemplate[]; |
1054 | | extern const SEC_ASN1Template ocsp_ResponseDataTemplate[]; |
1055 | | extern const SEC_ASN1Template ocsp_RevokedInfoTemplate[]; |
1056 | | extern const SEC_ASN1Template ocsp_SingleRequestTemplate[]; |
1057 | | extern const SEC_ASN1Template ocsp_SingleResponseTemplate[]; |
1058 | | extern const SEC_ASN1Template ocsp_TBSRequestTemplate[]; |
1059 | | |
1060 | | /* |
1061 | | * Request-related templates... |
1062 | | */ |
1063 | | |
1064 | | /* |
1065 | | * OCSPRequest ::= SEQUENCE { |
1066 | | * tbsRequest TBSRequest, |
1067 | | * optionalSignature [0] EXPLICIT Signature OPTIONAL } |
1068 | | */ |
1069 | | static const SEC_ASN1Template ocsp_OCSPRequestTemplate[] = { |
1070 | | { SEC_ASN1_SEQUENCE, |
1071 | | 0, NULL, sizeof(CERTOCSPRequest) }, |
1072 | | { SEC_ASN1_POINTER, |
1073 | | offsetof(CERTOCSPRequest, tbsRequest), |
1074 | | ocsp_TBSRequestTemplate }, |
1075 | | { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | |
1076 | | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, |
1077 | | offsetof(CERTOCSPRequest, optionalSignature), |
1078 | | ocsp_PointerToSignatureTemplate }, |
1079 | | { 0 } |
1080 | | }; |
1081 | | |
1082 | | /* |
1083 | | * TBSRequest ::= SEQUENCE { |
1084 | | * version [0] EXPLICIT Version DEFAULT v1, |
1085 | | * requestorName [1] EXPLICIT GeneralName OPTIONAL, |
1086 | | * requestList SEQUENCE OF Request, |
1087 | | * requestExtensions [2] EXPLICIT Extensions OPTIONAL } |
1088 | | * |
1089 | | * Version ::= INTEGER { v1(0) } |
1090 | | * |
1091 | | * Note: this should be static but the AIX compiler doesn't like it (because it |
1092 | | * was forward-declared above); it is not meant to be exported, but this |
1093 | | * is the only way it will compile. |
1094 | | */ |
1095 | | const SEC_ASN1Template ocsp_TBSRequestTemplate[] = { |
1096 | | { SEC_ASN1_SEQUENCE, |
1097 | | 0, NULL, sizeof(ocspTBSRequest) }, |
1098 | | { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | /* XXX DER_DEFAULT */ |
1099 | | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, |
1100 | | offsetof(ocspTBSRequest, version), |
1101 | | SEC_ASN1_SUB(SEC_IntegerTemplate) }, |
1102 | | { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | |
1103 | | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 1, |
1104 | | offsetof(ocspTBSRequest, derRequestorName), |
1105 | | SEC_ASN1_SUB(SEC_PointerToAnyTemplate) }, |
1106 | | { SEC_ASN1_SEQUENCE_OF, |
1107 | | offsetof(ocspTBSRequest, requestList), |
1108 | | ocsp_SingleRequestTemplate }, |
1109 | | { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | |
1110 | | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 2, |
1111 | | offsetof(ocspTBSRequest, requestExtensions), |
1112 | | CERT_SequenceOfCertExtensionTemplate }, |
1113 | | { 0 } |
1114 | | }; |
1115 | | |
1116 | | /* |
1117 | | * Signature ::= SEQUENCE { |
1118 | | * signatureAlgorithm AlgorithmIdentifier, |
1119 | | * signature BIT STRING, |
1120 | | * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } |
1121 | | */ |
1122 | | static const SEC_ASN1Template ocsp_SignatureTemplate[] = { |
1123 | | { SEC_ASN1_SEQUENCE, |
1124 | | 0, NULL, sizeof(ocspSignature) }, |
1125 | | { SEC_ASN1_INLINE | SEC_ASN1_XTRN, |
1126 | | offsetof(ocspSignature, signatureAlgorithm), |
1127 | | SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, |
1128 | | { SEC_ASN1_BIT_STRING, |
1129 | | offsetof(ocspSignature, signature) }, |
1130 | | { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | |
1131 | | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, |
1132 | | offsetof(ocspSignature, derCerts), |
1133 | | SEC_ASN1_SUB(SEC_SequenceOfAnyTemplate) }, |
1134 | | { 0 } |
1135 | | }; |
1136 | | |
1137 | | /* |
1138 | | * This template is just an extra level to use in an explicitly-tagged |
1139 | | * reference to a Signature. |
1140 | | * |
1141 | | * Note: this should be static but the AIX compiler doesn't like it (because it |
1142 | | * was forward-declared above); it is not meant to be exported, but this |
1143 | | * is the only way it will compile. |
1144 | | */ |
1145 | | const SEC_ASN1Template ocsp_PointerToSignatureTemplate[] = { |
1146 | | { SEC_ASN1_POINTER, 0, ocsp_SignatureTemplate } |
1147 | | }; |
1148 | | |
1149 | | /* |
1150 | | * Request ::= SEQUENCE { |
1151 | | * reqCert CertID, |
1152 | | * singleRequestExtensions [0] EXPLICIT Extensions OPTIONAL } |
1153 | | * |
1154 | | * Note: this should be static but the AIX compiler doesn't like it (because it |
1155 | | * was forward-declared above); it is not meant to be exported, but this |
1156 | | * is the only way it will compile. |
1157 | | */ |
1158 | | const SEC_ASN1Template ocsp_SingleRequestTemplate[] = { |
1159 | | { SEC_ASN1_SEQUENCE, |
1160 | | 0, NULL, sizeof(ocspSingleRequest) }, |
1161 | | { SEC_ASN1_POINTER, |
1162 | | offsetof(ocspSingleRequest, reqCert), |
1163 | | ocsp_CertIDTemplate }, |
1164 | | { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | |
1165 | | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, |
1166 | | offsetof(ocspSingleRequest, singleRequestExtensions), |
1167 | | CERT_SequenceOfCertExtensionTemplate }, |
1168 | | { 0 } |
1169 | | }; |
1170 | | |
1171 | | /* |
1172 | | * This data structure and template (CertID) is used by both OCSP |
1173 | | * requests and responses. It is the only one that is shared. |
1174 | | * |
1175 | | * CertID ::= SEQUENCE { |
1176 | | * hashAlgorithm AlgorithmIdentifier, |
1177 | | * issuerNameHash OCTET STRING, -- Hash of Issuer DN |
1178 | | * issuerKeyHash OCTET STRING, -- Hash of Issuer public key |
1179 | | * serialNumber CertificateSerialNumber } |
1180 | | * |
1181 | | * CertificateSerialNumber ::= INTEGER |
1182 | | * |
1183 | | * Note: this should be static but the AIX compiler doesn't like it (because it |
1184 | | * was forward-declared above); it is not meant to be exported, but this |
1185 | | * is the only way it will compile. |
1186 | | */ |
1187 | | const SEC_ASN1Template ocsp_CertIDTemplate[] = { |
1188 | | { SEC_ASN1_SEQUENCE, |
1189 | | 0, NULL, sizeof(CERTOCSPCertID) }, |
1190 | | { SEC_ASN1_INLINE | SEC_ASN1_XTRN, |
1191 | | offsetof(CERTOCSPCertID, hashAlgorithm), |
1192 | | SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, |
1193 | | { SEC_ASN1_OCTET_STRING, |
1194 | | offsetof(CERTOCSPCertID, issuerNameHash) }, |
1195 | | { SEC_ASN1_OCTET_STRING, |
1196 | | offsetof(CERTOCSPCertID, issuerKeyHash) }, |
1197 | | { SEC_ASN1_INTEGER, |
1198 | | offsetof(CERTOCSPCertID, serialNumber) }, |
1199 | | { 0 } |
1200 | | }; |
1201 | | |
1202 | | /* |
1203 | | * Response-related templates... |
1204 | | */ |
1205 | | |
1206 | | /* |
1207 | | * OCSPResponse ::= SEQUENCE { |
1208 | | * responseStatus OCSPResponseStatus, |
1209 | | * responseBytes [0] EXPLICIT ResponseBytes OPTIONAL } |
1210 | | */ |
1211 | | const SEC_ASN1Template ocsp_OCSPResponseTemplate[] = { |
1212 | | { SEC_ASN1_SEQUENCE, |
1213 | | 0, NULL, sizeof(CERTOCSPResponse) }, |
1214 | | { SEC_ASN1_ENUMERATED, |
1215 | | offsetof(CERTOCSPResponse, responseStatus) }, |
1216 | | { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | |
1217 | | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, |
1218 | | offsetof(CERTOCSPResponse, responseBytes), |
1219 | | ocsp_PointerToResponseBytesTemplate }, |
1220 | | { 0 } |
1221 | | }; |
1222 | | |
1223 | | /* |
1224 | | * ResponseBytes ::= SEQUENCE { |
1225 | | * responseType OBJECT IDENTIFIER, |
1226 | | * response OCTET STRING } |
1227 | | */ |
1228 | | const SEC_ASN1Template ocsp_ResponseBytesTemplate[] = { |
1229 | | { SEC_ASN1_SEQUENCE, |
1230 | | 0, NULL, sizeof(ocspResponseBytes) }, |
1231 | | { SEC_ASN1_OBJECT_ID, |
1232 | | offsetof(ocspResponseBytes, responseType) }, |
1233 | | { SEC_ASN1_OCTET_STRING, |
1234 | | offsetof(ocspResponseBytes, response) }, |
1235 | | { 0 } |
1236 | | }; |
1237 | | |
1238 | | /* |
1239 | | * This template is just an extra level to use in an explicitly-tagged |
1240 | | * reference to a ResponseBytes. |
1241 | | * |
1242 | | * Note: this should be static but the AIX compiler doesn't like it (because it |
1243 | | * was forward-declared above); it is not meant to be exported, but this |
1244 | | * is the only way it will compile. |
1245 | | */ |
1246 | | const SEC_ASN1Template ocsp_PointerToResponseBytesTemplate[] = { |
1247 | | { SEC_ASN1_POINTER, 0, ocsp_ResponseBytesTemplate } |
1248 | | }; |
1249 | | |
1250 | | /* |
1251 | | * BasicOCSPResponse ::= SEQUENCE { |
1252 | | * tbsResponseData ResponseData, |
1253 | | * signatureAlgorithm AlgorithmIdentifier, |
1254 | | * signature BIT STRING, |
1255 | | * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } |
1256 | | */ |
1257 | | static const SEC_ASN1Template ocsp_BasicOCSPResponseTemplate[] = { |
1258 | | { SEC_ASN1_SEQUENCE, |
1259 | | 0, NULL, sizeof(ocspBasicOCSPResponse) }, |
1260 | | { SEC_ASN1_ANY | SEC_ASN1_SAVE, |
1261 | | offsetof(ocspBasicOCSPResponse, tbsResponseDataDER) }, |
1262 | | { SEC_ASN1_POINTER, |
1263 | | offsetof(ocspBasicOCSPResponse, tbsResponseData), |
1264 | | ocsp_ResponseDataTemplate }, |
1265 | | { SEC_ASN1_INLINE | SEC_ASN1_XTRN, |
1266 | | offsetof(ocspBasicOCSPResponse, responseSignature.signatureAlgorithm), |
1267 | | SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, |
1268 | | { SEC_ASN1_BIT_STRING, |
1269 | | offsetof(ocspBasicOCSPResponse, responseSignature.signature) }, |
1270 | | { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | |
1271 | | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, |
1272 | | offsetof(ocspBasicOCSPResponse, responseSignature.derCerts), |
1273 | | SEC_ASN1_SUB(SEC_SequenceOfAnyTemplate) }, |
1274 | | { 0 } |
1275 | | }; |
1276 | | |
1277 | | /* |
1278 | | * ResponseData ::= SEQUENCE { |
1279 | | * version [0] EXPLICIT Version DEFAULT v1, |
1280 | | * responderID ResponderID, |
1281 | | * producedAt GeneralizedTime, |
1282 | | * responses SEQUENCE OF SingleResponse, |
1283 | | * responseExtensions [1] EXPLICIT Extensions OPTIONAL } |
1284 | | * |
1285 | | * Note: this should be static but the AIX compiler doesn't like it (because it |
1286 | | * was forward-declared above); it is not meant to be exported, but this |
1287 | | * is the only way it will compile. |
1288 | | */ |
1289 | | const SEC_ASN1Template ocsp_ResponseDataTemplate[] = { |
1290 | | { SEC_ASN1_SEQUENCE, |
1291 | | 0, NULL, sizeof(ocspResponseData) }, |
1292 | | { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | /* XXX DER_DEFAULT */ |
1293 | | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, |
1294 | | offsetof(ocspResponseData, version), |
1295 | | SEC_ASN1_SUB(SEC_IntegerTemplate) }, |
1296 | | { SEC_ASN1_ANY, |
1297 | | offsetof(ocspResponseData, derResponderID) }, |
1298 | | { SEC_ASN1_GENERALIZED_TIME, |
1299 | | offsetof(ocspResponseData, producedAt) }, |
1300 | | { SEC_ASN1_SEQUENCE_OF, |
1301 | | offsetof(ocspResponseData, responses), |
1302 | | ocsp_SingleResponseTemplate }, |
1303 | | { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | |
1304 | | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, |
1305 | | offsetof(ocspResponseData, responseExtensions), |
1306 | | CERT_SequenceOfCertExtensionTemplate }, |
1307 | | { 0 } |
1308 | | }; |
1309 | | |
1310 | | /* |
1311 | | * ResponderID ::= CHOICE { |
1312 | | * byName [1] EXPLICIT Name, |
1313 | | * byKey [2] EXPLICIT KeyHash } |
1314 | | * |
1315 | | * KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key |
1316 | | * (excluding the tag and length fields) |
1317 | | * |
1318 | | * XXX Because the ASN.1 encoder and decoder currently do not provide |
1319 | | * a way to automatically handle a CHOICE, we need to do it in two |
1320 | | * steps, looking at the type tag and feeding the exact choice back |
1321 | | * to the ASN.1 code. Hopefully that will change someday and this |
1322 | | * can all be simplified down into a single template. Anyway, for |
1323 | | * now we list each choice as its own template: |
1324 | | */ |
1325 | | const SEC_ASN1Template ocsp_ResponderIDByNameTemplate[] = { |
1326 | | { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, |
1327 | | offsetof(ocspResponderID, responderIDValue.name), |
1328 | | CERT_NameTemplate } |
1329 | | }; |
1330 | | const SEC_ASN1Template ocsp_ResponderIDByKeyTemplate[] = { |
1331 | | { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | |
1332 | | SEC_ASN1_XTRN | 2, |
1333 | | offsetof(ocspResponderID, responderIDValue.keyHash), |
1334 | | SEC_ASN1_SUB(SEC_OctetStringTemplate) } |
1335 | | }; |
1336 | | static const SEC_ASN1Template ocsp_ResponderIDOtherTemplate[] = { |
1337 | | { SEC_ASN1_ANY, |
1338 | | offsetof(ocspResponderID, responderIDValue.other) } |
1339 | | }; |
1340 | | |
1341 | | /* Decode choice container, but leave x509 name object encoded */ |
1342 | | static const SEC_ASN1Template ocsp_ResponderIDDerNameTemplate[] = { |
1343 | | { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | |
1344 | | SEC_ASN1_XTRN | 1, |
1345 | | 0, SEC_ASN1_SUB(SEC_AnyTemplate) } |
1346 | | }; |
1347 | | |
1348 | | /* |
1349 | | * SingleResponse ::= SEQUENCE { |
1350 | | * certID CertID, |
1351 | | * certStatus CertStatus, |
1352 | | * thisUpdate GeneralizedTime, |
1353 | | * nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, |
1354 | | * singleExtensions [1] EXPLICIT Extensions OPTIONAL } |
1355 | | * |
1356 | | * Note: this should be static but the AIX compiler doesn't like it (because it |
1357 | | * was forward-declared above); it is not meant to be exported, but this |
1358 | | * is the only way it will compile. |
1359 | | */ |
1360 | | const SEC_ASN1Template ocsp_SingleResponseTemplate[] = { |
1361 | | { SEC_ASN1_SEQUENCE, |
1362 | | 0, NULL, sizeof(CERTOCSPSingleResponse) }, |
1363 | | { SEC_ASN1_POINTER, |
1364 | | offsetof(CERTOCSPSingleResponse, certID), |
1365 | | ocsp_CertIDTemplate }, |
1366 | | { SEC_ASN1_ANY, |
1367 | | offsetof(CERTOCSPSingleResponse, derCertStatus) }, |
1368 | | { SEC_ASN1_GENERALIZED_TIME, |
1369 | | offsetof(CERTOCSPSingleResponse, thisUpdate) }, |
1370 | | { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | |
1371 | | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, |
1372 | | offsetof(CERTOCSPSingleResponse, nextUpdate), |
1373 | | SEC_ASN1_SUB(SEC_PointerToGeneralizedTimeTemplate) }, |
1374 | | { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | |
1375 | | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, |
1376 | | offsetof(CERTOCSPSingleResponse, singleExtensions), |
1377 | | CERT_SequenceOfCertExtensionTemplate }, |
1378 | | { 0 } |
1379 | | }; |
1380 | | |
1381 | | /* |
1382 | | * CertStatus ::= CHOICE { |
1383 | | * good [0] IMPLICIT NULL, |
1384 | | * revoked [1] IMPLICIT RevokedInfo, |
1385 | | * unknown [2] IMPLICIT UnknownInfo } |
1386 | | * |
1387 | | * Because the ASN.1 encoder and decoder currently do not provide |
1388 | | * a way to automatically handle a CHOICE, we need to do it in two |
1389 | | * steps, looking at the type tag and feeding the exact choice back |
1390 | | * to the ASN.1 code. Hopefully that will change someday and this |
1391 | | * can all be simplified down into a single template. Anyway, for |
1392 | | * now we list each choice as its own template: |
1393 | | */ |
1394 | | static const SEC_ASN1Template ocsp_CertStatusGoodTemplate[] = { |
1395 | | { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, |
1396 | | offsetof(ocspCertStatus, certStatusInfo.goodInfo), |
1397 | | SEC_ASN1_SUB(SEC_NullTemplate) } |
1398 | | }; |
1399 | | static const SEC_ASN1Template ocsp_CertStatusRevokedTemplate[] = { |
1400 | | { SEC_ASN1_POINTER | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, |
1401 | | offsetof(ocspCertStatus, certStatusInfo.revokedInfo), |
1402 | | ocsp_RevokedInfoTemplate } |
1403 | | }; |
1404 | | static const SEC_ASN1Template ocsp_CertStatusUnknownTemplate[] = { |
1405 | | { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 2, |
1406 | | offsetof(ocspCertStatus, certStatusInfo.unknownInfo), |
1407 | | SEC_ASN1_SUB(SEC_NullTemplate) } |
1408 | | }; |
1409 | | static const SEC_ASN1Template ocsp_CertStatusOtherTemplate[] = { |
1410 | | { SEC_ASN1_POINTER | SEC_ASN1_XTRN, |
1411 | | offsetof(ocspCertStatus, certStatusInfo.otherInfo), |
1412 | | SEC_ASN1_SUB(SEC_AnyTemplate) } |
1413 | | }; |
1414 | | |
1415 | | /* |
1416 | | * RevokedInfo ::= SEQUENCE { |
1417 | | * revocationTime GeneralizedTime, |
1418 | | * revocationReason [0] EXPLICIT CRLReason OPTIONAL } |
1419 | | * |
1420 | | * Note: this should be static but the AIX compiler doesn't like it (because it |
1421 | | * was forward-declared above); it is not meant to be exported, but this |
1422 | | * is the only way it will compile. |
1423 | | */ |
1424 | | const SEC_ASN1Template ocsp_RevokedInfoTemplate[] = { |
1425 | | { SEC_ASN1_SEQUENCE, |
1426 | | 0, NULL, sizeof(ocspRevokedInfo) }, |
1427 | | { SEC_ASN1_GENERALIZED_TIME, |
1428 | | offsetof(ocspRevokedInfo, revocationTime) }, |
1429 | | { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | |
1430 | | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | |
1431 | | SEC_ASN1_XTRN | 0, |
1432 | | offsetof(ocspRevokedInfo, revocationReason), |
1433 | | SEC_ASN1_SUB(SEC_PointerToEnumeratedTemplate) }, |
1434 | | { 0 } |
1435 | | }; |
1436 | | |
1437 | | /* |
1438 | | * OCSP-specific extension templates: |
1439 | | */ |
1440 | | |
1441 | | /* |
1442 | | * ServiceLocator ::= SEQUENCE { |
1443 | | * issuer Name, |
1444 | | * locator AuthorityInfoAccessSyntax OPTIONAL } |
1445 | | */ |
1446 | | static const SEC_ASN1Template ocsp_ServiceLocatorTemplate[] = { |
1447 | | { SEC_ASN1_SEQUENCE, |
1448 | | 0, NULL, sizeof(ocspServiceLocator) }, |
1449 | | { SEC_ASN1_POINTER, |
1450 | | offsetof(ocspServiceLocator, issuer), |
1451 | | CERT_NameTemplate }, |
1452 | | { SEC_ASN1_OPTIONAL | SEC_ASN1_ANY, |
1453 | | offsetof(ocspServiceLocator, locator) }, |
1454 | | { 0 } |
1455 | | }; |
1456 | | |
1457 | | /* |
1458 | | * REQUEST SUPPORT FUNCTIONS (encode/create/decode/destroy): |
1459 | | */ |
1460 | | |
1461 | | /* |
1462 | | * FUNCTION: CERT_EncodeOCSPRequest |
1463 | | * DER encodes an OCSP Request, possibly adding a signature as well. |
1464 | | * XXX Signing is not yet supported, however; see comments in code. |
1465 | | * INPUTS: |
1466 | | * PLArenaPool *arena |
1467 | | * The return value is allocated from here. |
1468 | | * If a NULL is passed in, allocation is done from the heap instead. |
1469 | | * CERTOCSPRequest *request |
1470 | | * The request to be encoded. |
1471 | | * void *pwArg |
1472 | | * Pointer to argument for password prompting, if needed. (Definitely |
1473 | | * not needed if not signing.) |
1474 | | * RETURN: |
1475 | | * Returns a NULL on error and a pointer to the SECItem with the |
1476 | | * encoded value otherwise. Any error is likely to be low-level |
1477 | | * (e.g. no memory). |
1478 | | */ |
1479 | | SECItem * |
1480 | | CERT_EncodeOCSPRequest(PLArenaPool *arena, CERTOCSPRequest *request, |
1481 | | void *pwArg) |
1482 | 0 | { |
1483 | 0 | SECStatus rv; |
1484 | 0 |
|
1485 | 0 | /* XXX All of these should generate errors if they fail. */ |
1486 | 0 | PORT_Assert(request); |
1487 | 0 | PORT_Assert(request->tbsRequest); |
1488 | 0 |
|
1489 | 0 | if (request->tbsRequest->extensionHandle != NULL) { |
1490 | 0 | rv = CERT_FinishExtensions(request->tbsRequest->extensionHandle); |
1491 | 0 | request->tbsRequest->extensionHandle = NULL; |
1492 | 0 | if (rv != SECSuccess) |
1493 | 0 | return NULL; |
1494 | 0 | } |
1495 | 0 | |
1496 | 0 | /* |
1497 | 0 | * XXX When signed requests are supported and request->optionalSignature |
1498 | 0 | * is not NULL: |
1499 | 0 | * - need to encode tbsRequest->requestorName |
1500 | 0 | * - need to encode tbsRequest |
1501 | 0 | * - need to sign that encoded result (using cert in sig), filling in the |
1502 | 0 | * request->optionalSignature structure with the result, the signing |
1503 | 0 | * algorithm and (perhaps?) the cert (and its chain?) in derCerts |
1504 | 0 | */ |
1505 | 0 | |
1506 | 0 | return SEC_ASN1EncodeItem(arena, NULL, request, ocsp_OCSPRequestTemplate); |
1507 | 0 | } |
1508 | | |
1509 | | /* |
1510 | | * FUNCTION: CERT_DecodeOCSPRequest |
1511 | | * Decode a DER encoded OCSP Request. |
1512 | | * INPUTS: |
1513 | | * SECItem *src |
1514 | | * Pointer to a SECItem holding DER encoded OCSP Request. |
1515 | | * RETURN: |
1516 | | * Returns a pointer to a CERTOCSPRequest containing the decoded request. |
1517 | | * On error, returns NULL. Most likely error is trouble decoding |
1518 | | * (SEC_ERROR_OCSP_MALFORMED_REQUEST), or low-level problem (no memory). |
1519 | | */ |
1520 | | CERTOCSPRequest * |
1521 | | CERT_DecodeOCSPRequest(const SECItem *src) |
1522 | 0 | { |
1523 | 0 | PLArenaPool *arena = NULL; |
1524 | 0 | SECStatus rv = SECFailure; |
1525 | 0 | CERTOCSPRequest *dest = NULL; |
1526 | 0 | int i; |
1527 | 0 | SECItem newSrc; |
1528 | 0 |
|
1529 | 0 | arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
1530 | 0 | if (arena == NULL) { |
1531 | 0 | goto loser; |
1532 | 0 | } |
1533 | 0 | dest = (CERTOCSPRequest *)PORT_ArenaZAlloc(arena, |
1534 | 0 | sizeof(CERTOCSPRequest)); |
1535 | 0 | if (dest == NULL) { |
1536 | 0 | goto loser; |
1537 | 0 | } |
1538 | 0 | dest->arena = arena; |
1539 | 0 |
|
1540 | 0 | /* copy the DER into the arena, since Quick DER returns data that points |
1541 | 0 | into the DER input, which may get freed by the caller */ |
1542 | 0 | rv = SECITEM_CopyItem(arena, &newSrc, src); |
1543 | 0 | if (rv != SECSuccess) { |
1544 | 0 | goto loser; |
1545 | 0 | } |
1546 | 0 | |
1547 | 0 | rv = SEC_QuickDERDecodeItem(arena, dest, ocsp_OCSPRequestTemplate, &newSrc); |
1548 | 0 | if (rv != SECSuccess) { |
1549 | 0 | if (PORT_GetError() == SEC_ERROR_BAD_DER) |
1550 | 0 | PORT_SetError(SEC_ERROR_OCSP_MALFORMED_REQUEST); |
1551 | 0 | goto loser; |
1552 | 0 | } |
1553 | 0 |
|
1554 | 0 | /* |
1555 | 0 | * XXX I would like to find a way to get rid of the necessity |
1556 | 0 | * of doing this copying of the arena pointer. |
1557 | 0 | */ |
1558 | 0 | for (i = 0; dest->tbsRequest->requestList[i] != NULL; i++) { |
1559 | 0 | dest->tbsRequest->requestList[i]->arena = arena; |
1560 | 0 | } |
1561 | 0 |
|
1562 | 0 | return dest; |
1563 | 0 |
|
1564 | 0 | loser: |
1565 | 0 | if (arena != NULL) { |
1566 | 0 | PORT_FreeArena(arena, PR_FALSE); |
1567 | 0 | } |
1568 | 0 | return NULL; |
1569 | 0 | } |
1570 | | |
1571 | | SECStatus |
1572 | | CERT_DestroyOCSPCertID(CERTOCSPCertID *certID) |
1573 | 0 | { |
1574 | 0 | if (certID && certID->poolp) { |
1575 | 0 | PORT_FreeArena(certID->poolp, PR_FALSE); |
1576 | 0 | return SECSuccess; |
1577 | 0 | } |
1578 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
1579 | 0 | return SECFailure; |
1580 | 0 | } |
1581 | | |
1582 | | /* |
1583 | | * Digest data using the specified algorithm. |
1584 | | * The necessary storage for the digest data is allocated. If "fill" is |
1585 | | * non-null, the data is put there, otherwise a SECItem is allocated. |
1586 | | * Allocation from "arena" if it is non-null, heap otherwise. Any problem |
1587 | | * results in a NULL being returned (and an appropriate error set). |
1588 | | */ |
1589 | | |
1590 | | SECItem * |
1591 | | ocsp_DigestValue(PLArenaPool *arena, SECOidTag digestAlg, |
1592 | | SECItem *fill, const SECItem *src) |
1593 | 0 | { |
1594 | 0 | const SECHashObject *digestObject; |
1595 | 0 | SECItem *result = NULL; |
1596 | 0 | void *mark = NULL; |
1597 | 0 | void *digestBuff = NULL; |
1598 | 0 |
|
1599 | 0 | if (arena != NULL) { |
1600 | 0 | mark = PORT_ArenaMark(arena); |
1601 | 0 | } |
1602 | 0 |
|
1603 | 0 | digestObject = HASH_GetHashObjectByOidTag(digestAlg); |
1604 | 0 | if (digestObject == NULL) { |
1605 | 0 | goto loser; |
1606 | 0 | } |
1607 | 0 | |
1608 | 0 | if (fill == NULL || fill->data == NULL) { |
1609 | 0 | result = SECITEM_AllocItem(arena, fill, digestObject->length); |
1610 | 0 | if (result == NULL) { |
1611 | 0 | goto loser; |
1612 | 0 | } |
1613 | 0 | digestBuff = result->data; |
1614 | 0 | } else { |
1615 | 0 | if (fill->len < digestObject->length) { |
1616 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
1617 | 0 | goto loser; |
1618 | 0 | } |
1619 | 0 | digestBuff = fill->data; |
1620 | 0 | } |
1621 | 0 |
|
1622 | 0 | if (PK11_HashBuf(digestAlg, digestBuff, |
1623 | 0 | src->data, src->len) != SECSuccess) { |
1624 | 0 | goto loser; |
1625 | 0 | } |
1626 | 0 | |
1627 | 0 | if (arena != NULL) { |
1628 | 0 | PORT_ArenaUnmark(arena, mark); |
1629 | 0 | } |
1630 | 0 |
|
1631 | 0 | if (result == NULL) { |
1632 | 0 | result = fill; |
1633 | 0 | } |
1634 | 0 | return result; |
1635 | 0 |
|
1636 | 0 | loser: |
1637 | 0 | if (arena != NULL) { |
1638 | 0 | PORT_ArenaRelease(arena, mark); |
1639 | 0 | } else { |
1640 | 0 | if (result != NULL) { |
1641 | 0 | SECITEM_FreeItem(result, (fill == NULL) ? PR_TRUE : PR_FALSE); |
1642 | 0 | } |
1643 | 0 | } |
1644 | 0 | return (NULL); |
1645 | 0 | } |
1646 | | |
1647 | | /* |
1648 | | * Digest the cert's subject public key using the specified algorithm. |
1649 | | * The necessary storage for the digest data is allocated. If "fill" is |
1650 | | * non-null, the data is put there, otherwise a SECItem is allocated. |
1651 | | * Allocation from "arena" if it is non-null, heap otherwise. Any problem |
1652 | | * results in a NULL being returned (and an appropriate error set). |
1653 | | */ |
1654 | | SECItem * |
1655 | | CERT_GetSubjectPublicKeyDigest(PLArenaPool *arena, const CERTCertificate *cert, |
1656 | | SECOidTag digestAlg, SECItem *fill) |
1657 | 0 | { |
1658 | 0 | SECItem spk; |
1659 | 0 |
|
1660 | 0 | /* |
1661 | 0 | * Copy just the length and data pointer (nothing needs to be freed) |
1662 | 0 | * of the subject public key so we can convert the length from bits |
1663 | 0 | * to bytes, which is what the digest function expects. |
1664 | 0 | */ |
1665 | 0 | spk = cert->subjectPublicKeyInfo.subjectPublicKey; |
1666 | 0 | DER_ConvertBitString(&spk); |
1667 | 0 |
|
1668 | 0 | return ocsp_DigestValue(arena, digestAlg, fill, &spk); |
1669 | 0 | } |
1670 | | |
1671 | | /* |
1672 | | * Digest the cert's subject name using the specified algorithm. |
1673 | | */ |
1674 | | SECItem * |
1675 | | CERT_GetSubjectNameDigest(PLArenaPool *arena, const CERTCertificate *cert, |
1676 | | SECOidTag digestAlg, SECItem *fill) |
1677 | 0 | { |
1678 | 0 | SECItem name; |
1679 | 0 |
|
1680 | 0 | /* |
1681 | 0 | * Copy just the length and data pointer (nothing needs to be freed) |
1682 | 0 | * of the subject name |
1683 | 0 | */ |
1684 | 0 | name = cert->derSubject; |
1685 | 0 |
|
1686 | 0 | return ocsp_DigestValue(arena, digestAlg, fill, &name); |
1687 | 0 | } |
1688 | | |
1689 | | /* |
1690 | | * Create and fill-in a CertID. This function fills in the hash values |
1691 | | * (issuerNameHash and issuerKeyHash), and is hardwired to use SHA1. |
1692 | | * Someday it might need to be more flexible about hash algorithm, but |
1693 | | * for now we have no intention/need to create anything else. |
1694 | | * |
1695 | | * Error causes a null to be returned; most likely cause is trouble |
1696 | | * finding the certificate issuer (SEC_ERROR_UNKNOWN_ISSUER). |
1697 | | * Other errors are low-level problems (no memory, bad database, etc.). |
1698 | | */ |
1699 | | static CERTOCSPCertID * |
1700 | | ocsp_CreateCertID(PLArenaPool *arena, CERTCertificate *cert, PRTime time) |
1701 | 0 | { |
1702 | 0 | CERTOCSPCertID *certID; |
1703 | 0 | CERTCertificate *issuerCert = NULL; |
1704 | 0 | void *mark = PORT_ArenaMark(arena); |
1705 | 0 | SECStatus rv; |
1706 | 0 |
|
1707 | 0 | PORT_Assert(arena != NULL); |
1708 | 0 |
|
1709 | 0 | certID = PORT_ArenaZNew(arena, CERTOCSPCertID); |
1710 | 0 | if (certID == NULL) { |
1711 | 0 | goto loser; |
1712 | 0 | } |
1713 | 0 | |
1714 | 0 | rv = SECOID_SetAlgorithmID(arena, &certID->hashAlgorithm, SEC_OID_SHA1, |
1715 | 0 | NULL); |
1716 | 0 | if (rv != SECSuccess) { |
1717 | 0 | goto loser; |
1718 | 0 | } |
1719 | 0 | |
1720 | 0 | issuerCert = CERT_FindCertIssuer(cert, time, certUsageAnyCA); |
1721 | 0 | if (issuerCert == NULL) { |
1722 | 0 | goto loser; |
1723 | 0 | } |
1724 | 0 | |
1725 | 0 | if (CERT_GetSubjectNameDigest(arena, issuerCert, SEC_OID_SHA1, |
1726 | 0 | &(certID->issuerNameHash)) == NULL) { |
1727 | 0 | goto loser; |
1728 | 0 | } |
1729 | 0 | certID->issuerSHA1NameHash.data = certID->issuerNameHash.data; |
1730 | 0 | certID->issuerSHA1NameHash.len = certID->issuerNameHash.len; |
1731 | 0 |
|
1732 | 0 | if (CERT_GetSubjectNameDigest(arena, issuerCert, SEC_OID_MD5, |
1733 | 0 | &(certID->issuerMD5NameHash)) == NULL) { |
1734 | 0 | goto loser; |
1735 | 0 | } |
1736 | 0 | |
1737 | 0 | if (CERT_GetSubjectNameDigest(arena, issuerCert, SEC_OID_MD2, |
1738 | 0 | &(certID->issuerMD2NameHash)) == NULL) { |
1739 | 0 | goto loser; |
1740 | 0 | } |
1741 | 0 | |
1742 | 0 | if (CERT_GetSubjectPublicKeyDigest(arena, issuerCert, SEC_OID_SHA1, |
1743 | 0 | &certID->issuerKeyHash) == NULL) { |
1744 | 0 | goto loser; |
1745 | 0 | } |
1746 | 0 | certID->issuerSHA1KeyHash.data = certID->issuerKeyHash.data; |
1747 | 0 | certID->issuerSHA1KeyHash.len = certID->issuerKeyHash.len; |
1748 | 0 | /* cache the other two hash algorithms as well */ |
1749 | 0 | if (CERT_GetSubjectPublicKeyDigest(arena, issuerCert, SEC_OID_MD5, |
1750 | 0 | &certID->issuerMD5KeyHash) == NULL) { |
1751 | 0 | goto loser; |
1752 | 0 | } |
1753 | 0 | if (CERT_GetSubjectPublicKeyDigest(arena, issuerCert, SEC_OID_MD2, |
1754 | 0 | &certID->issuerMD2KeyHash) == NULL) { |
1755 | 0 | goto loser; |
1756 | 0 | } |
1757 | 0 | |
1758 | 0 | /* now we are done with issuerCert */ |
1759 | 0 | CERT_DestroyCertificate(issuerCert); |
1760 | 0 | issuerCert = NULL; |
1761 | 0 |
|
1762 | 0 | rv = SECITEM_CopyItem(arena, &certID->serialNumber, &cert->serialNumber); |
1763 | 0 | if (rv != SECSuccess) { |
1764 | 0 | goto loser; |
1765 | 0 | } |
1766 | 0 | |
1767 | 0 | PORT_ArenaUnmark(arena, mark); |
1768 | 0 | return certID; |
1769 | 0 | |
1770 | 0 | loser: |
1771 | 0 | if (issuerCert != NULL) { |
1772 | 0 | CERT_DestroyCertificate(issuerCert); |
1773 | 0 | } |
1774 | 0 | PORT_ArenaRelease(arena, mark); |
1775 | 0 | return NULL; |
1776 | 0 | } |
1777 | | |
1778 | | CERTOCSPCertID * |
1779 | | CERT_CreateOCSPCertID(CERTCertificate *cert, PRTime time) |
1780 | 0 | { |
1781 | 0 | PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
1782 | 0 | CERTOCSPCertID *certID; |
1783 | 0 | PORT_Assert(arena != NULL); |
1784 | 0 | if (!arena) |
1785 | 0 | return NULL; |
1786 | 0 | |
1787 | 0 | certID = ocsp_CreateCertID(arena, cert, time); |
1788 | 0 | if (!certID) { |
1789 | 0 | PORT_FreeArena(arena, PR_FALSE); |
1790 | 0 | return NULL; |
1791 | 0 | } |
1792 | 0 | certID->poolp = arena; |
1793 | 0 | return certID; |
1794 | 0 | } |
1795 | | |
1796 | | static CERTOCSPCertID * |
1797 | | cert_DupOCSPCertID(const CERTOCSPCertID *src) |
1798 | 0 | { |
1799 | 0 | CERTOCSPCertID *dest; |
1800 | 0 | PLArenaPool *arena = NULL; |
1801 | 0 |
|
1802 | 0 | if (!src) { |
1803 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
1804 | 0 | return NULL; |
1805 | 0 | } |
1806 | 0 |
|
1807 | 0 | arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
1808 | 0 | if (!arena) |
1809 | 0 | goto loser; |
1810 | 0 | |
1811 | 0 | dest = PORT_ArenaZNew(arena, CERTOCSPCertID); |
1812 | 0 | if (!dest) |
1813 | 0 | goto loser; |
1814 | 0 | |
1815 | 0 | #define DUPHELP(element) \ |
1816 | 0 | if (src->element.data && \ |
1817 | 0 | SECITEM_CopyItem(arena, &dest->element, &src->element) != \ |
1818 | 0 | SECSuccess) { \ |
1819 | 0 | goto loser; \ |
1820 | 0 | } |
1821 | 0 | |
1822 | 0 | DUPHELP(hashAlgorithm.algorithm) |
1823 | 0 | DUPHELP(hashAlgorithm.parameters) |
1824 | 0 | DUPHELP(issuerNameHash) |
1825 | 0 | DUPHELP(issuerKeyHash) |
1826 | 0 | DUPHELP(serialNumber) |
1827 | 0 | DUPHELP(issuerSHA1NameHash) |
1828 | 0 | DUPHELP(issuerMD5NameHash) |
1829 | 0 | DUPHELP(issuerMD2NameHash) |
1830 | 0 | DUPHELP(issuerSHA1KeyHash) |
1831 | 0 | DUPHELP(issuerMD5KeyHash) |
1832 | 0 | DUPHELP(issuerMD2KeyHash) |
1833 | 0 |
|
1834 | 0 | dest->poolp = arena; |
1835 | 0 | return dest; |
1836 | 0 | |
1837 | 0 | loser: |
1838 | 0 | if (arena) |
1839 | 0 | PORT_FreeArena(arena, PR_FALSE); |
1840 | 0 | PORT_SetError(PR_OUT_OF_MEMORY_ERROR); |
1841 | 0 | return NULL; |
1842 | 0 | } |
1843 | | |
1844 | | /* |
1845 | | * Callback to set Extensions in request object |
1846 | | */ |
1847 | | void |
1848 | | SetSingleReqExts(void *object, CERTCertExtension **exts) |
1849 | 0 | { |
1850 | 0 | ocspSingleRequest *singleRequest = |
1851 | 0 | (ocspSingleRequest *)object; |
1852 | 0 |
|
1853 | 0 | singleRequest->singleRequestExtensions = exts; |
1854 | 0 | } |
1855 | | |
1856 | | /* |
1857 | | * Add the Service Locator extension to the singleRequestExtensions |
1858 | | * for the given singleRequest. |
1859 | | * |
1860 | | * All errors are internal or low-level problems (e.g. no memory). |
1861 | | */ |
1862 | | static SECStatus |
1863 | | ocsp_AddServiceLocatorExtension(ocspSingleRequest *singleRequest, |
1864 | | CERTCertificate *cert) |
1865 | 0 | { |
1866 | 0 | ocspServiceLocator *serviceLocator = NULL; |
1867 | 0 | void *extensionHandle = NULL; |
1868 | 0 | SECStatus rv = SECFailure; |
1869 | 0 |
|
1870 | 0 | serviceLocator = PORT_ZNew(ocspServiceLocator); |
1871 | 0 | if (serviceLocator == NULL) |
1872 | 0 | goto loser; |
1873 | 0 | |
1874 | 0 | /* |
1875 | 0 | * Normally it would be a bad idea to do a direct reference like |
1876 | 0 | * this rather than allocate and copy the name *or* at least dup |
1877 | 0 | * a reference of the cert. But all we need is to be able to read |
1878 | 0 | * the issuer name during the encoding we are about to do, so a |
1879 | 0 | * copy is just a waste of time. |
1880 | 0 | */ |
1881 | 0 | serviceLocator->issuer = &cert->issuer; |
1882 | 0 |
|
1883 | 0 | rv = CERT_FindCertExtension(cert, SEC_OID_X509_AUTH_INFO_ACCESS, |
1884 | 0 | &serviceLocator->locator); |
1885 | 0 | if (rv != SECSuccess) { |
1886 | 0 | if (PORT_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) |
1887 | 0 | goto loser; |
1888 | 0 | } |
1889 | 0 | |
1890 | 0 | /* prepare for following loser gotos */ |
1891 | 0 | rv = SECFailure; |
1892 | 0 | PORT_SetError(0); |
1893 | 0 |
|
1894 | 0 | extensionHandle = cert_StartExtensions(singleRequest, |
1895 | 0 | singleRequest->arena, SetSingleReqExts); |
1896 | 0 | if (extensionHandle == NULL) |
1897 | 0 | goto loser; |
1898 | 0 | |
1899 | 0 | rv = CERT_EncodeAndAddExtension(extensionHandle, |
1900 | 0 | SEC_OID_PKIX_OCSP_SERVICE_LOCATOR, |
1901 | 0 | serviceLocator, PR_FALSE, |
1902 | 0 | ocsp_ServiceLocatorTemplate); |
1903 | 0 |
|
1904 | 0 | loser: |
1905 | 0 | if (extensionHandle != NULL) { |
1906 | 0 | /* |
1907 | 0 | * Either way we have to finish out the extension context (so it gets |
1908 | 0 | * freed). But careful not to override any already-set bad status. |
1909 | 0 | */ |
1910 | 0 | SECStatus tmprv = CERT_FinishExtensions(extensionHandle); |
1911 | 0 | if (rv == SECSuccess) |
1912 | 0 | rv = tmprv; |
1913 | 0 | } |
1914 | 0 |
|
1915 | 0 | /* |
1916 | 0 | * Finally, free the serviceLocator structure itself and we are done. |
1917 | 0 | */ |
1918 | 0 | if (serviceLocator != NULL) { |
1919 | 0 | if (serviceLocator->locator.data != NULL) |
1920 | 0 | SECITEM_FreeItem(&serviceLocator->locator, PR_FALSE); |
1921 | 0 | PORT_Free(serviceLocator); |
1922 | 0 | } |
1923 | 0 |
|
1924 | 0 | return rv; |
1925 | 0 | } |
1926 | | |
1927 | | /* |
1928 | | * Creates an array of ocspSingleRequest based on a list of certs. |
1929 | | * Note that the code which later compares the request list with the |
1930 | | * response expects this array to be in the exact same order as the |
1931 | | * certs are found in the list. It would be harder to change that |
1932 | | * order than preserve it, but since the requirement is not obvious, |
1933 | | * it deserves to be mentioned. |
1934 | | * |
1935 | | * Any problem causes a null return and error set: |
1936 | | * SEC_ERROR_UNKNOWN_ISSUER |
1937 | | * Other errors are low-level problems (no memory, bad database, etc.). |
1938 | | */ |
1939 | | static ocspSingleRequest ** |
1940 | | ocsp_CreateSingleRequestList(PLArenaPool *arena, CERTCertList *certList, |
1941 | | PRTime time, PRBool includeLocator) |
1942 | 0 | { |
1943 | 0 | ocspSingleRequest **requestList = NULL; |
1944 | 0 | CERTCertListNode *node = NULL; |
1945 | 0 | int i, count; |
1946 | 0 | void *mark = PORT_ArenaMark(arena); |
1947 | 0 |
|
1948 | 0 | node = CERT_LIST_HEAD(certList); |
1949 | 0 | for (count = 0; !CERT_LIST_END(node, certList); count++) { |
1950 | 0 | node = CERT_LIST_NEXT(node); |
1951 | 0 | } |
1952 | 0 |
|
1953 | 0 | if (count == 0) |
1954 | 0 | goto loser; |
1955 | 0 | |
1956 | 0 | requestList = PORT_ArenaNewArray(arena, ocspSingleRequest *, count + 1); |
1957 | 0 | if (requestList == NULL) |
1958 | 0 | goto loser; |
1959 | 0 | |
1960 | 0 | node = CERT_LIST_HEAD(certList); |
1961 | 0 | for (i = 0; !CERT_LIST_END(node, certList); i++) { |
1962 | 0 | requestList[i] = PORT_ArenaZNew(arena, ocspSingleRequest); |
1963 | 0 | if (requestList[i] == NULL) |
1964 | 0 | goto loser; |
1965 | 0 | |
1966 | 0 | OCSP_TRACE(("OCSP CERT_CreateOCSPRequest %s\n", node->cert->subjectName)); |
1967 | 0 | requestList[i]->arena = arena; |
1968 | 0 | requestList[i]->reqCert = ocsp_CreateCertID(arena, node->cert, time); |
1969 | 0 | if (requestList[i]->reqCert == NULL) |
1970 | 0 | goto loser; |
1971 | 0 | |
1972 | 0 | if (includeLocator == PR_TRUE) { |
1973 | 0 | SECStatus rv; |
1974 | 0 |
|
1975 | 0 | rv = ocsp_AddServiceLocatorExtension(requestList[i], node->cert); |
1976 | 0 | if (rv != SECSuccess) |
1977 | 0 | goto loser; |
1978 | 0 | } |
1979 | 0 | |
1980 | 0 | node = CERT_LIST_NEXT(node); |
1981 | 0 | } |
1982 | 0 |
|
1983 | 0 | PORT_Assert(i == count); |
1984 | 0 |
|
1985 | 0 | PORT_ArenaUnmark(arena, mark); |
1986 | 0 | requestList[i] = NULL; |
1987 | 0 | return requestList; |
1988 | 0 |
|
1989 | 0 | loser: |
1990 | 0 | PORT_ArenaRelease(arena, mark); |
1991 | 0 | return NULL; |
1992 | 0 | } |
1993 | | |
1994 | | static ocspSingleRequest ** |
1995 | | ocsp_CreateRequestFromCert(PLArenaPool *arena, |
1996 | | CERTOCSPCertID *certID, |
1997 | | CERTCertificate *singleCert, |
1998 | | PRTime time, |
1999 | | PRBool includeLocator) |
2000 | 0 | { |
2001 | 0 | ocspSingleRequest **requestList = NULL; |
2002 | 0 | void *mark = PORT_ArenaMark(arena); |
2003 | 0 | PORT_Assert(certID != NULL && singleCert != NULL); |
2004 | 0 |
|
2005 | 0 | /* meaning of value 2: one entry + one end marker */ |
2006 | 0 | requestList = PORT_ArenaNewArray(arena, ocspSingleRequest *, 2); |
2007 | 0 | if (requestList == NULL) |
2008 | 0 | goto loser; |
2009 | 0 | requestList[0] = PORT_ArenaZNew(arena, ocspSingleRequest); |
2010 | 0 | if (requestList[0] == NULL) |
2011 | 0 | goto loser; |
2012 | 0 | requestList[0]->arena = arena; |
2013 | 0 | /* certID will live longer than the request */ |
2014 | 0 | requestList[0]->reqCert = certID; |
2015 | 0 |
|
2016 | 0 | if (includeLocator == PR_TRUE) { |
2017 | 0 | SECStatus rv; |
2018 | 0 | rv = ocsp_AddServiceLocatorExtension(requestList[0], singleCert); |
2019 | 0 | if (rv != SECSuccess) |
2020 | 0 | goto loser; |
2021 | 0 | } |
2022 | 0 | |
2023 | 0 | PORT_ArenaUnmark(arena, mark); |
2024 | 0 | requestList[1] = NULL; |
2025 | 0 | return requestList; |
2026 | 0 | |
2027 | 0 | loser: |
2028 | 0 | PORT_ArenaRelease(arena, mark); |
2029 | 0 | return NULL; |
2030 | 0 | } |
2031 | | |
2032 | | static CERTOCSPRequest * |
2033 | | ocsp_prepareEmptyOCSPRequest(void) |
2034 | 0 | { |
2035 | 0 | PLArenaPool *arena = NULL; |
2036 | 0 | CERTOCSPRequest *request = NULL; |
2037 | 0 | ocspTBSRequest *tbsRequest = NULL; |
2038 | 0 |
|
2039 | 0 | arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
2040 | 0 | if (arena == NULL) { |
2041 | 0 | goto loser; |
2042 | 0 | } |
2043 | 0 | request = PORT_ArenaZNew(arena, CERTOCSPRequest); |
2044 | 0 | if (request == NULL) { |
2045 | 0 | goto loser; |
2046 | 0 | } |
2047 | 0 | request->arena = arena; |
2048 | 0 |
|
2049 | 0 | tbsRequest = PORT_ArenaZNew(arena, ocspTBSRequest); |
2050 | 0 | if (tbsRequest == NULL) { |
2051 | 0 | goto loser; |
2052 | 0 | } |
2053 | 0 | request->tbsRequest = tbsRequest; |
2054 | 0 | /* version 1 is the default, so we need not fill in a version number */ |
2055 | 0 | return request; |
2056 | 0 | |
2057 | 0 | loser: |
2058 | 0 | if (arena != NULL) { |
2059 | 0 | PORT_FreeArena(arena, PR_FALSE); |
2060 | 0 | } |
2061 | 0 | return NULL; |
2062 | 0 | } |
2063 | | |
2064 | | CERTOCSPRequest * |
2065 | | cert_CreateSingleCertOCSPRequest(CERTOCSPCertID *certID, |
2066 | | CERTCertificate *singleCert, |
2067 | | PRTime time, |
2068 | | PRBool addServiceLocator, |
2069 | | CERTCertificate *signerCert) |
2070 | 0 | { |
2071 | 0 | CERTOCSPRequest *request; |
2072 | 0 | OCSP_TRACE(("OCSP cert_CreateSingleCertOCSPRequest %s\n", singleCert->subjectName)); |
2073 | 0 |
|
2074 | 0 | /* XXX Support for signerCert may be implemented later, |
2075 | 0 | * see also the comment in CERT_CreateOCSPRequest. |
2076 | 0 | */ |
2077 | 0 | if (signerCert != NULL) { |
2078 | 0 | PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); |
2079 | 0 | return NULL; |
2080 | 0 | } |
2081 | 0 |
|
2082 | 0 | request = ocsp_prepareEmptyOCSPRequest(); |
2083 | 0 | if (!request) |
2084 | 0 | return NULL; |
2085 | 0 | /* |
2086 | 0 | * Version 1 is the default, so we need not fill in a version number. |
2087 | 0 | * Now create the list of single requests, one for each cert. |
2088 | 0 | */ |
2089 | 0 | request->tbsRequest->requestList = |
2090 | 0 | ocsp_CreateRequestFromCert(request->arena, |
2091 | 0 | certID, |
2092 | 0 | singleCert, |
2093 | 0 | time, |
2094 | 0 | addServiceLocator); |
2095 | 0 | if (request->tbsRequest->requestList == NULL) { |
2096 | 0 | PORT_FreeArena(request->arena, PR_FALSE); |
2097 | 0 | return NULL; |
2098 | 0 | } |
2099 | 0 | return request; |
2100 | 0 | } |
2101 | | |
2102 | | /* |
2103 | | * FUNCTION: CERT_CreateOCSPRequest |
2104 | | * Creates a CERTOCSPRequest, requesting the status of the certs in |
2105 | | * the given list. |
2106 | | * INPUTS: |
2107 | | * CERTCertList *certList |
2108 | | * A list of certs for which status will be requested. |
2109 | | * Note that all of these certificates should have the same issuer, |
2110 | | * or it's expected the response will be signed by a trusted responder. |
2111 | | * If the certs need to be broken up into multiple requests, that |
2112 | | * must be handled by the caller (and thus by having multiple calls |
2113 | | * to this routine), who knows about where the request(s) are being |
2114 | | * sent and whether there are any trusted responders in place. |
2115 | | * PRTime time |
2116 | | * Indicates the time for which the certificate status is to be |
2117 | | * determined -- this may be used in the search for the cert's issuer |
2118 | | * but has no effect on the request itself. |
2119 | | * PRBool addServiceLocator |
2120 | | * If true, the Service Locator extension should be added to the |
2121 | | * single request(s) for each cert. |
2122 | | * CERTCertificate *signerCert |
2123 | | * If non-NULL, means sign the request using this cert. Otherwise, |
2124 | | * do not sign. |
2125 | | * XXX note that request signing is not yet supported; see comment in code |
2126 | | * RETURN: |
2127 | | * A pointer to a CERTOCSPRequest structure containing an OCSP request |
2128 | | * for the cert list. On error, null is returned, with an error set |
2129 | | * indicating the reason. This is likely SEC_ERROR_UNKNOWN_ISSUER. |
2130 | | * (The issuer is needed to create a request for the certificate.) |
2131 | | * Other errors are low-level problems (no memory, bad database, etc.). |
2132 | | */ |
2133 | | CERTOCSPRequest * |
2134 | | CERT_CreateOCSPRequest(CERTCertList *certList, PRTime time, |
2135 | | PRBool addServiceLocator, |
2136 | | CERTCertificate *signerCert) |
2137 | 0 | { |
2138 | 0 | CERTOCSPRequest *request = NULL; |
2139 | 0 |
|
2140 | 0 | if (!certList) { |
2141 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
2142 | 0 | return NULL; |
2143 | 0 | } |
2144 | 0 | /* |
2145 | 0 | * XXX When we are prepared to put signing of requests back in, |
2146 | 0 | * we will need to allocate a signature |
2147 | 0 | * structure for the request, fill in the "derCerts" field in it, |
2148 | 0 | * save the signerCert there, as well as fill in the "requestorName" |
2149 | 0 | * field of the tbsRequest. |
2150 | 0 | */ |
2151 | 0 | if (signerCert != NULL) { |
2152 | 0 | PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); |
2153 | 0 | return NULL; |
2154 | 0 | } |
2155 | 0 | request = ocsp_prepareEmptyOCSPRequest(); |
2156 | 0 | if (!request) |
2157 | 0 | return NULL; |
2158 | 0 | /* |
2159 | 0 | * Now create the list of single requests, one for each cert. |
2160 | 0 | */ |
2161 | 0 | request->tbsRequest->requestList = |
2162 | 0 | ocsp_CreateSingleRequestList(request->arena, |
2163 | 0 | certList, |
2164 | 0 | time, |
2165 | 0 | addServiceLocator); |
2166 | 0 | if (request->tbsRequest->requestList == NULL) { |
2167 | 0 | PORT_FreeArena(request->arena, PR_FALSE); |
2168 | 0 | return NULL; |
2169 | 0 | } |
2170 | 0 | return request; |
2171 | 0 | } |
2172 | | |
2173 | | /* |
2174 | | * FUNCTION: CERT_AddOCSPAcceptableResponses |
2175 | | * Add the AcceptableResponses extension to an OCSP Request. |
2176 | | * INPUTS: |
2177 | | * CERTOCSPRequest *request |
2178 | | * The request to which the extension should be added. |
2179 | | * ... |
2180 | | * A list (of one or more) of SECOidTag -- each of the response types |
2181 | | * to be added. The last OID *must* be SEC_OID_PKIX_OCSP_BASIC_RESPONSE. |
2182 | | * (This marks the end of the list, and it must be specified because a |
2183 | | * client conforming to the OCSP standard is required to handle the basic |
2184 | | * response type.) The OIDs are not checked in any way. |
2185 | | * RETURN: |
2186 | | * SECSuccess if the extension is added; SECFailure if anything goes wrong. |
2187 | | * All errors are internal or low-level problems (e.g. no memory). |
2188 | | */ |
2189 | | |
2190 | | void |
2191 | | SetRequestExts(void *object, CERTCertExtension **exts) |
2192 | 0 | { |
2193 | 0 | CERTOCSPRequest *request = (CERTOCSPRequest *)object; |
2194 | 0 |
|
2195 | 0 | request->tbsRequest->requestExtensions = exts; |
2196 | 0 | } |
2197 | | |
2198 | | #if defined(__GNUC__) && !defined(NSS_NO_GCC48) |
2199 | | #pragma GCC diagnostic push |
2200 | | #pragma GCC diagnostic ignored "-Wvarargs" |
2201 | | #endif |
2202 | | SECStatus |
2203 | | CERT_AddOCSPAcceptableResponses(CERTOCSPRequest *request, |
2204 | | SECOidTag responseType0, ...) |
2205 | 0 | { |
2206 | 0 | void *extHandle; |
2207 | 0 | va_list ap; |
2208 | 0 | int i, count; |
2209 | 0 | SECOidTag responseType; |
2210 | 0 | SECOidData *responseOid; |
2211 | 0 | SECItem **acceptableResponses = NULL; |
2212 | 0 | SECStatus rv = SECFailure; |
2213 | 0 |
|
2214 | 0 | extHandle = request->tbsRequest->extensionHandle; |
2215 | 0 | if (extHandle == NULL) { |
2216 | 0 | extHandle = cert_StartExtensions(request, request->arena, SetRequestExts); |
2217 | 0 | if (extHandle == NULL) |
2218 | 0 | goto loser; |
2219 | 0 | } |
2220 | 0 | |
2221 | 0 | /* Count number of OIDS going into the extension value. */ |
2222 | 0 | count = 1; |
2223 | 0 | if (responseType0 != SEC_OID_PKIX_OCSP_BASIC_RESPONSE) { |
2224 | 0 | va_start(ap, responseType0); |
2225 | 0 | do { |
2226 | 0 | count++; |
2227 | 0 | responseType = va_arg(ap, SECOidTag); |
2228 | 0 | } while (responseType != SEC_OID_PKIX_OCSP_BASIC_RESPONSE); |
2229 | 0 | va_end(ap); |
2230 | 0 | } |
2231 | 0 |
|
2232 | 0 | acceptableResponses = PORT_NewArray(SECItem *, count + 1); |
2233 | 0 | if (acceptableResponses == NULL) |
2234 | 0 | goto loser; |
2235 | 0 | |
2236 | 0 | i = 0; |
2237 | 0 | responseOid = SECOID_FindOIDByTag(responseType0); |
2238 | 0 | acceptableResponses[i++] = &(responseOid->oid); |
2239 | 0 | if (count > 1) { |
2240 | 0 | va_start(ap, responseType0); |
2241 | 0 | for (; i < count; i++) { |
2242 | 0 | responseType = va_arg(ap, SECOidTag); |
2243 | 0 | responseOid = SECOID_FindOIDByTag(responseType); |
2244 | 0 | acceptableResponses[i] = &(responseOid->oid); |
2245 | 0 | } |
2246 | 0 | va_end(ap); |
2247 | 0 | } |
2248 | 0 | acceptableResponses[i] = NULL; |
2249 | 0 |
|
2250 | 0 | rv = CERT_EncodeAndAddExtension(extHandle, SEC_OID_PKIX_OCSP_RESPONSE, |
2251 | 0 | &acceptableResponses, PR_FALSE, |
2252 | 0 | SEC_ASN1_GET(SEC_SequenceOfObjectIDTemplate)); |
2253 | 0 | if (rv != SECSuccess) |
2254 | 0 | goto loser; |
2255 | 0 | |
2256 | 0 | PORT_Free(acceptableResponses); |
2257 | 0 | if (request->tbsRequest->extensionHandle == NULL) |
2258 | 0 | request->tbsRequest->extensionHandle = extHandle; |
2259 | 0 | return SECSuccess; |
2260 | 0 |
|
2261 | 0 | loser: |
2262 | 0 | if (acceptableResponses != NULL) |
2263 | 0 | PORT_Free(acceptableResponses); |
2264 | 0 | if (extHandle != NULL) |
2265 | 0 | (void)CERT_FinishExtensions(extHandle); |
2266 | 0 | return rv; |
2267 | 0 | } |
2268 | | #if defined(__GNUC__) && !defined(NSS_NO_GCC48) |
2269 | | #pragma GCC diagnostic pop |
2270 | | #endif |
2271 | | |
2272 | | /* |
2273 | | * FUNCTION: CERT_DestroyOCSPRequest |
2274 | | * Frees an OCSP Request structure. |
2275 | | * INPUTS: |
2276 | | * CERTOCSPRequest *request |
2277 | | * Pointer to CERTOCSPRequest to be freed. |
2278 | | * RETURN: |
2279 | | * No return value; no errors. |
2280 | | */ |
2281 | | void |
2282 | | CERT_DestroyOCSPRequest(CERTOCSPRequest *request) |
2283 | 0 | { |
2284 | 0 | if (request == NULL) |
2285 | 0 | return; |
2286 | 0 | |
2287 | 0 | if (request->tbsRequest != NULL) { |
2288 | 0 | if (request->tbsRequest->requestorName != NULL) |
2289 | 0 | CERT_DestroyGeneralNameList(request->tbsRequest->requestorName); |
2290 | 0 | if (request->tbsRequest->extensionHandle != NULL) |
2291 | 0 | (void)CERT_FinishExtensions(request->tbsRequest->extensionHandle); |
2292 | 0 | } |
2293 | 0 |
|
2294 | 0 | if (request->optionalSignature != NULL) { |
2295 | 0 | if (request->optionalSignature->cert != NULL) |
2296 | 0 | CERT_DestroyCertificate(request->optionalSignature->cert); |
2297 | 0 |
|
2298 | 0 | /* |
2299 | 0 | * XXX Need to free derCerts? Or do they come out of arena? |
2300 | 0 | * (Currently we never fill in derCerts, which is why the |
2301 | 0 | * answer is not obvious. Once we do, add any necessary code |
2302 | 0 | * here and remove this comment.) |
2303 | 0 | */ |
2304 | 0 | } |
2305 | 0 |
|
2306 | 0 | /* |
2307 | 0 | * We should actually never have a request without an arena, |
2308 | 0 | * but check just in case. (If there isn't one, there is not |
2309 | 0 | * much we can do about it...) |
2310 | 0 | */ |
2311 | 0 | PORT_Assert(request->arena != NULL); |
2312 | 0 | if (request->arena != NULL) |
2313 | 0 | PORT_FreeArena(request->arena, PR_FALSE); |
2314 | 0 | } |
2315 | | |
2316 | | /* |
2317 | | * RESPONSE SUPPORT FUNCTIONS (encode/create/decode/destroy): |
2318 | | */ |
2319 | | |
2320 | | /* |
2321 | | * Helper function for encoding or decoding a ResponderID -- based on the |
2322 | | * given type, return the associated template for that choice. |
2323 | | */ |
2324 | | static const SEC_ASN1Template * |
2325 | | ocsp_ResponderIDTemplateByType(CERTOCSPResponderIDType responderIDType) |
2326 | 0 | { |
2327 | 0 | const SEC_ASN1Template *responderIDTemplate; |
2328 | 0 |
|
2329 | 0 | switch (responderIDType) { |
2330 | 0 | case ocspResponderID_byName: |
2331 | 0 | responderIDTemplate = ocsp_ResponderIDByNameTemplate; |
2332 | 0 | break; |
2333 | 0 | case ocspResponderID_byKey: |
2334 | 0 | responderIDTemplate = ocsp_ResponderIDByKeyTemplate; |
2335 | 0 | break; |
2336 | 0 | case ocspResponderID_other: |
2337 | 0 | default: |
2338 | 0 | PORT_Assert(responderIDType == ocspResponderID_other); |
2339 | 0 | responderIDTemplate = ocsp_ResponderIDOtherTemplate; |
2340 | 0 | break; |
2341 | 0 | } |
2342 | 0 |
|
2343 | 0 | return responderIDTemplate; |
2344 | 0 | } |
2345 | | |
2346 | | /* |
2347 | | * Helper function for encoding or decoding a CertStatus -- based on the |
2348 | | * given type, return the associated template for that choice. |
2349 | | */ |
2350 | | static const SEC_ASN1Template * |
2351 | | ocsp_CertStatusTemplateByType(ocspCertStatusType certStatusType) |
2352 | 0 | { |
2353 | 0 | const SEC_ASN1Template *certStatusTemplate; |
2354 | 0 |
|
2355 | 0 | switch (certStatusType) { |
2356 | 0 | case ocspCertStatus_good: |
2357 | 0 | certStatusTemplate = ocsp_CertStatusGoodTemplate; |
2358 | 0 | break; |
2359 | 0 | case ocspCertStatus_revoked: |
2360 | 0 | certStatusTemplate = ocsp_CertStatusRevokedTemplate; |
2361 | 0 | break; |
2362 | 0 | case ocspCertStatus_unknown: |
2363 | 0 | certStatusTemplate = ocsp_CertStatusUnknownTemplate; |
2364 | 0 | break; |
2365 | 0 | case ocspCertStatus_other: |
2366 | 0 | default: |
2367 | 0 | PORT_Assert(certStatusType == ocspCertStatus_other); |
2368 | 0 | certStatusTemplate = ocsp_CertStatusOtherTemplate; |
2369 | 0 | break; |
2370 | 0 | } |
2371 | 0 |
|
2372 | 0 | return certStatusTemplate; |
2373 | 0 | } |
2374 | | |
2375 | | /* |
2376 | | * Helper function for decoding a certStatus -- turn the actual DER tag |
2377 | | * into our local translation. |
2378 | | */ |
2379 | | static ocspCertStatusType |
2380 | | ocsp_CertStatusTypeByTag(int derTag) |
2381 | 0 | { |
2382 | 0 | ocspCertStatusType certStatusType; |
2383 | 0 |
|
2384 | 0 | switch (derTag) { |
2385 | 0 | case 0: |
2386 | 0 | certStatusType = ocspCertStatus_good; |
2387 | 0 | break; |
2388 | 0 | case 1: |
2389 | 0 | certStatusType = ocspCertStatus_revoked; |
2390 | 0 | break; |
2391 | 0 | case 2: |
2392 | 0 | certStatusType = ocspCertStatus_unknown; |
2393 | 0 | break; |
2394 | 0 | default: |
2395 | 0 | certStatusType = ocspCertStatus_other; |
2396 | 0 | break; |
2397 | 0 | } |
2398 | 0 | |
2399 | 0 | return certStatusType; |
2400 | 0 | } |
2401 | | |
2402 | | /* |
2403 | | * Helper function for decoding SingleResponses -- they each contain |
2404 | | * a status which is encoded as CHOICE, which needs to be decoded "by hand". |
2405 | | * |
2406 | | * Note -- on error, this routine does not release the memory it may |
2407 | | * have allocated; it expects its caller to do that. |
2408 | | */ |
2409 | | static SECStatus |
2410 | | ocsp_FinishDecodingSingleResponses(PLArenaPool *reqArena, |
2411 | | CERTOCSPSingleResponse **responses) |
2412 | 0 | { |
2413 | 0 | ocspCertStatus *certStatus; |
2414 | 0 | ocspCertStatusType certStatusType; |
2415 | 0 | const SEC_ASN1Template *certStatusTemplate; |
2416 | 0 | int derTag; |
2417 | 0 | int i; |
2418 | 0 | SECStatus rv = SECFailure; |
2419 | 0 |
|
2420 | 0 | if (!reqArena) { |
2421 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
2422 | 0 | return SECFailure; |
2423 | 0 | } |
2424 | 0 |
|
2425 | 0 | if (responses == NULL) /* nothing to do */ |
2426 | 0 | return SECSuccess; |
2427 | 0 | |
2428 | 0 | for (i = 0; responses[i] != NULL; i++) { |
2429 | 0 | SECItem *newStatus; |
2430 | 0 | /* |
2431 | 0 | * The following assert points out internal errors (problems in |
2432 | 0 | * the template definitions or in the ASN.1 decoder itself, etc.). |
2433 | 0 | */ |
2434 | 0 | PORT_Assert(responses[i]->derCertStatus.data != NULL); |
2435 | 0 |
|
2436 | 0 | derTag = responses[i]->derCertStatus.data[0] & SEC_ASN1_TAGNUM_MASK; |
2437 | 0 | certStatusType = ocsp_CertStatusTypeByTag(derTag); |
2438 | 0 | certStatusTemplate = ocsp_CertStatusTemplateByType(certStatusType); |
2439 | 0 |
|
2440 | 0 | certStatus = PORT_ArenaZAlloc(reqArena, sizeof(ocspCertStatus)); |
2441 | 0 | if (certStatus == NULL) { |
2442 | 0 | goto loser; |
2443 | 0 | } |
2444 | 0 | newStatus = SECITEM_ArenaDupItem(reqArena, &responses[i]->derCertStatus); |
2445 | 0 | if (!newStatus) { |
2446 | 0 | goto loser; |
2447 | 0 | } |
2448 | 0 | rv = SEC_QuickDERDecodeItem(reqArena, certStatus, certStatusTemplate, |
2449 | 0 | newStatus); |
2450 | 0 | if (rv != SECSuccess) { |
2451 | 0 | if (PORT_GetError() == SEC_ERROR_BAD_DER) |
2452 | 0 | PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE); |
2453 | 0 | goto loser; |
2454 | 0 | } |
2455 | 0 |
|
2456 | 0 | certStatus->certStatusType = certStatusType; |
2457 | 0 | responses[i]->certStatus = certStatus; |
2458 | 0 | } |
2459 | 0 |
|
2460 | 0 | return SECSuccess; |
2461 | 0 | |
2462 | 0 | loser: |
2463 | 0 | return rv; |
2464 | 0 | } |
2465 | | |
2466 | | /* |
2467 | | * Helper function for decoding a responderID -- turn the actual DER tag |
2468 | | * into our local translation. |
2469 | | */ |
2470 | | static CERTOCSPResponderIDType |
2471 | | ocsp_ResponderIDTypeByTag(int derTag) |
2472 | 0 | { |
2473 | 0 | CERTOCSPResponderIDType responderIDType; |
2474 | 0 |
|
2475 | 0 | switch (derTag) { |
2476 | 0 | case 1: |
2477 | 0 | responderIDType = ocspResponderID_byName; |
2478 | 0 | break; |
2479 | 0 | case 2: |
2480 | 0 | responderIDType = ocspResponderID_byKey; |
2481 | 0 | break; |
2482 | 0 | default: |
2483 | 0 | responderIDType = ocspResponderID_other; |
2484 | 0 | break; |
2485 | 0 | } |
2486 | 0 | |
2487 | 0 | return responderIDType; |
2488 | 0 | } |
2489 | | |
2490 | | /* |
2491 | | * Decode "src" as a BasicOCSPResponse, returning the result. |
2492 | | */ |
2493 | | static ocspBasicOCSPResponse * |
2494 | | ocsp_DecodeBasicOCSPResponse(PLArenaPool *arena, SECItem *src) |
2495 | 0 | { |
2496 | 0 | void *mark; |
2497 | 0 | ocspBasicOCSPResponse *basicResponse; |
2498 | 0 | ocspResponseData *responseData; |
2499 | 0 | ocspResponderID *responderID; |
2500 | 0 | CERTOCSPResponderIDType responderIDType; |
2501 | 0 | const SEC_ASN1Template *responderIDTemplate; |
2502 | 0 | int derTag; |
2503 | 0 | SECStatus rv; |
2504 | 0 | SECItem newsrc; |
2505 | 0 |
|
2506 | 0 | mark = PORT_ArenaMark(arena); |
2507 | 0 |
|
2508 | 0 | basicResponse = PORT_ArenaZAlloc(arena, sizeof(ocspBasicOCSPResponse)); |
2509 | 0 | if (basicResponse == NULL) { |
2510 | 0 | goto loser; |
2511 | 0 | } |
2512 | 0 | |
2513 | 0 | /* copy the DER into the arena, since Quick DER returns data that points |
2514 | 0 | into the DER input, which may get freed by the caller */ |
2515 | 0 | rv = SECITEM_CopyItem(arena, &newsrc, src); |
2516 | 0 | if (rv != SECSuccess) { |
2517 | 0 | goto loser; |
2518 | 0 | } |
2519 | 0 | |
2520 | 0 | rv = SEC_QuickDERDecodeItem(arena, basicResponse, |
2521 | 0 | ocsp_BasicOCSPResponseTemplate, &newsrc); |
2522 | 0 | if (rv != SECSuccess) { |
2523 | 0 | if (PORT_GetError() == SEC_ERROR_BAD_DER) |
2524 | 0 | PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE); |
2525 | 0 | goto loser; |
2526 | 0 | } |
2527 | 0 |
|
2528 | 0 | responseData = basicResponse->tbsResponseData; |
2529 | 0 |
|
2530 | 0 | /* |
2531 | 0 | * The following asserts point out internal errors (problems in |
2532 | 0 | * the template definitions or in the ASN.1 decoder itself, etc.). |
2533 | 0 | */ |
2534 | 0 | PORT_Assert(responseData != NULL); |
2535 | 0 | PORT_Assert(responseData->derResponderID.data != NULL); |
2536 | 0 |
|
2537 | 0 | /* |
2538 | 0 | * XXX Because responderID is a CHOICE, which is not currently handled |
2539 | 0 | * by our ASN.1 decoder, we have to decode it "by hand". |
2540 | 0 | */ |
2541 | 0 | derTag = responseData->derResponderID.data[0] & SEC_ASN1_TAGNUM_MASK; |
2542 | 0 | responderIDType = ocsp_ResponderIDTypeByTag(derTag); |
2543 | 0 | responderIDTemplate = ocsp_ResponderIDTemplateByType(responderIDType); |
2544 | 0 |
|
2545 | 0 | responderID = PORT_ArenaZAlloc(arena, sizeof(ocspResponderID)); |
2546 | 0 | if (responderID == NULL) { |
2547 | 0 | goto loser; |
2548 | 0 | } |
2549 | 0 | |
2550 | 0 | rv = SEC_QuickDERDecodeItem(arena, responderID, responderIDTemplate, |
2551 | 0 | &responseData->derResponderID); |
2552 | 0 | if (rv != SECSuccess) { |
2553 | 0 | if (PORT_GetError() == SEC_ERROR_BAD_DER) |
2554 | 0 | PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE); |
2555 | 0 | goto loser; |
2556 | 0 | } |
2557 | 0 |
|
2558 | 0 | responderID->responderIDType = responderIDType; |
2559 | 0 | responseData->responderID = responderID; |
2560 | 0 |
|
2561 | 0 | /* |
2562 | 0 | * XXX Each SingleResponse also contains a CHOICE, which has to be |
2563 | 0 | * fixed up by hand. |
2564 | 0 | */ |
2565 | 0 | rv = ocsp_FinishDecodingSingleResponses(arena, responseData->responses); |
2566 | 0 | if (rv != SECSuccess) { |
2567 | 0 | goto loser; |
2568 | 0 | } |
2569 | 0 | |
2570 | 0 | PORT_ArenaUnmark(arena, mark); |
2571 | 0 | return basicResponse; |
2572 | 0 | |
2573 | 0 | loser: |
2574 | 0 | PORT_ArenaRelease(arena, mark); |
2575 | 0 | return NULL; |
2576 | 0 | } |
2577 | | |
2578 | | /* |
2579 | | * Decode the responseBytes based on the responseType found in "rbytes", |
2580 | | * leaving the resulting translated/decoded information in there as well. |
2581 | | */ |
2582 | | static SECStatus |
2583 | | ocsp_DecodeResponseBytes(PLArenaPool *arena, ocspResponseBytes *rbytes) |
2584 | 0 | { |
2585 | 0 | if (rbytes == NULL) { |
2586 | 0 | PORT_SetError(SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE); |
2587 | 0 | return SECFailure; |
2588 | 0 | } |
2589 | 0 |
|
2590 | 0 | rbytes->responseTypeTag = SECOID_FindOIDTag(&rbytes->responseType); |
2591 | 0 | switch (rbytes->responseTypeTag) { |
2592 | 0 | case SEC_OID_PKIX_OCSP_BASIC_RESPONSE: { |
2593 | 0 | ocspBasicOCSPResponse *basicResponse; |
2594 | 0 |
|
2595 | 0 | basicResponse = ocsp_DecodeBasicOCSPResponse(arena, |
2596 | 0 | &rbytes->response); |
2597 | 0 | if (basicResponse == NULL) |
2598 | 0 | return SECFailure; |
2599 | 0 | |
2600 | 0 | rbytes->decodedResponse.basic = basicResponse; |
2601 | 0 | } break; |
2602 | 0 |
|
2603 | 0 | /* |
2604 | 0 | * Add new/future response types here. |
2605 | 0 | */ |
2606 | 0 |
|
2607 | 0 | default: |
2608 | 0 | PORT_SetError(SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE); |
2609 | 0 | return SECFailure; |
2610 | 0 | } |
2611 | 0 |
|
2612 | 0 | return SECSuccess; |
2613 | 0 | } |
2614 | | |
2615 | | /* |
2616 | | * FUNCTION: CERT_DecodeOCSPResponse |
2617 | | * Decode a DER encoded OCSP Response. |
2618 | | * INPUTS: |
2619 | | * SECItem *src |
2620 | | * Pointer to a SECItem holding DER encoded OCSP Response. |
2621 | | * RETURN: |
2622 | | * Returns a pointer to a CERTOCSPResponse (the decoded OCSP Response); |
2623 | | * the caller is responsible for destroying it. Or NULL if error (either |
2624 | | * response could not be decoded (SEC_ERROR_OCSP_MALFORMED_RESPONSE), |
2625 | | * it was of an unexpected type (SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE), |
2626 | | * or a low-level or internal error occurred). |
2627 | | */ |
2628 | | CERTOCSPResponse * |
2629 | | CERT_DecodeOCSPResponse(const SECItem *src) |
2630 | 0 | { |
2631 | 0 | PLArenaPool *arena = NULL; |
2632 | 0 | CERTOCSPResponse *response = NULL; |
2633 | 0 | SECStatus rv = SECFailure; |
2634 | 0 | ocspResponseStatus sv; |
2635 | 0 | SECItem newSrc; |
2636 | 0 |
|
2637 | 0 | arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
2638 | 0 | if (arena == NULL) { |
2639 | 0 | goto loser; |
2640 | 0 | } |
2641 | 0 | response = (CERTOCSPResponse *)PORT_ArenaZAlloc(arena, |
2642 | 0 | sizeof(CERTOCSPResponse)); |
2643 | 0 | if (response == NULL) { |
2644 | 0 | goto loser; |
2645 | 0 | } |
2646 | 0 | response->arena = arena; |
2647 | 0 |
|
2648 | 0 | /* copy the DER into the arena, since Quick DER returns data that points |
2649 | 0 | into the DER input, which may get freed by the caller */ |
2650 | 0 | rv = SECITEM_CopyItem(arena, &newSrc, src); |
2651 | 0 | if (rv != SECSuccess) { |
2652 | 0 | goto loser; |
2653 | 0 | } |
2654 | 0 | |
2655 | 0 | rv = SEC_QuickDERDecodeItem(arena, response, ocsp_OCSPResponseTemplate, &newSrc); |
2656 | 0 | if (rv != SECSuccess) { |
2657 | 0 | if (PORT_GetError() == SEC_ERROR_BAD_DER) |
2658 | 0 | PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE); |
2659 | 0 | goto loser; |
2660 | 0 | } |
2661 | 0 |
|
2662 | 0 | sv = (ocspResponseStatus)DER_GetInteger(&response->responseStatus); |
2663 | 0 | response->statusValue = sv; |
2664 | 0 | if (sv != ocspResponse_successful) { |
2665 | 0 | /* |
2666 | 0 | * If the response status is anything but successful, then we |
2667 | 0 | * are all done with decoding; the status is all there is. |
2668 | 0 | */ |
2669 | 0 | return response; |
2670 | 0 | } |
2671 | 0 | |
2672 | 0 | /* |
2673 | 0 | * A successful response contains much more information, still encoded. |
2674 | 0 | * Now we need to decode that. |
2675 | 0 | */ |
2676 | 0 | rv = ocsp_DecodeResponseBytes(arena, response->responseBytes); |
2677 | 0 | if (rv != SECSuccess) { |
2678 | 0 | goto loser; |
2679 | 0 | } |
2680 | 0 | |
2681 | 0 | return response; |
2682 | 0 | |
2683 | 0 | loser: |
2684 | 0 | if (arena != NULL) { |
2685 | 0 | PORT_FreeArena(arena, PR_FALSE); |
2686 | 0 | } |
2687 | 0 | return NULL; |
2688 | 0 | } |
2689 | | |
2690 | | /* |
2691 | | * The way an OCSPResponse is defined, there are many levels to descend |
2692 | | * before getting to the actual response information. And along the way |
2693 | | * we need to check that the response *type* is recognizable, which for |
2694 | | * now means that it is a BasicOCSPResponse, because that is the only |
2695 | | * type currently defined. Rather than force all routines to perform |
2696 | | * a bunch of sanity checking every time they want to work on a response, |
2697 | | * this function isolates that and gives back the interesting part. |
2698 | | * Note that no copying is done, this just returns a pointer into the |
2699 | | * substructure of the response which is passed in. |
2700 | | * |
2701 | | * XXX This routine only works when a valid response structure is passed |
2702 | | * into it; this is checked with many assertions. Assuming the response |
2703 | | * was creating by decoding, it wouldn't make it this far without being |
2704 | | * okay. That is a sufficient assumption since the entire OCSP interface |
2705 | | * is only used internally. When this interface is officially exported, |
2706 | | * each assertion below will need to be followed-up with setting an error |
2707 | | * and returning (null). |
2708 | | * |
2709 | | * FUNCTION: ocsp_GetResponseData |
2710 | | * Returns ocspResponseData structure and a pointer to tbs response |
2711 | | * data DER from a valid ocsp response. |
2712 | | * INPUTS: |
2713 | | * CERTOCSPResponse *response |
2714 | | * structure of a valid ocsp response |
2715 | | * RETURN: |
2716 | | * Returns a pointer to ocspResponseData structure: decoded OCSP response |
2717 | | * data, and a pointer(tbsResponseDataDER) to its undecoded data DER. |
2718 | | */ |
2719 | | ocspResponseData * |
2720 | | ocsp_GetResponseData(CERTOCSPResponse *response, SECItem **tbsResponseDataDER) |
2721 | 0 | { |
2722 | 0 | ocspBasicOCSPResponse *basic; |
2723 | 0 | ocspResponseData *responseData; |
2724 | 0 |
|
2725 | 0 | PORT_Assert(response != NULL); |
2726 | 0 |
|
2727 | 0 | PORT_Assert(response->responseBytes != NULL); |
2728 | 0 |
|
2729 | 0 | PORT_Assert(response->responseBytes->responseTypeTag == |
2730 | 0 | SEC_OID_PKIX_OCSP_BASIC_RESPONSE); |
2731 | 0 |
|
2732 | 0 | basic = response->responseBytes->decodedResponse.basic; |
2733 | 0 | PORT_Assert(basic != NULL); |
2734 | 0 |
|
2735 | 0 | responseData = basic->tbsResponseData; |
2736 | 0 | PORT_Assert(responseData != NULL); |
2737 | 0 |
|
2738 | 0 | if (tbsResponseDataDER) { |
2739 | 0 | *tbsResponseDataDER = &basic->tbsResponseDataDER; |
2740 | 0 |
|
2741 | 0 | PORT_Assert((*tbsResponseDataDER)->data != NULL); |
2742 | 0 | PORT_Assert((*tbsResponseDataDER)->len != 0); |
2743 | 0 | } |
2744 | 0 |
|
2745 | 0 | return responseData; |
2746 | 0 | } |
2747 | | |
2748 | | /* |
2749 | | * Much like the routine above, except it returns the response signature. |
2750 | | * Again, no copy is done. |
2751 | | */ |
2752 | | ocspSignature * |
2753 | | ocsp_GetResponseSignature(CERTOCSPResponse *response) |
2754 | 0 | { |
2755 | 0 | ocspBasicOCSPResponse *basic; |
2756 | 0 |
|
2757 | 0 | PORT_Assert(response != NULL); |
2758 | 0 | if (NULL == response->responseBytes) { |
2759 | 0 | return NULL; |
2760 | 0 | } |
2761 | 0 | if (response->responseBytes->responseTypeTag != |
2762 | 0 | SEC_OID_PKIX_OCSP_BASIC_RESPONSE) { |
2763 | 0 | return NULL; |
2764 | 0 | } |
2765 | 0 | basic = response->responseBytes->decodedResponse.basic; |
2766 | 0 | PORT_Assert(basic != NULL); |
2767 | 0 |
|
2768 | 0 | return &(basic->responseSignature); |
2769 | 0 | } |
2770 | | |
2771 | | /* |
2772 | | * FUNCTION: CERT_DestroyOCSPResponse |
2773 | | * Frees an OCSP Response structure. |
2774 | | * INPUTS: |
2775 | | * CERTOCSPResponse *request |
2776 | | * Pointer to CERTOCSPResponse to be freed. |
2777 | | * RETURN: |
2778 | | * No return value; no errors. |
2779 | | */ |
2780 | | void |
2781 | | CERT_DestroyOCSPResponse(CERTOCSPResponse *response) |
2782 | 0 | { |
2783 | 0 | if (response != NULL) { |
2784 | 0 | ocspSignature *signature = ocsp_GetResponseSignature(response); |
2785 | 0 | if (signature && signature->cert != NULL) |
2786 | 0 | CERT_DestroyCertificate(signature->cert); |
2787 | 0 |
|
2788 | 0 | /* |
2789 | 0 | * We should actually never have a response without an arena, |
2790 | 0 | * but check just in case. (If there isn't one, there is not |
2791 | 0 | * much we can do about it...) |
2792 | 0 | */ |
2793 | 0 | PORT_Assert(response->arena != NULL); |
2794 | 0 | if (response->arena != NULL) { |
2795 | 0 | PORT_FreeArena(response->arena, PR_FALSE); |
2796 | 0 | } |
2797 | 0 | } |
2798 | 0 | } |
2799 | | |
2800 | | /* |
2801 | | * OVERALL OCSP CLIENT SUPPORT (make and send a request, verify a response): |
2802 | | */ |
2803 | | |
2804 | | /* |
2805 | | * Pick apart a URL, saving the important things in the passed-in pointers. |
2806 | | * |
2807 | | * We expect to find "http://<hostname>[:<port>]/[path]", though we will |
2808 | | * tolerate that final slash character missing, as well as beginning and |
2809 | | * trailing whitespace, and any-case-characters for "http". All of that |
2810 | | * tolerance is what complicates this routine. What we want is just to |
2811 | | * pick out the hostname, the port, and the path. |
2812 | | * |
2813 | | * On a successful return, the caller will need to free the output pieces |
2814 | | * of hostname and path, which are copies of the values found in the url. |
2815 | | */ |
2816 | | static SECStatus |
2817 | | ocsp_ParseURL(const char *url, char **pHostname, PRUint16 *pPort, char **pPath) |
2818 | 0 | { |
2819 | 0 | unsigned short port = 80; /* default, in case not in url */ |
2820 | 0 | char *hostname = NULL; |
2821 | 0 | char *path = NULL; |
2822 | 0 | const char *save; |
2823 | 0 | char c; |
2824 | 0 | int len; |
2825 | 0 |
|
2826 | 0 | if (url == NULL) |
2827 | 0 | goto loser; |
2828 | 0 | |
2829 | 0 | /* |
2830 | 0 | * Skip beginning whitespace. |
2831 | 0 | */ |
2832 | 0 | c = *url; |
2833 | 0 | while ((c == ' ' || c == '\t') && c != '\0') { |
2834 | 0 | url++; |
2835 | 0 | c = *url; |
2836 | 0 | } |
2837 | 0 | if (c == '\0') |
2838 | 0 | goto loser; |
2839 | 0 | |
2840 | 0 | /* |
2841 | 0 | * Confirm, then skip, protocol. (Since we only know how to do http, |
2842 | 0 | * that is all we will accept). |
2843 | 0 | */ |
2844 | 0 | if (PORT_Strncasecmp(url, "http://", 7) != 0) |
2845 | 0 | goto loser; |
2846 | 0 | url += 7; |
2847 | 0 |
|
2848 | 0 | /* |
2849 | 0 | * Whatever comes next is the hostname (or host IP address). We just |
2850 | 0 | * save it aside and then search for its end so we can determine its |
2851 | 0 | * length and copy it. |
2852 | 0 | * |
2853 | 0 | * XXX Note that because we treat a ':' as a terminator character |
2854 | 0 | * (and below, we expect that to mean there is a port specification |
2855 | 0 | * immediately following), we will not handle IPv6 addresses. That is |
2856 | 0 | * apparently an acceptable limitation, for the time being. Some day, |
2857 | 0 | * when there is a clear way to specify a URL with an IPv6 address that |
2858 | 0 | * can be parsed unambiguously, this code should be made to do that. |
2859 | 0 | */ |
2860 | 0 | save = url; |
2861 | 0 | c = *url; |
2862 | 0 | while (c != '/' && c != ':' && c != '\0' && c != ' ' && c != '\t') { |
2863 | 0 | url++; |
2864 | 0 | c = *url; |
2865 | 0 | } |
2866 | 0 | len = url - save; |
2867 | 0 | hostname = PORT_Alloc(len + 1); |
2868 | 0 | if (hostname == NULL) |
2869 | 0 | goto loser; |
2870 | 0 | PORT_Memcpy(hostname, save, len); |
2871 | 0 | hostname[len] = '\0'; |
2872 | 0 |
|
2873 | 0 | /* |
2874 | 0 | * Now we figure out if there was a port specified or not. |
2875 | 0 | * If so, we need to parse it (as a number) and skip it. |
2876 | 0 | */ |
2877 | 0 | if (c == ':') { |
2878 | 0 | url++; |
2879 | 0 | port = (unsigned short)PORT_Atoi(url); |
2880 | 0 | c = *url; |
2881 | 0 | while (c != '/' && c != '\0' && c != ' ' && c != '\t') { |
2882 | 0 | if (c < '0' || c > '9') |
2883 | 0 | goto loser; |
2884 | 0 | url++; |
2885 | 0 | c = *url; |
2886 | 0 | } |
2887 | 0 | } |
2888 | 0 |
|
2889 | 0 | /* |
2890 | 0 | * Last thing to find is a path. There *should* be a slash, |
2891 | 0 | * if nothing else -- but if there is not we provide one. |
2892 | 0 | */ |
2893 | 0 | if (c == '/') { |
2894 | 0 | save = url; |
2895 | 0 | while (c != '\0' && c != ' ' && c != '\t') { |
2896 | 0 | url++; |
2897 | 0 | c = *url; |
2898 | 0 | } |
2899 | 0 | len = url - save; |
2900 | 0 | path = PORT_Alloc(len + 1); |
2901 | 0 | if (path == NULL) |
2902 | 0 | goto loser; |
2903 | 0 | PORT_Memcpy(path, save, len); |
2904 | 0 | path[len] = '\0'; |
2905 | 0 | } else { |
2906 | 0 | path = PORT_Strdup("/"); |
2907 | 0 | if (path == NULL) |
2908 | 0 | goto loser; |
2909 | 0 | } |
2910 | 0 | |
2911 | 0 | *pHostname = hostname; |
2912 | 0 | *pPort = port; |
2913 | 0 | *pPath = path; |
2914 | 0 | return SECSuccess; |
2915 | 0 | |
2916 | 0 | loser: |
2917 | 0 | if (hostname != NULL) |
2918 | 0 | PORT_Free(hostname); |
2919 | 0 | PORT_SetError(SEC_ERROR_CERT_BAD_ACCESS_LOCATION); |
2920 | 0 | return SECFailure; |
2921 | 0 | } |
2922 | | |
2923 | | /* |
2924 | | * Open a socket to the specified host on the specified port, and return it. |
2925 | | * The host is either a hostname or an IP address. |
2926 | | */ |
2927 | | static PRFileDesc * |
2928 | | ocsp_ConnectToHost(const char *host, PRUint16 port) |
2929 | 0 | { |
2930 | 0 | PRFileDesc *sock = NULL; |
2931 | 0 | PRIntervalTime timeout; |
2932 | 0 | PRNetAddr addr; |
2933 | 0 | char *netdbbuf = NULL; |
2934 | 0 |
|
2935 | 0 | sock = PR_NewTCPSocket(); |
2936 | 0 | if (sock == NULL) |
2937 | 0 | goto loser; |
2938 | 0 | |
2939 | 0 | /* XXX Some day need a way to set (and get?) the following value */ |
2940 | 0 | timeout = PR_SecondsToInterval(30); |
2941 | 0 |
|
2942 | 0 | /* |
2943 | 0 | * If the following converts an IP address string in "dot notation" |
2944 | 0 | * into a PRNetAddr. If it fails, we assume that is because we do not |
2945 | 0 | * have such an address, but instead a host *name*. In that case we |
2946 | 0 | * then lookup the host by name. Using the NSPR function this way |
2947 | 0 | * means we do not have to have our own logic for distinguishing a |
2948 | 0 | * valid numerical IP address from a hostname. |
2949 | 0 | */ |
2950 | 0 | if (PR_StringToNetAddr(host, &addr) != PR_SUCCESS) { |
2951 | 0 | PRIntn hostIndex; |
2952 | 0 | PRHostEnt hostEntry; |
2953 | 0 |
|
2954 | 0 | netdbbuf = PORT_Alloc(PR_NETDB_BUF_SIZE); |
2955 | 0 | if (netdbbuf == NULL) |
2956 | 0 | goto loser; |
2957 | 0 | |
2958 | 0 | if (PR_GetHostByName(host, netdbbuf, PR_NETDB_BUF_SIZE, |
2959 | 0 | &hostEntry) != PR_SUCCESS) |
2960 | 0 | goto loser; |
2961 | 0 | |
2962 | 0 | hostIndex = 0; |
2963 | 0 | do { |
2964 | 0 | hostIndex = PR_EnumerateHostEnt(hostIndex, &hostEntry, port, &addr); |
2965 | 0 | if (hostIndex <= 0) |
2966 | 0 | goto loser; |
2967 | 0 | } while (PR_Connect(sock, &addr, timeout) != PR_SUCCESS); |
2968 | 0 |
|
2969 | 0 | PORT_Free(netdbbuf); |
2970 | 0 | } else { |
2971 | 0 | /* |
2972 | 0 | * First put the port into the address, then connect. |
2973 | 0 | */ |
2974 | 0 | if (PR_InitializeNetAddr(PR_IpAddrNull, port, &addr) != PR_SUCCESS) |
2975 | 0 | goto loser; |
2976 | 0 | if (PR_Connect(sock, &addr, timeout) != PR_SUCCESS) |
2977 | 0 | goto loser; |
2978 | 0 | } |
2979 | 0 | |
2980 | 0 | return sock; |
2981 | 0 | |
2982 | 0 | loser: |
2983 | 0 | if (sock != NULL) |
2984 | 0 | PR_Close(sock); |
2985 | 0 | if (netdbbuf != NULL) |
2986 | 0 | PORT_Free(netdbbuf); |
2987 | 0 | return NULL; |
2988 | 0 | } |
2989 | | |
2990 | | /* |
2991 | | * Sends an encoded OCSP request to the server identified by "location", |
2992 | | * and returns the socket on which it was sent (so can listen for the reply). |
2993 | | * "location" is expected to be a valid URL -- an error parsing it produces |
2994 | | * SEC_ERROR_CERT_BAD_ACCESS_LOCATION. Other errors are likely problems |
2995 | | * connecting to it, or writing to it, or allocating memory, and the low-level |
2996 | | * errors appropriate to the problem will be set. |
2997 | | * if (encodedRequest == NULL) |
2998 | | * then location MUST already include the full request, |
2999 | | * including base64 and urlencode, |
3000 | | * and the request will be sent with GET |
3001 | | * if (encodedRequest != NULL) |
3002 | | * then the request will be sent with POST |
3003 | | */ |
3004 | | static PRFileDesc * |
3005 | | ocsp_SendEncodedRequest(const char *location, const SECItem *encodedRequest) |
3006 | 0 | { |
3007 | 0 | char *hostname = NULL; |
3008 | 0 | char *path = NULL; |
3009 | 0 | PRUint16 port; |
3010 | 0 | SECStatus rv; |
3011 | 0 | PRFileDesc *sock = NULL; |
3012 | 0 | PRFileDesc *returnSock = NULL; |
3013 | 0 | char *header = NULL; |
3014 | 0 | char portstr[16]; |
3015 | 0 |
|
3016 | 0 | /* |
3017 | 0 | * Take apart the location, getting the hostname, port, and path. |
3018 | 0 | */ |
3019 | 0 | rv = ocsp_ParseURL(location, &hostname, &port, &path); |
3020 | 0 | if (rv != SECSuccess) |
3021 | 0 | goto loser; |
3022 | 0 | |
3023 | 0 | PORT_Assert(hostname != NULL); |
3024 | 0 | PORT_Assert(path != NULL); |
3025 | 0 |
|
3026 | 0 | sock = ocsp_ConnectToHost(hostname, port); |
3027 | 0 | if (sock == NULL) |
3028 | 0 | goto loser; |
3029 | 0 | |
3030 | 0 | portstr[0] = '\0'; |
3031 | 0 | if (port != 80) { |
3032 | 0 | PR_snprintf(portstr, sizeof(portstr), ":%d", port); |
3033 | 0 | } |
3034 | 0 |
|
3035 | 0 | if (!encodedRequest) { |
3036 | 0 | header = PR_smprintf("GET %s HTTP/1.0\r\n" |
3037 | 0 | "Host: %s%s\r\n\r\n", |
3038 | 0 | path, hostname, portstr); |
3039 | 0 | if (header == NULL) |
3040 | 0 | goto loser; |
3041 | 0 | |
3042 | 0 | /* |
3043 | 0 | * The NSPR documentation promises that if it can, it will write the full |
3044 | 0 | * amount; this will not return a partial value expecting us to loop. |
3045 | 0 | */ |
3046 | 0 | if (PR_Write(sock, header, (PRInt32)PORT_Strlen(header)) < 0) |
3047 | 0 | goto loser; |
3048 | 0 | } else { |
3049 | 0 | header = PR_smprintf("POST %s HTTP/1.0\r\n" |
3050 | 0 | "Host: %s%s\r\n" |
3051 | 0 | "Content-Type: application/ocsp-request\r\n" |
3052 | 0 | "Content-Length: %u\r\n\r\n", |
3053 | 0 | path, hostname, portstr, encodedRequest->len); |
3054 | 0 | if (header == NULL) |
3055 | 0 | goto loser; |
3056 | 0 | |
3057 | 0 | /* |
3058 | 0 | * The NSPR documentation promises that if it can, it will write the full |
3059 | 0 | * amount; this will not return a partial value expecting us to loop. |
3060 | 0 | */ |
3061 | 0 | if (PR_Write(sock, header, (PRInt32)PORT_Strlen(header)) < 0) |
3062 | 0 | goto loser; |
3063 | 0 | |
3064 | 0 | if (PR_Write(sock, encodedRequest->data, |
3065 | 0 | (PRInt32)encodedRequest->len) < 0) |
3066 | 0 | goto loser; |
3067 | 0 | } |
3068 | 0 | |
3069 | 0 | returnSock = sock; |
3070 | 0 | sock = NULL; |
3071 | 0 |
|
3072 | 0 | loser: |
3073 | 0 | if (header != NULL) |
3074 | 0 | PORT_Free(header); |
3075 | 0 | if (sock != NULL) |
3076 | 0 | PR_Close(sock); |
3077 | 0 | if (path != NULL) |
3078 | 0 | PORT_Free(path); |
3079 | 0 | if (hostname != NULL) |
3080 | 0 | PORT_Free(hostname); |
3081 | 0 |
|
3082 | 0 | return returnSock; |
3083 | 0 | } |
3084 | | |
3085 | | /* |
3086 | | * Read from "fd" into "buf" -- expect/attempt to read a given number of bytes |
3087 | | * Obviously, stop if hit end-of-stream. Timeout is passed in. |
3088 | | */ |
3089 | | |
3090 | | static int |
3091 | | ocsp_read(PRFileDesc *fd, char *buf, int toread, PRIntervalTime timeout) |
3092 | 0 | { |
3093 | 0 | int total = 0; |
3094 | 0 |
|
3095 | 0 | while (total < toread) { |
3096 | 0 | PRInt32 got; |
3097 | 0 |
|
3098 | 0 | got = PR_Recv(fd, buf + total, (PRInt32)(toread - total), 0, timeout); |
3099 | 0 | if (got < 0) { |
3100 | 0 | if (0 == total) { |
3101 | 0 | total = -1; /* report the error if we didn't read anything yet */ |
3102 | 0 | } |
3103 | 0 | break; |
3104 | 0 | } else if (got == 0) { /* EOS */ |
3105 | 0 | break; |
3106 | 0 | } |
3107 | 0 | |
3108 | 0 | total += got; |
3109 | 0 | } |
3110 | 0 |
|
3111 | 0 | return total; |
3112 | 0 | } |
3113 | | |
3114 | 0 | #define OCSP_BUFSIZE 1024 |
3115 | | |
3116 | | #define AbortHttpDecode(error) \ |
3117 | 0 | { \ |
3118 | 0 | if (inBuffer) \ |
3119 | 0 | PORT_Free(inBuffer); \ |
3120 | 0 | PORT_SetError(error); \ |
3121 | 0 | return NULL; \ |
3122 | 0 | } |
3123 | | |
3124 | | /* |
3125 | | * Reads on the given socket and returns an encoded response when received. |
3126 | | * Properly formatted HTTP/1.0 response headers are expected to be read |
3127 | | * from the socket, preceding a binary-encoded OCSP response. Problems |
3128 | | * with parsing cause the error SEC_ERROR_OCSP_BAD_HTTP_RESPONSE to be |
3129 | | * set; any other problems are likely low-level i/o or memory allocation |
3130 | | * errors. |
3131 | | */ |
3132 | | static SECItem * |
3133 | | ocsp_GetEncodedResponse(PLArenaPool *arena, PRFileDesc *sock) |
3134 | 0 | { |
3135 | 0 | /* first read HTTP status line and headers */ |
3136 | 0 |
|
3137 | 0 | char *inBuffer = NULL; |
3138 | 0 | PRInt32 offset = 0; |
3139 | 0 | PRInt32 inBufsize = 0; |
3140 | 0 | const PRInt32 bufSizeIncrement = OCSP_BUFSIZE; /* 1 KB at a time */ |
3141 | 0 | const PRInt32 maxBufSize = 8 * bufSizeIncrement; /* 8 KB max */ |
3142 | 0 | const char *CRLF = "\r\n"; |
3143 | 0 | const PRInt32 CRLFlen = strlen(CRLF); |
3144 | 0 | const char *headerEndMark = "\r\n\r\n"; |
3145 | 0 | const PRInt32 markLen = strlen(headerEndMark); |
3146 | 0 | const PRIntervalTime ocsptimeout = |
3147 | 0 | PR_SecondsToInterval(30); /* hardcoded to 30s for now */ |
3148 | 0 | char *headerEnd = NULL; |
3149 | 0 | PRBool EOS = PR_FALSE; |
3150 | 0 | const char *httpprotocol = "HTTP/"; |
3151 | 0 | const PRInt32 httplen = strlen(httpprotocol); |
3152 | 0 | const char *httpcode = NULL; |
3153 | 0 | const char *contenttype = NULL; |
3154 | 0 | PRInt32 contentlength = 0; |
3155 | 0 | PRInt32 bytesRead = 0; |
3156 | 0 | char *statusLineEnd = NULL; |
3157 | 0 | char *space = NULL; |
3158 | 0 | char *nextHeader = NULL; |
3159 | 0 | SECItem *result = NULL; |
3160 | 0 |
|
3161 | 0 | /* read up to at least the end of the HTTP headers */ |
3162 | 0 | do { |
3163 | 0 | inBufsize += bufSizeIncrement; |
3164 | 0 | inBuffer = PORT_Realloc(inBuffer, inBufsize + 1); |
3165 | 0 | if (NULL == inBuffer) { |
3166 | 0 | AbortHttpDecode(SEC_ERROR_NO_MEMORY); |
3167 | 0 | } |
3168 | 0 | bytesRead = ocsp_read(sock, inBuffer + offset, bufSizeIncrement, |
3169 | 0 | ocsptimeout); |
3170 | 0 | if (bytesRead > 0) { |
3171 | 0 | PRInt32 searchOffset = (offset - markLen) > 0 ? offset - markLen : 0; |
3172 | 0 | offset += bytesRead; |
3173 | 0 | *(inBuffer + offset) = '\0'; /* NULL termination */ |
3174 | 0 | headerEnd = strstr((const char *)inBuffer + searchOffset, headerEndMark); |
3175 | 0 | if (bytesRead < bufSizeIncrement) { |
3176 | 0 | /* we read less data than requested, therefore we are at |
3177 | 0 | EOS or there was a read error */ |
3178 | 0 | EOS = PR_TRUE; |
3179 | 0 | } |
3180 | 0 | } else { |
3181 | 0 | /* recv error or EOS */ |
3182 | 0 | EOS = PR_TRUE; |
3183 | 0 | } |
3184 | 0 | } while ((!headerEnd) && (PR_FALSE == EOS) && |
3185 | 0 | (inBufsize < maxBufSize)); |
3186 | 0 |
|
3187 | 0 | if (!headerEnd) { |
3188 | 0 | AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE); |
3189 | 0 | } |
3190 | 0 |
|
3191 | 0 | /* parse the HTTP status line */ |
3192 | 0 | statusLineEnd = strstr((const char *)inBuffer, CRLF); |
3193 | 0 | if (!statusLineEnd) { |
3194 | 0 | AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE); |
3195 | 0 | } |
3196 | 0 | *statusLineEnd = '\0'; |
3197 | 0 |
|
3198 | 0 | /* check for HTTP/ response */ |
3199 | 0 | space = strchr((const char *)inBuffer, ' '); |
3200 | 0 | if (!space || PORT_Strncasecmp((const char *)inBuffer, httpprotocol, httplen) != 0) { |
3201 | 0 | AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE); |
3202 | 0 | } |
3203 | 0 |
|
3204 | 0 | /* check the HTTP status code of 200 */ |
3205 | 0 | httpcode = space + 1; |
3206 | 0 | space = strchr(httpcode, ' '); |
3207 | 0 | if (!space) { |
3208 | 0 | AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE); |
3209 | 0 | } |
3210 | 0 | *space = 0; |
3211 | 0 | if (0 != strcmp(httpcode, "200")) { |
3212 | 0 | AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE); |
3213 | 0 | } |
3214 | 0 |
|
3215 | 0 | /* parse the HTTP headers in the buffer . We only care about |
3216 | 0 | content-type and content-length |
3217 | 0 | */ |
3218 | 0 |
|
3219 | 0 | nextHeader = statusLineEnd + CRLFlen; |
3220 | 0 | *headerEnd = '\0'; /* terminate */ |
3221 | 0 | do { |
3222 | 0 | char *thisHeaderEnd = NULL; |
3223 | 0 | char *value = NULL; |
3224 | 0 | char *colon = strchr(nextHeader, ':'); |
3225 | 0 |
|
3226 | 0 | if (!colon) { |
3227 | 0 | AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE); |
3228 | 0 | } |
3229 | 0 |
|
3230 | 0 | *colon = '\0'; |
3231 | 0 | value = colon + 1; |
3232 | 0 |
|
3233 | 0 | /* jpierre - note : the following code will only handle the basic form |
3234 | 0 | of HTTP/1.0 response headers, of the form "name: value" . Headers |
3235 | 0 | split among multiple lines are not supported. This is not common |
3236 | 0 | and should not be an issue, but it could become one in the |
3237 | 0 | future */ |
3238 | 0 |
|
3239 | 0 | if (*value != ' ') { |
3240 | 0 | AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE); |
3241 | 0 | } |
3242 | 0 |
|
3243 | 0 | value++; |
3244 | 0 | thisHeaderEnd = strstr(value, CRLF); |
3245 | 0 | if (thisHeaderEnd) { |
3246 | 0 | *thisHeaderEnd = '\0'; |
3247 | 0 | } |
3248 | 0 |
|
3249 | 0 | if (0 == PORT_Strcasecmp(nextHeader, "content-type")) { |
3250 | 0 | contenttype = value; |
3251 | 0 | } else if (0 == PORT_Strcasecmp(nextHeader, "content-length")) { |
3252 | 0 | contentlength = atoi(value); |
3253 | 0 | } |
3254 | 0 |
|
3255 | 0 | if (thisHeaderEnd) { |
3256 | 0 | nextHeader = thisHeaderEnd + CRLFlen; |
3257 | 0 | } else { |
3258 | 0 | nextHeader = NULL; |
3259 | 0 | } |
3260 | 0 |
|
3261 | 0 | } while (nextHeader && (nextHeader < (headerEnd + CRLFlen))); |
3262 | 0 |
|
3263 | 0 | /* check content-type */ |
3264 | 0 | if (!contenttype || |
3265 | 0 | (0 != PORT_Strcasecmp(contenttype, "application/ocsp-response"))) { |
3266 | 0 | AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE); |
3267 | 0 | } |
3268 | 0 |
|
3269 | 0 | /* read the body of the OCSP response */ |
3270 | 0 | offset = offset - (PRInt32)(headerEnd - (const char *)inBuffer) - markLen; |
3271 | 0 | if (offset) { |
3272 | 0 | /* move all data to the beginning of the buffer */ |
3273 | 0 | PORT_Memmove(inBuffer, headerEnd + markLen, offset); |
3274 | 0 | } |
3275 | 0 |
|
3276 | 0 | /* resize buffer to only what's needed to hold the current response */ |
3277 | 0 | inBufsize = (1 + (offset - 1) / bufSizeIncrement) * bufSizeIncrement; |
3278 | 0 |
|
3279 | 0 | while ((PR_FALSE == EOS) && |
3280 | 0 | ((contentlength == 0) || (offset < contentlength)) && |
3281 | 0 | (inBufsize < maxBufSize)) { |
3282 | 0 | /* we still need to receive more body data */ |
3283 | 0 | inBufsize += bufSizeIncrement; |
3284 | 0 | inBuffer = PORT_Realloc(inBuffer, inBufsize + 1); |
3285 | 0 | if (NULL == inBuffer) { |
3286 | 0 | AbortHttpDecode(SEC_ERROR_NO_MEMORY); |
3287 | 0 | } |
3288 | 0 | bytesRead = ocsp_read(sock, inBuffer + offset, bufSizeIncrement, |
3289 | 0 | ocsptimeout); |
3290 | 0 | if (bytesRead > 0) { |
3291 | 0 | offset += bytesRead; |
3292 | 0 | if (bytesRead < bufSizeIncrement) { |
3293 | 0 | /* we read less data than requested, therefore we are at |
3294 | 0 | EOS or there was a read error */ |
3295 | 0 | EOS = PR_TRUE; |
3296 | 0 | } |
3297 | 0 | } else { |
3298 | 0 | /* recv error or EOS */ |
3299 | 0 | EOS = PR_TRUE; |
3300 | 0 | } |
3301 | 0 | } |
3302 | 0 |
|
3303 | 0 | if (0 == offset) { |
3304 | 0 | AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE); |
3305 | 0 | } |
3306 | 0 |
|
3307 | 0 | /* |
3308 | 0 | * Now allocate the item to hold the data. |
3309 | 0 | */ |
3310 | 0 | result = SECITEM_AllocItem(arena, NULL, offset); |
3311 | 0 | if (NULL == result) { |
3312 | 0 | AbortHttpDecode(SEC_ERROR_NO_MEMORY); |
3313 | 0 | } |
3314 | 0 |
|
3315 | 0 | /* |
3316 | 0 | * And copy the data left in the buffer. |
3317 | 0 | */ |
3318 | 0 | PORT_Memcpy(result->data, inBuffer, offset); |
3319 | 0 |
|
3320 | 0 | /* and free the temporary buffer */ |
3321 | 0 | PORT_Free(inBuffer); |
3322 | 0 | return result; |
3323 | 0 | } |
3324 | | |
3325 | | SECStatus |
3326 | | CERT_ParseURL(const char *url, char **pHostname, PRUint16 *pPort, char **pPath) |
3327 | 0 | { |
3328 | 0 | return ocsp_ParseURL(url, pHostname, pPort, pPath); |
3329 | 0 | } |
3330 | | |
3331 | | /* |
3332 | | * Limit the size of http responses we are willing to accept. |
3333 | | */ |
3334 | 0 | #define MAX_WANTED_OCSP_RESPONSE_LEN 64 * 1024 |
3335 | | |
3336 | | /* if (encodedRequest == NULL) |
3337 | | * then location MUST already include the full request, |
3338 | | * including base64 and urlencode, |
3339 | | * and the request will be sent with GET |
3340 | | * if (encodedRequest != NULL) |
3341 | | * then the request will be sent with POST |
3342 | | */ |
3343 | | static SECItem * |
3344 | | fetchOcspHttpClientV1(PLArenaPool *arena, |
3345 | | const SEC_HttpClientFcnV1 *hcv1, |
3346 | | const char *location, |
3347 | | const SECItem *encodedRequest) |
3348 | 0 | { |
3349 | 0 | char *hostname = NULL; |
3350 | 0 | char *path = NULL; |
3351 | 0 | PRUint16 port; |
3352 | 0 | SECItem *encodedResponse = NULL; |
3353 | 0 | SEC_HTTP_SERVER_SESSION pServerSession = NULL; |
3354 | 0 | SEC_HTTP_REQUEST_SESSION pRequestSession = NULL; |
3355 | 0 | PRUint16 myHttpResponseCode; |
3356 | 0 | const char *myHttpResponseData; |
3357 | 0 | PRUint32 myHttpResponseDataLen; |
3358 | 0 |
|
3359 | 0 | if (ocsp_ParseURL(location, &hostname, &port, &path) == SECFailure) { |
3360 | 0 | PORT_SetError(SEC_ERROR_OCSP_MALFORMED_REQUEST); |
3361 | 0 | goto loser; |
3362 | 0 | } |
3363 | 0 |
|
3364 | 0 | PORT_Assert(hostname != NULL); |
3365 | 0 | PORT_Assert(path != NULL); |
3366 | 0 |
|
3367 | 0 | if ((*hcv1->createSessionFcn)( |
3368 | 0 | hostname, |
3369 | 0 | port, |
3370 | 0 | &pServerSession) != SECSuccess) { |
3371 | 0 | PORT_SetError(SEC_ERROR_OCSP_SERVER_ERROR); |
3372 | 0 | goto loser; |
3373 | 0 | } |
3374 | 0 |
|
3375 | 0 | /* We use a non-zero timeout, which means: |
3376 | 0 | - the client will use blocking I/O |
3377 | 0 | - TryFcn will not return WOULD_BLOCK nor a poll descriptor |
3378 | 0 | - it's sufficient to call TryFcn once |
3379 | 0 | No lock for accessing OCSP_Global.timeoutSeconds, bug 406120 |
3380 | 0 | */ |
3381 | 0 |
|
3382 | 0 | if ((*hcv1->createFcn)( |
3383 | 0 | pServerSession, |
3384 | 0 | "http", |
3385 | 0 | path, |
3386 | 0 | encodedRequest ? "POST" : "GET", |
3387 | 0 | PR_TicksPerSecond() * OCSP_Global.timeoutSeconds, |
3388 | 0 | &pRequestSession) != SECSuccess) { |
3389 | 0 | PORT_SetError(SEC_ERROR_OCSP_SERVER_ERROR); |
3390 | 0 | goto loser; |
3391 | 0 | } |
3392 | 0 |
|
3393 | 0 | if (encodedRequest && |
3394 | 0 | (*hcv1->setPostDataFcn)( |
3395 | 0 | pRequestSession, |
3396 | 0 | (char *)encodedRequest->data, |
3397 | 0 | encodedRequest->len, |
3398 | 0 | "application/ocsp-request") != SECSuccess) { |
3399 | 0 | PORT_SetError(SEC_ERROR_OCSP_SERVER_ERROR); |
3400 | 0 | goto loser; |
3401 | 0 | } |
3402 | 0 |
|
3403 | 0 | /* we don't want result objects larger than this: */ |
3404 | 0 | myHttpResponseDataLen = MAX_WANTED_OCSP_RESPONSE_LEN; |
3405 | 0 |
|
3406 | 0 | OCSP_TRACE(("OCSP trySendAndReceive %s\n", location)); |
3407 | 0 |
|
3408 | 0 | if ((*hcv1->trySendAndReceiveFcn)( |
3409 | 0 | pRequestSession, |
3410 | 0 | NULL, |
3411 | 0 | &myHttpResponseCode, |
3412 | 0 | NULL, |
3413 | 0 | NULL, |
3414 | 0 | &myHttpResponseData, |
3415 | 0 | &myHttpResponseDataLen) != SECSuccess) { |
3416 | 0 | PORT_SetError(SEC_ERROR_OCSP_SERVER_ERROR); |
3417 | 0 | goto loser; |
3418 | 0 | } |
3419 | 0 |
|
3420 | 0 | OCSP_TRACE(("OCSP trySendAndReceive result http %d\n", myHttpResponseCode)); |
3421 | 0 |
|
3422 | 0 | if (myHttpResponseCode != 200) { |
3423 | 0 | PORT_SetError(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE); |
3424 | 0 | goto loser; |
3425 | 0 | } |
3426 | 0 |
|
3427 | 0 | encodedResponse = SECITEM_AllocItem(arena, NULL, myHttpResponseDataLen); |
3428 | 0 |
|
3429 | 0 | if (!encodedResponse) { |
3430 | 0 | PORT_SetError(SEC_ERROR_NO_MEMORY); |
3431 | 0 | goto loser; |
3432 | 0 | } |
3433 | 0 |
|
3434 | 0 | PORT_Memcpy(encodedResponse->data, myHttpResponseData, myHttpResponseDataLen); |
3435 | 0 |
|
3436 | 0 | loser: |
3437 | 0 | if (pRequestSession != NULL) |
3438 | 0 | (*hcv1->freeFcn)(pRequestSession); |
3439 | 0 | if (pServerSession != NULL) |
3440 | 0 | (*hcv1->freeSessionFcn)(pServerSession); |
3441 | 0 | if (path != NULL) |
3442 | 0 | PORT_Free(path); |
3443 | 0 | if (hostname != NULL) |
3444 | 0 | PORT_Free(hostname); |
3445 | 0 |
|
3446 | 0 | return encodedResponse; |
3447 | 0 | } |
3448 | | |
3449 | | /* |
3450 | | * FUNCTION: CERT_GetEncodedOCSPResponseByMethod |
3451 | | * Creates and sends a request to an OCSP responder, then reads and |
3452 | | * returns the (encoded) response. |
3453 | | * INPUTS: |
3454 | | * PLArenaPool *arena |
3455 | | * Pointer to arena from which return value will be allocated. |
3456 | | * If NULL, result will be allocated from the heap (and thus should |
3457 | | * be freed via SECITEM_FreeItem). |
3458 | | * CERTCertList *certList |
3459 | | * A list of certs for which status will be requested. |
3460 | | * Note that all of these certificates should have the same issuer, |
3461 | | * or it's expected the response will be signed by a trusted responder. |
3462 | | * If the certs need to be broken up into multiple requests, that |
3463 | | * must be handled by the caller (and thus by having multiple calls |
3464 | | * to this routine), who knows about where the request(s) are being |
3465 | | * sent and whether there are any trusted responders in place. |
3466 | | * const char *location |
3467 | | * The location of the OCSP responder (a URL). |
3468 | | * const char *method |
3469 | | * The protocol method used when retrieving the OCSP response. |
3470 | | * Currently support: "GET" (http GET) and "POST" (http POST). |
3471 | | * Additionals methods for http or other protocols might be added |
3472 | | * in the future. |
3473 | | * PRTime time |
3474 | | * Indicates the time for which the certificate status is to be |
3475 | | * determined -- this may be used in the search for the cert's issuer |
3476 | | * but has no other bearing on the operation. |
3477 | | * PRBool addServiceLocator |
3478 | | * If true, the Service Locator extension should be added to the |
3479 | | * single request(s) for each cert. |
3480 | | * CERTCertificate *signerCert |
3481 | | * If non-NULL, means sign the request using this cert. Otherwise, |
3482 | | * do not sign. |
3483 | | * void *pwArg |
3484 | | * Pointer to argument for password prompting, if needed. (Definitely |
3485 | | * not needed if not signing.) |
3486 | | * OUTPUTS: |
3487 | | * CERTOCSPRequest **pRequest |
3488 | | * Pointer in which to store the OCSP request created for the given |
3489 | | * list of certificates. It is only filled in if the entire operation |
3490 | | * is successful and the pointer is not null -- and in that case the |
3491 | | * caller is then reponsible for destroying it. |
3492 | | * RETURN: |
3493 | | * Returns a pointer to the SECItem holding the response. |
3494 | | * On error, returns null with error set describing the reason: |
3495 | | * SEC_ERROR_UNKNOWN_ISSUER |
3496 | | * SEC_ERROR_CERT_BAD_ACCESS_LOCATION |
3497 | | * SEC_ERROR_OCSP_BAD_HTTP_RESPONSE |
3498 | | * Other errors are low-level problems (no memory, bad database, etc.). |
3499 | | */ |
3500 | | SECItem * |
3501 | | CERT_GetEncodedOCSPResponseByMethod(PLArenaPool *arena, CERTCertList *certList, |
3502 | | const char *location, const char *method, |
3503 | | PRTime time, PRBool addServiceLocator, |
3504 | | CERTCertificate *signerCert, void *pwArg, |
3505 | | CERTOCSPRequest **pRequest) |
3506 | 0 | { |
3507 | 0 | CERTOCSPRequest *request; |
3508 | 0 | request = CERT_CreateOCSPRequest(certList, time, addServiceLocator, |
3509 | 0 | signerCert); |
3510 | 0 | if (!request) |
3511 | 0 | return NULL; |
3512 | 0 | return ocsp_GetEncodedOCSPResponseFromRequest(arena, request, location, |
3513 | 0 | method, time, addServiceLocator, |
3514 | 0 | pwArg, pRequest); |
3515 | 0 | } |
3516 | | |
3517 | | /* |
3518 | | * FUNCTION: CERT_GetEncodedOCSPResponse |
3519 | | * Creates and sends a request to an OCSP responder, then reads and |
3520 | | * returns the (encoded) response. |
3521 | | * |
3522 | | * This is a legacy API that behaves identically to |
3523 | | * CERT_GetEncodedOCSPResponseByMethod using the "POST" method. |
3524 | | */ |
3525 | | SECItem * |
3526 | | CERT_GetEncodedOCSPResponse(PLArenaPool *arena, CERTCertList *certList, |
3527 | | const char *location, PRTime time, |
3528 | | PRBool addServiceLocator, |
3529 | | CERTCertificate *signerCert, void *pwArg, |
3530 | | CERTOCSPRequest **pRequest) |
3531 | 0 | { |
3532 | 0 | return CERT_GetEncodedOCSPResponseByMethod(arena, certList, location, |
3533 | 0 | "POST", time, addServiceLocator, |
3534 | 0 | signerCert, pwArg, pRequest); |
3535 | 0 | } |
3536 | | |
3537 | | /* URL encode a buffer that consists of base64-characters, only, |
3538 | | * which means we can use a simple encoding logic. |
3539 | | * |
3540 | | * No output buffer size checking is performed. |
3541 | | * You should call the function twice, to calculate the required buffer size. |
3542 | | * |
3543 | | * If the outpufBuf parameter is NULL, the function will calculate the |
3544 | | * required size, including the trailing zero termination char. |
3545 | | * |
3546 | | * The function returns the number of bytes calculated or produced. |
3547 | | */ |
3548 | | size_t |
3549 | | ocsp_UrlEncodeBase64Buf(const char *base64Buf, char *outputBuf) |
3550 | 0 | { |
3551 | 0 | const char *walkInput = NULL; |
3552 | 0 | char *walkOutput = outputBuf; |
3553 | 0 | size_t count = 0; |
3554 | 0 |
|
3555 | 0 | for (walkInput = base64Buf; *walkInput; ++walkInput) { |
3556 | 0 | char c = *walkInput; |
3557 | 0 | if (isspace(c)) |
3558 | 0 | continue; |
3559 | 0 | switch (c) { |
3560 | 0 | case '+': |
3561 | 0 | if (outputBuf) { |
3562 | 0 | strcpy(walkOutput, "%2B"); |
3563 | 0 | walkOutput += 3; |
3564 | 0 | } |
3565 | 0 | count += 3; |
3566 | 0 | break; |
3567 | 0 | case '/': |
3568 | 0 | if (outputBuf) { |
3569 | 0 | strcpy(walkOutput, "%2F"); |
3570 | 0 | walkOutput += 3; |
3571 | 0 | } |
3572 | 0 | count += 3; |
3573 | 0 | break; |
3574 | 0 | case '=': |
3575 | 0 | if (outputBuf) { |
3576 | 0 | strcpy(walkOutput, "%3D"); |
3577 | 0 | walkOutput += 3; |
3578 | 0 | } |
3579 | 0 | count += 3; |
3580 | 0 | break; |
3581 | 0 | default: |
3582 | 0 | if (outputBuf) { |
3583 | 0 | *walkOutput = *walkInput; |
3584 | 0 | ++walkOutput; |
3585 | 0 | } |
3586 | 0 | ++count; |
3587 | 0 | break; |
3588 | 0 | } |
3589 | 0 | } |
3590 | 0 | if (outputBuf) { |
3591 | 0 | *walkOutput = 0; |
3592 | 0 | } |
3593 | 0 | ++count; |
3594 | 0 | return count; |
3595 | 0 | } |
3596 | | |
3597 | | enum { max_get_request_size = 255 }; /* defined by RFC2560 */ |
3598 | | |
3599 | | static SECItem * |
3600 | | cert_GetOCSPResponse(PLArenaPool *arena, const char *location, |
3601 | | const SECItem *encodedRequest); |
3602 | | |
3603 | | static SECItem * |
3604 | | ocsp_GetEncodedOCSPResponseFromRequest(PLArenaPool *arena, |
3605 | | CERTOCSPRequest *request, |
3606 | | const char *location, |
3607 | | const char *method, |
3608 | | PRTime time, |
3609 | | PRBool addServiceLocator, |
3610 | | void *pwArg, |
3611 | | CERTOCSPRequest **pRequest) |
3612 | | { |
3613 | | SECItem *encodedRequest = NULL; |
3614 | | SECItem *encodedResponse = NULL; |
3615 | | SECStatus rv; |
3616 | | |
3617 | | if (!location || !*location) /* location should be at least one byte */ |
3618 | | goto loser; |
3619 | | |
3620 | | rv = CERT_AddOCSPAcceptableResponses(request, |
3621 | | SEC_OID_PKIX_OCSP_BASIC_RESPONSE); |
3622 | | if (rv != SECSuccess) |
3623 | | goto loser; |
3624 | | |
3625 | | encodedRequest = CERT_EncodeOCSPRequest(NULL, request, pwArg); |
3626 | | if (encodedRequest == NULL) |
3627 | | goto loser; |
3628 | | |
3629 | | if (!strcmp(method, "GET")) { |
3630 | | encodedResponse = cert_GetOCSPResponse(arena, location, encodedRequest); |
3631 | | } else if (!strcmp(method, "POST")) { |
3632 | | encodedResponse = CERT_PostOCSPRequest(arena, location, encodedRequest); |
3633 | | } else { |
3634 | | goto loser; |
3635 | | } |
3636 | | |
3637 | | if (encodedResponse != NULL && pRequest != NULL) { |
3638 | | *pRequest = request; |
3639 | | request = NULL; /* avoid destroying below */ |
3640 | | } |
3641 | | |
3642 | | loser: |
3643 | | if (request != NULL) |
3644 | | CERT_DestroyOCSPRequest(request); |
3645 | | if (encodedRequest != NULL) |
3646 | | SECITEM_FreeItem(encodedRequest, PR_TRUE); |
3647 | | return encodedResponse; |
3648 | | } |
3649 | | |
3650 | | static SECItem * |
3651 | | cert_FetchOCSPResponse(PLArenaPool *arena, const char *location, |
3652 | | const SECItem *encodedRequest); |
3653 | | |
3654 | | /* using HTTP GET method */ |
3655 | | static SECItem * |
3656 | | cert_GetOCSPResponse(PLArenaPool *arena, const char *location, |
3657 | | const SECItem *encodedRequest) |
3658 | 0 | { |
3659 | 0 | char *walkOutput = NULL; |
3660 | 0 | char *fullGetPath = NULL; |
3661 | 0 | size_t pathLength; |
3662 | 0 | PRInt32 urlEncodedBufLength; |
3663 | 0 | size_t base64size; |
3664 | 0 | char b64ReqBuf[max_get_request_size + 1]; |
3665 | 0 | size_t slashLengthIfNeeded = 0; |
3666 | 0 | size_t getURLLength; |
3667 | 0 | SECItem *item; |
3668 | 0 |
|
3669 | 0 | if (!location || !*location) { |
3670 | 0 | return NULL; |
3671 | 0 | } |
3672 | 0 | |
3673 | 0 | pathLength = strlen(location); |
3674 | 0 | if (location[pathLength - 1] != '/') { |
3675 | 0 | slashLengthIfNeeded = 1; |
3676 | 0 | } |
3677 | 0 |
|
3678 | 0 | /* Calculation as documented by PL_Base64Encode function. |
3679 | 0 | * Use integer conversion to avoid having to use function ceil(). |
3680 | 0 | */ |
3681 | 0 | base64size = (((encodedRequest->len + 2) / 3) * 4); |
3682 | 0 | if (base64size > max_get_request_size) { |
3683 | 0 | return NULL; |
3684 | 0 | } |
3685 | 0 | memset(b64ReqBuf, 0, sizeof(b64ReqBuf)); |
3686 | 0 | PL_Base64Encode((const char *)encodedRequest->data, encodedRequest->len, |
3687 | 0 | b64ReqBuf); |
3688 | 0 |
|
3689 | 0 | urlEncodedBufLength = ocsp_UrlEncodeBase64Buf(b64ReqBuf, NULL); |
3690 | 0 | getURLLength = pathLength + urlEncodedBufLength + slashLengthIfNeeded; |
3691 | 0 |
|
3692 | 0 | /* urlEncodedBufLength already contains room for the zero terminator. |
3693 | 0 | * Add another if we must add the '/' char. |
3694 | 0 | */ |
3695 | 0 | if (arena) { |
3696 | 0 | fullGetPath = (char *)PORT_ArenaAlloc(arena, getURLLength); |
3697 | 0 | } else { |
3698 | 0 | fullGetPath = (char *)PORT_Alloc(getURLLength); |
3699 | 0 | } |
3700 | 0 | if (!fullGetPath) { |
3701 | 0 | return NULL; |
3702 | 0 | } |
3703 | 0 | |
3704 | 0 | strcpy(fullGetPath, location); |
3705 | 0 | walkOutput = fullGetPath + pathLength; |
3706 | 0 |
|
3707 | 0 | if (walkOutput > fullGetPath && slashLengthIfNeeded) { |
3708 | 0 | strcpy(walkOutput, "/"); |
3709 | 0 | ++walkOutput; |
3710 | 0 | } |
3711 | 0 | ocsp_UrlEncodeBase64Buf(b64ReqBuf, walkOutput); |
3712 | 0 |
|
3713 | 0 | item = cert_FetchOCSPResponse(arena, fullGetPath, NULL); |
3714 | 0 | if (!arena) { |
3715 | 0 | PORT_Free(fullGetPath); |
3716 | 0 | } |
3717 | 0 | return item; |
3718 | 0 | } |
3719 | | |
3720 | | SECItem * |
3721 | | CERT_PostOCSPRequest(PLArenaPool *arena, const char *location, |
3722 | | const SECItem *encodedRequest) |
3723 | 0 | { |
3724 | 0 | return cert_FetchOCSPResponse(arena, location, encodedRequest); |
3725 | 0 | } |
3726 | | |
3727 | | SECItem * |
3728 | | cert_FetchOCSPResponse(PLArenaPool *arena, const char *location, |
3729 | | const SECItem *encodedRequest) |
3730 | 0 | { |
3731 | 0 | const SEC_HttpClientFcn *registeredHttpClient; |
3732 | 0 | SECItem *encodedResponse = NULL; |
3733 | 0 |
|
3734 | 0 | registeredHttpClient = SEC_GetRegisteredHttpClient(); |
3735 | 0 |
|
3736 | 0 | if (registeredHttpClient && registeredHttpClient->version == 1) { |
3737 | 0 | encodedResponse = fetchOcspHttpClientV1( |
3738 | 0 | arena, |
3739 | 0 | ®isteredHttpClient->fcnTable.ftable1, |
3740 | 0 | location, |
3741 | 0 | encodedRequest); |
3742 | 0 | } else { |
3743 | 0 | /* use internal http client */ |
3744 | 0 | PRFileDesc *sock = ocsp_SendEncodedRequest(location, encodedRequest); |
3745 | 0 | if (sock) { |
3746 | 0 | encodedResponse = ocsp_GetEncodedResponse(arena, sock); |
3747 | 0 | PR_Close(sock); |
3748 | 0 | } |
3749 | 0 | } |
3750 | 0 |
|
3751 | 0 | return encodedResponse; |
3752 | 0 | } |
3753 | | |
3754 | | static SECItem * |
3755 | | ocsp_GetEncodedOCSPResponseForSingleCert(PLArenaPool *arena, |
3756 | | CERTOCSPCertID *certID, |
3757 | | CERTCertificate *singleCert, |
3758 | | const char *location, |
3759 | | const char *method, |
3760 | | PRTime time, |
3761 | | PRBool addServiceLocator, |
3762 | | void *pwArg, |
3763 | | CERTOCSPRequest **pRequest) |
3764 | 0 | { |
3765 | 0 | CERTOCSPRequest *request; |
3766 | 0 | request = cert_CreateSingleCertOCSPRequest(certID, singleCert, time, |
3767 | 0 | addServiceLocator, NULL); |
3768 | 0 | if (!request) |
3769 | 0 | return NULL; |
3770 | 0 | return ocsp_GetEncodedOCSPResponseFromRequest(arena, request, location, |
3771 | 0 | method, time, addServiceLocator, |
3772 | 0 | pwArg, pRequest); |
3773 | 0 | } |
3774 | | |
3775 | | /* Checks a certificate for the key usage extension of OCSP signer. */ |
3776 | | static PRBool |
3777 | | ocsp_CertIsOCSPDesignatedResponder(CERTCertificate *cert) |
3778 | 0 | { |
3779 | 0 | SECStatus rv; |
3780 | 0 | SECItem extItem; |
3781 | 0 | SECItem **oids; |
3782 | 0 | SECItem *oid; |
3783 | 0 | SECOidTag oidTag; |
3784 | 0 | PRBool retval; |
3785 | 0 | CERTOidSequence *oidSeq = NULL; |
3786 | 0 |
|
3787 | 0 | extItem.data = NULL; |
3788 | 0 | rv = CERT_FindCertExtension(cert, SEC_OID_X509_EXT_KEY_USAGE, &extItem); |
3789 | 0 | if (rv != SECSuccess) { |
3790 | 0 | goto loser; |
3791 | 0 | } |
3792 | 0 | |
3793 | 0 | oidSeq = CERT_DecodeOidSequence(&extItem); |
3794 | 0 | if (oidSeq == NULL) { |
3795 | 0 | goto loser; |
3796 | 0 | } |
3797 | 0 | |
3798 | 0 | oids = oidSeq->oids; |
3799 | 0 | while (*oids != NULL) { |
3800 | 0 | oid = *oids; |
3801 | 0 |
|
3802 | 0 | oidTag = SECOID_FindOIDTag(oid); |
3803 | 0 |
|
3804 | 0 | if (oidTag == SEC_OID_OCSP_RESPONDER) { |
3805 | 0 | goto success; |
3806 | 0 | } |
3807 | 0 | |
3808 | 0 | oids++; |
3809 | 0 | } |
3810 | 0 |
|
3811 | 0 | loser: |
3812 | 0 | retval = PR_FALSE; |
3813 | 0 | PORT_SetError(SEC_ERROR_OCSP_INVALID_SIGNING_CERT); |
3814 | 0 | goto done; |
3815 | 0 | success: |
3816 | 0 | retval = PR_TRUE; |
3817 | 0 | done: |
3818 | 0 | if (extItem.data != NULL) { |
3819 | 0 | PORT_Free(extItem.data); |
3820 | 0 | } |
3821 | 0 | if (oidSeq != NULL) { |
3822 | 0 | CERT_DestroyOidSequence(oidSeq); |
3823 | 0 | } |
3824 | 0 |
|
3825 | 0 | return (retval); |
3826 | 0 | } |
3827 | | |
3828 | | #ifdef LATER /* \ |
3829 | | * XXX This function is not currently used, but will \ |
3830 | | * be needed later when we do revocation checking of \ |
3831 | | * the responder certificate. Of course, it may need \ |
3832 | | * revising then, if the cert extension interface has \ |
3833 | | * changed. (Hopefully it will!) \ |
3834 | | */ |
3835 | | |
3836 | | /* Checks a certificate to see if it has the OCSP no check extension. */ |
3837 | | static PRBool |
3838 | | ocsp_CertHasNoCheckExtension(CERTCertificate *cert) |
3839 | | { |
3840 | | SECStatus rv; |
3841 | | |
3842 | | rv = CERT_FindCertExtension(cert, SEC_OID_PKIX_OCSP_NO_CHECK, |
3843 | | NULL); |
3844 | | if (rv == SECSuccess) { |
3845 | | return PR_TRUE; |
3846 | | } |
3847 | | return PR_FALSE; |
3848 | | } |
3849 | | #endif /* LATER */ |
3850 | | |
3851 | | static PRBool |
3852 | | ocsp_matchcert(SECItem *certIndex, CERTCertificate *testCert) |
3853 | 0 | { |
3854 | 0 | SECItem item; |
3855 | 0 | unsigned char buf[HASH_LENGTH_MAX]; |
3856 | 0 |
|
3857 | 0 | item.data = buf; |
3858 | 0 | item.len = SHA1_LENGTH; |
3859 | 0 |
|
3860 | 0 | if (CERT_GetSubjectPublicKeyDigest(NULL, testCert, SEC_OID_SHA1, |
3861 | 0 | &item) == NULL) { |
3862 | 0 | return PR_FALSE; |
3863 | 0 | } |
3864 | 0 | if (SECITEM_ItemsAreEqual(certIndex, &item)) { |
3865 | 0 | return PR_TRUE; |
3866 | 0 | } |
3867 | 0 | if (CERT_GetSubjectPublicKeyDigest(NULL, testCert, SEC_OID_MD5, |
3868 | 0 | &item) == NULL) { |
3869 | 0 | return PR_FALSE; |
3870 | 0 | } |
3871 | 0 | if (SECITEM_ItemsAreEqual(certIndex, &item)) { |
3872 | 0 | return PR_TRUE; |
3873 | 0 | } |
3874 | 0 | if (CERT_GetSubjectPublicKeyDigest(NULL, testCert, SEC_OID_MD2, |
3875 | 0 | &item) == NULL) { |
3876 | 0 | return PR_FALSE; |
3877 | 0 | } |
3878 | 0 | if (SECITEM_ItemsAreEqual(certIndex, &item)) { |
3879 | 0 | return PR_TRUE; |
3880 | 0 | } |
3881 | 0 |
|
3882 | 0 | return PR_FALSE; |
3883 | 0 | } |
3884 | | |
3885 | | static CERTCertificate * |
3886 | | ocsp_CertGetDefaultResponder(CERTCertDBHandle *handle, CERTOCSPCertID *certID); |
3887 | | |
3888 | | CERTCertificate * |
3889 | | ocsp_GetSignerCertificate(CERTCertDBHandle *handle, ocspResponseData *tbsData, |
3890 | | ocspSignature *signature, CERTCertificate *issuer) |
3891 | 0 | { |
3892 | 0 | CERTCertificate **certs = NULL; |
3893 | 0 | CERTCertificate *signerCert = NULL; |
3894 | 0 | SECStatus rv = SECFailure; |
3895 | 0 | PRBool lookupByName = PR_TRUE; |
3896 | 0 | void *certIndex = NULL; |
3897 | 0 | int certCount = 0; |
3898 | 0 |
|
3899 | 0 | PORT_Assert(tbsData->responderID != NULL); |
3900 | 0 | switch (tbsData->responderID->responderIDType) { |
3901 | 0 | case ocspResponderID_byName: |
3902 | 0 | lookupByName = PR_TRUE; |
3903 | 0 | certIndex = &tbsData->derResponderID; |
3904 | 0 | break; |
3905 | 0 | case ocspResponderID_byKey: |
3906 | 0 | lookupByName = PR_FALSE; |
3907 | 0 | certIndex = &tbsData->responderID->responderIDValue.keyHash; |
3908 | 0 | break; |
3909 | 0 | case ocspResponderID_other: |
3910 | 0 | default: |
3911 | 0 | PORT_Assert(0); |
3912 | 0 | PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE); |
3913 | 0 | return NULL; |
3914 | 0 | } |
3915 | 0 |
|
3916 | 0 | /* |
3917 | 0 | * If the signature contains some certificates as well, temporarily |
3918 | 0 | * import them in case they are needed for verification. |
3919 | 0 | * |
3920 | 0 | * Note that the result of this is that each cert in "certs" needs |
3921 | 0 | * to be destroyed. |
3922 | 0 | */ |
3923 | 0 | if (signature->derCerts != NULL) { |
3924 | 0 | for (; signature->derCerts[certCount] != NULL; certCount++) { |
3925 | 0 | /* just counting */ |
3926 | 0 | } |
3927 | 0 | rv = CERT_ImportCerts(handle, certUsageStatusResponder, certCount, |
3928 | 0 | signature->derCerts, &certs, |
3929 | 0 | PR_FALSE, PR_FALSE, NULL); |
3930 | 0 | if (rv != SECSuccess) |
3931 | 0 | goto finish; |
3932 | 0 | } |
3933 | 0 | |
3934 | 0 | /* |
3935 | 0 | * Now look up the certificate that did the signing. |
3936 | 0 | * The signer can be specified either by name or by key hash. |
3937 | 0 | */ |
3938 | 0 | if (lookupByName) { |
3939 | 0 | SECItem *crIndex = (SECItem *)certIndex; |
3940 | 0 | SECItem encodedName; |
3941 | 0 | PLArenaPool *arena; |
3942 | 0 |
|
3943 | 0 | arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
3944 | 0 | if (arena != NULL) { |
3945 | 0 |
|
3946 | 0 | rv = SEC_QuickDERDecodeItem(arena, &encodedName, |
3947 | 0 | ocsp_ResponderIDDerNameTemplate, |
3948 | 0 | crIndex); |
3949 | 0 | if (rv != SECSuccess) { |
3950 | 0 | if (PORT_GetError() == SEC_ERROR_BAD_DER) |
3951 | 0 | PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE); |
3952 | 0 | } else { |
3953 | 0 | signerCert = CERT_FindCertByName(handle, &encodedName); |
3954 | 0 | } |
3955 | 0 | PORT_FreeArena(arena, PR_FALSE); |
3956 | 0 | } |
3957 | 0 | } else { |
3958 | 0 | /* |
3959 | 0 | * The signer is either 1) a known issuer CA we passed in, |
3960 | 0 | * 2) the default OCSP responder, or 3) an intermediate CA |
3961 | 0 | * passed in the cert list to use. Figure out which it is. |
3962 | 0 | */ |
3963 | 0 | int i; |
3964 | 0 | CERTCertificate *responder = |
3965 | 0 | ocsp_CertGetDefaultResponder(handle, NULL); |
3966 | 0 | if (responder && ocsp_matchcert(certIndex, responder)) { |
3967 | 0 | signerCert = CERT_DupCertificate(responder); |
3968 | 0 | } else if (issuer && ocsp_matchcert(certIndex, issuer)) { |
3969 | 0 | signerCert = CERT_DupCertificate(issuer); |
3970 | 0 | } |
3971 | 0 | for (i = 0; (signerCert == NULL) && (i < certCount); i++) { |
3972 | 0 | if (ocsp_matchcert(certIndex, certs[i])) { |
3973 | 0 | signerCert = CERT_DupCertificate(certs[i]); |
3974 | 0 | } |
3975 | 0 | } |
3976 | 0 | if (signerCert == NULL) { |
3977 | 0 | PORT_SetError(SEC_ERROR_UNKNOWN_CERT); |
3978 | 0 | } |
3979 | 0 | } |
3980 | 0 |
|
3981 | 0 | finish: |
3982 | 0 | if (certs != NULL) { |
3983 | 0 | CERT_DestroyCertArray(certs, certCount); |
3984 | 0 | } |
3985 | 0 |
|
3986 | 0 | return signerCert; |
3987 | 0 | } |
3988 | | |
3989 | | SECStatus |
3990 | | ocsp_VerifyResponseSignature(CERTCertificate *signerCert, |
3991 | | ocspSignature *signature, |
3992 | | SECItem *tbsResponseDataDER, |
3993 | | void *pwArg) |
3994 | 0 | { |
3995 | 0 | SECKEYPublicKey *signerKey = NULL; |
3996 | 0 | SECStatus rv = SECFailure; |
3997 | 0 | CERTSignedData signedData; |
3998 | 0 |
|
3999 | 0 | /* |
4000 | 0 | * Now get the public key from the signer's certificate; we need |
4001 | 0 | * it to perform the verification. |
4002 | 0 | */ |
4003 | 0 | signerKey = CERT_ExtractPublicKey(signerCert); |
4004 | 0 | if (signerKey == NULL) { |
4005 | 0 | return SECFailure; |
4006 | 0 | } |
4007 | 0 | |
4008 | 0 | /* |
4009 | 0 | * We copy the signature data *pointer* and length, so that we can |
4010 | 0 | * modify the length without damaging the original copy. This is a |
4011 | 0 | * simple copy, not a dup, so no destroy/free is necessary. |
4012 | 0 | */ |
4013 | 0 | signedData.signature = signature->signature; |
4014 | 0 | signedData.signatureAlgorithm = signature->signatureAlgorithm; |
4015 | 0 | signedData.data = *tbsResponseDataDER; |
4016 | 0 |
|
4017 | 0 | rv = CERT_VerifySignedDataWithPublicKey(&signedData, signerKey, pwArg); |
4018 | 0 | if (rv != SECSuccess && |
4019 | 0 | (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE || |
4020 | 0 | PORT_GetError() == SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED)) { |
4021 | 0 | PORT_SetError(SEC_ERROR_OCSP_BAD_SIGNATURE); |
4022 | 0 | } |
4023 | 0 |
|
4024 | 0 | if (signerKey != NULL) { |
4025 | 0 | SECKEY_DestroyPublicKey(signerKey); |
4026 | 0 | } |
4027 | 0 |
|
4028 | 0 | return rv; |
4029 | 0 | } |
4030 | | |
4031 | | /* |
4032 | | * FUNCTION: CERT_VerifyOCSPResponseSignature |
4033 | | * Check the signature on an OCSP Response. Will also perform a |
4034 | | * verification of the signer's certificate. Note, however, that a |
4035 | | * successful verification does not make any statement about the |
4036 | | * signer's *authority* to provide status for the certificate(s), |
4037 | | * that must be checked individually for each certificate. |
4038 | | * INPUTS: |
4039 | | * CERTOCSPResponse *response |
4040 | | * Pointer to response structure with signature to be checked. |
4041 | | * CERTCertDBHandle *handle |
4042 | | * Pointer to CERTCertDBHandle for certificate DB to use for verification. |
4043 | | * void *pwArg |
4044 | | * Pointer to argument for password prompting, if needed. |
4045 | | * OUTPUTS: |
4046 | | * CERTCertificate **pSignerCert |
4047 | | * Pointer in which to store signer's certificate; only filled-in if |
4048 | | * non-null. |
4049 | | * RETURN: |
4050 | | * Returns SECSuccess when signature is valid, anything else means invalid. |
4051 | | * Possible errors set: |
4052 | | * SEC_ERROR_OCSP_MALFORMED_RESPONSE - unknown type of ResponderID |
4053 | | * SEC_ERROR_INVALID_TIME - bad format of "ProducedAt" time |
4054 | | * SEC_ERROR_UNKNOWN_SIGNER - signer's cert could not be found |
4055 | | * SEC_ERROR_BAD_SIGNATURE - the signature did not verify |
4056 | | * Other errors are any of the many possible failures in cert verification |
4057 | | * (e.g. SEC_ERROR_REVOKED_CERTIFICATE, SEC_ERROR_UNTRUSTED_ISSUER) when |
4058 | | * verifying the signer's cert, or low-level problems (no memory, etc.) |
4059 | | */ |
4060 | | SECStatus |
4061 | | CERT_VerifyOCSPResponseSignature(CERTOCSPResponse *response, |
4062 | | CERTCertDBHandle *handle, void *pwArg, |
4063 | | CERTCertificate **pSignerCert, |
4064 | | CERTCertificate *issuer) |
4065 | 0 | { |
4066 | 0 | SECItem *tbsResponseDataDER; |
4067 | 0 | CERTCertificate *signerCert = NULL; |
4068 | 0 | SECStatus rv = SECFailure; |
4069 | 0 | PRTime producedAt; |
4070 | 0 |
|
4071 | 0 | /* ocsp_DecodeBasicOCSPResponse will fail if asn1 decoder is unable |
4072 | 0 | * to properly decode tbsData (see the function and |
4073 | 0 | * ocsp_BasicOCSPResponseTemplate). Thus, tbsData can not be |
4074 | 0 | * equal to null */ |
4075 | 0 | ocspResponseData *tbsData = ocsp_GetResponseData(response, |
4076 | 0 | &tbsResponseDataDER); |
4077 | 0 | ocspSignature *signature = ocsp_GetResponseSignature(response); |
4078 | 0 |
|
4079 | 0 | if (!signature) { |
4080 | 0 | PORT_SetError(SEC_ERROR_OCSP_BAD_SIGNATURE); |
4081 | 0 | return SECFailure; |
4082 | 0 | } |
4083 | 0 |
|
4084 | 0 | /* |
4085 | 0 | * If this signature has already gone through verification, just |
4086 | 0 | * return the cached result. |
4087 | 0 | */ |
4088 | 0 | if (signature->wasChecked) { |
4089 | 0 | if (signature->status == SECSuccess) { |
4090 | 0 | if (pSignerCert != NULL) |
4091 | 0 | *pSignerCert = CERT_DupCertificate(signature->cert); |
4092 | 0 | } else { |
4093 | 0 | PORT_SetError(signature->failureReason); |
4094 | 0 | } |
4095 | 0 | return signature->status; |
4096 | 0 | } |
4097 | 0 |
|
4098 | 0 | signerCert = ocsp_GetSignerCertificate(handle, tbsData, |
4099 | 0 | signature, issuer); |
4100 | 0 | if (signerCert == NULL) { |
4101 | 0 | rv = SECFailure; |
4102 | 0 | if (PORT_GetError() == SEC_ERROR_UNKNOWN_CERT) { |
4103 | 0 | /* Make the error a little more specific. */ |
4104 | 0 | PORT_SetError(SEC_ERROR_OCSP_INVALID_SIGNING_CERT); |
4105 | 0 | } |
4106 | 0 | goto finish; |
4107 | 0 | } |
4108 | 0 |
|
4109 | 0 | /* |
4110 | 0 | * We could mark this true at the top of this function, or always |
4111 | 0 | * below at "finish", but if the problem was just that we could not |
4112 | 0 | * find the signer's cert, leave that as if the signature hasn't |
4113 | 0 | * been checked in case a subsequent call might have better luck. |
4114 | 0 | */ |
4115 | 0 | signature->wasChecked = PR_TRUE; |
4116 | 0 |
|
4117 | 0 | /* |
4118 | 0 | * The function will also verify the signer certificate; we |
4119 | 0 | * need to tell it *when* that certificate must be valid -- for our |
4120 | 0 | * purposes we expect it to be valid when the response was signed. |
4121 | 0 | * The value of "producedAt" is the signing time. |
4122 | 0 | */ |
4123 | 0 | rv = DER_GeneralizedTimeToTime(&producedAt, &tbsData->producedAt); |
4124 | 0 | if (rv != SECSuccess) |
4125 | 0 | goto finish; |
4126 | 0 | |
4127 | 0 | /* |
4128 | 0 | * Just because we have a cert does not mean it is any good; check |
4129 | 0 | * it for validity, trust and usage. |
4130 | 0 | */ |
4131 | 0 | if (!ocsp_CertIsOCSPDefaultResponder(handle, signerCert)) { |
4132 | 0 | SECCertUsage certUsage; |
4133 | 0 | if (CERT_IsCACert(signerCert, NULL)) { |
4134 | 0 | certUsage = certUsageAnyCA; |
4135 | 0 | } else { |
4136 | 0 | certUsage = certUsageStatusResponder; |
4137 | 0 | } |
4138 | 0 | rv = cert_VerifyCertWithFlags(handle, signerCert, PR_TRUE, certUsage, |
4139 | 0 | producedAt, CERT_VERIFYCERT_SKIP_OCSP, |
4140 | 0 | pwArg, NULL); |
4141 | 0 | if (rv != SECSuccess) { |
4142 | 0 | PORT_SetError(SEC_ERROR_OCSP_INVALID_SIGNING_CERT); |
4143 | 0 | goto finish; |
4144 | 0 | } |
4145 | 0 | } |
4146 | 0 |
|
4147 | 0 | rv = ocsp_VerifyResponseSignature(signerCert, signature, |
4148 | 0 | tbsResponseDataDER, |
4149 | 0 | pwArg); |
4150 | 0 |
|
4151 | 0 | finish: |
4152 | 0 | if (signature->wasChecked) |
4153 | 0 | signature->status = rv; |
4154 | 0 |
|
4155 | 0 | if (rv != SECSuccess) { |
4156 | 0 | signature->failureReason = PORT_GetError(); |
4157 | 0 | if (signerCert != NULL) |
4158 | 0 | CERT_DestroyCertificate(signerCert); |
4159 | 0 | } else { |
4160 | 0 | /* |
4161 | 0 | * Save signer's certificate in signature. |
4162 | 0 | */ |
4163 | 0 | signature->cert = signerCert; |
4164 | 0 | if (pSignerCert != NULL) { |
4165 | 0 | /* |
4166 | 0 | * Pass pointer to signer's certificate back to our caller, |
4167 | 0 | * who is also now responsible for destroying it. |
4168 | 0 | */ |
4169 | 0 | *pSignerCert = CERT_DupCertificate(signerCert); |
4170 | 0 | } |
4171 | 0 | } |
4172 | 0 |
|
4173 | 0 | return rv; |
4174 | 0 | } |
4175 | | |
4176 | | /* |
4177 | | * See if the request's certID and the single response's certID match. |
4178 | | * This can be easy or difficult, depending on whether the same hash |
4179 | | * algorithm was used. |
4180 | | */ |
4181 | | static PRBool |
4182 | | ocsp_CertIDsMatch(CERTOCSPCertID *requestCertID, |
4183 | | CERTOCSPCertID *responseCertID) |
4184 | 0 | { |
4185 | 0 | PRBool match = PR_FALSE; |
4186 | 0 | SECOidTag hashAlg; |
4187 | 0 | SECItem *keyHash = NULL; |
4188 | 0 | SECItem *nameHash = NULL; |
4189 | 0 |
|
4190 | 0 | /* |
4191 | 0 | * In order to match, they must have the same issuer and the same |
4192 | 0 | * serial number. |
4193 | 0 | * |
4194 | 0 | * We just compare the easier things first. |
4195 | 0 | */ |
4196 | 0 | if (SECITEM_CompareItem(&requestCertID->serialNumber, |
4197 | 0 | &responseCertID->serialNumber) != SECEqual) { |
4198 | 0 | goto done; |
4199 | 0 | } |
4200 | 0 | |
4201 | 0 | /* |
4202 | 0 | * Make sure the "parameters" are not too bogus. Since we encoded |
4203 | 0 | * requestCertID->hashAlgorithm, we don't need to check it. |
4204 | 0 | */ |
4205 | 0 | if (responseCertID->hashAlgorithm.parameters.len > 2) { |
4206 | 0 | goto done; |
4207 | 0 | } |
4208 | 0 | if (SECITEM_CompareItem(&requestCertID->hashAlgorithm.algorithm, |
4209 | 0 | &responseCertID->hashAlgorithm.algorithm) == |
4210 | 0 | SECEqual) { |
4211 | 0 | /* |
4212 | 0 | * If the hash algorithms match then we can do a simple compare |
4213 | 0 | * of the hash values themselves. |
4214 | 0 | */ |
4215 | 0 | if ((SECITEM_CompareItem(&requestCertID->issuerNameHash, |
4216 | 0 | &responseCertID->issuerNameHash) == SECEqual) && |
4217 | 0 | (SECITEM_CompareItem(&requestCertID->issuerKeyHash, |
4218 | 0 | &responseCertID->issuerKeyHash) == SECEqual)) { |
4219 | 0 | match = PR_TRUE; |
4220 | 0 | } |
4221 | 0 | goto done; |
4222 | 0 | } |
4223 | 0 |
|
4224 | 0 | hashAlg = SECOID_FindOIDTag(&responseCertID->hashAlgorithm.algorithm); |
4225 | 0 | switch (hashAlg) { |
4226 | 0 | case SEC_OID_SHA1: |
4227 | 0 | keyHash = &requestCertID->issuerSHA1KeyHash; |
4228 | 0 | nameHash = &requestCertID->issuerSHA1NameHash; |
4229 | 0 | break; |
4230 | 0 | case SEC_OID_MD5: |
4231 | 0 | keyHash = &requestCertID->issuerMD5KeyHash; |
4232 | 0 | nameHash = &requestCertID->issuerMD5NameHash; |
4233 | 0 | break; |
4234 | 0 | case SEC_OID_MD2: |
4235 | 0 | keyHash = &requestCertID->issuerMD2KeyHash; |
4236 | 0 | nameHash = &requestCertID->issuerMD2NameHash; |
4237 | 0 | break; |
4238 | 0 | default: |
4239 | 0 | PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); |
4240 | 0 | return PR_FALSE; |
4241 | 0 | } |
4242 | 0 | |
4243 | 0 | if ((keyHash != NULL) && |
4244 | 0 | (SECITEM_CompareItem(nameHash, |
4245 | 0 | &responseCertID->issuerNameHash) == SECEqual) && |
4246 | 0 | (SECITEM_CompareItem(keyHash, |
4247 | 0 | &responseCertID->issuerKeyHash) == SECEqual)) { |
4248 | 0 | match = PR_TRUE; |
4249 | 0 | } |
4250 | 0 |
|
4251 | 0 | done: |
4252 | 0 | return match; |
4253 | 0 | } |
4254 | | |
4255 | | /* |
4256 | | * Find the single response for the cert specified by certID. |
4257 | | * No copying is done; this just returns a pointer to the appropriate |
4258 | | * response within responses, if it is found (and null otherwise). |
4259 | | * This is fine, of course, since this function is internal-use only. |
4260 | | */ |
4261 | | static CERTOCSPSingleResponse * |
4262 | | ocsp_GetSingleResponseForCertID(CERTOCSPSingleResponse **responses, |
4263 | | CERTCertDBHandle *handle, |
4264 | | CERTOCSPCertID *certID) |
4265 | 0 | { |
4266 | 0 | CERTOCSPSingleResponse *single; |
4267 | 0 | int i; |
4268 | 0 |
|
4269 | 0 | if (responses == NULL) |
4270 | 0 | return NULL; |
4271 | 0 | |
4272 | 0 | for (i = 0; responses[i] != NULL; i++) { |
4273 | 0 | single = responses[i]; |
4274 | 0 | if (ocsp_CertIDsMatch(certID, single->certID)) { |
4275 | 0 | return single; |
4276 | 0 | } |
4277 | 0 | } |
4278 | 0 |
|
4279 | 0 | /* |
4280 | 0 | * The OCSP server should have included a response even if it knew |
4281 | 0 | * nothing about the certificate in question. Since it did not, |
4282 | 0 | * this will make it look as if it had. |
4283 | 0 | * |
4284 | 0 | * XXX Should we make this a separate error to notice the server's |
4285 | 0 | * bad behavior? |
4286 | 0 | */ |
4287 | 0 | PORT_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT); |
4288 | 0 | return NULL; |
4289 | 0 | } |
4290 | | |
4291 | | static ocspCheckingContext * |
4292 | | ocsp_GetCheckingContext(CERTCertDBHandle *handle) |
4293 | 0 | { |
4294 | 0 | CERTStatusConfig *statusConfig; |
4295 | 0 | ocspCheckingContext *ocspcx = NULL; |
4296 | 0 |
|
4297 | 0 | statusConfig = CERT_GetStatusConfig(handle); |
4298 | 0 | if (statusConfig != NULL) { |
4299 | 0 | ocspcx = statusConfig->statusContext; |
4300 | 0 |
|
4301 | 0 | /* |
4302 | 0 | * This is actually an internal error, because we should never |
4303 | 0 | * have a good statusConfig without a good statusContext, too. |
4304 | 0 | * For lack of anything better, though, we just assert and use |
4305 | 0 | * the same error as if there were no statusConfig (set below). |
4306 | 0 | */ |
4307 | 0 | PORT_Assert(ocspcx != NULL); |
4308 | 0 | } |
4309 | 0 |
|
4310 | 0 | if (ocspcx == NULL) |
4311 | 0 | PORT_SetError(SEC_ERROR_OCSP_NOT_ENABLED); |
4312 | 0 |
|
4313 | 0 | return ocspcx; |
4314 | 0 | } |
4315 | | |
4316 | | /* |
4317 | | * Return cert reference if the given signerCert is the default responder for |
4318 | | * the given certID. If not, or if any error, return NULL. |
4319 | | */ |
4320 | | static CERTCertificate * |
4321 | | ocsp_CertGetDefaultResponder(CERTCertDBHandle *handle, CERTOCSPCertID *certID) |
4322 | 0 | { |
4323 | 0 | ocspCheckingContext *ocspcx; |
4324 | 0 |
|
4325 | 0 | ocspcx = ocsp_GetCheckingContext(handle); |
4326 | 0 | if (ocspcx == NULL) |
4327 | 0 | goto loser; |
4328 | 0 | |
4329 | 0 | /* |
4330 | 0 | * Right now we have only one default responder. It applies to |
4331 | 0 | * all certs when it is used, so the check is simple and certID |
4332 | 0 | * has no bearing on the answer. Someday in the future we may |
4333 | 0 | * allow configuration of different responders for different |
4334 | 0 | * issuers, and then we would have to use the issuer specified |
4335 | 0 | * in certID to determine if signerCert is the right one. |
4336 | 0 | */ |
4337 | 0 | if (ocspcx->useDefaultResponder) { |
4338 | 0 | PORT_Assert(ocspcx->defaultResponderCert != NULL); |
4339 | 0 | return ocspcx->defaultResponderCert; |
4340 | 0 | } |
4341 | 0 |
|
4342 | 0 | loser: |
4343 | 0 | return NULL; |
4344 | 0 | } |
4345 | | |
4346 | | /* |
4347 | | * Return true if the cert is one of the default responders configured for |
4348 | | * ocsp context. If not, or if any error, return false. |
4349 | | */ |
4350 | | PRBool |
4351 | | ocsp_CertIsOCSPDefaultResponder(CERTCertDBHandle *handle, CERTCertificate *cert) |
4352 | 0 | { |
4353 | 0 | ocspCheckingContext *ocspcx; |
4354 | 0 |
|
4355 | 0 | ocspcx = ocsp_GetCheckingContext(handle); |
4356 | 0 | if (ocspcx == NULL) |
4357 | 0 | return PR_FALSE; |
4358 | 0 | |
4359 | 0 | /* |
4360 | 0 | * Right now we have only one default responder. It applies to |
4361 | 0 | * all certs when it is used, so the check is simple and certID |
4362 | 0 | * has no bearing on the answer. Someday in the future we may |
4363 | 0 | * allow configuration of different responders for different |
4364 | 0 | * issuers, and then we would have to use the issuer specified |
4365 | 0 | * in certID to determine if signerCert is the right one. |
4366 | 0 | */ |
4367 | 0 | if (ocspcx->useDefaultResponder && |
4368 | 0 | CERT_CompareCerts(ocspcx->defaultResponderCert, cert)) { |
4369 | 0 | return PR_TRUE; |
4370 | 0 | } |
4371 | 0 |
|
4372 | 0 | return PR_FALSE; |
4373 | 0 | } |
4374 | | |
4375 | | /* |
4376 | | * Check that the given signer certificate is authorized to sign status |
4377 | | * information for the given certID. Return true if it is, false if not |
4378 | | * (or if there is any error along the way). If false is returned because |
4379 | | * the signer is not authorized, the following error will be set: |
4380 | | * SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE |
4381 | | * Other errors are low-level problems (no memory, bad database, etc.). |
4382 | | * |
4383 | | * There are three ways to be authorized. In the order in which we check, |
4384 | | * using the terms used in the OCSP spec, the signer must be one of: |
4385 | | * 1. A "trusted responder" -- it matches a local configuration |
4386 | | * of OCSP signing authority for the certificate in question. |
4387 | | * 2. The CA who issued the certificate in question. |
4388 | | * 3. A "CA designated responder", aka an "authorized responder" -- it |
4389 | | * must be represented by a special cert issued by the CA who issued |
4390 | | * the certificate in question. |
4391 | | */ |
4392 | | static PRBool |
4393 | | ocsp_AuthorizedResponderForCertID(CERTCertDBHandle *handle, |
4394 | | CERTCertificate *signerCert, |
4395 | | CERTOCSPCertID *certID, |
4396 | | PRTime thisUpdate) |
4397 | 0 | { |
4398 | 0 | CERTCertificate *issuerCert = NULL, *defRespCert; |
4399 | 0 | SECItem *keyHash = NULL; |
4400 | 0 | SECItem *nameHash = NULL; |
4401 | 0 | SECOidTag hashAlg; |
4402 | 0 | PRBool keyHashEQ = PR_FALSE, nameHashEQ = PR_FALSE; |
4403 | 0 |
|
4404 | 0 | /* |
4405 | 0 | * Check first for a trusted responder, which overrides everything else. |
4406 | 0 | */ |
4407 | 0 | if ((defRespCert = ocsp_CertGetDefaultResponder(handle, certID)) && |
4408 | 0 | CERT_CompareCerts(defRespCert, signerCert)) { |
4409 | 0 | return PR_TRUE; |
4410 | 0 | } |
4411 | 0 |
|
4412 | 0 | /* |
4413 | 0 | * In the other two cases, we need to do an issuer comparison. |
4414 | 0 | * How we do it depends on whether the signer certificate has the |
4415 | 0 | * special extension (for a designated responder) or not. |
4416 | 0 | * |
4417 | 0 | * First, lets check if signer of the response is the actual issuer |
4418 | 0 | * of the cert. For that we will use signer cert key hash and cert subj |
4419 | 0 | * name hash and will compare them with already calculated issuer key |
4420 | 0 | * hash and issuer name hash. The hash algorithm is picked from response |
4421 | 0 | * certID hash to avoid second hash calculation. |
4422 | 0 | */ |
4423 | 0 |
|
4424 | 0 | hashAlg = SECOID_FindOIDTag(&certID->hashAlgorithm.algorithm); |
4425 | 0 |
|
4426 | 0 | keyHash = CERT_GetSubjectPublicKeyDigest(NULL, signerCert, hashAlg, NULL); |
4427 | 0 | if (keyHash != NULL) { |
4428 | 0 |
|
4429 | 0 | keyHashEQ = |
4430 | 0 | (SECITEM_CompareItem(keyHash, |
4431 | 0 | &certID->issuerKeyHash) == SECEqual); |
4432 | 0 | SECITEM_FreeItem(keyHash, PR_TRUE); |
4433 | 0 | } |
4434 | 0 | if (keyHashEQ && |
4435 | 0 | (nameHash = CERT_GetSubjectNameDigest(NULL, signerCert, |
4436 | 0 | hashAlg, NULL))) { |
4437 | 0 | nameHashEQ = |
4438 | 0 | (SECITEM_CompareItem(nameHash, |
4439 | 0 | &certID->issuerNameHash) == SECEqual); |
4440 | 0 |
|
4441 | 0 | SECITEM_FreeItem(nameHash, PR_TRUE); |
4442 | 0 | if (nameHashEQ) { |
4443 | 0 | /* The issuer of the cert is the the signer of the response */ |
4444 | 0 | return PR_TRUE; |
4445 | 0 | } |
4446 | 0 | } |
4447 | 0 |
|
4448 | 0 | keyHashEQ = PR_FALSE; |
4449 | 0 | nameHashEQ = PR_FALSE; |
4450 | 0 |
|
4451 | 0 | if (!ocsp_CertIsOCSPDesignatedResponder(signerCert)) { |
4452 | 0 | PORT_SetError(SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE); |
4453 | 0 | return PR_FALSE; |
4454 | 0 | } |
4455 | 0 |
|
4456 | 0 | /* |
4457 | 0 | * The signer is a designated responder. Its issuer must match |
4458 | 0 | * the issuer of the cert being checked. |
4459 | 0 | */ |
4460 | 0 | issuerCert = CERT_FindCertIssuer(signerCert, thisUpdate, |
4461 | 0 | certUsageAnyCA); |
4462 | 0 | if (issuerCert == NULL) { |
4463 | 0 | /* |
4464 | 0 | * We could leave the SEC_ERROR_UNKNOWN_ISSUER error alone, |
4465 | 0 | * but the following will give slightly more information. |
4466 | 0 | * Once we have an error stack, things will be much better. |
4467 | 0 | */ |
4468 | 0 | PORT_SetError(SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE); |
4469 | 0 | return PR_FALSE; |
4470 | 0 | } |
4471 | 0 |
|
4472 | 0 | keyHash = CERT_GetSubjectPublicKeyDigest(NULL, issuerCert, hashAlg, NULL); |
4473 | 0 | nameHash = CERT_GetSubjectNameDigest(NULL, issuerCert, hashAlg, NULL); |
4474 | 0 |
|
4475 | 0 | CERT_DestroyCertificate(issuerCert); |
4476 | 0 |
|
4477 | 0 | if (keyHash != NULL && nameHash != NULL) { |
4478 | 0 | keyHashEQ = |
4479 | 0 | (SECITEM_CompareItem(keyHash, |
4480 | 0 | &certID->issuerKeyHash) == SECEqual); |
4481 | 0 |
|
4482 | 0 | nameHashEQ = |
4483 | 0 | (SECITEM_CompareItem(nameHash, |
4484 | 0 | &certID->issuerNameHash) == SECEqual); |
4485 | 0 | } |
4486 | 0 |
|
4487 | 0 | if (keyHash) { |
4488 | 0 | SECITEM_FreeItem(keyHash, PR_TRUE); |
4489 | 0 | } |
4490 | 0 | if (nameHash) { |
4491 | 0 | SECITEM_FreeItem(nameHash, PR_TRUE); |
4492 | 0 | } |
4493 | 0 |
|
4494 | 0 | if (keyHashEQ && nameHashEQ) { |
4495 | 0 | return PR_TRUE; |
4496 | 0 | } |
4497 | 0 |
|
4498 | 0 | PORT_SetError(SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE); |
4499 | 0 | return PR_FALSE; |
4500 | 0 | } |
4501 | | |
4502 | | /* |
4503 | | * We need to check that a responder gives us "recent" information. |
4504 | | * Since a responder can pre-package responses, we need to pick an amount |
4505 | | * of time that is acceptable to us, and reject any response that is |
4506 | | * older than that. |
4507 | | * |
4508 | | * XXX This *should* be based on some configuration parameter, so that |
4509 | | * different usages could specify exactly what constitutes "sufficiently |
4510 | | * recent". But that is not going to happen right away. For now, we |
4511 | | * want something from within the last 24 hours. This macro defines that |
4512 | | * number in seconds. |
4513 | | */ |
4514 | | #define OCSP_ALLOWABLE_LAPSE_SECONDS (24L * 60L * 60L) |
4515 | | |
4516 | | static PRBool |
4517 | | ocsp_TimeIsRecent(PRTime checkTime) |
4518 | 0 | { |
4519 | 0 | PRTime now = PR_Now(); |
4520 | 0 | PRTime lapse, tmp; |
4521 | 0 |
|
4522 | 0 | LL_I2L(lapse, OCSP_ALLOWABLE_LAPSE_SECONDS); |
4523 | 0 | LL_I2L(tmp, PR_USEC_PER_SEC); |
4524 | 0 | LL_MUL(lapse, lapse, tmp); /* allowable lapse in microseconds */ |
4525 | 0 |
|
4526 | 0 | LL_ADD(checkTime, checkTime, lapse); |
4527 | 0 | if (LL_CMP(now, >, checkTime)) |
4528 | 0 | return PR_FALSE; |
4529 | 0 | |
4530 | 0 | return PR_TRUE; |
4531 | 0 | } |
4532 | | |
4533 | | #define OCSP_SLOP (5L * 60L) /* OCSP responses are allowed to be 5 minutes \ |
4534 | | in the future by default */ |
4535 | | |
4536 | | static PRUint32 ocspsloptime = OCSP_SLOP; /* seconds */ |
4537 | | |
4538 | | /* |
4539 | | * If an old response contains the revoked certificate status, we want |
4540 | | * to return SECSuccess so the response will be used. |
4541 | | */ |
4542 | | static SECStatus |
4543 | | ocsp_HandleOldSingleResponse(CERTOCSPSingleResponse *single, PRTime time) |
4544 | 0 | { |
4545 | 0 | SECStatus rv; |
4546 | 0 | ocspCertStatus *status = single->certStatus; |
4547 | 0 | if (status->certStatusType == ocspCertStatus_revoked) { |
4548 | 0 | rv = ocsp_CertRevokedAfter(status->certStatusInfo.revokedInfo, time); |
4549 | 0 | if (rv != SECSuccess && |
4550 | 0 | PORT_GetError() == SEC_ERROR_REVOKED_CERTIFICATE) { |
4551 | 0 | /* |
4552 | 0 | * Return SECSuccess now. The subsequent ocsp_CertRevokedAfter |
4553 | 0 | * call in ocsp_CertHasGoodStatus will cause |
4554 | 0 | * ocsp_CertHasGoodStatus to fail with |
4555 | 0 | * SEC_ERROR_REVOKED_CERTIFICATE. |
4556 | 0 | */ |
4557 | 0 | return SECSuccess; |
4558 | 0 | } |
4559 | 0 | } |
4560 | 0 | PORT_SetError(SEC_ERROR_OCSP_OLD_RESPONSE); |
4561 | 0 | return SECFailure; |
4562 | 0 | } |
4563 | | |
4564 | | /* |
4565 | | * Check that this single response is okay. A return of SECSuccess means: |
4566 | | * 1. The signer (represented by "signerCert") is authorized to give status |
4567 | | * for the cert represented by the individual response in "single". |
4568 | | * 2. The value of thisUpdate is earlier than now. |
4569 | | * 3. The value of producedAt is later than or the same as thisUpdate. |
4570 | | * 4. If nextUpdate is given: |
4571 | | * - The value of nextUpdate is later than now. |
4572 | | * - The value of producedAt is earlier than nextUpdate. |
4573 | | * Else if no nextUpdate: |
4574 | | * - The value of thisUpdate is fairly recent. |
4575 | | * - The value of producedAt is fairly recent. |
4576 | | * However we do not need to perform an explicit check for this last |
4577 | | * constraint because it is already guaranteed by checking that |
4578 | | * producedAt is later than thisUpdate and thisUpdate is recent. |
4579 | | * Oh, and any responder is "authorized" to say that a cert is unknown to it. |
4580 | | * |
4581 | | * If any of those checks fail, SECFailure is returned and an error is set: |
4582 | | * SEC_ERROR_OCSP_FUTURE_RESPONSE |
4583 | | * SEC_ERROR_OCSP_OLD_RESPONSE |
4584 | | * SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE |
4585 | | * Other errors are low-level problems (no memory, bad database, etc.). |
4586 | | */ |
4587 | | static SECStatus |
4588 | | ocsp_VerifySingleResponse(CERTOCSPSingleResponse *single, |
4589 | | CERTCertDBHandle *handle, |
4590 | | CERTCertificate *signerCert, |
4591 | | PRTime producedAt) |
4592 | 0 | { |
4593 | 0 | CERTOCSPCertID *certID = single->certID; |
4594 | 0 | PRTime now, thisUpdate, nextUpdate, tmstamp, tmp; |
4595 | 0 | SECStatus rv; |
4596 | 0 |
|
4597 | 0 | OCSP_TRACE(("OCSP ocsp_VerifySingleResponse, nextUpdate: %d\n", |
4598 | 0 | ((single->nextUpdate) != 0))); |
4599 | 0 | /* |
4600 | 0 | * If all the responder said was that the given cert was unknown to it, |
4601 | 0 | * that is a valid response. Not very interesting to us, of course, |
4602 | 0 | * but all this function is concerned with is validity of the response, |
4603 | 0 | * not the status of the cert. |
4604 | 0 | */ |
4605 | 0 | PORT_Assert(single->certStatus != NULL); |
4606 | 0 | if (single->certStatus->certStatusType == ocspCertStatus_unknown) |
4607 | 0 | return SECSuccess; |
4608 | 0 | |
4609 | 0 | /* |
4610 | 0 | * We need to extract "thisUpdate" for use below and to pass along |
4611 | 0 | * to AuthorizedResponderForCertID in case it needs it for doing an |
4612 | 0 | * issuer look-up. |
4613 | 0 | */ |
4614 | 0 | rv = DER_GeneralizedTimeToTime(&thisUpdate, &single->thisUpdate); |
4615 | 0 | if (rv != SECSuccess) |
4616 | 0 | return rv; |
4617 | 0 | |
4618 | 0 | /* |
4619 | 0 | * First confirm that signerCert is authorized to give this status. |
4620 | 0 | */ |
4621 | 0 | if (ocsp_AuthorizedResponderForCertID(handle, signerCert, certID, |
4622 | 0 | thisUpdate) != PR_TRUE) |
4623 | 0 | return SECFailure; |
4624 | 0 | |
4625 | 0 | /* |
4626 | 0 | * Now check the time stuff, as described above. |
4627 | 0 | */ |
4628 | 0 | now = PR_Now(); |
4629 | 0 | /* allow slop time for future response */ |
4630 | 0 | LL_UI2L(tmstamp, ocspsloptime); /* get slop time in seconds */ |
4631 | 0 | LL_UI2L(tmp, PR_USEC_PER_SEC); |
4632 | 0 | LL_MUL(tmp, tmstamp, tmp); /* convert the slop time to PRTime */ |
4633 | 0 | LL_ADD(tmstamp, tmp, now); /* add current time to it */ |
4634 | 0 |
|
4635 | 0 | if (LL_CMP(thisUpdate, >, tmstamp) || LL_CMP(producedAt, <, thisUpdate)) { |
4636 | 0 | PORT_SetError(SEC_ERROR_OCSP_FUTURE_RESPONSE); |
4637 | 0 | return SECFailure; |
4638 | 0 | } |
4639 | 0 | if (single->nextUpdate != NULL) { |
4640 | 0 | rv = DER_GeneralizedTimeToTime(&nextUpdate, single->nextUpdate); |
4641 | 0 | if (rv != SECSuccess) |
4642 | 0 | return rv; |
4643 | 0 | |
4644 | 0 | LL_ADD(tmp, tmp, nextUpdate); |
4645 | 0 | if (LL_CMP(tmp, <, now) || LL_CMP(producedAt, >, nextUpdate)) |
4646 | 0 | return ocsp_HandleOldSingleResponse(single, now); |
4647 | 0 | } else if (ocsp_TimeIsRecent(thisUpdate) != PR_TRUE) { |
4648 | 0 | return ocsp_HandleOldSingleResponse(single, now); |
4649 | 0 | } |
4650 | 0 | |
4651 | 0 | return SECSuccess; |
4652 | 0 | } |
4653 | | |
4654 | | /* |
4655 | | * FUNCTION: CERT_GetOCSPAuthorityInfoAccessLocation |
4656 | | * Get the value of the URI of the OCSP responder for the given cert. |
4657 | | * This is found in the (optional) Authority Information Access extension |
4658 | | * in the cert. |
4659 | | * INPUTS: |
4660 | | * CERTCertificate *cert |
4661 | | * The certificate being examined. |
4662 | | * RETURN: |
4663 | | * char * |
4664 | | * A copy of the URI for the OCSP method, if found. If either the |
4665 | | * extension is not present or it does not contain an entry for OCSP, |
4666 | | * SEC_ERROR_CERT_BAD_ACCESS_LOCATION will be set and a NULL returned. |
4667 | | * Any other error will also result in a NULL being returned. |
4668 | | * |
4669 | | * This result should be freed (via PORT_Free) when no longer in use. |
4670 | | */ |
4671 | | char * |
4672 | | CERT_GetOCSPAuthorityInfoAccessLocation(const CERTCertificate *cert) |
4673 | 0 | { |
4674 | 0 | CERTGeneralName *locname = NULL; |
4675 | 0 | SECItem *location = NULL; |
4676 | 0 | SECItem *encodedAuthInfoAccess = NULL; |
4677 | 0 | CERTAuthInfoAccess **authInfoAccess = NULL; |
4678 | 0 | char *locURI = NULL; |
4679 | 0 | PLArenaPool *arena = NULL; |
4680 | 0 | SECStatus rv; |
4681 | 0 | int i; |
4682 | 0 |
|
4683 | 0 | /* |
4684 | 0 | * Allocate this one from the heap because it will get filled in |
4685 | 0 | * by CERT_FindCertExtension which will also allocate from the heap, |
4686 | 0 | * and we can free the entire thing on our way out. |
4687 | 0 | */ |
4688 | 0 | encodedAuthInfoAccess = SECITEM_AllocItem(NULL, NULL, 0); |
4689 | 0 | if (encodedAuthInfoAccess == NULL) |
4690 | 0 | goto loser; |
4691 | 0 | |
4692 | 0 | rv = CERT_FindCertExtension(cert, SEC_OID_X509_AUTH_INFO_ACCESS, |
4693 | 0 | encodedAuthInfoAccess); |
4694 | 0 | if (rv == SECFailure) { |
4695 | 0 | PORT_SetError(SEC_ERROR_CERT_BAD_ACCESS_LOCATION); |
4696 | 0 | goto loser; |
4697 | 0 | } |
4698 | 0 |
|
4699 | 0 | /* |
4700 | 0 | * The rest of the things allocated in the routine will come out of |
4701 | 0 | * this arena, which is temporary just for us to decode and get at the |
4702 | 0 | * AIA extension. The whole thing will be destroyed on our way out, |
4703 | 0 | * after we have copied the location string (url) itself (if found). |
4704 | 0 | */ |
4705 | 0 | arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
4706 | 0 | if (arena == NULL) |
4707 | 0 | goto loser; |
4708 | 0 | |
4709 | 0 | authInfoAccess = CERT_DecodeAuthInfoAccessExtension(arena, |
4710 | 0 | encodedAuthInfoAccess); |
4711 | 0 | if (authInfoAccess == NULL) |
4712 | 0 | goto loser; |
4713 | 0 | |
4714 | 0 | for (i = 0; authInfoAccess[i] != NULL; i++) { |
4715 | 0 | if (SECOID_FindOIDTag(&authInfoAccess[i]->method) == SEC_OID_PKIX_OCSP) |
4716 | 0 | locname = authInfoAccess[i]->location; |
4717 | 0 | } |
4718 | 0 |
|
4719 | 0 | /* |
4720 | 0 | * If we found an AIA extension, but it did not include an OCSP method, |
4721 | 0 | * that should look to our caller as if we did not find the extension |
4722 | 0 | * at all, because it is only an OCSP method that we care about. |
4723 | 0 | * So set the same error that would be set if the AIA extension was |
4724 | 0 | * not there at all. |
4725 | 0 | */ |
4726 | 0 | if (locname == NULL) { |
4727 | 0 | PORT_SetError(SEC_ERROR_CERT_BAD_ACCESS_LOCATION); |
4728 | 0 | goto loser; |
4729 | 0 | } |
4730 | 0 |
|
4731 | 0 | /* |
4732 | 0 | * The following is just a pointer back into locname (i.e. not a copy); |
4733 | 0 | * thus it should not be freed. |
4734 | 0 | */ |
4735 | 0 | location = CERT_GetGeneralNameByType(locname, certURI, PR_FALSE); |
4736 | 0 | if (location == NULL) { |
4737 | 0 | /* |
4738 | 0 | * XXX Appears that CERT_GetGeneralNameByType does not set an |
4739 | 0 | * error if there is no name by that type. For lack of anything |
4740 | 0 | * better, act as if the extension was not found. In the future |
4741 | 0 | * this should probably be something more like the extension was |
4742 | 0 | * badly formed. |
4743 | 0 | */ |
4744 | 0 | PORT_SetError(SEC_ERROR_CERT_BAD_ACCESS_LOCATION); |
4745 | 0 | goto loser; |
4746 | 0 | } |
4747 | 0 |
|
4748 | 0 | /* |
4749 | 0 | * That location is really a string, but it has a specified length |
4750 | 0 | * without a null-terminator. We need a real string that does have |
4751 | 0 | * a null-terminator, and we need a copy of it anyway to return to |
4752 | 0 | * our caller -- so allocate and copy. |
4753 | 0 | */ |
4754 | 0 | locURI = PORT_Alloc(location->len + 1); |
4755 | 0 | if (locURI == NULL) { |
4756 | 0 | goto loser; |
4757 | 0 | } |
4758 | 0 | PORT_Memcpy(locURI, location->data, location->len); |
4759 | 0 | locURI[location->len] = '\0'; |
4760 | 0 |
|
4761 | 0 | loser: |
4762 | 0 | if (arena != NULL) |
4763 | 0 | PORT_FreeArena(arena, PR_FALSE); |
4764 | 0 |
|
4765 | 0 | if (encodedAuthInfoAccess != NULL) |
4766 | 0 | SECITEM_FreeItem(encodedAuthInfoAccess, PR_TRUE); |
4767 | 0 |
|
4768 | 0 | return locURI; |
4769 | 0 | } |
4770 | | |
4771 | | /* |
4772 | | * Figure out where we should go to find out the status of the given cert |
4773 | | * via OCSP. If allowed to use a default responder uri and a default |
4774 | | * responder is set up, then that is our answer. |
4775 | | * If not, see if the certificate has an Authority Information Access (AIA) |
4776 | | * extension for OCSP, and return the value of that. Otherwise return NULL. |
4777 | | * We also let our caller know whether or not the responder chosen was |
4778 | | * a default responder or not through the output variable isDefault; |
4779 | | * its value has no meaning unless a good (non-null) value is returned |
4780 | | * for the location. |
4781 | | * |
4782 | | * The result needs to be freed (PORT_Free) when no longer in use. |
4783 | | */ |
4784 | | char * |
4785 | | ocsp_GetResponderLocation(CERTCertDBHandle *handle, CERTCertificate *cert, |
4786 | | PRBool canUseDefault, PRBool *isDefault) |
4787 | 0 | { |
4788 | 0 | ocspCheckingContext *ocspcx = NULL; |
4789 | 0 | char *ocspUrl = NULL; |
4790 | 0 |
|
4791 | 0 | if (canUseDefault) { |
4792 | 0 | ocspcx = ocsp_GetCheckingContext(handle); |
4793 | 0 | } |
4794 | 0 | if (ocspcx != NULL && ocspcx->useDefaultResponder) { |
4795 | 0 | /* |
4796 | 0 | * A default responder wins out, if specified. |
4797 | 0 | * XXX Someday this may be a more complicated determination based |
4798 | 0 | * on the cert's issuer. (That is, we could have different default |
4799 | 0 | * responders configured for different issuers.) |
4800 | 0 | */ |
4801 | 0 | PORT_Assert(ocspcx->defaultResponderURI != NULL); |
4802 | 0 | *isDefault = PR_TRUE; |
4803 | 0 | return (PORT_Strdup(ocspcx->defaultResponderURI)); |
4804 | 0 | } |
4805 | 0 |
|
4806 | 0 | /* |
4807 | 0 | * No default responder set up, so go see if we can find an AIA |
4808 | 0 | * extension that has a value for OCSP, and get the url from that. |
4809 | 0 | */ |
4810 | 0 | *isDefault = PR_FALSE; |
4811 | 0 | ocspUrl = CERT_GetOCSPAuthorityInfoAccessLocation(cert); |
4812 | 0 | if (!ocspUrl) { |
4813 | 0 | CERT_StringFromCertFcn altFcn; |
4814 | 0 |
|
4815 | 0 | PR_EnterMonitor(OCSP_Global.monitor); |
4816 | 0 | altFcn = OCSP_Global.alternateOCSPAIAFcn; |
4817 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
4818 | 0 | if (altFcn) { |
4819 | 0 | ocspUrl = (*altFcn)(cert); |
4820 | 0 | if (ocspUrl) |
4821 | 0 | *isDefault = PR_TRUE; |
4822 | 0 | } |
4823 | 0 | } |
4824 | 0 | return ocspUrl; |
4825 | 0 | } |
4826 | | |
4827 | | /* |
4828 | | * Return SECSuccess if the cert was revoked *after* "time", |
4829 | | * SECFailure otherwise. |
4830 | | */ |
4831 | | static SECStatus |
4832 | | ocsp_CertRevokedAfter(ocspRevokedInfo *revokedInfo, PRTime time) |
4833 | 0 | { |
4834 | 0 | PRTime revokedTime; |
4835 | 0 | SECStatus rv; |
4836 | 0 |
|
4837 | 0 | rv = DER_GeneralizedTimeToTime(&revokedTime, &revokedInfo->revocationTime); |
4838 | 0 | if (rv != SECSuccess) |
4839 | 0 | return rv; |
4840 | 0 | |
4841 | 0 | /* |
4842 | 0 | * Set the error even if we will return success; someone might care. |
4843 | 0 | */ |
4844 | 0 | PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE); |
4845 | 0 |
|
4846 | 0 | if (LL_CMP(revokedTime, >, time)) |
4847 | 0 | return SECSuccess; |
4848 | 0 | |
4849 | 0 | return SECFailure; |
4850 | 0 | } |
4851 | | |
4852 | | /* |
4853 | | * See if the cert represented in the single response had a good status |
4854 | | * at the specified time. |
4855 | | */ |
4856 | | SECStatus |
4857 | | ocsp_CertHasGoodStatus(ocspCertStatus *status, PRTime time) |
4858 | 0 | { |
4859 | 0 | SECStatus rv; |
4860 | 0 | switch (status->certStatusType) { |
4861 | 0 | case ocspCertStatus_good: |
4862 | 0 | rv = SECSuccess; |
4863 | 0 | break; |
4864 | 0 | case ocspCertStatus_revoked: |
4865 | 0 | rv = ocsp_CertRevokedAfter(status->certStatusInfo.revokedInfo, time); |
4866 | 0 | break; |
4867 | 0 | case ocspCertStatus_unknown: |
4868 | 0 | PORT_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT); |
4869 | 0 | rv = SECFailure; |
4870 | 0 | break; |
4871 | 0 | case ocspCertStatus_other: |
4872 | 0 | default: |
4873 | 0 | PORT_Assert(0); |
4874 | 0 | PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE); |
4875 | 0 | rv = SECFailure; |
4876 | 0 | break; |
4877 | 0 | } |
4878 | 0 | return rv; |
4879 | 0 | } |
4880 | | |
4881 | | static SECStatus |
4882 | | ocsp_SingleResponseCertHasGoodStatus(CERTOCSPSingleResponse *single, |
4883 | | PRTime time) |
4884 | 0 | { |
4885 | 0 | return ocsp_CertHasGoodStatus(single->certStatus, time); |
4886 | 0 | } |
4887 | | |
4888 | | /* SECFailure means the arguments were invalid. |
4889 | | * On SECSuccess, the out parameters contain the OCSP status. |
4890 | | * rvOcsp contains the overall result of the OCSP operation. |
4891 | | * Depending on input parameter ignoreGlobalOcspFailureSetting, |
4892 | | * a soft failure might be converted into *rvOcsp=SECSuccess. |
4893 | | * If the cached attempt to obtain OCSP information had resulted |
4894 | | * in a failure, missingResponseError shows the error code of |
4895 | | * that failure. |
4896 | | * cacheFreshness is ocspMissing if no entry was found, |
4897 | | * ocspFresh if a fresh entry was found, or |
4898 | | * ocspStale if a stale entry was found. |
4899 | | */ |
4900 | | SECStatus |
4901 | | ocsp_GetCachedOCSPResponseStatus(CERTOCSPCertID *certID, |
4902 | | PRTime time, |
4903 | | PRBool ignoreGlobalOcspFailureSetting, |
4904 | | SECStatus *rvOcsp, |
4905 | | SECErrorCodes *missingResponseError, |
4906 | | OCSPFreshness *cacheFreshness) |
4907 | 0 | { |
4908 | 0 | OCSPCacheItem *cacheItem = NULL; |
4909 | 0 |
|
4910 | 0 | if (!certID || !missingResponseError || !rvOcsp || !cacheFreshness) { |
4911 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
4912 | 0 | return SECFailure; |
4913 | 0 | } |
4914 | 0 | *rvOcsp = SECFailure; |
4915 | 0 | *missingResponseError = 0; |
4916 | 0 | *cacheFreshness = ocspMissing; |
4917 | 0 |
|
4918 | 0 | PR_EnterMonitor(OCSP_Global.monitor); |
4919 | 0 | cacheItem = ocsp_FindCacheEntry(&OCSP_Global.cache, certID); |
4920 | 0 | if (cacheItem) { |
4921 | 0 | *cacheFreshness = ocsp_IsCacheItemFresh(cacheItem) ? ocspFresh |
4922 | 0 | : ocspStale; |
4923 | 0 | /* having an arena means, we have a cached certStatus */ |
4924 | 0 | if (cacheItem->certStatusArena) { |
4925 | 0 | *rvOcsp = ocsp_CertHasGoodStatus(&cacheItem->certStatus, time); |
4926 | 0 | if (*rvOcsp != SECSuccess) { |
4927 | 0 | *missingResponseError = PORT_GetError(); |
4928 | 0 | } |
4929 | 0 | } else { |
4930 | 0 | /* |
4931 | 0 | * No status cached, the previous attempt failed. |
4932 | 0 | * If OCSP is required, we never decide based on a failed attempt |
4933 | 0 | * However, if OCSP is optional, a recent OCSP failure is |
4934 | 0 | * an allowed good state. |
4935 | 0 | */ |
4936 | 0 | if (*cacheFreshness == ocspFresh && |
4937 | 0 | !ignoreGlobalOcspFailureSetting && |
4938 | 0 | OCSP_Global.ocspFailureMode == |
4939 | 0 | ocspMode_FailureIsNotAVerificationFailure) { |
4940 | 0 | *rvOcsp = SECSuccess; |
4941 | 0 | } |
4942 | 0 | *missingResponseError = cacheItem->missingResponseError; |
4943 | 0 | } |
4944 | 0 | } |
4945 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
4946 | 0 | return SECSuccess; |
4947 | 0 | } |
4948 | | |
4949 | | PRBool |
4950 | | ocsp_FetchingFailureIsVerificationFailure(void) |
4951 | 0 | { |
4952 | 0 | PRBool isFailure; |
4953 | 0 |
|
4954 | 0 | PR_EnterMonitor(OCSP_Global.monitor); |
4955 | 0 | isFailure = |
4956 | 0 | OCSP_Global.ocspFailureMode == ocspMode_FailureIsVerificationFailure; |
4957 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
4958 | 0 | return isFailure; |
4959 | 0 | } |
4960 | | |
4961 | | /* |
4962 | | * FUNCTION: CERT_CheckOCSPStatus |
4963 | | * Checks the status of a certificate via OCSP. Will only check status for |
4964 | | * a certificate that has an AIA (Authority Information Access) extension |
4965 | | * for OCSP *or* when a "default responder" is specified and enabled. |
4966 | | * (If no AIA extension for OCSP and no default responder in place, the |
4967 | | * cert is considered to have a good status and SECSuccess is returned.) |
4968 | | * INPUTS: |
4969 | | * CERTCertDBHandle *handle |
4970 | | * certificate DB of the cert that is being checked |
4971 | | * CERTCertificate *cert |
4972 | | * the certificate being checked |
4973 | | * XXX in the long term also need a boolean parameter that specifies |
4974 | | * whether to check the cert chain, as well; for now we check only |
4975 | | * the leaf (the specified certificate) |
4976 | | * PRTime time |
4977 | | * time for which status is to be determined |
4978 | | * void *pwArg |
4979 | | * argument for password prompting, if needed |
4980 | | * RETURN: |
4981 | | * Returns SECSuccess if an approved OCSP responder "knows" the cert |
4982 | | * *and* returns a non-revoked status for it; SECFailure otherwise, |
4983 | | * with an error set describing the reason: |
4984 | | * |
4985 | | * SEC_ERROR_OCSP_BAD_HTTP_RESPONSE |
4986 | | * SEC_ERROR_OCSP_FUTURE_RESPONSE |
4987 | | * SEC_ERROR_OCSP_MALFORMED_REQUEST |
4988 | | * SEC_ERROR_OCSP_MALFORMED_RESPONSE |
4989 | | * SEC_ERROR_OCSP_OLD_RESPONSE |
4990 | | * SEC_ERROR_OCSP_REQUEST_NEEDS_SIG |
4991 | | * SEC_ERROR_OCSP_SERVER_ERROR |
4992 | | * SEC_ERROR_OCSP_TRY_SERVER_LATER |
4993 | | * SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST |
4994 | | * SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE |
4995 | | * SEC_ERROR_OCSP_UNKNOWN_CERT |
4996 | | * SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS |
4997 | | * SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE |
4998 | | * |
4999 | | * SEC_ERROR_BAD_SIGNATURE |
5000 | | * SEC_ERROR_CERT_BAD_ACCESS_LOCATION |
5001 | | * SEC_ERROR_INVALID_TIME |
5002 | | * SEC_ERROR_REVOKED_CERTIFICATE |
5003 | | * SEC_ERROR_UNKNOWN_ISSUER |
5004 | | * SEC_ERROR_UNKNOWN_SIGNER |
5005 | | * |
5006 | | * Other errors are any of the many possible failures in cert verification |
5007 | | * (e.g. SEC_ERROR_REVOKED_CERTIFICATE, SEC_ERROR_UNTRUSTED_ISSUER) when |
5008 | | * verifying the signer's cert, or low-level problems (error allocating |
5009 | | * memory, error performing ASN.1 decoding, etc.). |
5010 | | */ |
5011 | | SECStatus |
5012 | | CERT_CheckOCSPStatus(CERTCertDBHandle *handle, CERTCertificate *cert, |
5013 | | PRTime time, void *pwArg) |
5014 | 0 | { |
5015 | 0 | CERTOCSPCertID *certID; |
5016 | 0 | PRBool certIDWasConsumed = PR_FALSE; |
5017 | 0 | SECStatus rv; |
5018 | 0 | SECStatus rvOcsp; |
5019 | 0 | SECErrorCodes cachedErrorCode; |
5020 | 0 | OCSPFreshness cachedResponseFreshness; |
5021 | 0 |
|
5022 | 0 | OCSP_TRACE_CERT(cert); |
5023 | 0 | OCSP_TRACE_TIME("## requested validity time:", time); |
5024 | 0 |
|
5025 | 0 | certID = CERT_CreateOCSPCertID(cert, time); |
5026 | 0 | if (!certID) |
5027 | 0 | return SECFailure; |
5028 | 0 | rv = ocsp_GetCachedOCSPResponseStatus( |
5029 | 0 | certID, time, PR_FALSE, /* ignoreGlobalOcspFailureSetting */ |
5030 | 0 | &rvOcsp, &cachedErrorCode, &cachedResponseFreshness); |
5031 | 0 | if (rv != SECSuccess) { |
5032 | 0 | CERT_DestroyOCSPCertID(certID); |
5033 | 0 | return SECFailure; |
5034 | 0 | } |
5035 | 0 | if (cachedResponseFreshness == ocspFresh) { |
5036 | 0 | CERT_DestroyOCSPCertID(certID); |
5037 | 0 | if (rvOcsp != SECSuccess) { |
5038 | 0 | PORT_SetError(cachedErrorCode); |
5039 | 0 | } |
5040 | 0 | return rvOcsp; |
5041 | 0 | } |
5042 | 0 |
|
5043 | 0 | rv = ocsp_GetOCSPStatusFromNetwork(handle, certID, cert, time, pwArg, |
5044 | 0 | &certIDWasConsumed, |
5045 | 0 | &rvOcsp); |
5046 | 0 | if (rv != SECSuccess) { |
5047 | 0 | PRErrorCode err = PORT_GetError(); |
5048 | 0 | if (ocsp_FetchingFailureIsVerificationFailure()) { |
5049 | 0 | PORT_SetError(err); |
5050 | 0 | rvOcsp = SECFailure; |
5051 | 0 | } else if (cachedResponseFreshness == ocspStale && |
5052 | 0 | (cachedErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT || |
5053 | 0 | cachedErrorCode == SEC_ERROR_REVOKED_CERTIFICATE)) { |
5054 | 0 | /* If we couldn't get a response for a certificate that the OCSP |
5055 | 0 | * responder previously told us was bad, then assume it is still |
5056 | 0 | * bad until we hear otherwise, as it is very unlikely that the |
5057 | 0 | * certificate status has changed from "revoked" to "good" and it |
5058 | 0 | * is also unlikely that the certificate status has changed from |
5059 | 0 | * "unknown" to "good", except for some buggy OCSP responders. |
5060 | 0 | */ |
5061 | 0 | PORT_SetError(cachedErrorCode); |
5062 | 0 | rvOcsp = SECFailure; |
5063 | 0 | } else { |
5064 | 0 | rvOcsp = SECSuccess; |
5065 | 0 | } |
5066 | 0 | } |
5067 | 0 | if (!certIDWasConsumed) { |
5068 | 0 | CERT_DestroyOCSPCertID(certID); |
5069 | 0 | } |
5070 | 0 | return rvOcsp; |
5071 | 0 | } |
5072 | | |
5073 | | /* |
5074 | | * FUNCTION: CERT_CacheOCSPResponseFromSideChannel |
5075 | | * First, this function checks the OCSP cache to see if a good response |
5076 | | * for the given certificate already exists. If it does, then the function |
5077 | | * returns successfully. |
5078 | | * |
5079 | | * If not, then it validates that the given OCSP response is a valid, |
5080 | | * good response for the given certificate and inserts it into the |
5081 | | * cache. |
5082 | | * |
5083 | | * This function is intended for use when OCSP responses are provided via a |
5084 | | * side-channel, i.e. TLS OCSP stapling (a.k.a. the status_request extension). |
5085 | | * |
5086 | | * INPUTS: |
5087 | | * CERTCertDBHandle *handle |
5088 | | * certificate DB of the cert that is being checked |
5089 | | * CERTCertificate *cert |
5090 | | * the certificate being checked |
5091 | | * PRTime time |
5092 | | * time for which status is to be determined |
5093 | | * SECItem *encodedResponse |
5094 | | * the DER encoded bytes of the OCSP response |
5095 | | * void *pwArg |
5096 | | * argument for password prompting, if needed |
5097 | | * RETURN: |
5098 | | * SECSuccess if the cert was found in the cache, or if the OCSP response was |
5099 | | * found to be valid and inserted into the cache. SECFailure otherwise. |
5100 | | */ |
5101 | | SECStatus |
5102 | | CERT_CacheOCSPResponseFromSideChannel(CERTCertDBHandle *handle, |
5103 | | CERTCertificate *cert, |
5104 | | PRTime time, |
5105 | | const SECItem *encodedResponse, |
5106 | | void *pwArg) |
5107 | 0 | { |
5108 | 0 | CERTOCSPCertID *certID = NULL; |
5109 | 0 | PRBool certIDWasConsumed = PR_FALSE; |
5110 | 0 | SECStatus rv = SECFailure; |
5111 | 0 | SECStatus rvOcsp = SECFailure; |
5112 | 0 | SECErrorCodes dummy_error_code; /* we ignore this */ |
5113 | 0 | CERTOCSPResponse *decodedResponse = NULL; |
5114 | 0 | CERTOCSPSingleResponse *singleResponse = NULL; |
5115 | 0 | OCSPFreshness freshness; |
5116 | 0 |
|
5117 | 0 | /* The OCSP cache can be in three states regarding this certificate: |
5118 | 0 | * + Good (cached, timely, 'good' response, or revoked in the future) |
5119 | 0 | * + Revoked (cached, timely, but doesn't fit in the last category) |
5120 | 0 | * + Miss (no knowledge) |
5121 | 0 | * |
5122 | 0 | * Likewise, the side-channel information can be |
5123 | 0 | * + Good (timely, 'good' response, or revoked in the future) |
5124 | 0 | * + Revoked (timely, but doesn't fit in the last category) |
5125 | 0 | * + Invalid (bad syntax, bad signature, not timely etc) |
5126 | 0 | * |
5127 | 0 | * The common case is that the cache result is Good and so is the |
5128 | 0 | * side-channel information. We want to save processing time in this case |
5129 | 0 | * so we say that any time we see a Good result from the cache we return |
5130 | 0 | * early. |
5131 | 0 | * |
5132 | 0 | * Cache result |
5133 | 0 | * | Good Revoked Miss |
5134 | 0 | * ---+-------------------------------------------- |
5135 | 0 | * G | noop Cache more Cache it |
5136 | 0 | * S | recent result |
5137 | 0 | * i | |
5138 | 0 | * d | |
5139 | 0 | * e | |
5140 | 0 | * R | noop Cache more Cache it |
5141 | 0 | * C | recent result |
5142 | 0 | * h | |
5143 | 0 | * a | |
5144 | 0 | * n | |
5145 | 0 | * n I | noop Noop Noop |
5146 | 0 | * e | |
5147 | 0 | * l | |
5148 | 0 | * |
5149 | 0 | * When we fetch from the network we might choose to cache a negative |
5150 | 0 | * result when the response is invalid. This saves us hammering, uselessly, |
5151 | 0 | * at a broken responder. However, side channels are commonly attacker |
5152 | 0 | * controlled and so we must not cache a negative result for an Invalid |
5153 | 0 | * side channel. |
5154 | 0 | */ |
5155 | 0 |
|
5156 | 0 | if (!cert || !encodedResponse) { |
5157 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
5158 | 0 | return SECFailure; |
5159 | 0 | } |
5160 | 0 | certID = CERT_CreateOCSPCertID(cert, time); |
5161 | 0 | if (!certID) |
5162 | 0 | return SECFailure; |
5163 | 0 | |
5164 | 0 | /* We pass PR_TRUE for ignoreGlobalOcspFailureSetting so that a cached |
5165 | 0 | * error entry is not interpreted as being a 'Good' entry here. |
5166 | 0 | */ |
5167 | 0 | rv = ocsp_GetCachedOCSPResponseStatus( |
5168 | 0 | certID, time, PR_TRUE, /* ignoreGlobalOcspFailureSetting */ |
5169 | 0 | &rvOcsp, &dummy_error_code, &freshness); |
5170 | 0 | if (rv == SECSuccess && rvOcsp == SECSuccess && freshness == ocspFresh) { |
5171 | 0 | /* The cached value is good. We don't want to waste time validating |
5172 | 0 | * this OCSP response. This is the first column in the table above. */ |
5173 | 0 | CERT_DestroyOCSPCertID(certID); |
5174 | 0 | return rv; |
5175 | 0 | } |
5176 | 0 | |
5177 | 0 | /* The logic for caching the more recent response is handled in |
5178 | 0 | * ocsp_CacheSingleResponse. */ |
5179 | 0 | |
5180 | 0 | rv = ocsp_GetDecodedVerifiedSingleResponseForID(handle, certID, cert, |
5181 | 0 | time, pwArg, |
5182 | 0 | encodedResponse, |
5183 | 0 | &decodedResponse, |
5184 | 0 | &singleResponse); |
5185 | 0 | if (rv == SECSuccess) { |
5186 | 0 | rvOcsp = ocsp_SingleResponseCertHasGoodStatus(singleResponse, time); |
5187 | 0 | /* Cache any valid singleResponse, regardless of status. */ |
5188 | 0 | ocsp_CacheSingleResponse(certID, singleResponse, &certIDWasConsumed); |
5189 | 0 | } |
5190 | 0 | if (decodedResponse) { |
5191 | 0 | CERT_DestroyOCSPResponse(decodedResponse); |
5192 | 0 | } |
5193 | 0 | if (!certIDWasConsumed) { |
5194 | 0 | CERT_DestroyOCSPCertID(certID); |
5195 | 0 | } |
5196 | 0 | return rv == SECSuccess ? rvOcsp : rv; |
5197 | 0 | } |
5198 | | |
5199 | | /* |
5200 | | * Status in *certIDWasConsumed will always be correct, regardless of |
5201 | | * return value. |
5202 | | */ |
5203 | | static SECStatus |
5204 | | ocsp_GetOCSPStatusFromNetwork(CERTCertDBHandle *handle, |
5205 | | CERTOCSPCertID *certID, |
5206 | | CERTCertificate *cert, |
5207 | | PRTime time, |
5208 | | void *pwArg, |
5209 | | PRBool *certIDWasConsumed, |
5210 | | SECStatus *rv_ocsp) |
5211 | 0 | { |
5212 | 0 | char *location = NULL; |
5213 | 0 | PRBool locationIsDefault; |
5214 | 0 | SECItem *encodedResponse = NULL; |
5215 | 0 | CERTOCSPRequest *request = NULL; |
5216 | 0 | SECStatus rv = SECFailure; |
5217 | 0 |
|
5218 | 0 | CERTOCSPResponse *decodedResponse = NULL; |
5219 | 0 | CERTOCSPSingleResponse *singleResponse = NULL; |
5220 | 0 | enum { stageGET, |
5221 | 0 | stagePOST } currentStage; |
5222 | 0 | PRBool retry = PR_FALSE; |
5223 | 0 |
|
5224 | 0 | if (!certIDWasConsumed || !rv_ocsp) { |
5225 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
5226 | 0 | return SECFailure; |
5227 | 0 | } |
5228 | 0 | *certIDWasConsumed = PR_FALSE; |
5229 | 0 | *rv_ocsp = SECFailure; |
5230 | 0 |
|
5231 | 0 | if (!OCSP_Global.monitor) { |
5232 | 0 | PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
5233 | 0 | return SECFailure; |
5234 | 0 | } |
5235 | 0 | PR_EnterMonitor(OCSP_Global.monitor); |
5236 | 0 | if (OCSP_Global.forcePost) { |
5237 | 0 | currentStage = stagePOST; |
5238 | 0 | } else { |
5239 | 0 | currentStage = stageGET; |
5240 | 0 | } |
5241 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
5242 | 0 |
|
5243 | 0 | /* |
5244 | 0 | * The first thing we need to do is find the location of the responder. |
5245 | 0 | * This will be the value of the default responder (if enabled), else |
5246 | 0 | * it will come out of the AIA extension in the cert (if present). |
5247 | 0 | * If we have no such location, then this cert does not "deserve" to |
5248 | 0 | * be checked -- that is, we consider it a success and just return. |
5249 | 0 | * The way we tell that is by looking at the error number to see if |
5250 | 0 | * the problem was no AIA extension was found; any other error was |
5251 | 0 | * a true failure that we unfortunately have to treat as an overall |
5252 | 0 | * failure here. |
5253 | 0 | */ |
5254 | 0 | location = ocsp_GetResponderLocation(handle, cert, PR_TRUE, |
5255 | 0 | &locationIsDefault); |
5256 | 0 | if (location == NULL) { |
5257 | 0 | int err = PORT_GetError(); |
5258 | 0 | if (err == SEC_ERROR_EXTENSION_NOT_FOUND || |
5259 | 0 | err == SEC_ERROR_CERT_BAD_ACCESS_LOCATION) { |
5260 | 0 | PORT_SetError(0); |
5261 | 0 | *rv_ocsp = SECSuccess; |
5262 | 0 | return SECSuccess; |
5263 | 0 | } |
5264 | 0 | return SECFailure; |
5265 | 0 | } |
5266 | 0 | |
5267 | 0 | /* |
5268 | 0 | * XXX In the fullness of time, we will want/need to handle a |
5269 | 0 | * certificate chain. This will be done either when a new parameter |
5270 | 0 | * tells us to, or some configuration variable tells us to. In any |
5271 | 0 | * case, handling it is complicated because we may need to send as |
5272 | 0 | * many requests (and receive as many responses) as we have certs |
5273 | 0 | * in the chain. If we are going to talk to a default responder, |
5274 | 0 | * and we only support one default responder, we can put all of the |
5275 | 0 | * certs together into one request. Otherwise, we must break them up |
5276 | 0 | * into multiple requests. (Even if all of the requests will go to |
5277 | 0 | * the same location, the signature on each response will be different, |
5278 | 0 | * because each issuer is different. Carefully read the OCSP spec |
5279 | 0 | * if you do not understand this.) |
5280 | 0 | */ |
5281 | 0 | |
5282 | 0 | /* |
5283 | 0 | * XXX If/when signing of requests is supported, that second NULL |
5284 | 0 | * should be changed to be the signer certificate. Not sure if that |
5285 | 0 | * should be passed into this function or retrieved via some operation |
5286 | 0 | * on the handle/context. |
5287 | 0 | */ |
5288 | 0 | |
5289 | 0 | do { |
5290 | 0 | const char *method; |
5291 | 0 | PRBool validResponseWithAccurateInfo = PR_FALSE; |
5292 | 0 | retry = PR_FALSE; |
5293 | 0 | *rv_ocsp = SECFailure; |
5294 | 0 |
|
5295 | 0 | if (currentStage == stageGET) { |
5296 | 0 | method = "GET"; |
5297 | 0 | } else { |
5298 | 0 | PORT_Assert(currentStage == stagePOST); |
5299 | 0 | method = "POST"; |
5300 | 0 | } |
5301 | 0 |
|
5302 | 0 | encodedResponse = |
5303 | 0 | ocsp_GetEncodedOCSPResponseForSingleCert(NULL, certID, cert, |
5304 | 0 | location, method, |
5305 | 0 | time, locationIsDefault, |
5306 | 0 | pwArg, &request); |
5307 | 0 |
|
5308 | 0 | if (encodedResponse) { |
5309 | 0 | rv = ocsp_GetDecodedVerifiedSingleResponseForID(handle, certID, cert, |
5310 | 0 | time, pwArg, |
5311 | 0 | encodedResponse, |
5312 | 0 | &decodedResponse, |
5313 | 0 | &singleResponse); |
5314 | 0 | if (rv == SECSuccess) { |
5315 | 0 | switch (singleResponse->certStatus->certStatusType) { |
5316 | 0 | case ocspCertStatus_good: |
5317 | 0 | case ocspCertStatus_revoked: |
5318 | 0 | validResponseWithAccurateInfo = PR_TRUE; |
5319 | 0 | break; |
5320 | 0 | default: |
5321 | 0 | break; |
5322 | 0 | } |
5323 | 0 | *rv_ocsp = ocsp_SingleResponseCertHasGoodStatus(singleResponse, time); |
5324 | 0 | } |
5325 | 0 | } |
5326 | 0 |
|
5327 | 0 | if (currentStage == stageGET) { |
5328 | 0 | /* only accept GET response if good or revoked */ |
5329 | 0 | if (validResponseWithAccurateInfo) { |
5330 | 0 | ocsp_CacheSingleResponse(certID, singleResponse, |
5331 | 0 | certIDWasConsumed); |
5332 | 0 | } else { |
5333 | 0 | retry = PR_TRUE; |
5334 | 0 | currentStage = stagePOST; |
5335 | 0 | } |
5336 | 0 | } else { |
5337 | 0 | /* cache the POST respone, regardless of status */ |
5338 | 0 | if (!singleResponse) { |
5339 | 0 | cert_RememberOCSPProcessingFailure(certID, certIDWasConsumed); |
5340 | 0 | } else { |
5341 | 0 | ocsp_CacheSingleResponse(certID, singleResponse, |
5342 | 0 | certIDWasConsumed); |
5343 | 0 | } |
5344 | 0 | } |
5345 | 0 |
|
5346 | 0 | if (encodedResponse) { |
5347 | 0 | SECITEM_FreeItem(encodedResponse, PR_TRUE); |
5348 | 0 | encodedResponse = NULL; |
5349 | 0 | } |
5350 | 0 | if (request) { |
5351 | 0 | CERT_DestroyOCSPRequest(request); |
5352 | 0 | request = NULL; |
5353 | 0 | } |
5354 | 0 | if (decodedResponse) { |
5355 | 0 | CERT_DestroyOCSPResponse(decodedResponse); |
5356 | 0 | decodedResponse = NULL; |
5357 | 0 | } |
5358 | 0 | singleResponse = NULL; |
5359 | 0 |
|
5360 | 0 | } while (retry); |
5361 | 0 |
|
5362 | 0 | PORT_Free(location); |
5363 | 0 | return rv; |
5364 | 0 | } |
5365 | | |
5366 | | /* |
5367 | | * FUNCTION: ocsp_GetDecodedVerifiedSingleResponseForID |
5368 | | * This function decodes an OCSP response and checks for a valid response |
5369 | | * concerning the given certificate. |
5370 | | * |
5371 | | * Note: a 'valid' response is one that parses successfully, is not an OCSP |
5372 | | * exception (see RFC 2560 Section 2.3), is correctly signed and is current. |
5373 | | * A 'good' response is a valid response that attests that the certificate |
5374 | | * is not currently revoked (see RFC 2560 Section 2.2). |
5375 | | * |
5376 | | * INPUTS: |
5377 | | * CERTCertDBHandle *handle |
5378 | | * certificate DB of the cert that is being checked |
5379 | | * CERTOCSPCertID *certID |
5380 | | * the cert ID corresponding to |cert| |
5381 | | * CERTCertificate *cert |
5382 | | * the certificate being checked |
5383 | | * PRTime time |
5384 | | * time for which status is to be determined |
5385 | | * void *pwArg |
5386 | | * the opaque argument to the password prompting function. |
5387 | | * SECItem *encodedResponse |
5388 | | * the DER encoded bytes of the OCSP response |
5389 | | * CERTOCSPResponse **pDecodedResponse |
5390 | | * (output) The caller must ALWAYS check for this output parameter, |
5391 | | * and if it's non-null, must destroy it using CERT_DestroyOCSPResponse. |
5392 | | * CERTOCSPSingleResponse **pSingle |
5393 | | * (output) on success, this points to the single response that corresponds |
5394 | | * to the certID parameter. Points to the inside of pDecodedResponse. |
5395 | | * It isn't a copy, don't free it. |
5396 | | * RETURN: |
5397 | | * SECSuccess iff the response is valid. |
5398 | | */ |
5399 | | static SECStatus |
5400 | | ocsp_GetDecodedVerifiedSingleResponseForID(CERTCertDBHandle *handle, |
5401 | | CERTOCSPCertID *certID, |
5402 | | CERTCertificate *cert, |
5403 | | PRTime time, |
5404 | | void *pwArg, |
5405 | | const SECItem *encodedResponse, |
5406 | | CERTOCSPResponse **pDecodedResponse, |
5407 | | CERTOCSPSingleResponse **pSingle) |
5408 | 0 | { |
5409 | 0 | CERTCertificate *signerCert = NULL; |
5410 | 0 | CERTCertificate *issuerCert = NULL; |
5411 | 0 | SECStatus rv = SECFailure; |
5412 | 0 |
|
5413 | 0 | if (!pSingle || !pDecodedResponse) { |
5414 | 0 | return SECFailure; |
5415 | 0 | } |
5416 | 0 | *pSingle = NULL; |
5417 | 0 | *pDecodedResponse = CERT_DecodeOCSPResponse(encodedResponse); |
5418 | 0 | if (!*pDecodedResponse) { |
5419 | 0 | return SECFailure; |
5420 | 0 | } |
5421 | 0 | |
5422 | 0 | /* |
5423 | 0 | * Okay, we at least have a response that *looks* like a response! |
5424 | 0 | * Now see if the overall response status value is good or not. |
5425 | 0 | * If not, we set an error and give up. (It means that either the |
5426 | 0 | * server had a problem, or it didn't like something about our |
5427 | 0 | * request. Either way there is nothing to do but give up.) |
5428 | 0 | * Otherwise, we continue to find the actual per-cert status |
5429 | 0 | * in the response. |
5430 | 0 | */ |
5431 | 0 | if (CERT_GetOCSPResponseStatus(*pDecodedResponse) != SECSuccess) { |
5432 | 0 | goto loser; |
5433 | 0 | } |
5434 | 0 | |
5435 | 0 | /* |
5436 | 0 | * If we've made it this far, we expect a response with a good signature. |
5437 | 0 | * So, check for that. |
5438 | 0 | */ |
5439 | 0 | issuerCert = CERT_FindCertIssuer(cert, time, certUsageAnyCA); |
5440 | 0 | rv = CERT_VerifyOCSPResponseSignature(*pDecodedResponse, handle, pwArg, |
5441 | 0 | &signerCert, issuerCert); |
5442 | 0 | if (rv != SECSuccess) { |
5443 | 0 | goto loser; |
5444 | 0 | } |
5445 | 0 | |
5446 | 0 | PORT_Assert(signerCert != NULL); /* internal consistency check */ |
5447 | 0 | /* XXX probably should set error, return failure if signerCert is null */ |
5448 | 0 |
|
5449 | 0 | /* |
5450 | 0 | * Again, we are only doing one request for one cert. |
5451 | 0 | * XXX When we handle cert chains, the following code will obviously |
5452 | 0 | * have to be modified, in coordation with the code above that will |
5453 | 0 | * have to determine how to make multiple requests, etc. |
5454 | 0 | */ |
5455 | 0 | rv = ocsp_GetVerifiedSingleResponseForCertID(handle, *pDecodedResponse, certID, |
5456 | 0 | signerCert, time, pSingle); |
5457 | 0 | loser: |
5458 | 0 | if (issuerCert != NULL) |
5459 | 0 | CERT_DestroyCertificate(issuerCert); |
5460 | 0 | if (signerCert != NULL) |
5461 | 0 | CERT_DestroyCertificate(signerCert); |
5462 | 0 | return rv; |
5463 | 0 | } |
5464 | | |
5465 | | /* |
5466 | | * FUNCTION: ocsp_CacheSingleResponse |
5467 | | * This function requires that the caller has checked that the response |
5468 | | * is valid and verified. |
5469 | | * The (positive or negative) valid response will be used to update the cache. |
5470 | | * INPUTS: |
5471 | | * CERTOCSPCertID *certID |
5472 | | * the cert ID corresponding to |cert| |
5473 | | * PRBool *certIDWasConsumed |
5474 | | * (output) on return, this is true iff |certID| was consumed by this |
5475 | | * function. |
5476 | | */ |
5477 | | void |
5478 | | ocsp_CacheSingleResponse(CERTOCSPCertID *certID, |
5479 | | CERTOCSPSingleResponse *single, |
5480 | | PRBool *certIDWasConsumed) |
5481 | 0 | { |
5482 | 0 | if (single != NULL) { |
5483 | 0 | PR_EnterMonitor(OCSP_Global.monitor); |
5484 | 0 | if (OCSP_Global.maxCacheEntries >= 0) { |
5485 | 0 | ocsp_CreateOrUpdateCacheEntry(&OCSP_Global.cache, certID, single, |
5486 | 0 | certIDWasConsumed); |
5487 | 0 | /* ignore cache update failures */ |
5488 | 0 | } |
5489 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
5490 | 0 | } |
5491 | 0 | } |
5492 | | |
5493 | | SECStatus |
5494 | | ocsp_GetVerifiedSingleResponseForCertID(CERTCertDBHandle *handle, |
5495 | | CERTOCSPResponse *response, |
5496 | | CERTOCSPCertID *certID, |
5497 | | CERTCertificate *signerCert, |
5498 | | PRTime time, |
5499 | | CERTOCSPSingleResponse |
5500 | | **pSingleResponse) |
5501 | 0 | { |
5502 | 0 | SECStatus rv; |
5503 | 0 | ocspResponseData *responseData; |
5504 | 0 | PRTime producedAt; |
5505 | 0 | CERTOCSPSingleResponse *single; |
5506 | 0 |
|
5507 | 0 | /* |
5508 | 0 | * The ResponseData part is the real guts of the response. |
5509 | 0 | */ |
5510 | 0 | responseData = ocsp_GetResponseData(response, NULL); |
5511 | 0 | if (responseData == NULL) { |
5512 | 0 | rv = SECFailure; |
5513 | 0 | goto loser; |
5514 | 0 | } |
5515 | 0 | |
5516 | 0 | /* |
5517 | 0 | * There is one producedAt time for the entire response (and a separate |
5518 | 0 | * thisUpdate time for each individual single response). We need to |
5519 | 0 | * compare them, so get the overall time to pass into the check of each |
5520 | 0 | * single response. |
5521 | 0 | */ |
5522 | 0 | rv = DER_GeneralizedTimeToTime(&producedAt, &responseData->producedAt); |
5523 | 0 | if (rv != SECSuccess) |
5524 | 0 | goto loser; |
5525 | 0 | |
5526 | 0 | single = ocsp_GetSingleResponseForCertID(responseData->responses, |
5527 | 0 | handle, certID); |
5528 | 0 | if (single == NULL) { |
5529 | 0 | rv = SECFailure; |
5530 | 0 | goto loser; |
5531 | 0 | } |
5532 | 0 | |
5533 | 0 | rv = ocsp_VerifySingleResponse(single, handle, signerCert, producedAt); |
5534 | 0 | if (rv != SECSuccess) |
5535 | 0 | goto loser; |
5536 | 0 | *pSingleResponse = single; |
5537 | 0 |
|
5538 | 0 | loser: |
5539 | 0 | return rv; |
5540 | 0 | } |
5541 | | |
5542 | | SECStatus |
5543 | | CERT_GetOCSPStatusForCertID(CERTCertDBHandle *handle, |
5544 | | CERTOCSPResponse *response, |
5545 | | CERTOCSPCertID *certID, |
5546 | | CERTCertificate *signerCert, |
5547 | | PRTime time) |
5548 | 0 | { |
5549 | 0 | /* |
5550 | 0 | * We do not update the cache, because: |
5551 | 0 | * |
5552 | 0 | * CERT_GetOCSPStatusForCertID is an old exported API that was introduced |
5553 | 0 | * before the OCSP cache got implemented. |
5554 | 0 | * |
5555 | 0 | * The implementation of helper function cert_ProcessOCSPResponse |
5556 | 0 | * requires the ability to transfer ownership of the the given certID to |
5557 | 0 | * the cache. The external API doesn't allow us to prevent the caller from |
5558 | 0 | * destroying the certID. We don't have the original certificate available, |
5559 | 0 | * therefore we are unable to produce another certID object (that could |
5560 | 0 | * be stored in the cache). |
5561 | 0 | * |
5562 | 0 | * Should we ever implement code to produce a deep copy of certID, |
5563 | 0 | * then this could be changed to allow updating the cache. |
5564 | 0 | * The duplication would have to be done in |
5565 | 0 | * cert_ProcessOCSPResponse, if the out parameter to indicate |
5566 | 0 | * a transfer of ownership is NULL. |
5567 | 0 | */ |
5568 | 0 | return cert_ProcessOCSPResponse(handle, response, certID, |
5569 | 0 | signerCert, time, |
5570 | 0 | NULL, NULL); |
5571 | 0 | } |
5572 | | |
5573 | | /* |
5574 | | * The first 5 parameters match the definition of CERT_GetOCSPStatusForCertID. |
5575 | | */ |
5576 | | SECStatus |
5577 | | cert_ProcessOCSPResponse(CERTCertDBHandle *handle, |
5578 | | CERTOCSPResponse *response, |
5579 | | CERTOCSPCertID *certID, |
5580 | | CERTCertificate *signerCert, |
5581 | | PRTime time, |
5582 | | PRBool *certIDWasConsumed, |
5583 | | SECStatus *cacheUpdateStatus) |
5584 | 0 | { |
5585 | 0 | SECStatus rv; |
5586 | 0 | SECStatus rv_cache = SECSuccess; |
5587 | 0 | CERTOCSPSingleResponse *single = NULL; |
5588 | 0 |
|
5589 | 0 | rv = ocsp_GetVerifiedSingleResponseForCertID(handle, response, certID, |
5590 | 0 | signerCert, time, &single); |
5591 | 0 | if (rv == SECSuccess) { |
5592 | 0 | /* |
5593 | 0 | * Check whether the status says revoked, and if so |
5594 | 0 | * how that compares to the time value passed into this routine. |
5595 | 0 | */ |
5596 | 0 | rv = ocsp_SingleResponseCertHasGoodStatus(single, time); |
5597 | 0 | } |
5598 | 0 |
|
5599 | 0 | if (certIDWasConsumed) { |
5600 | 0 | /* |
5601 | 0 | * We don't have copy-of-certid implemented. In order to update |
5602 | 0 | * the cache, the caller must supply an out variable |
5603 | 0 | * certIDWasConsumed, allowing us to return ownership status. |
5604 | 0 | */ |
5605 | 0 |
|
5606 | 0 | PR_EnterMonitor(OCSP_Global.monitor); |
5607 | 0 | if (OCSP_Global.maxCacheEntries >= 0) { |
5608 | 0 | /* single == NULL means: remember response failure */ |
5609 | 0 | rv_cache = |
5610 | 0 | ocsp_CreateOrUpdateCacheEntry(&OCSP_Global.cache, certID, |
5611 | 0 | single, certIDWasConsumed); |
5612 | 0 | } |
5613 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
5614 | 0 | if (cacheUpdateStatus) { |
5615 | 0 | *cacheUpdateStatus = rv_cache; |
5616 | 0 | } |
5617 | 0 | } |
5618 | 0 |
|
5619 | 0 | return rv; |
5620 | 0 | } |
5621 | | |
5622 | | SECStatus |
5623 | | cert_RememberOCSPProcessingFailure(CERTOCSPCertID *certID, |
5624 | | PRBool *certIDWasConsumed) |
5625 | 0 | { |
5626 | 0 | SECStatus rv = SECSuccess; |
5627 | 0 | PR_EnterMonitor(OCSP_Global.monitor); |
5628 | 0 | if (OCSP_Global.maxCacheEntries >= 0) { |
5629 | 0 | rv = ocsp_CreateOrUpdateCacheEntry(&OCSP_Global.cache, certID, NULL, |
5630 | 0 | certIDWasConsumed); |
5631 | 0 | } |
5632 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
5633 | 0 | return rv; |
5634 | 0 | } |
5635 | | |
5636 | | /* |
5637 | | * Disable status checking and destroy related structures/data. |
5638 | | */ |
5639 | | static SECStatus |
5640 | | ocsp_DestroyStatusChecking(CERTStatusConfig *statusConfig) |
5641 | 0 | { |
5642 | 0 | ocspCheckingContext *statusContext; |
5643 | 0 |
|
5644 | 0 | /* |
5645 | 0 | * Disable OCSP checking |
5646 | 0 | */ |
5647 | 0 | statusConfig->statusChecker = NULL; |
5648 | 0 |
|
5649 | 0 | statusContext = statusConfig->statusContext; |
5650 | 0 | PORT_Assert(statusContext != NULL); |
5651 | 0 | if (statusContext == NULL) |
5652 | 0 | return SECFailure; |
5653 | 0 | |
5654 | 0 | if (statusContext->defaultResponderURI != NULL) |
5655 | 0 | PORT_Free(statusContext->defaultResponderURI); |
5656 | 0 | if (statusContext->defaultResponderNickname != NULL) |
5657 | 0 | PORT_Free(statusContext->defaultResponderNickname); |
5658 | 0 |
|
5659 | 0 | PORT_Free(statusContext); |
5660 | 0 | statusConfig->statusContext = NULL; |
5661 | 0 |
|
5662 | 0 | PORT_Free(statusConfig); |
5663 | 0 |
|
5664 | 0 | return SECSuccess; |
5665 | 0 | } |
5666 | | |
5667 | | /* |
5668 | | * FUNCTION: CERT_DisableOCSPChecking |
5669 | | * Turns off OCSP checking for the given certificate database. |
5670 | | * This routine disables OCSP checking. Though it will return |
5671 | | * SECFailure if OCSP checking is not enabled, it is "safe" to |
5672 | | * call it that way and just ignore the return value, if it is |
5673 | | * easier to just call it than to "remember" whether it is enabled. |
5674 | | * INPUTS: |
5675 | | * CERTCertDBHandle *handle |
5676 | | * Certificate database for which OCSP checking will be disabled. |
5677 | | * RETURN: |
5678 | | * Returns SECFailure if an error occurred (usually means that OCSP |
5679 | | * checking was not enabled or status contexts were not initialized -- |
5680 | | * error set will be SEC_ERROR_OCSP_NOT_ENABLED); SECSuccess otherwise. |
5681 | | */ |
5682 | | SECStatus |
5683 | | CERT_DisableOCSPChecking(CERTCertDBHandle *handle) |
5684 | 0 | { |
5685 | 0 | CERTStatusConfig *statusConfig; |
5686 | 0 | ocspCheckingContext *statusContext; |
5687 | 0 |
|
5688 | 0 | if (handle == NULL) { |
5689 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
5690 | 0 | return SECFailure; |
5691 | 0 | } |
5692 | 0 |
|
5693 | 0 | statusConfig = CERT_GetStatusConfig(handle); |
5694 | 0 | statusContext = ocsp_GetCheckingContext(handle); |
5695 | 0 | if (statusContext == NULL) |
5696 | 0 | return SECFailure; |
5697 | 0 | |
5698 | 0 | if (statusConfig->statusChecker != CERT_CheckOCSPStatus) { |
5699 | 0 | /* |
5700 | 0 | * Status configuration is present, but either not currently |
5701 | 0 | * enabled or not for OCSP. |
5702 | 0 | */ |
5703 | 0 | PORT_SetError(SEC_ERROR_OCSP_NOT_ENABLED); |
5704 | 0 | return SECFailure; |
5705 | 0 | } |
5706 | 0 |
|
5707 | 0 | /* cache no longer necessary */ |
5708 | 0 | CERT_ClearOCSPCache(); |
5709 | 0 |
|
5710 | 0 | /* |
5711 | 0 | * This is how we disable status checking. Everything else remains |
5712 | 0 | * in place in case we are enabled again. |
5713 | 0 | */ |
5714 | 0 | statusConfig->statusChecker = NULL; |
5715 | 0 |
|
5716 | 0 | return SECSuccess; |
5717 | 0 | } |
5718 | | |
5719 | | /* |
5720 | | * Allocate and initialize the informational structures for status checking. |
5721 | | * This is done when some configuration of OCSP is being done or when OCSP |
5722 | | * checking is being turned on, whichever comes first. |
5723 | | */ |
5724 | | static SECStatus |
5725 | | ocsp_InitStatusChecking(CERTCertDBHandle *handle) |
5726 | 0 | { |
5727 | 0 | CERTStatusConfig *statusConfig = NULL; |
5728 | 0 | ocspCheckingContext *statusContext = NULL; |
5729 | 0 |
|
5730 | 0 | PORT_Assert(CERT_GetStatusConfig(handle) == NULL); |
5731 | 0 | if (CERT_GetStatusConfig(handle) != NULL) { |
5732 | 0 | /* XXX or call statusConfig->statusDestroy and continue? */ |
5733 | 0 | return SECFailure; |
5734 | 0 | } |
5735 | 0 | |
5736 | 0 | statusConfig = PORT_ZNew(CERTStatusConfig); |
5737 | 0 | if (statusConfig == NULL) |
5738 | 0 | goto loser; |
5739 | 0 | |
5740 | 0 | statusContext = PORT_ZNew(ocspCheckingContext); |
5741 | 0 | if (statusContext == NULL) |
5742 | 0 | goto loser; |
5743 | 0 | |
5744 | 0 | statusConfig->statusDestroy = ocsp_DestroyStatusChecking; |
5745 | 0 | statusConfig->statusContext = statusContext; |
5746 | 0 |
|
5747 | 0 | CERT_SetStatusConfig(handle, statusConfig); |
5748 | 0 |
|
5749 | 0 | return SECSuccess; |
5750 | 0 | |
5751 | 0 | loser: |
5752 | 0 | if (statusConfig != NULL) |
5753 | 0 | PORT_Free(statusConfig); |
5754 | 0 | return SECFailure; |
5755 | 0 | } |
5756 | | |
5757 | | /* |
5758 | | * FUNCTION: CERT_EnableOCSPChecking |
5759 | | * Turns on OCSP checking for the given certificate database. |
5760 | | * INPUTS: |
5761 | | * CERTCertDBHandle *handle |
5762 | | * Certificate database for which OCSP checking will be enabled. |
5763 | | * RETURN: |
5764 | | * Returns SECFailure if an error occurred (likely only problem |
5765 | | * allocating memory); SECSuccess otherwise. |
5766 | | */ |
5767 | | SECStatus |
5768 | | CERT_EnableOCSPChecking(CERTCertDBHandle *handle) |
5769 | 0 | { |
5770 | 0 | CERTStatusConfig *statusConfig; |
5771 | 0 |
|
5772 | 0 | SECStatus rv; |
5773 | 0 |
|
5774 | 0 | if (handle == NULL) { |
5775 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
5776 | 0 | return SECFailure; |
5777 | 0 | } |
5778 | 0 |
|
5779 | 0 | statusConfig = CERT_GetStatusConfig(handle); |
5780 | 0 | if (statusConfig == NULL) { |
5781 | 0 | rv = ocsp_InitStatusChecking(handle); |
5782 | 0 | if (rv != SECSuccess) |
5783 | 0 | return rv; |
5784 | 0 | |
5785 | 0 | /* Get newly established value */ |
5786 | 0 | statusConfig = CERT_GetStatusConfig(handle); |
5787 | 0 | PORT_Assert(statusConfig != NULL); |
5788 | 0 | } |
5789 | 0 |
|
5790 | 0 | /* |
5791 | 0 | * Setting the checker function is what really enables the checking |
5792 | 0 | * when each cert verification is done. |
5793 | 0 | */ |
5794 | 0 | statusConfig->statusChecker = CERT_CheckOCSPStatus; |
5795 | 0 |
|
5796 | 0 | return SECSuccess; |
5797 | 0 | } |
5798 | | |
5799 | | /* |
5800 | | * FUNCTION: CERT_SetOCSPDefaultResponder |
5801 | | * Specify the location and cert of the default responder. |
5802 | | * If OCSP checking is already enabled *and* use of a default responder |
5803 | | * is also already enabled, all OCSP checking from now on will go directly |
5804 | | * to the specified responder. If OCSP checking is not enabled, or if |
5805 | | * it is but use of a default responder is not enabled, the information |
5806 | | * will be recorded and take effect whenever both are enabled. |
5807 | | * INPUTS: |
5808 | | * CERTCertDBHandle *handle |
5809 | | * Cert database on which OCSP checking should use the default responder. |
5810 | | * char *url |
5811 | | * The location of the default responder (e.g. "http://foo.com:80/ocsp") |
5812 | | * Note that the location will not be tested until the first attempt |
5813 | | * to send a request there. |
5814 | | * char *name |
5815 | | * The nickname of the cert to trust (expected) to sign the OCSP responses. |
5816 | | * If the corresponding cert cannot be found, SECFailure is returned. |
5817 | | * RETURN: |
5818 | | * Returns SECFailure if an error occurred; SECSuccess otherwise. |
5819 | | * The most likely error is that the cert for "name" could not be found |
5820 | | * (probably SEC_ERROR_UNKNOWN_CERT). Other errors are low-level (no memory, |
5821 | | * bad database, etc.). |
5822 | | */ |
5823 | | SECStatus |
5824 | | CERT_SetOCSPDefaultResponder(CERTCertDBHandle *handle, |
5825 | | const char *url, const char *name) |
5826 | 0 | { |
5827 | 0 | CERTCertificate *cert; |
5828 | 0 | ocspCheckingContext *statusContext; |
5829 | 0 | char *url_copy = NULL; |
5830 | 0 | char *name_copy = NULL; |
5831 | 0 | SECStatus rv; |
5832 | 0 |
|
5833 | 0 | if (handle == NULL || url == NULL || name == NULL) { |
5834 | 0 | /* |
5835 | 0 | * XXX When interface is exported, probably want better errors; |
5836 | 0 | * perhaps different one for each parameter. |
5837 | 0 | */ |
5838 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
5839 | 0 | return SECFailure; |
5840 | 0 | } |
5841 | 0 |
|
5842 | 0 | /* |
5843 | 0 | * Find the certificate for the specified nickname. Do this first |
5844 | 0 | * because it seems the most likely to fail. |
5845 | 0 | * |
5846 | 0 | * XXX Shouldn't need that cast if the FindCertByNickname interface |
5847 | 0 | * used const to convey that it does not modify the name. Maybe someday. |
5848 | 0 | */ |
5849 | 0 | cert = CERT_FindCertByNickname(handle, (char *)name); |
5850 | 0 | if (cert == NULL) { |
5851 | 0 | /* |
5852 | 0 | * look for the cert on an external token. |
5853 | 0 | */ |
5854 | 0 | cert = PK11_FindCertFromNickname((char *)name, NULL); |
5855 | 0 | } |
5856 | 0 | if (cert == NULL) |
5857 | 0 | return SECFailure; |
5858 | 0 | |
5859 | 0 | /* |
5860 | 0 | * Make a copy of the url and nickname. |
5861 | 0 | */ |
5862 | 0 | url_copy = PORT_Strdup(url); |
5863 | 0 | name_copy = PORT_Strdup(name); |
5864 | 0 | if (url_copy == NULL || name_copy == NULL) { |
5865 | 0 | rv = SECFailure; |
5866 | 0 | goto loser; |
5867 | 0 | } |
5868 | 0 | |
5869 | 0 | statusContext = ocsp_GetCheckingContext(handle); |
5870 | 0 |
|
5871 | 0 | /* |
5872 | 0 | * Allocate and init the context if it doesn't already exist. |
5873 | 0 | */ |
5874 | 0 | if (statusContext == NULL) { |
5875 | 0 | rv = ocsp_InitStatusChecking(handle); |
5876 | 0 | if (rv != SECSuccess) |
5877 | 0 | goto loser; |
5878 | 0 | |
5879 | 0 | statusContext = ocsp_GetCheckingContext(handle); |
5880 | 0 | PORT_Assert(statusContext != NULL); /* extreme paranoia */ |
5881 | 0 | } |
5882 | 0 |
|
5883 | 0 | /* |
5884 | 0 | * Note -- we do not touch the status context until after all of |
5885 | 0 | * the steps which could cause errors. If something goes wrong, |
5886 | 0 | * we want to leave things as they were. |
5887 | 0 | */ |
5888 | 0 |
|
5889 | 0 | /* |
5890 | 0 | * Get rid of old url and name if there. |
5891 | 0 | */ |
5892 | 0 | if (statusContext->defaultResponderNickname != NULL) |
5893 | 0 | PORT_Free(statusContext->defaultResponderNickname); |
5894 | 0 | if (statusContext->defaultResponderURI != NULL) |
5895 | 0 | PORT_Free(statusContext->defaultResponderURI); |
5896 | 0 |
|
5897 | 0 | /* |
5898 | 0 | * And replace them with the new ones. |
5899 | 0 | */ |
5900 | 0 | statusContext->defaultResponderURI = url_copy; |
5901 | 0 | statusContext->defaultResponderNickname = name_copy; |
5902 | 0 |
|
5903 | 0 | /* |
5904 | 0 | * If there was already a cert in place, get rid of it and replace it. |
5905 | 0 | * Otherwise, we are not currently enabled, so we don't want to save it; |
5906 | 0 | * it will get re-found and set whenever use of a default responder is |
5907 | 0 | * enabled. |
5908 | 0 | */ |
5909 | 0 | if (statusContext->defaultResponderCert != NULL) { |
5910 | 0 | CERT_DestroyCertificate(statusContext->defaultResponderCert); |
5911 | 0 | statusContext->defaultResponderCert = cert; |
5912 | 0 | /*OCSP enabled, switching responder: clear cache*/ |
5913 | 0 | CERT_ClearOCSPCache(); |
5914 | 0 | } else { |
5915 | 0 | PORT_Assert(statusContext->useDefaultResponder == PR_FALSE); |
5916 | 0 | CERT_DestroyCertificate(cert); |
5917 | 0 | /*OCSP currently not enabled, no need to clear cache*/ |
5918 | 0 | } |
5919 | 0 |
|
5920 | 0 | return SECSuccess; |
5921 | 0 |
|
5922 | 0 | loser: |
5923 | 0 | CERT_DestroyCertificate(cert); |
5924 | 0 | if (url_copy != NULL) |
5925 | 0 | PORT_Free(url_copy); |
5926 | 0 | if (name_copy != NULL) |
5927 | 0 | PORT_Free(name_copy); |
5928 | 0 | return rv; |
5929 | 0 | } |
5930 | | |
5931 | | /* |
5932 | | * FUNCTION: CERT_EnableOCSPDefaultResponder |
5933 | | * Turns on use of a default responder when OCSP checking. |
5934 | | * If OCSP checking is already enabled, this will make subsequent checks |
5935 | | * go directly to the default responder. (The location of the responder |
5936 | | * and the nickname of the responder cert must already be specified.) |
5937 | | * If OCSP checking is not enabled, this will be recorded and take effect |
5938 | | * whenever it is enabled. |
5939 | | * INPUTS: |
5940 | | * CERTCertDBHandle *handle |
5941 | | * Cert database on which OCSP checking should use the default responder. |
5942 | | * RETURN: |
5943 | | * Returns SECFailure if an error occurred; SECSuccess otherwise. |
5944 | | * No errors are especially likely unless the caller did not previously |
5945 | | * perform a successful call to SetOCSPDefaultResponder (in which case |
5946 | | * the error set will be SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER). |
5947 | | */ |
5948 | | SECStatus |
5949 | | CERT_EnableOCSPDefaultResponder(CERTCertDBHandle *handle) |
5950 | 0 | { |
5951 | 0 | ocspCheckingContext *statusContext; |
5952 | 0 | CERTCertificate *cert; |
5953 | 0 | SECStatus rv; |
5954 | 0 | SECCertificateUsage usage; |
5955 | 0 |
|
5956 | 0 | if (handle == NULL) { |
5957 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
5958 | 0 | return SECFailure; |
5959 | 0 | } |
5960 | 0 |
|
5961 | 0 | statusContext = ocsp_GetCheckingContext(handle); |
5962 | 0 |
|
5963 | 0 | if (statusContext == NULL) { |
5964 | 0 | /* |
5965 | 0 | * Strictly speaking, the error already set is "correct", |
5966 | 0 | * but cover over it with one more helpful in this context. |
5967 | 0 | */ |
5968 | 0 | PORT_SetError(SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER); |
5969 | 0 | return SECFailure; |
5970 | 0 | } |
5971 | 0 |
|
5972 | 0 | if (statusContext->defaultResponderURI == NULL) { |
5973 | 0 | PORT_SetError(SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER); |
5974 | 0 | return SECFailure; |
5975 | 0 | } |
5976 | 0 |
|
5977 | 0 | if (statusContext->defaultResponderNickname == NULL) { |
5978 | 0 | PORT_SetError(SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER); |
5979 | 0 | return SECFailure; |
5980 | 0 | } |
5981 | 0 |
|
5982 | 0 | /* |
5983 | 0 | * Find the cert for the nickname. |
5984 | 0 | */ |
5985 | 0 | cert = CERT_FindCertByNickname(handle, |
5986 | 0 | statusContext->defaultResponderNickname); |
5987 | 0 | if (cert == NULL) { |
5988 | 0 | cert = PK11_FindCertFromNickname(statusContext->defaultResponderNickname, |
5989 | 0 | NULL); |
5990 | 0 | } |
5991 | 0 | /* |
5992 | 0 | * We should never have trouble finding the cert, because its |
5993 | 0 | * existence should have been proven by SetOCSPDefaultResponder. |
5994 | 0 | */ |
5995 | 0 | PORT_Assert(cert != NULL); |
5996 | 0 | if (cert == NULL) |
5997 | 0 | return SECFailure; |
5998 | 0 | |
5999 | 0 | /* |
6000 | 0 | * Supplied cert should at least have a signing capability in order for us |
6001 | 0 | * to use it as a trusted responder cert. Ability to sign is guaranteed if |
6002 | 0 | * cert is validated to have any set of the usages below. |
6003 | 0 | */ |
6004 | 0 | rv = CERT_VerifyCertificateNow(handle, cert, PR_TRUE, |
6005 | 0 | certificateUsageCheckAllUsages, |
6006 | 0 | NULL, &usage); |
6007 | 0 | if (rv != SECSuccess || (usage & (certificateUsageSSLClient | certificateUsageSSLServer | certificateUsageSSLServerWithStepUp | certificateUsageEmailSigner | certificateUsageObjectSigner | certificateUsageStatusResponder | certificateUsageSSLCA)) == 0) { |
6008 | 0 | PORT_SetError(SEC_ERROR_OCSP_RESPONDER_CERT_INVALID); |
6009 | 0 | return SECFailure; |
6010 | 0 | } |
6011 | 0 |
|
6012 | 0 | /* |
6013 | 0 | * And hang onto it. |
6014 | 0 | */ |
6015 | 0 | statusContext->defaultResponderCert = cert; |
6016 | 0 |
|
6017 | 0 | /* we don't allow a mix of cache entries from different responders */ |
6018 | 0 | CERT_ClearOCSPCache(); |
6019 | 0 |
|
6020 | 0 | /* |
6021 | 0 | * Finally, record the fact that we now have a default responder enabled. |
6022 | 0 | */ |
6023 | 0 | statusContext->useDefaultResponder = PR_TRUE; |
6024 | 0 | return SECSuccess; |
6025 | 0 | } |
6026 | | |
6027 | | /* |
6028 | | * FUNCTION: CERT_DisableOCSPDefaultResponder |
6029 | | * Turns off use of a default responder when OCSP checking. |
6030 | | * (Does nothing if use of a default responder is not enabled.) |
6031 | | * INPUTS: |
6032 | | * CERTCertDBHandle *handle |
6033 | | * Cert database on which OCSP checking should stop using a default |
6034 | | * responder. |
6035 | | * RETURN: |
6036 | | * Returns SECFailure if an error occurred; SECSuccess otherwise. |
6037 | | * Errors very unlikely (like random memory corruption...). |
6038 | | */ |
6039 | | SECStatus |
6040 | | CERT_DisableOCSPDefaultResponder(CERTCertDBHandle *handle) |
6041 | 0 | { |
6042 | 0 | CERTStatusConfig *statusConfig; |
6043 | 0 | ocspCheckingContext *statusContext; |
6044 | 0 | CERTCertificate *tmpCert; |
6045 | 0 |
|
6046 | 0 | if (handle == NULL) { |
6047 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
6048 | 0 | return SECFailure; |
6049 | 0 | } |
6050 | 0 |
|
6051 | 0 | statusConfig = CERT_GetStatusConfig(handle); |
6052 | 0 | if (statusConfig == NULL) |
6053 | 0 | return SECSuccess; |
6054 | 0 | |
6055 | 0 | statusContext = ocsp_GetCheckingContext(handle); |
6056 | 0 | PORT_Assert(statusContext != NULL); |
6057 | 0 | if (statusContext == NULL) |
6058 | 0 | return SECFailure; |
6059 | 0 | |
6060 | 0 | tmpCert = statusContext->defaultResponderCert; |
6061 | 0 | if (tmpCert) { |
6062 | 0 | statusContext->defaultResponderCert = NULL; |
6063 | 0 | CERT_DestroyCertificate(tmpCert); |
6064 | 0 | /* we don't allow a mix of cache entries from different responders */ |
6065 | 0 | CERT_ClearOCSPCache(); |
6066 | 0 | } |
6067 | 0 |
|
6068 | 0 | /* |
6069 | 0 | * Finally, record the fact. |
6070 | 0 | */ |
6071 | 0 | statusContext->useDefaultResponder = PR_FALSE; |
6072 | 0 | return SECSuccess; |
6073 | 0 | } |
6074 | | |
6075 | | SECStatus |
6076 | | CERT_ForcePostMethodForOCSP(PRBool forcePost) |
6077 | 0 | { |
6078 | 0 | if (!OCSP_Global.monitor) { |
6079 | 0 | PORT_SetError(SEC_ERROR_NOT_INITIALIZED); |
6080 | 0 | return SECFailure; |
6081 | 0 | } |
6082 | 0 |
|
6083 | 0 | PR_EnterMonitor(OCSP_Global.monitor); |
6084 | 0 | OCSP_Global.forcePost = forcePost; |
6085 | 0 | PR_ExitMonitor(OCSP_Global.monitor); |
6086 | 0 |
|
6087 | 0 | return SECSuccess; |
6088 | 0 | } |
6089 | | |
6090 | | SECStatus |
6091 | | CERT_GetOCSPResponseStatus(CERTOCSPResponse *response) |
6092 | 0 | { |
6093 | 0 | PORT_Assert(response); |
6094 | 0 | if (response->statusValue == ocspResponse_successful) |
6095 | 0 | return SECSuccess; |
6096 | 0 | |
6097 | 0 | switch (response->statusValue) { |
6098 | 0 | case ocspResponse_malformedRequest: |
6099 | 0 | PORT_SetError(SEC_ERROR_OCSP_MALFORMED_REQUEST); |
6100 | 0 | break; |
6101 | 0 | case ocspResponse_internalError: |
6102 | 0 | PORT_SetError(SEC_ERROR_OCSP_SERVER_ERROR); |
6103 | 0 | break; |
6104 | 0 | case ocspResponse_tryLater: |
6105 | 0 | PORT_SetError(SEC_ERROR_OCSP_TRY_SERVER_LATER); |
6106 | 0 | break; |
6107 | 0 | case ocspResponse_sigRequired: |
6108 | 0 | /* XXX We *should* retry with a signature, if possible. */ |
6109 | 0 | PORT_SetError(SEC_ERROR_OCSP_REQUEST_NEEDS_SIG); |
6110 | 0 | break; |
6111 | 0 | case ocspResponse_unauthorized: |
6112 | 0 | PORT_SetError(SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST); |
6113 | 0 | break; |
6114 | 0 | case ocspResponse_unused: |
6115 | 0 | default: |
6116 | 0 | PORT_SetError(SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS); |
6117 | 0 | break; |
6118 | 0 | } |
6119 | 0 | return SECFailure; |
6120 | 0 | } |