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