• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package org.conscrypt;
18 
19 import static org.conscrypt.Preconditions.checkNotNull;
20 
21 import java.security.Principal;
22 import java.security.cert.Certificate;
23 import java.security.cert.X509Certificate;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 import javax.net.ssl.SSLPeerUnverifiedException;
29 import javax.net.ssl.SSLSession;
30 import javax.net.ssl.SSLSessionBindingEvent;
31 import javax.net.ssl.SSLSessionBindingListener;
32 import javax.net.ssl.SSLSessionContext;
33 
34 /**
35  * A session that is dedicated a single connection and operates directly on the underlying
36  * {@code SSL}.
37  */
38 final class ActiveSession implements SSLSession {
39     private final SslWrapper ssl;
40     private AbstractSessionContext sessionContext;
41     private byte[] id;
42     private long creationTime;
43     private String cipherSuite;
44     private String protocol;
45     private String peerHost;
46     private int peerPort = -1;
47     private long lastAccessedTime = 0;
48     private volatile javax.security.cert.X509Certificate[] peerCertificateChain;
49     private X509Certificate[] localCertificates;
50     private X509Certificate[] peerCertificates;
51     private byte[] peerCertificateOcspData;
52     private byte[] peerTlsSctData;
53 
54     // lazy init for memory reasons
55     private Map<String, Object> values;
56 
ActiveSession(SslWrapper ssl, AbstractSessionContext sessionContext)57     ActiveSession(SslWrapper ssl, AbstractSessionContext sessionContext) {
58         this.ssl = checkNotNull(ssl, "ssl");
59         this.sessionContext = checkNotNull(sessionContext, "sessionContext");
60     }
61 
62     @Override
getId()63     public byte[] getId() {
64         if (id == null) {
65             id = ssl.getSessionId();
66         }
67         return id != null ? id.clone() : EmptyArray.BYTE;
68     }
69 
70     /**
71      * Indicates that this session's ID may have changed and should be re-cached.
72      */
resetId()73     void resetId() {
74         id = null;
75     }
76 
77     @Override
getSessionContext()78     public SSLSessionContext getSessionContext() {
79         return isValid() ? sessionContext : null;
80     }
81 
82     @Override
getCreationTime()83     public long getCreationTime() {
84         if (creationTime == 0) {
85             creationTime = ssl.getTime();
86         }
87         return creationTime;
88     }
89 
90     /**
91      * Returns the last time this SSL session was accessed. Accessing
92      * here is to mean that a new connection with the same SSL context data was
93      * established.
94      *
95      * @return the session's last access time in milliseconds since the epoch
96      */
97     // TODO(nathanmittler): Does lastAccessedTime need to account for session reuse?
98     @Override
getLastAccessedTime()99     public long getLastAccessedTime() {
100         return lastAccessedTime == 0 ? getCreationTime() : lastAccessedTime;
101     }
102 
setLastAccessedTime(long accessTimeMillis)103     void setLastAccessedTime(long accessTimeMillis) {
104         lastAccessedTime = accessTimeMillis;
105     }
106 
107     /**
108      * Returns the OCSP stapled response. Returns a copy of the internal arrays.
109      *
110      * The method signature matches
111      * <a
112      * href="http://download.java.net/java/jdk9/docs/api/javax/net/ssl/ExtendedSSLSession.html#getStatusResponses--">Java
113      * 9</a>.
114      *
115      * @see <a href="https://tools.ietf.org/html/rfc6066">RFC 6066</a>
116      * @see <a href="https://tools.ietf.org/html/rfc6961">RFC 6961</a>
117      */
118     /* @Override */
119     @SuppressWarnings("MissingOverride") // For Pre-Java9 compatibility.
getStatusResponses()120     public List<byte[]> getStatusResponses() {
121         if (peerCertificateOcspData == null) {
122             return Collections.<byte[]>emptyList();
123         }
124 
125         return Collections.singletonList(peerCertificateOcspData.clone());
126     }
127 
128     /**
129      * Returns the signed certificate timestamp (SCT) received from the peer. Returns a
130      * copy of the internal array.
131      *
132      * @see <a href="https://tools.ietf.org/html/rfc6962">RFC 6962</a>
133      */
getPeerSignedCertificateTimestamp()134     byte[] getPeerSignedCertificateTimestamp() {
135         if (peerTlsSctData == null) {
136             return null;
137         }
138         return peerTlsSctData.clone();
139     }
140 
getRequestedServerName()141     String getRequestedServerName() {
142         return ssl.getRequestedServerName();
143     }
144 
145     @Override
invalidate()146     public void invalidate() {
147         ssl.setTimeout(0L);
148     }
149 
150     @Override
isValid()151     public boolean isValid() {
152         long creationTimeMillis = ssl.getTime();
153         long timeoutMillis = ssl.getTimeout();
154         return (System.currentTimeMillis() - timeoutMillis) < creationTimeMillis;
155     }
156 
157     @Override
putValue(String name, Object value)158     public void putValue(String name, Object value) {
159         if (name == null) {
160             throw new NullPointerException("name");
161         }
162         if (value == null) {
163             throw new NullPointerException("value");
164         }
165         Map<String, Object> values = this.values;
166         if (values == null) {
167             // Use size of 2 to keep the memory overhead small
168             values = this.values = new HashMap<String, Object>(2);
169         }
170         Object old = values.put(name, value);
171         if (value instanceof SSLSessionBindingListener) {
172             ((SSLSessionBindingListener) value).valueBound(new SSLSessionBindingEvent(this, name));
173         }
174         if (old instanceof SSLSessionBindingListener) {
175             ((SSLSessionBindingListener) old).valueUnbound(new SSLSessionBindingEvent(this, name));
176         }
177         notifyUnbound(old, name);
178     }
179 
180     @Override
getValue(String name)181     public Object getValue(String name) {
182         if (name == null) {
183             throw new NullPointerException("name");
184         }
185         if (values == null) {
186             return null;
187         }
188         return values.get(name);
189     }
190 
191     @Override
removeValue(String name)192     public void removeValue(String name) {
193         if (name == null) {
194             throw new NullPointerException("name");
195         }
196         Map<String, Object> values = this.values;
197         if (values == null) {
198             return;
199         }
200         Object old = values.remove(name);
201         notifyUnbound(old, name);
202     }
203 
204     @Override
getValueNames()205     public String[] getValueNames() {
206         Map<String, Object> values = this.values;
207         if (values == null || values.isEmpty()) {
208             return EmptyArray.STRING;
209         }
210         return values.keySet().toArray(new String[values.size()]);
211     }
212 
213     @Override
getPeerCertificates()214     public X509Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
215         checkPeerCertificatesPresent();
216         return peerCertificates.clone();
217     }
218 
219     @Override
getLocalCertificates()220     public Certificate[] getLocalCertificates() {
221         return localCertificates == null ? null : localCertificates.clone();
222     }
223 
224     /**
225      * Returns the certificate(s) of the peer in this SSL session
226      * used in the handshaking phase of the connection.
227      * Please notice hat this method is superseded by
228      * <code>getPeerCertificates()</code>.
229      * @return an array of X509 certificates (the peer's one first and then
230      *         eventually that of the certification authority) or null if no
231      *         certificate were used during the SSL connection.
232      * @throws SSLPeerUnverifiedException if either a non-X.509 certificate
233      *         was used (i.e. Kerberos certificates) or the peer could not
234      *         be verified.
235      */
236     @Override
getPeerCertificateChain()237     public javax.security.cert.X509Certificate[] getPeerCertificateChain()
238             throws SSLPeerUnverifiedException {
239         checkPeerCertificatesPresent();
240         // TODO(nathanmittler): Should we clone?
241         javax.security.cert.X509Certificate[] result = peerCertificateChain;
242         if (result == null) {
243             // single-check idiom
244             peerCertificateChain = result = SSLUtils.toCertificateChain(peerCertificates);
245         }
246         return result;
247     }
248 
249     @Override
getPeerPrincipal()250     public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
251         checkPeerCertificatesPresent();
252         return peerCertificates[0].getSubjectX500Principal();
253     }
254 
255     @Override
getLocalPrincipal()256     public Principal getLocalPrincipal() {
257         if (localCertificates != null && localCertificates.length > 0) {
258             return localCertificates[0].getSubjectX500Principal();
259         } else {
260             return null;
261         }
262     }
263 
264     @Override
getCipherSuite()265     public String getCipherSuite() {
266         if (cipherSuite == null) {
267             cipherSuite = ssl.getCipherSuite();
268         }
269         return cipherSuite;
270     }
271 
272     @Override
getProtocol()273     public String getProtocol() {
274         String protocol = this.protocol;
275         if (protocol == null) {
276             protocol = ssl.getVersion();
277             this.protocol = protocol;
278         }
279         return protocol;
280     }
281 
282     @Override
getPeerHost()283     public String getPeerHost() {
284         return peerHost;
285     }
286 
287     @Override
getPeerPort()288     public int getPeerPort() {
289         return peerPort;
290     }
291 
292     @Override
getPacketBufferSize()293     public int getPacketBufferSize() {
294         return NativeConstants.SSL3_RT_MAX_PACKET_SIZE;
295     }
296 
297     @Override
getApplicationBufferSize()298     public int getApplicationBufferSize() {
299         return NativeConstants.SSL3_RT_MAX_PLAIN_LENGTH;
300     }
301 
302     /**
303      * Configures the peer information once it has been received by the handshake.
304      */
onPeerCertificatesReceived( String peerHost, int peerPort, OpenSSLX509Certificate[] peerCertificates)305     void onPeerCertificatesReceived(
306             String peerHost, int peerPort, OpenSSLX509Certificate[] peerCertificates) {
307         configurePeer(peerHost, peerPort, peerCertificates);
308     }
309 
310     /**
311      * Configures the peer and local state from a newly created BoringSSL session.
312      */
onSessionEstablished(String peerHost, int peerPort)313     void onSessionEstablished(String peerHost, int peerPort) {
314         id = null;
315         this.localCertificates = ssl.getLocalCertificates();
316         configurePeer(peerHost, peerPort, ssl.getPeerCertificates());
317     }
318 
configurePeer( String peerHost, int peerPort, OpenSSLX509Certificate[] peerCertificates)319     private void configurePeer(
320             String peerHost, int peerPort, OpenSSLX509Certificate[] peerCertificates) {
321         this.peerHost = peerHost;
322         this.peerPort = peerPort;
323         this.peerCertificates = peerCertificates;
324         this.peerCertificateOcspData = ssl.getPeerCertificateOcspData();
325         this.peerTlsSctData = ssl.getPeerTlsSctData();
326     }
327 
getX509PeerCertificates()328     private X509Certificate[] getX509PeerCertificates() throws SSLPeerUnverifiedException {
329         if (peerCertificates == null || peerCertificates.length == 0) {
330             throw new SSLPeerUnverifiedException("No peer certificates");
331         }
332         return peerCertificates;
333     }
334 
335     /**
336      * Throw SSLPeerUnverifiedException on null or empty peerCertificates array
337      */
checkPeerCertificatesPresent()338     private void checkPeerCertificatesPresent() throws SSLPeerUnverifiedException {
339         if (peerCertificates == null || peerCertificates.length == 0) {
340             throw new SSLPeerUnverifiedException("No peer certificates");
341         }
342     }
343 
notifyUnbound(Object value, String name)344     private void notifyUnbound(Object value, String name) {
345         if (value instanceof SSLSessionBindingListener) {
346             ((SSLSessionBindingListener) value)
347                     .valueUnbound(new SSLSessionBindingEvent(this, name));
348         }
349     }
350 }
351