< prev index next >

src/java.base/share/classes/sun/security/ssl/StatusResponseManager.java

Print this page


   1 /*
   2  * Copyright (c) 2015, 2016, 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 
  26 package sun.security.ssl;
  27 
  28 import java.io.IOException;
  29 import java.net.URI;
  30 import java.net.URISyntaxException;
  31 import java.security.AccessController;
  32 import java.security.cert.X509Certificate;
  33 import java.security.cert.Extension;
  34 import java.util.*;
  35 import java.util.concurrent.*;
  36 
















  37 import sun.security.provider.certpath.CertId;
  38 import sun.security.provider.certpath.OCSP;
  39 import sun.security.provider.certpath.OCSPResponse;
  40 import sun.security.provider.certpath.ResponderId;
  41 import sun.security.util.Cache;
  42 import sun.security.x509.PKIXExtensions;
  43 import sun.security.x509.SerialNumber;
  44 import sun.security.action.GetBooleanAction;
  45 import sun.security.action.GetIntegerAction;
  46 import sun.security.action.GetPropertyAction;
  47 
  48 final class StatusResponseManager {
  49     private static final int DEFAULT_CORE_THREADS = 8;
  50     private static final int DEFAULT_CACHE_SIZE = 256;
  51     private static final int DEFAULT_CACHE_LIFETIME = 3600;         // seconds
  52     private static final Debug debug = Debug.getInstance("ssl");
  53 
  54     private final ScheduledThreadPoolExecutor threadMgr;
  55     private final Cache<CertId, ResponseCacheEntry> responseCache;
  56     private final URI defaultResponder;
  57     private final boolean respOverride;
  58     private final int cacheCapacity;
  59     private final int cacheLifetime;
  60     private final boolean ignoreExtensions;
  61 
  62     /**
  63      * Create a StatusResponseManager with default parameters.
  64      */
  65     StatusResponseManager() {
  66         int cap = AccessController.doPrivileged(
  67                 new GetIntegerAction("jdk.tls.stapling.cacheSize",
  68                     DEFAULT_CACHE_SIZE));
  69         cacheCapacity = cap > 0 ? cap : 0;
  70 
  71         int life = AccessController.doPrivileged(
  72                 new GetIntegerAction("jdk.tls.stapling.cacheLifetime",


  82         } catch (URISyntaxException urise) {
  83             tmpURI = null;
  84         }
  85         defaultResponder = tmpURI;
  86 
  87         respOverride = AccessController.doPrivileged(
  88                 new GetBooleanAction("jdk.tls.stapling.responderOverride"));
  89         ignoreExtensions = AccessController.doPrivileged(
  90                 new GetBooleanAction("jdk.tls.stapling.ignoreExtensions"));
  91 
  92         threadMgr = new ScheduledThreadPoolExecutor(DEFAULT_CORE_THREADS,
  93                 new ThreadFactory() {
  94             @Override
  95             public Thread newThread(Runnable r) {
  96                 Thread t = Executors.defaultThreadFactory().newThread(r);
  97                 t.setDaemon(true);
  98                 return t;
  99             }
 100         }, new ThreadPoolExecutor.DiscardPolicy());
 101         threadMgr.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
 102         threadMgr.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);

 103         threadMgr.setKeepAliveTime(5000, TimeUnit.MILLISECONDS);
 104         threadMgr.allowCoreThreadTimeOut(true);
 105         responseCache = Cache.newSoftMemoryCache(cacheCapacity, cacheLifetime);

 106     }
 107 
 108     /**
 109      * Get the current cache lifetime setting
 110      *
 111      * @return the current cache lifetime value
 112      */
 113     int getCacheLifetime() {
 114         return cacheLifetime;
 115     }
 116 
 117     /**
 118      * Get the current maximum cache size.
 119      *
 120      * @return the current maximum cache size
 121      */
 122     int getCacheCapacity() {
 123         return cacheCapacity;
 124     }
 125 


 130      *      it has not been set.
 131      */
 132     URI getDefaultResponder() {
 133         return defaultResponder;
 134     }
 135 
 136     /**
 137      * Get the URI override setting
 138      *
 139      * @return {@code true} if URI override has been set, {@code false}
 140      * otherwise.
 141      */
 142     boolean getURIOverride() {
 143         return respOverride;
 144     }
 145 
 146     /**
 147      * Get the ignore extensions setting.
 148      *
 149      * @return {@code true} if the {@code StatusResponseManager} will not
 150      * pass OCSP Extensions in the TLS {@code status_request[_v2]} extensions,
 151      * {@code false} if extensions will be passed (the default).
 152      */
 153     boolean getIgnoreExtensions() {
 154         return ignoreExtensions;
 155     }
 156 
 157     /**
 158      * Clear the status response cache
 159      */
 160     void clear() {
 161         debugLog("Clearing response cache");


 162         responseCache.clear();
 163     }
 164 
 165     /**
 166      * Returns the number of currently valid objects in the response cache.
 167      *
 168      * @return the number of valid objects in the response cache.
 169      */
 170     int size() {
 171         return responseCache.size();
 172     }
 173 
 174     /**
 175      * Obtain the URI use by the {@code StatusResponseManager} during lookups.


 176      * This method takes into account not only the AIA extension from a
 177      * certificate to be checked, but also any default URI and possible
 178      * override settings for the response manager.
 179      *
 180      * @param cert the subject to get the responder URI from
 181      *
 182      * @return a {@code URI} containing the address to the OCSP responder, or
 183      *      {@code null} if no AIA extension exists in the certificate and no
 184      *      default responder has been configured.
 185      *
 186      * @throws NullPointerException if {@code cert} is {@code null}.
 187      */
 188     URI getURI(X509Certificate cert) {
 189         Objects.requireNonNull(cert);
 190 
 191         if (cert.getExtensionValue(
 192                 PKIXExtensions.OCSPNoCheck_Id.toString()) != null) {
 193             debugLog("OCSP NoCheck extension found.  OCSP will be skipped");



 194             return null;
 195         } else if (defaultResponder != null && respOverride) {
 196             debugLog("Responder override: URI is " + defaultResponder);



 197             return defaultResponder;
 198         } else {
 199             URI certURI = OCSP.getResponderURI(cert);
 200             return (certURI != null ? certURI : defaultResponder);
 201         }
 202     }
 203 
 204     /**
 205      * Shutdown the thread pool
 206      */
 207     void shutdown() {
 208         debugLog("Shutting down " + threadMgr.getActiveCount() +

 209                 " active threads");

 210         threadMgr.shutdown();
 211     }
 212 
 213     /**
 214      * Get a list of responses for a chain of certificates.
 215      * This will find OCSP responses from the cache, or failing that, directly
 216      * contact the OCSP responder.  It is assumed that the certificates in
 217      * the provided chain are in their proper order (from end-entity to
 218      * trust anchor).

 219      *
 220      * @param type the type of request being made of the
 221      *      {@code StatusResponseManager}
 222      * @param request the {@code StatusRequest} from the status_request or
 223      *      status_request_v2 ClientHello extension.  A value of {@code null}
 224      *      is interpreted as providing no responder IDs or extensions.
 225      * @param chain an array of 2 or more certificates.  Each certificate must
 226      *      be issued by the next certificate in the chain.

 227      * @param delay the number of time units to delay before returning
 228      *      responses.
 229      * @param unit the unit of time applied to the {@code delay} parameter
 230      *
 231      * @return an unmodifiable {@code Map} containing the certificate and
 232      *      its usually
 233      *
 234      * @throws SSLHandshakeException if an unsupported {@code StatusRequest}
 235      *      is provided.
 236      */
 237     Map<X509Certificate, byte[]> get(StatusRequestType type,
 238             StatusRequest request, X509Certificate[] chain, long delay,
 239             TimeUnit unit) {
 240         Map<X509Certificate, byte[]> responseMap = new HashMap<>();
 241         List<OCSPFetchCall> requestList = new ArrayList<>();
 242 
 243         debugLog("Beginning check: Type = " + type + ", Chain length = " +


 244                 chain.length);

 245 
 246         // It is assumed that the caller has ordered the certs in the chain
 247         // in the proper order (each certificate is issued by the next entry
 248         // in the provided chain).
 249         if (chain.length < 2) {
 250             return Collections.emptyMap();
 251         }
 252 
 253         if (type == StatusRequestType.OCSP) {
 254             try {
 255                 // For type OCSP, we only check the end-entity certificate
 256                 OCSPStatusRequest ocspReq = (OCSPStatusRequest)request;
 257                 CertId cid = new CertId(chain[1],
 258                         new SerialNumber(chain[0].getSerialNumber()));
 259                 ResponseCacheEntry cacheEntry = getFromCache(cid, ocspReq);
 260                 if (cacheEntry != null) {
 261                     responseMap.put(chain[0], cacheEntry.ocspBytes);
 262                 } else {
 263                     StatusInfo sInfo = new StatusInfo(chain[0], cid);
 264                     requestList.add(new OCSPFetchCall(sInfo, ocspReq));
 265                 }
 266             } catch (IOException exc) {
 267                 debugLog("Exception during CertId creation: " + exc);



 268             }
 269         } else if (type == StatusRequestType.OCSP_MULTI) {
 270             // For type OCSP_MULTI, we check every cert in the chain that
 271             // has a direct issuer at the next index.  We won't have an issuer
 272             // certificate for the last certificate in the chain and will
 273             // not be able to create a CertId because of that.
 274             OCSPStatusRequest ocspReq = (OCSPStatusRequest)request;
 275             int ctr;
 276             for (ctr = 0; ctr < chain.length - 1; ctr++) {
 277                 try {
 278                     // The cert at "ctr" is the subject cert, "ctr + 1" is the
 279                     // issuer certificate.
 280                     CertId cid = new CertId(chain[ctr + 1],
 281                             new SerialNumber(chain[ctr].getSerialNumber()));
 282                     ResponseCacheEntry cacheEntry = getFromCache(cid, ocspReq);

 283                     if (cacheEntry != null) {
 284                         responseMap.put(chain[ctr], cacheEntry.ocspBytes);
 285                     } else {
 286                         StatusInfo sInfo = new StatusInfo(chain[ctr], cid);
 287                         requestList.add(new OCSPFetchCall(sInfo, ocspReq));
 288                     }
 289                 } catch (IOException exc) {
 290                     debugLog("Exception during CertId creation: " + exc);



 291                 }
 292             }
 293         } else {
 294             debugLog("Unsupported status request type: " + type);


 295         }
 296 
 297         // If we were able to create one or more Fetches, go and run all
 298         // of them in separate threads.  For all the threads that completed
 299         // in the allotted time, put those status responses into the returned
 300         // Map.
 301         if (!requestList.isEmpty()) {
 302             try {
 303                 // Set a bunch of threads to go do the fetching
 304                 List<Future<StatusInfo>> resultList =
 305                         threadMgr.invokeAll(requestList, delay, unit);
 306 
 307                 // Go through the Futures and from any non-cancelled task,
 308                 // get the bytes and attach them to the responseMap.
 309                 for (Future<StatusInfo> task : resultList) {
 310                     if (task.isDone()) {



 311                         if (!task.isCancelled()) {
 312                             StatusInfo info = task.get();
 313                             if (info != null && info.responseData != null) {
 314                                 responseMap.put(info.cert,
 315                                         info.responseData.ocspBytes);
 316                             } else {
 317                                 debugLog("Completed task had no response data");


 318                             }
 319                         } else {
 320                             debugLog("Found cancelled task");

 321                         }
 322                     }
 323                 }
 324             } catch (InterruptedException | ExecutionException exc) {
 325                 // Not sure what else to do here
 326                 debugLog("Exception when getting data: " + exc);


 327             }
 328         }
 329 
 330         return Collections.unmodifiableMap(responseMap);
 331     }
 332 
 333     /**
 334      * Check the cache for a given {@code CertId}.
 335      *
 336      * @param cid the CertId of the response to look up
 337      * @param ocspRequest the OCSP request structure sent by the client
 338      *      in the TLS status_request[_v2] hello extension.
 339      *
 340      * @return the {@code ResponseCacheEntry} for a specific CertId, or
 341      *      {@code null} if it is not found or a nonce extension has been
 342      *      requested by the caller.
 343      */
 344     private ResponseCacheEntry getFromCache(CertId cid,
 345             OCSPStatusRequest ocspRequest) {
 346         // Determine if the nonce extension is present in the request.  If
 347         // so, then do not attempt to retrieve the response from the cache.
 348         for (Extension ext : ocspRequest.getExtensions()) {
 349             if (ext.getId().equals(PKIXExtensions.OCSPNonce_Id.toString())) {
 350                 debugLog("Nonce extension found, skipping cache check");




 351                 return null;
 352             }
 353         }
 354 
 355         ResponseCacheEntry respEntry = responseCache.get(cid);
 356 
 357         // If the response entry has a nextUpdate and it has expired
 358         // before the cache expiration, purge it from the cache
 359         // and do not return it as a cache hit.
 360         if (respEntry != null && respEntry.nextUpdate != null &&
 361                 respEntry.nextUpdate.before(new Date())) {
 362             debugLog("nextUpdate threshold exceeded, purging from cache");



 363             respEntry = null;
 364         }
 365 
 366         debugLog("Check cache for SN" + cid.getSerialNumber() + ": " +


 367                 (respEntry != null ? "HIT" : "MISS"));

 368         return respEntry;
 369     }
 370 
 371     @Override
 372     public String toString() {
 373         StringBuilder sb = new StringBuilder("StatusResponseManager: ");
 374 
 375         sb.append("Core threads: ").append(threadMgr.getCorePoolSize());
 376         sb.append(", Cache timeout: ");
 377         if (cacheLifetime > 0) {
 378             sb.append(cacheLifetime).append(" seconds");
 379         } else {
 380             sb.append(" indefinite");
 381         }
 382 
 383         sb.append(", Cache MaxSize: ");
 384         if (cacheCapacity > 0) {
 385             sb.append(cacheCapacity).append(" items");
 386         } else {
 387             sb.append(" unbounded");
 388         }
 389 
 390         sb.append(", Default URI: ");
 391         if (defaultResponder != null) {
 392             sb.append(defaultResponder);
 393         } else {
 394             sb.append("NONE");
 395         }
 396 
 397         return sb.toString();
 398     }
 399 
 400     /**
 401      * Log messages through the SSL Debug facility.
 402      *
 403      * @param message the message to be displayed
 404      */
 405     static void debugLog(String message) {
 406         if (debug != null && Debug.isOn("respmgr")) {
 407             StringBuilder sb = new StringBuilder();
 408             sb.append("[").append(Thread.currentThread().getName());
 409             sb.append("] ").append(message);
 410             System.out.println(sb.toString());
 411         }
 412     }
 413 
 414     /**
 415      * Inner class used to group request and response data.
 416      */
 417     class StatusInfo {
 418         final X509Certificate cert;
 419         final CertId cid;
 420         final URI responder;
 421         ResponseCacheEntry responseData;
 422 
 423         /**
 424          * Create a StatusInfo object from certificate data.
 425          *
 426          * @param subjectCert the certificate to be checked for revocation
 427          * @param issuerCert the issuer of the {@code subjectCert}
 428          *
 429          * @throws IOException if CertId creation from the certificates fails
 430          */
 431         StatusInfo(X509Certificate subjectCert, X509Certificate issuerCert)
 432                 throws IOException {
 433             this(subjectCert, new CertId(issuerCert,
 434                     new SerialNumber(subjectCert.getSerialNumber())));
 435         }
 436 
 437         /**
 438          * Create a StatusInfo object from an existing subject certificate
 439          * and its corresponding CertId.
 440          *
 441          * @param subjectCert the certificate to be checked for revocation
 442          * @param cid the CertId for {@code subjectCert}
 443          */
 444         StatusInfo(X509Certificate subjectCert, CertId certId) {
 445             cert = subjectCert;
 446             cid = certId;
 447             responder = getURI(cert);
 448             responseData = null;
 449         }


 454          * responseData and extensions fields, which should not persist
 455          * in a rescheduled fetch.
 456          *
 457          * @param orig the original {@code StatusInfo}
 458          */
 459         StatusInfo(StatusInfo orig) {
 460             this.cert = orig.cert;
 461             this.cid = orig.cid;
 462             this.responder = orig.responder;
 463             this.responseData = null;
 464         }
 465 
 466         /**
 467          * Return a String representation of the {@code StatusInfo}
 468          *
 469          * @return a {@code String} representation of this object
 470          */
 471         @Override
 472         public String toString() {
 473             StringBuilder sb = new StringBuilder("StatusInfo:");
 474             sb.append("\n\tCert: ").append(this.cert.getSubjectX500Principal());

 475             sb.append("\n\tSerial: ").append(this.cert.getSerialNumber());
 476             sb.append("\n\tResponder: ").append(this.responder);
 477             sb.append("\n\tResponse data: ").append(this.responseData != null ?
 478                     (this.responseData.ocspBytes.length + " bytes") : "<NULL>");


 479             return sb.toString();
 480         }
 481     }
 482 
 483     /**
 484      * Static nested class used as the data kept in the response cache.
 485      */
 486     static class ResponseCacheEntry {
 487         final OCSPResponse.ResponseStatus status;
 488         final byte[] ocspBytes;
 489         final Date nextUpdate;
 490         final OCSPResponse.SingleResponse singleResp;
 491         final ResponderId respId;
 492 
 493         /**
 494          * Create a new cache entry from the raw bytes of the response
 495          *
 496          * @param responseBytes the DER encoding for the OCSP response
 497          *
 498          * @throws IOException if an {@code OCSPResponse} cannot be created from
 499          *      the encoded bytes.
 500          */
 501         ResponseCacheEntry(byte[] responseBytes, CertId cid)
 502                 throws IOException {
 503             Objects.requireNonNull(responseBytes,
 504                     "Non-null responseBytes required");
 505             Objects.requireNonNull(cid, "Non-null Cert ID required");
 506 
 507             ocspBytes = responseBytes.clone();
 508             OCSPResponse oResp = new OCSPResponse(ocspBytes);
 509             status = oResp.getResponseStatus();
 510             respId = oResp.getResponderId();
 511             singleResp = oResp.getSingleResponse(cid);
 512             if (status == OCSPResponse.ResponseStatus.SUCCESSFUL) {
 513                 if (singleResp != null) {
 514                     // Pull out the nextUpdate field in advance because the
 515                     // Date is cloned.
 516                     nextUpdate = singleResp.getNextUpdate();
 517                 } else {
 518                     throw new IOException("Unable to find SingleResponse for " +
 519                             "SN " + cid.getSerialNumber());

 520                 }
 521             } else {
 522                 nextUpdate = null;
 523             }
 524         }
 525     }
 526 
 527     /**
 528      * Inner Callable class that does the actual work of looking up OCSP
 529      * responses, first looking at the cache and doing OCSP requests if
 530      * a cache miss occurs.
 531      */
 532     class OCSPFetchCall implements Callable<StatusInfo> {
 533         StatusInfo statInfo;
 534         OCSPStatusRequest ocspRequest;
 535         List<Extension> extensions;
 536         List<ResponderId> responderIds;
 537 
 538         /**
 539          * A constructor that builds the OCSPFetchCall from the provided
 540          * StatusInfo and information from the status_request[_v2] extension.

 541          *
 542          * @param info the {@code StatusInfo} containing the subject
 543          * certificate, CertId, and other supplemental info.
 544          * @param request the {@code OCSPStatusRequest} containing any
 545          * responder IDs and extensions.
 546          */
 547         public OCSPFetchCall(StatusInfo info, OCSPStatusRequest request) {
 548             statInfo = Objects.requireNonNull(info,
 549                     "Null StatusInfo not allowed");
 550             ocspRequest = Objects.requireNonNull(request,
 551                     "Null OCSPStatusRequest not allowed");
 552             extensions = ocspRequest.getExtensions();
 553             responderIds = ocspRequest.getResponderIds();
 554         }
 555 
 556         /**
 557          * Get an OCSP response, either from the cache or from a responder.
 558          *
 559          * @return The StatusInfo object passed into the {@code OCSPFetchCall}
 560          * constructor, with the {@code responseData} field filled in with the
 561          * response or {@code null} if no response can be obtained.

 562          */
 563         @Override
 564         public StatusInfo call() {
 565             debugLog("Starting fetch for SN " + statInfo.cid.getSerialNumber());




 566             try {
 567                 ResponseCacheEntry cacheEntry;
 568                 List<Extension> extsToSend;
 569 
 570                 if (statInfo.responder == null) {
 571                     // If we have no URI then there's nothing to do but return
 572                     debugLog("Null URI detected, OCSP fetch aborted.");




 573                     return statInfo;
 574                 } else {
 575                     debugLog("Attempting fetch from " + statInfo.responder);



 576                 }
 577 
 578                 // If the StatusResponseManager has been configured to not
 579                 // forward extensions, then set extensions to an empty list.
 580                 // We will forward the extensions unless one of two conditions
 581                 // occur: (1) The jdk.tls.stapling.ignoreExtensions property is
 582                 // true or (2) There is a non-empty ResponderId list.





 583                 // ResponderId selection is a feature that will be
 584                 // supported in the future.
 585                 extsToSend = (ignoreExtensions || !responderIds.isEmpty()) ?
 586                         Collections.emptyList() : extensions;
 587 
 588                 byte[] respBytes = OCSP.getOCSPBytes(
 589                         Collections.singletonList(statInfo.cid),
 590                         statInfo.responder, extsToSend);
 591 
 592                 if (respBytes != null) {
 593                     // Place the data into the response cache
 594                     cacheEntry = new ResponseCacheEntry(respBytes,
 595                             statInfo.cid);
 596 
 597                     // Get the response status and act on it appropriately
 598                     debugLog("OCSP Status: " + cacheEntry.status +

 599                             " (" + respBytes.length + " bytes)");

 600                     if (cacheEntry.status ==
 601                             OCSPResponse.ResponseStatus.SUCCESSFUL) {
 602                         // Set the response in the returned StatusInfo
 603                         statInfo.responseData = cacheEntry;
 604 
 605                         // Add the response to the cache (if applicable)
 606                         addToCache(statInfo.cid, cacheEntry);
 607                     }
 608                 } else {
 609                     debugLog("No data returned from OCSP Responder");



 610                 }
 611             } catch (IOException ioe) {
 612                 debugLog("Caught exception: " + ioe);


 613             }
 614 
 615             return statInfo;
 616         }
 617 
 618         /**
 619          * Add a response to the cache.
 620          *
 621          * @param certId The {@code CertId} for the OCSP response
 622          * @param entry A cache entry containing the response bytes and
 623          *      the {@code OCSPResponse} built from those bytes.
 624          */
 625         private void addToCache(CertId certId, ResponseCacheEntry entry) {
 626             // If no cache lifetime has been set on entries then
 627             // don't cache this response if there is no nextUpdate field
 628             if (entry.nextUpdate == null && cacheLifetime == 0) {
 629                 debugLog("Not caching this OCSP response");


 630             } else {
 631                 responseCache.put(certId, entry);
 632                 debugLog("Added response for SN " + certId.getSerialNumber() +



 633                         " to cache");
 634             }
 635         }

 636 
 637         /**
 638          * Determine the delay to use when scheduling the task that will
 639          * update the OCSP response.  This is the shorter time between the
 640          * cache lifetime and the nextUpdate.  If no nextUpdate is present in
 641          * the response, then only the cache lifetime is used.
 642          * If cache timeouts are disabled (a zero value) and there's no
 643          * nextUpdate, then the entry is not cached and no rescheduling will
 644          * take place.
 645          *
 646          * @param nextUpdate a {@code Date} object corresponding to the
 647          *      next update time from a SingleResponse.
 648          *
 649          * @return the number of seconds of delay before the next fetch
 650          *      should be executed.  A zero value means that the fetch
 651          *      should happen immediately, while a value less than zero
 652          *      indicates no rescheduling should be done.
 653          */
 654         private long getNextTaskDelay(Date nextUpdate) {
 655             long delaySec;
 656             int lifetime = getCacheLifetime();
 657 
 658             if (nextUpdate != null) {
 659                 long nuDiffSec = (nextUpdate.getTime() -
 660                         System.currentTimeMillis()) / 1000;
 661                 delaySec = lifetime > 0 ? Long.min(nuDiffSec, lifetime) :
 662                         nuDiffSec;
 663             } else {
 664                 delaySec = lifetime > 0 ? lifetime : -1;
 665             }
 666 
 667             return delaySec;
 668         }
 669     }





















































































































































































































 670 }

   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      */
  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",


  94         } catch (URISyntaxException urise) {
  95             tmpURI = null;
  96         }
  97         defaultResponder = tmpURI;
  98 
  99         respOverride = AccessController.doPrivileged(
 100                 new GetBooleanAction("jdk.tls.stapling.responderOverride"));
 101         ignoreExtensions = AccessController.doPrivileged(
 102                 new GetBooleanAction("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      */
 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      */
 136     int getCacheCapacity() {
 137         return cacheCapacity;
 138     }
 139 


 144      *      it has not been set.
 145      */
 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      */
 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      */
 167     boolean getIgnoreExtensions() {
 168         return ignoreExtensions;
 169     }
 170 
 171     /**
 172      * Clear the status response cache
 173      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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
 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          */
 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          */
 488         StatusInfo(X509Certificate subjectCert, CertId certId) {
 489             cert = subjectCert;
 490             cid = certId;
 491             responder = getURI(cert);
 492             responseData = null;
 493         }


 498          * responseData and extensions fields, which should not persist
 499          * in a rescheduled fetch.
 500          *
 501          * @param orig the original {@code StatusInfo}
 502          */
 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
 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          */
 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          */
 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
 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          */
 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          */
 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 
 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 
 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 
< prev index next >