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