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