security/nss/lib/certhigh/ocsp.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial