1 /*
2  * Copyright (c) 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 
26 package sun.security.ssl;
27 
28 import java.io.IOException;
29 import java.nio.ByteBuffer;
30 import java.text.MessageFormat;
31 import java.util.Locale;
32 import javax.net.ssl.SSLProtocolException;
33 
34 import sun.security.ssl.ClientHello.ClientHelloMessage;
35 import sun.security.ssl.SSLExtension.ExtensionConsumer;
36 import sun.security.ssl.SSLHandshake.HandshakeMessage;
37 import sun.security.ssl.SSLExtension.SSLExtensionSpec;
38 import sun.security.ssl.ServerHello.ServerHelloMessage;
39 import sun.security.util.HexDumpEncoder;
40 
41 public class CookieExtension {
42     static final HandshakeProducer chNetworkProducer =
43             new CHCookieProducer();
44     static final ExtensionConsumer chOnLoadConsumer =
45             new CHCookieConsumer();
46     static final HandshakeConsumer chOnTradeConsumer =
47             new CHCookieUpdate();
48 
49     static final HandshakeProducer hrrNetworkProducer =
50             new HRRCookieProducer();
51     static final ExtensionConsumer hrrOnLoadConsumer =
52             new HRRCookieConsumer();
53 
54     static final HandshakeProducer hrrNetworkReproducer =
55             new HRRCookieReproducer();
56 
57     static final CookieStringizer cookieStringizer =
58             new CookieStringizer();
59 
60     /**
61      * The "cookie" extension.
62      */
63     static class CookieSpec implements SSLExtensionSpec {
64         final byte[] cookie;
65 
CookieSpec(ByteBuffer m)66         private CookieSpec(ByteBuffer m) throws IOException {
67             // opaque cookie<1..2^16-1>;
68             if (m.remaining() < 3) {
69                 throw new SSLProtocolException(
70                     "Invalid cookie extension: insufficient data");
71             }
72 
73             this.cookie = Record.getBytes16(m);
74         }
75 
76         @Override
toString()77         public String toString() {
78             MessageFormat messageFormat = new MessageFormat(
79                     "\"cookie\": '{'\n" +
80                     "{0}\n" +
81                     "'}',", Locale.ENGLISH);
82             HexDumpEncoder hexEncoder = new HexDumpEncoder();
83             Object[] messageFields = {
84                 Utilities.indent(hexEncoder.encode(cookie))
85             };
86 
87             return messageFormat.format(messageFields);
88         }
89     }
90 
91     private static final class CookieStringizer implements SSLStringizer {
92         @Override
toString(ByteBuffer buffer)93         public String toString(ByteBuffer buffer) {
94             try {
95                 return (new CookieSpec(buffer)).toString();
96             } catch (IOException ioe) {
97                 // For debug logging only, so please swallow exceptions.
98                 return ioe.getMessage();
99             }
100         }
101     }
102 
103     private static final
104             class CHCookieProducer implements HandshakeProducer {
105         // Prevent instantiation of this class.
CHCookieProducer()106         private CHCookieProducer() {
107             // blank
108         }
109 
110         @Override
produce(ConnectionContext context, HandshakeMessage message)111         public byte[] produce(ConnectionContext context,
112                 HandshakeMessage message) throws IOException {
113             ClientHandshakeContext chc = (ClientHandshakeContext) context;
114 
115             // Is it a supported and enabled extension?
116             if (!chc.sslConfig.isAvailable(SSLExtension.CH_COOKIE)) {
117                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
118                     SSLLogger.fine(
119                             "Ignore unavailable cookie extension");
120                 }
121                 return null;
122             }
123 
124             // response to an HelloRetryRequest cookie
125             CookieSpec spec = (CookieSpec)chc.handshakeExtensions.get(
126                     SSLExtension.HRR_COOKIE);
127 
128             if (spec != null &&
129                     spec.cookie != null && spec.cookie.length != 0) {
130                 byte[] extData = new byte[spec.cookie.length + 2];
131                 ByteBuffer m = ByteBuffer.wrap(extData);
132                 Record.putBytes16(m, spec.cookie);
133                 return extData;
134             }
135 
136             return null;
137         }
138     }
139 
140     private static final
141             class CHCookieConsumer implements ExtensionConsumer {
142         // Prevent instantiation of this class.
CHCookieConsumer()143         private CHCookieConsumer() {
144             // blank
145         }
146 
147         @Override
consume(ConnectionContext context, HandshakeMessage message, ByteBuffer buffer)148         public void consume(ConnectionContext context,
149             HandshakeMessage message, ByteBuffer buffer) throws IOException {
150             // The consuming happens in server side only.
151             ServerHandshakeContext shc = (ServerHandshakeContext)context;
152 
153             // Is it a supported and enabled extension?
154             if (!shc.sslConfig.isAvailable(SSLExtension.CH_COOKIE)) {
155                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
156                     SSLLogger.fine(
157                             "Ignore unavailable cookie extension");
158                 }
159                 return;     // ignore the extension
160             }
161 
162             CookieSpec spec;
163             try {
164                 spec = new CookieSpec(buffer);
165             } catch (IOException ioe) {
166                 throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
167             }
168 
169             shc.handshakeExtensions.put(SSLExtension.CH_COOKIE, spec);
170 
171             // No impact on session resumption.
172             //
173             // Note that the protocol version negotiation happens before the
174             // session resumption negotiation.  And the session resumption
175             // negotiation depends on the negotiated protocol version.
176         }
177     }
178 
179     private static final
180             class CHCookieUpdate implements HandshakeConsumer {
181         // Prevent instantiation of this class.
CHCookieUpdate()182         private CHCookieUpdate() {
183             // blank
184         }
185 
186         @Override
consume(ConnectionContext context, HandshakeMessage message)187         public void consume(ConnectionContext context,
188                 HandshakeMessage message) throws IOException {
189             // The consuming happens in server side only.
190             ServerHandshakeContext shc = (ServerHandshakeContext)context;
191             ClientHelloMessage clientHello = (ClientHelloMessage)message;
192 
193             CookieSpec spec = (CookieSpec)
194                     shc.handshakeExtensions.get(SSLExtension.CH_COOKIE);
195             if (spec == null) {
196                 // Ignore, no "cookie" extension requested.
197                 return;
198             }
199 
200             HelloCookieManager hcm =
201                 shc.sslContext.getHelloCookieManager(shc.negotiatedProtocol);
202             if (!hcm.isCookieValid(shc, clientHello, spec.cookie)) {
203                 throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
204                         "unrecognized cookie");
205             }
206         }
207     }
208 
209     private static final
210             class HRRCookieProducer implements HandshakeProducer {
211         // Prevent instantiation of this class.
HRRCookieProducer()212         private HRRCookieProducer() {
213             // blank
214         }
215 
216         @Override
produce(ConnectionContext context, HandshakeMessage message)217         public byte[] produce(ConnectionContext context,
218                 HandshakeMessage message) throws IOException {
219             // The producing happens in server side only.
220             ServerHandshakeContext shc = (ServerHandshakeContext)context;
221             ServerHelloMessage hrrm = (ServerHelloMessage)message;
222 
223             // Is it a supported and enabled extension?
224             if (!shc.sslConfig.isAvailable(SSLExtension.HRR_COOKIE)) {
225                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
226                     SSLLogger.fine(
227                             "Ignore unavailable cookie extension");
228                 }
229                 return null;
230             }
231 
232             HelloCookieManager hcm =
233                 shc.sslContext.getHelloCookieManager(shc.negotiatedProtocol);
234 
235             byte[] cookie = hcm.createCookie(shc, hrrm.clientHello);
236 
237             byte[] extData = new byte[cookie.length + 2];
238             ByteBuffer m = ByteBuffer.wrap(extData);
239             Record.putBytes16(m, cookie);
240 
241             return extData;
242         }
243     }
244 
245     private static final
246             class HRRCookieConsumer implements ExtensionConsumer {
247         // Prevent instantiation of this class.
HRRCookieConsumer()248         private HRRCookieConsumer() {
249             // blank
250         }
251 
252         @Override
consume(ConnectionContext context, HandshakeMessage message, ByteBuffer buffer)253         public void consume(ConnectionContext context,
254             HandshakeMessage message, ByteBuffer buffer) throws IOException {
255             // The consuming happens in client side only.
256             ClientHandshakeContext chc = (ClientHandshakeContext)context;
257 
258             // Is it a supported and enabled extension?
259             if (!chc.sslConfig.isAvailable(SSLExtension.HRR_COOKIE)) {
260                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
261                     SSLLogger.fine(
262                             "Ignore unavailable cookie extension");
263                 }
264                 return;     // ignore the extension
265             }
266 
267             CookieSpec spec;
268             try {
269                 spec = new CookieSpec(buffer);
270             } catch (IOException ioe) {
271                 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
272             }
273 
274             chc.handshakeExtensions.put(SSLExtension.HRR_COOKIE, spec);
275         }
276     }
277 
278     private static final
279             class HRRCookieReproducer implements HandshakeProducer {
280         // Prevent instantiation of this class.
HRRCookieReproducer()281         private HRRCookieReproducer() {
282             // blank
283         }
284 
285         @Override
produce(ConnectionContext context, HandshakeMessage message)286         public byte[] produce(ConnectionContext context,
287                 HandshakeMessage message) throws IOException {
288             // The producing happens in server side only.
289             ServerHandshakeContext shc = (ServerHandshakeContext) context;
290 
291             // Is it a supported and enabled extension?
292             if (!shc.sslConfig.isAvailable(SSLExtension.HRR_COOKIE)) {
293                 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
294                     SSLLogger.fine(
295                             "Ignore unavailable cookie extension");
296                 }
297                 return null;
298             }
299 
300             // copy of the ClientHello cookie
301             CookieSpec spec = (CookieSpec)shc.handshakeExtensions.get(
302                     SSLExtension.CH_COOKIE);
303 
304             if (spec != null &&
305                     spec.cookie != null && spec.cookie.length != 0) {
306                 byte[] extData = new byte[spec.cookie.length + 2];
307                 ByteBuffer m = ByteBuffer.wrap(extData);
308                 Record.putBytes16(m, spec.cookie);
309                 return extData;
310             }
311 
312             return null;
313         }
314     }
315 }
316