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