1 /* 2 * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package sun.security.ssl; 26 27 import java.io.IOException; 28 import java.net.URI; 29 import java.net.URISyntaxException; 30 import java.security.AccessController; 31 import java.security.cert.Extension; 32 import java.security.cert.X509Certificate; 33 import java.util.ArrayList; 34 import java.util.Collections; 35 import java.util.Date; 36 import java.util.HashMap; 37 import java.util.List; 38 import java.util.Map; 39 import java.util.Objects; 40 import java.util.concurrent.Callable; 41 import java.util.concurrent.ExecutionException; 42 import java.util.concurrent.Executors; 43 import java.util.concurrent.Future; 44 import java.util.concurrent.ScheduledThreadPoolExecutor; 45 import java.util.concurrent.ThreadFactory; 46 import java.util.concurrent.ThreadPoolExecutor; 47 import java.util.concurrent.TimeUnit; 48 import sun.security.action.GetBooleanAction; 49 import sun.security.action.GetIntegerAction; 50 import sun.security.action.GetPropertyAction; 51 import sun.security.provider.certpath.CertId; 52 import sun.security.provider.certpath.OCSP; 53 import sun.security.provider.certpath.OCSPResponse; 54 import sun.security.provider.certpath.ResponderId; 55 import sun.security.util.Cache; 56 import sun.security.x509.PKIXExtensions; 57 import sun.security.x509.SerialNumber; 58 import sun.security.ssl.X509Authentication.X509Possession; 59 import static sun.security.ssl.CertStatusExtension.*; 60 61 final class StatusResponseManager { 62 private static final int DEFAULT_CORE_THREADS = 8; 63 private static final int DEFAULT_CACHE_SIZE = 256; 64 private static final int DEFAULT_CACHE_LIFETIME = 3600; // seconds 65 66 private final ScheduledThreadPoolExecutor threadMgr; 67 private final Cache<CertId, ResponseCacheEntry> responseCache; 68 private final URI defaultResponder; 69 private final boolean respOverride; 70 private final int cacheCapacity; 71 private final int cacheLifetime; 72 private final boolean ignoreExtensions; 73 74 /** 75 * Create a StatusResponseManager with default parameters. 76 */ StatusResponseManager()77 StatusResponseManager() { 78 int cap = AccessController.doPrivileged( 79 new GetIntegerAction("jdk.tls.stapling.cacheSize", 80 DEFAULT_CACHE_SIZE)); 81 cacheCapacity = cap > 0 ? cap : 0; 82 83 int life = AccessController.doPrivileged( 84 new GetIntegerAction("jdk.tls.stapling.cacheLifetime", 85 DEFAULT_CACHE_LIFETIME)); 86 cacheLifetime = life > 0 ? life : 0; 87 88 String uriStr = GetPropertyAction 89 .privilegedGetProperty("jdk.tls.stapling.responderURI"); 90 URI tmpURI; 91 try { 92 tmpURI = ((uriStr != null && !uriStr.isEmpty()) ? 93 new URI(uriStr) : null); 94 } catch (URISyntaxException urise) { 95 tmpURI = null; 96 } 97 defaultResponder = tmpURI; 98 99 respOverride = GetBooleanAction 100 .privilegedGetProperty("jdk.tls.stapling.responderOverride"); 101 ignoreExtensions = GetBooleanAction 102 .privilegedGetProperty("jdk.tls.stapling.ignoreExtensions"); 103 104 threadMgr = new ScheduledThreadPoolExecutor(DEFAULT_CORE_THREADS, 105 new ThreadFactory() { 106 @Override 107 public Thread newThread(Runnable r) { 108 Thread t = Executors.defaultThreadFactory().newThread(r); 109 t.setDaemon(true); 110 return t; 111 } 112 }, new ThreadPoolExecutor.DiscardPolicy()); 113 threadMgr.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); 114 threadMgr.setContinueExistingPeriodicTasksAfterShutdownPolicy( 115 false); 116 threadMgr.setKeepAliveTime(5000, TimeUnit.MILLISECONDS); 117 threadMgr.allowCoreThreadTimeOut(true); 118 responseCache = Cache.newSoftMemoryCache( 119 cacheCapacity, cacheLifetime); 120 } 121 122 /** 123 * Get the current cache lifetime setting 124 * 125 * @return the current cache lifetime value 126 */ getCacheLifetime()127 int getCacheLifetime() { 128 return cacheLifetime; 129 } 130 131 /** 132 * Get the current maximum cache size. 133 * 134 * @return the current maximum cache size 135 */ getCacheCapacity()136 int getCacheCapacity() { 137 return cacheCapacity; 138 } 139 140 /** 141 * Get the default OCSP responder URI, if previously set. 142 * 143 * @return the current default OCSP responder URI, or {@code null} if 144 * it has not been set. 145 */ getDefaultResponder()146 URI getDefaultResponder() { 147 return defaultResponder; 148 } 149 150 /** 151 * Get the URI override setting 152 * 153 * @return {@code true} if URI override has been set, {@code false} 154 * otherwise. 155 */ getURIOverride()156 boolean getURIOverride() { 157 return respOverride; 158 } 159 160 /** 161 * Get the ignore extensions setting. 162 * 163 * @return {@code true} if the {@code StatusResponseManager} will not 164 * pass OCSP Extensions in the TLS {@code status_request[_v2]} 165 * extensions, {@code false} if extensions will be passed (the default). 166 */ getIgnoreExtensions()167 boolean getIgnoreExtensions() { 168 return ignoreExtensions; 169 } 170 171 /** 172 * Clear the status response cache 173 */ clear()174 void clear() { 175 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 176 SSLLogger.fine("Clearing response cache"); 177 } 178 responseCache.clear(); 179 } 180 181 /** 182 * Returns the number of currently valid objects in the response cache. 183 * 184 * @return the number of valid objects in the response cache. 185 */ size()186 int size() { 187 return responseCache.size(); 188 } 189 190 /** 191 * Obtain the URI use by the {@code StatusResponseManager} during 192 * lookups. 193 * 194 * This method takes into account not only the AIA extension from a 195 * certificate to be checked, but also any default URI and possible 196 * override settings for the response manager. 197 * 198 * @param cert the subject to get the responder URI from 199 * 200 * @return a {@code URI} containing the address to the OCSP responder, 201 * or {@code null} if no AIA extension exists in the certificate 202 * and no default responder has been configured. 203 * 204 * @throws NullPointerException if {@code cert} is {@code null}. 205 */ getURI(X509Certificate cert)206 URI getURI(X509Certificate cert) { 207 Objects.requireNonNull(cert); 208 209 if (cert.getExtensionValue( 210 PKIXExtensions.OCSPNoCheck_Id.toString()) != null) { 211 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 212 SSLLogger.fine( 213 "OCSP NoCheck extension found. OCSP will be skipped"); 214 } 215 return null; 216 } else if (defaultResponder != null && respOverride) { 217 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 218 SSLLogger.fine( 219 "Responder override: URI is " + defaultResponder); 220 } 221 return defaultResponder; 222 } else { 223 URI certURI = OCSP.getResponderURI(cert); 224 return (certURI != null ? certURI : defaultResponder); 225 } 226 } 227 228 /** 229 * Shutdown the thread pool 230 */ shutdown()231 void shutdown() { 232 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 233 SSLLogger.fine("Shutting down " + threadMgr.getActiveCount() + 234 " active threads"); 235 } 236 threadMgr.shutdown(); 237 } 238 239 /** 240 * Get a list of responses for a chain of certificates. 241 * 242 * This will find OCSP responses from the cache, or failing that, 243 * directly contact the OCSP responder. It is assumed that the 244 * certificates in the provided chain are in their proper order 245 * (from end-entity to trust anchor). 246 * 247 * @param type the type of request being made of the 248 * {@code StatusResponseManager} 249 * @param request the {@code CertStatusRequest} from the 250 * status_request or status_request_v2 ClientHello extension. 251 * A value of {@code null} is interpreted as providing no 252 * responder IDs or extensions. 253 * @param chain an array of 2 or more certificates. Each certificate 254 * must be issued by the next certificate in the chain. 255 * @param delay the number of time units to delay before returning 256 * responses. 257 * @param unit the unit of time applied to the {@code delay} parameter 258 * 259 * @return an unmodifiable {@code Map} containing the certificate and 260 * its usually 261 * 262 * @throws SSLHandshakeException if an unsupported 263 * {@code CertStatusRequest} is provided. 264 */ get(CertStatusRequestType type, CertStatusRequest request, X509Certificate[] chain, long delay, TimeUnit unit)265 Map<X509Certificate, byte[]> get(CertStatusRequestType type, 266 CertStatusRequest request, X509Certificate[] chain, long delay, 267 TimeUnit unit) { 268 Map<X509Certificate, byte[]> responseMap = new HashMap<>(); 269 List<OCSPFetchCall> requestList = new ArrayList<>(); 270 271 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 272 SSLLogger.fine( 273 "Beginning check: Type = " + type + ", Chain length = " + 274 chain.length); 275 } 276 277 // It is assumed that the caller has ordered the certs in the chain 278 // in the proper order (each certificate is issued by the next entry 279 // in the provided chain). 280 if (chain.length < 2) { 281 return Collections.emptyMap(); 282 } 283 284 if (type == CertStatusRequestType.OCSP) { 285 try { 286 // For type OCSP, we only check the end-entity certificate 287 OCSPStatusRequest ocspReq = (OCSPStatusRequest)request; 288 CertId cid = new CertId(chain[1], 289 new SerialNumber(chain[0].getSerialNumber())); 290 ResponseCacheEntry cacheEntry = getFromCache(cid, ocspReq); 291 if (cacheEntry != null) { 292 responseMap.put(chain[0], cacheEntry.ocspBytes); 293 } else { 294 StatusInfo sInfo = new StatusInfo(chain[0], cid); 295 requestList.add(new OCSPFetchCall(sInfo, ocspReq)); 296 } 297 } catch (IOException exc) { 298 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 299 SSLLogger.fine( 300 "Exception during CertId creation: ", exc); 301 } 302 } 303 } else if (type == CertStatusRequestType.OCSP_MULTI) { 304 // For type OCSP_MULTI, we check every cert in the chain that 305 // has a direct issuer at the next index. We won't have an 306 // issuer certificate for the last certificate in the chain 307 // and will not be able to create a CertId because of that. 308 OCSPStatusRequest ocspReq = (OCSPStatusRequest)request; 309 int ctr; 310 for (ctr = 0; ctr < chain.length - 1; ctr++) { 311 try { 312 // The cert at "ctr" is the subject cert, "ctr + 1" 313 // is the issuer certificate. 314 CertId cid = new CertId(chain[ctr + 1], 315 new SerialNumber(chain[ctr].getSerialNumber())); 316 ResponseCacheEntry cacheEntry = 317 getFromCache(cid, ocspReq); 318 if (cacheEntry != null) { 319 responseMap.put(chain[ctr], cacheEntry.ocspBytes); 320 } else { 321 StatusInfo sInfo = new StatusInfo(chain[ctr], cid); 322 requestList.add(new OCSPFetchCall(sInfo, ocspReq)); 323 } 324 } catch (IOException exc) { 325 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 326 SSLLogger.fine( 327 "Exception during CertId creation: ", exc); 328 } 329 } 330 } 331 } else { 332 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 333 SSLLogger.fine("Unsupported status request type: " + type); 334 } 335 } 336 337 // If we were able to create one or more Fetches, go and run all 338 // of them in separate threads. For all the threads that completed 339 // in the allotted time, put those status responses into the 340 // returned Map. 341 if (!requestList.isEmpty()) { 342 try { 343 // Set a bunch of threads to go do the fetching 344 List<Future<StatusInfo>> resultList = 345 threadMgr.invokeAll(requestList, delay, unit); 346 347 // Go through the Futures and from any non-cancelled task, 348 // get the bytes and attach them to the responseMap. 349 for (Future<StatusInfo> task : resultList) { 350 if (!task.isDone()) { 351 continue; 352 } 353 354 if (!task.isCancelled()) { 355 StatusInfo info = task.get(); 356 if (info != null && info.responseData != null) { 357 responseMap.put(info.cert, 358 info.responseData.ocspBytes); 359 } else if (SSLLogger.isOn && 360 SSLLogger.isOn("respmgr")) { 361 SSLLogger.fine( 362 "Completed task had no response data"); 363 } 364 } else { 365 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 366 SSLLogger.fine("Found cancelled task"); 367 } 368 } 369 } 370 } catch (InterruptedException | ExecutionException exc) { 371 // Not sure what else to do here 372 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 373 SSLLogger.fine("Exception when getting data: ", exc); 374 } 375 } 376 } 377 378 return Collections.unmodifiableMap(responseMap); 379 } 380 381 /** 382 * Check the cache for a given {@code CertId}. 383 * 384 * @param cid the CertId of the response to look up 385 * @param ocspRequest the OCSP request structure sent by the client 386 * in the TLS status_request[_v2] hello extension. 387 * 388 * @return the {@code ResponseCacheEntry} for a specific CertId, or 389 * {@code null} if it is not found or a nonce extension has been 390 * requested by the caller. 391 */ getFromCache(CertId cid, OCSPStatusRequest ocspRequest)392 private ResponseCacheEntry getFromCache(CertId cid, 393 OCSPStatusRequest ocspRequest) { 394 // Determine if the nonce extension is present in the request. If 395 // so, then do not attempt to retrieve the response from the cache. 396 for (Extension ext : ocspRequest.extensions) { 397 if (ext.getId().equals( 398 PKIXExtensions.OCSPNonce_Id.toString())) { 399 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 400 SSLLogger.fine( 401 "Nonce extension found, skipping cache check"); 402 } 403 return null; 404 } 405 } 406 407 ResponseCacheEntry respEntry = responseCache.get(cid); 408 409 // If the response entry has a nextUpdate and it has expired 410 // before the cache expiration, purge it from the cache 411 // and do not return it as a cache hit. 412 if (respEntry != null && respEntry.nextUpdate != null && 413 respEntry.nextUpdate.before(new Date())) { 414 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 415 SSLLogger.fine( 416 "nextUpdate threshold exceeded, purging from cache"); 417 } 418 respEntry = null; 419 } 420 421 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 422 SSLLogger.fine( 423 "Check cache for SN" + cid.getSerialNumber() + ": " + 424 (respEntry != null ? "HIT" : "MISS")); 425 } 426 return respEntry; 427 } 428 429 @Override toString()430 public String toString() { 431 StringBuilder sb = new StringBuilder("StatusResponseManager: "); 432 433 sb.append("Core threads: ").append(threadMgr.getCorePoolSize()); 434 sb.append(", Cache timeout: "); 435 if (cacheLifetime > 0) { 436 sb.append(cacheLifetime).append(" seconds"); 437 } else { 438 sb.append(" indefinite"); 439 } 440 441 sb.append(", Cache MaxSize: "); 442 if (cacheCapacity > 0) { 443 sb.append(cacheCapacity).append(" items"); 444 } else { 445 sb.append(" unbounded"); 446 } 447 448 sb.append(", Default URI: "); 449 if (defaultResponder != null) { 450 sb.append(defaultResponder); 451 } else { 452 sb.append("NONE"); 453 } 454 455 return sb.toString(); 456 } 457 458 /** 459 * Inner class used to group request and response data. 460 */ 461 class StatusInfo { 462 final X509Certificate cert; 463 final CertId cid; 464 final URI responder; 465 ResponseCacheEntry responseData; 466 467 /** 468 * Create a StatusInfo object from certificate data. 469 * 470 * @param subjectCert the certificate to be checked for revocation 471 * @param issuerCert the issuer of the {@code subjectCert} 472 * 473 * @throws IOException if CertId creation from the certificate fails 474 */ StatusInfo(X509Certificate subjectCert, X509Certificate issuerCert)475 StatusInfo(X509Certificate subjectCert, X509Certificate issuerCert) 476 throws IOException { 477 this(subjectCert, new CertId(issuerCert, 478 new SerialNumber(subjectCert.getSerialNumber()))); 479 } 480 481 /** 482 * Create a StatusInfo object from an existing subject certificate 483 * and its corresponding CertId. 484 * 485 * @param subjectCert the certificate to be checked for revocation 486 * @param cid the CertId for {@code subjectCert} 487 */ StatusInfo(X509Certificate subjectCert, CertId certId)488 StatusInfo(X509Certificate subjectCert, CertId certId) { 489 cert = subjectCert; 490 cid = certId; 491 responder = getURI(cert); 492 responseData = null; 493 } 494 495 /** 496 * Copy constructor (used primarily for rescheduling). 497 * This will do a member-wise copy with the exception of the 498 * responseData and extensions fields, which should not persist 499 * in a rescheduled fetch. 500 * 501 * @param orig the original {@code StatusInfo} 502 */ StatusInfo(StatusInfo orig)503 StatusInfo(StatusInfo orig) { 504 this.cert = orig.cert; 505 this.cid = orig.cid; 506 this.responder = orig.responder; 507 this.responseData = null; 508 } 509 510 /** 511 * Return a String representation of the {@code StatusInfo} 512 * 513 * @return a {@code String} representation of this object 514 */ 515 @Override toString()516 public String toString() { 517 StringBuilder sb = new StringBuilder("StatusInfo:"); 518 sb.append("\n\tCert: ").append( 519 this.cert.getSubjectX500Principal()); 520 sb.append("\n\tSerial: ").append(this.cert.getSerialNumber()); 521 sb.append("\n\tResponder: ").append(this.responder); 522 sb.append("\n\tResponse data: ").append( 523 this.responseData != null ? 524 (this.responseData.ocspBytes.length + " bytes") : 525 "<NULL>"); 526 return sb.toString(); 527 } 528 } 529 530 /** 531 * Static nested class used as the data kept in the response cache. 532 */ 533 class ResponseCacheEntry { 534 final OCSPResponse.ResponseStatus status; 535 final byte[] ocspBytes; 536 final Date nextUpdate; 537 final OCSPResponse.SingleResponse singleResp; 538 final ResponderId respId; 539 540 /** 541 * Create a new cache entry from the raw bytes of the response 542 * 543 * @param responseBytes the DER encoding for the OCSP response 544 * 545 * @throws IOException if an {@code OCSPResponse} cannot be 546 * created from the encoded bytes. 547 */ ResponseCacheEntry(byte[] responseBytes, CertId cid)548 ResponseCacheEntry(byte[] responseBytes, CertId cid) 549 throws IOException { 550 Objects.requireNonNull(responseBytes, 551 "Non-null responseBytes required"); 552 Objects.requireNonNull(cid, "Non-null Cert ID required"); 553 554 ocspBytes = responseBytes.clone(); 555 OCSPResponse oResp = new OCSPResponse(ocspBytes); 556 status = oResp.getResponseStatus(); 557 respId = oResp.getResponderId(); 558 singleResp = oResp.getSingleResponse(cid); 559 if (status == OCSPResponse.ResponseStatus.SUCCESSFUL) { 560 if (singleResp != null) { 561 // Pull out the nextUpdate field in advance because the 562 // Date is cloned. 563 nextUpdate = singleResp.getNextUpdate(); 564 } else { 565 throw new IOException( 566 "Unable to find SingleResponse for SN " + 567 cid.getSerialNumber()); 568 } 569 } else { 570 nextUpdate = null; 571 } 572 } 573 } 574 575 /** 576 * Inner Callable class that does the actual work of looking up OCSP 577 * responses, first looking at the cache and doing OCSP requests if 578 * a cache miss occurs. 579 */ 580 class OCSPFetchCall implements Callable<StatusInfo> { 581 StatusInfo statInfo; 582 OCSPStatusRequest ocspRequest; 583 List<Extension> extensions; 584 List<ResponderId> responderIds; 585 586 /** 587 * A constructor that builds the OCSPFetchCall from the provided 588 * StatusInfo and information from the status_request[_v2] 589 * extension. 590 * 591 * @param info the {@code StatusInfo} containing the subject 592 * certificate, CertId, and other supplemental info. 593 * @param request the {@code OCSPStatusRequest} containing any 594 * responder IDs and extensions. 595 */ OCSPFetchCall(StatusInfo info, OCSPStatusRequest request)596 public OCSPFetchCall(StatusInfo info, OCSPStatusRequest request) { 597 statInfo = Objects.requireNonNull(info, 598 "Null StatusInfo not allowed"); 599 ocspRequest = Objects.requireNonNull(request, 600 "Null OCSPStatusRequest not allowed"); 601 extensions = ocspRequest.extensions; 602 responderIds = ocspRequest.responderIds; 603 } 604 605 /** 606 * Get an OCSP response, either from the cache or from a responder. 607 * 608 * @return The StatusInfo object passed into the 609 * {@code OCSPFetchCall} constructor, with the 610 * {@code responseData} field filled in with the response 611 * or {@code null} if no response can be obtained. 612 */ 613 @Override call()614 public StatusInfo call() { 615 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 616 SSLLogger.fine( 617 "Starting fetch for SN " + 618 statInfo.cid.getSerialNumber()); 619 } 620 try { 621 ResponseCacheEntry cacheEntry; 622 List<Extension> extsToSend; 623 624 if (statInfo.responder == null) { 625 // If we have no URI then there's nothing to do 626 // but return. 627 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 628 SSLLogger.fine( 629 "Null URI detected, OCSP fetch aborted"); 630 } 631 return statInfo; 632 } else { 633 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 634 SSLLogger.fine( 635 "Attempting fetch from " + statInfo.responder); 636 } 637 } 638 639 // If the StatusResponseManager has been configured to not 640 // forward extensions, then set extensions to an empty 641 // list. 642 // 643 // We will forward the extensions unless one of two 644 // conditions occur: 645 // (1) The jdk.tls.stapling.ignoreExtensions property is 646 // true, or 647 // (2) There is a non-empty ResponderId list. 648 // 649 // ResponderId selection is a feature that will be 650 // supported in the future. 651 extsToSend = (ignoreExtensions || !responderIds.isEmpty()) ? 652 Collections.emptyList() : extensions; 653 654 byte[] respBytes = OCSP.getOCSPBytes( 655 Collections.singletonList(statInfo.cid), 656 statInfo.responder, extsToSend); 657 658 if (respBytes != null) { 659 // Place the data into the response cache 660 cacheEntry = new ResponseCacheEntry(respBytes, 661 statInfo.cid); 662 663 // Get the response status and act on it appropriately 664 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 665 SSLLogger.fine("OCSP Status: " + cacheEntry.status + 666 " (" + respBytes.length + " bytes)"); 667 } 668 if (cacheEntry.status == 669 OCSPResponse.ResponseStatus.SUCCESSFUL) { 670 // Set the response in the returned StatusInfo 671 statInfo.responseData = cacheEntry; 672 673 // Add the response to the cache (if applicable) 674 addToCache(statInfo.cid, cacheEntry); 675 } 676 } else { 677 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 678 SSLLogger.fine( 679 "No data returned from OCSP Responder"); 680 } 681 } 682 } catch (IOException ioe) { 683 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 684 SSLLogger.fine("Caught exception: ", ioe); 685 } 686 } 687 688 return statInfo; 689 } 690 691 /** 692 * Add a response to the cache. 693 * 694 * @param certId The {@code CertId} for the OCSP response 695 * @param entry A cache entry containing the response bytes and 696 * the {@code OCSPResponse} built from those bytes. 697 */ addToCache(CertId certId, ResponseCacheEntry entry)698 private void addToCache(CertId certId, ResponseCacheEntry entry) { 699 // If no cache lifetime has been set on entries then 700 // don't cache this response if there is no nextUpdate field 701 if (entry.nextUpdate == null && cacheLifetime == 0) { 702 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 703 SSLLogger.fine("Not caching this OCSP response"); 704 } 705 } else { 706 responseCache.put(certId, entry); 707 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 708 SSLLogger.fine( 709 "Added response for SN " + 710 certId.getSerialNumber() + 711 " to cache"); 712 } 713 } 714 } 715 716 /** 717 * Determine the delay to use when scheduling the task that will 718 * update the OCSP response. This is the shorter time between the 719 * cache lifetime and the nextUpdate. If no nextUpdate is present 720 * in the response, then only the cache lifetime is used. 721 * If cache timeouts are disabled (a zero value) and there's no 722 * nextUpdate, then the entry is not cached and no rescheduling 723 * will take place. 724 * 725 * @param nextUpdate a {@code Date} object corresponding to the 726 * next update time from a SingleResponse. 727 * 728 * @return the number of seconds of delay before the next fetch 729 * should be executed. A zero value means that the fetch 730 * should happen immediately, while a value less than zero 731 * indicates no rescheduling should be done. 732 */ getNextTaskDelay(Date nextUpdate)733 private long getNextTaskDelay(Date nextUpdate) { 734 long delaySec; 735 int lifetime = getCacheLifetime(); 736 737 if (nextUpdate != null) { 738 long nuDiffSec = (nextUpdate.getTime() - 739 System.currentTimeMillis()) / 1000; 740 delaySec = lifetime > 0 ? Long.min(nuDiffSec, lifetime) : 741 nuDiffSec; 742 } else { 743 delaySec = lifetime > 0 ? lifetime : -1; 744 } 745 746 return delaySec; 747 } 748 } 749 processStapling( ServerHandshakeContext shc)750 static final StaplingParameters processStapling( 751 ServerHandshakeContext shc) { 752 StaplingParameters params = null; 753 SSLExtension ext = null; 754 CertStatusRequestType type = null; 755 CertStatusRequest req = null; 756 Map<X509Certificate, byte[]> responses; 757 758 // If this feature has not been enabled, then no more processing 759 // is necessary. Also we will only staple if we're doing a full 760 // handshake. 761 if (!shc.sslContext.isStaplingEnabled(false) || shc.isResumption) { 762 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 763 SSLLogger.fine("Staping disabled or is a resumed session"); 764 } 765 return null; 766 } 767 768 // Check if the client has asserted the status_request[_v2] extension(s) 769 Map<SSLExtension, SSLExtension.SSLExtensionSpec> exts = 770 shc.handshakeExtensions; 771 CertStatusRequestSpec statReq = (CertStatusRequestSpec)exts.get( 772 SSLExtension.CH_STATUS_REQUEST); 773 CertStatusRequestV2Spec statReqV2 = (CertStatusRequestV2Spec) 774 exts.get(SSLExtension.CH_STATUS_REQUEST_V2); 775 776 // Determine which type of stapling we are doing and assert the 777 // proper extension in the server hello. 778 // Favor status_request_v2 over status_request and ocsp_multi 779 // over ocsp. 780 // If multiple ocsp or ocsp_multi types exist, select the first 781 // instance of a given type. Also since we don't support ResponderId 782 // selection yet, only accept a request if the ResponderId field 783 // is empty. Finally, we'll only do this in (D)TLS 1.2 or earlier. 784 if (statReqV2 != null && !shc.negotiatedProtocol.useTLS13PlusSpec()) { 785 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) { 786 SSLLogger.fine("SH Processing status_request_v2 extension"); 787 } 788 // RFC 6961 stapling 789 ext = SSLExtension.CH_STATUS_REQUEST_V2; 790 int ocspIdx = -1; 791 int ocspMultiIdx = -1; 792 CertStatusRequest[] reqItems = statReqV2.certStatusRequests; 793 for (int pos = 0; (pos < reqItems.length && 794 (ocspIdx == -1 || ocspMultiIdx == -1)); pos++) { 795 CertStatusRequest item = reqItems[pos]; 796 CertStatusRequestType curType = 797 CertStatusRequestType.valueOf(item.statusType); 798 if (ocspIdx < 0 && curType == CertStatusRequestType.OCSP) { 799 OCSPStatusRequest ocspReq = (OCSPStatusRequest)item; 800 // We currently only accept empty responder ID lists 801 // but may support them in the future 802 if (ocspReq.responderIds.isEmpty()) { 803 ocspIdx = pos; 804 } 805 } else if (ocspMultiIdx < 0 && 806 curType == CertStatusRequestType.OCSP_MULTI) { 807 OCSPStatusRequest ocspReq = (OCSPStatusRequest)item; 808 // We currently only accept empty responder ID lists 809 // but may support them in the future 810 if (ocspReq.responderIds.isEmpty()) { 811 ocspMultiIdx = pos; 812 } 813 } 814 } 815 if (ocspMultiIdx >= 0) { 816 req = reqItems[ocspMultiIdx]; 817 type = CertStatusRequestType.valueOf(req.statusType); 818 } else if (ocspIdx >= 0) { 819 req = reqItems[ocspIdx]; 820 type = CertStatusRequestType.valueOf(req.statusType); 821 } else { 822 if (SSLLogger.isOn && 823 SSLLogger.isOn("ssl,handshake")) { 824 SSLLogger.finest("Warning: No suitable request " + 825 "found in the status_request_v2 extension."); 826 } 827 } 828 } 829 830 // Only attempt to process a status_request extension if: 831 // * The status_request extension is set AND 832 // * either the status_request_v2 extension is not present OR 833 // * none of the underlying OCSPStatusRequest structures is 834 // suitable for stapling. 835 // If either of the latter two bullet items is true the ext, 836 // type and req variables should all be null. If any are null 837 // we will try processing an asserted status_request. 838 if ((statReq != null) && 839 (ext == null || type == null || req == null)) { 840 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) { 841 SSLLogger.fine("SH Processing status_request extension"); 842 } 843 ext = SSLExtension.CH_STATUS_REQUEST; 844 type = CertStatusRequestType.valueOf( 845 statReq.statusRequest.statusType); 846 if (type == CertStatusRequestType.OCSP) { 847 // If the type is OCSP, then the request is guaranteed 848 // to be OCSPStatusRequest 849 OCSPStatusRequest ocspReq = 850 (OCSPStatusRequest)statReq.statusRequest; 851 if (ocspReq.responderIds.isEmpty()) { 852 req = ocspReq; 853 } else { 854 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 855 SSLLogger.finest("Warning: No suitable request " + 856 "found in the status_request extension."); 857 } 858 } 859 } 860 } 861 862 // If, after walking through the extensions we were unable to 863 // find a suitable StatusRequest, then stapling is disabled. 864 // The ext, type and req variables must have been set to continue. 865 if (type == null || req == null || ext == null) { 866 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 867 SSLLogger.fine("No suitable status_request or " + 868 "status_request_v2, stapling is disabled"); 869 } 870 return null; 871 } 872 873 // Get the cert chain since we'll need it for OCSP checking 874 X509Possession x509Possession = null; 875 for (SSLPossession possession : shc.handshakePossessions) { 876 if (possession instanceof X509Possession) { 877 x509Possession = (X509Possession)possession; 878 break; 879 } 880 } 881 882 if (x509Possession == null) { // unlikely 883 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 884 SSLLogger.finest("Warning: no X.509 certificates found. " + 885 "Stapling is disabled."); 886 } 887 return null; 888 } 889 890 // Get the OCSP responses from the StatusResponseManager 891 X509Certificate[] certs = x509Possession.popCerts; 892 StatusResponseManager statRespMgr = 893 shc.sslContext.getStatusResponseManager(); 894 if (statRespMgr != null) { 895 // For the purposes of the fetch from the SRM, override the 896 // type when it is TLS 1.3 so it always gets responses for 897 // all certs it can. This should not change the type field 898 // in the StaplingParameters though. 899 CertStatusRequestType fetchType = 900 shc.negotiatedProtocol.useTLS13PlusSpec() ? 901 CertStatusRequestType.OCSP_MULTI : type; 902 responses = statRespMgr.get(fetchType, req, certs, 903 shc.statusRespTimeout, TimeUnit.MILLISECONDS); 904 if (!responses.isEmpty()) { 905 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 906 SSLLogger.finest("Response manager returned " + 907 responses.size() + " entries."); 908 } 909 // If this RFC 6066-style stapling (SSL cert only) then the 910 // response cannot be zero length 911 if (type == CertStatusRequestType.OCSP) { 912 byte[] respDER = responses.get(certs[0]); 913 if (respDER == null || respDER.length <= 0) { 914 if (SSLLogger.isOn && 915 SSLLogger.isOn("ssl,handshake")) { 916 SSLLogger.finest("Warning: Null or zero-length " + 917 "response found for leaf certificate. " + 918 "Stapling is disabled."); 919 } 920 return null; 921 } 922 } 923 params = new StaplingParameters(ext, type, req, responses); 924 } else { 925 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 926 SSLLogger.finest("Warning: no OCSP responses obtained. " + 927 "Stapling is disabled."); 928 } 929 } 930 } else { 931 // This should not happen, but if lazy initialization of the 932 // StatusResponseManager doesn't occur we should turn off stapling. 933 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 934 SSLLogger.finest("Warning: lazy initialization " + 935 "of the StatusResponseManager failed. " + 936 "Stapling is disabled."); 937 } 938 params = null; 939 } 940 941 return params; 942 } 943 944 /** 945 * Inner class used to hold stapling parameters needed by the handshaker 946 * when stapling is active. 947 */ 948 static final class StaplingParameters { 949 final SSLExtension statusRespExt; 950 final CertStatusRequestType statReqType; 951 final CertStatusRequest statReqData; 952 final Map<X509Certificate, byte[]> responseMap; 953 StaplingParameters(SSLExtension ext, CertStatusRequestType type, CertStatusRequest req, Map<X509Certificate, byte[]> responses)954 StaplingParameters(SSLExtension ext, CertStatusRequestType type, 955 CertStatusRequest req, Map<X509Certificate, byte[]> responses) { 956 statusRespExt = ext; 957 statReqType = type; 958 statReqData = req; 959 responseMap = responses; 960 } 961 } 962 } 963 964