1 /*
2  * Copyright (c) 2003, 2019, 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.SSLException;
33 import javax.net.ssl.SSLHandshakeException;
34 import javax.net.ssl.SSLProtocolException;
35 
36 /**
37  * SSL/(D)TLS Alter description
38  */
39 enum Alert {
40     // Please refer to TLS Alert Registry for the latest (D)TLS Alert values:
41     //     https://www.iana.org/assignments/tls-parameters/
42     CLOSE_NOTIFY            ((byte)0,   "close_notify", false),
43     UNEXPECTED_MESSAGE      ((byte)10,  "unexpected_message", false),
44     BAD_RECORD_MAC          ((byte)20,  "bad_record_mac", false),
45     DECRYPTION_FAILED       ((byte)21,  "decryption_failed", false),
46     RECORD_OVERFLOW         ((byte)22,  "record_overflow", false),
47     DECOMPRESSION_FAILURE   ((byte)30,  "decompression_failure", false),
48     HANDSHAKE_FAILURE       ((byte)40,  "handshake_failure", true),
49     NO_CERTIFICATE          ((byte)41,  "no_certificate", true),
50     BAD_CERTIFICATE         ((byte)42,  "bad_certificate", true),
51     UNSUPPORTED_CERTIFICATE ((byte)43,  "unsupported_certificate", true),
52     CERTIFICATE_REVOKED     ((byte)44,  "certificate_revoked", true),
53     CERTIFICATE_EXPIRED     ((byte)45,  "certificate_expired", true),
54     CERTIFICATE_UNKNOWN     ((byte)46,  "certificate_unknown", true),
55     ILLEGAL_PARAMETER       ((byte)47,  "illegal_parameter", true),
56     UNKNOWN_CA              ((byte)48,  "unknown_ca", true),
57     ACCESS_DENIED           ((byte)49,  "access_denied", true),
58     DECODE_ERROR            ((byte)50,  "decode_error", true),
59     DECRYPT_ERROR           ((byte)51,  "decrypt_error", true),
60     EXPORT_RESTRICTION      ((byte)60,  "export_restriction", true),
61     PROTOCOL_VERSION        ((byte)70,  "protocol_version", true),
62     INSUFFICIENT_SECURITY   ((byte)71,  "insufficient_security", true),
63     INTERNAL_ERROR          ((byte)80,  "internal_error", false),
64     INAPPROPRIATE_FALLBACK  ((byte)86,  "inappropriate_fallback", false),
65     USER_CANCELED           ((byte)90,  "user_canceled", false),
66     NO_RENEGOTIATION        ((byte)100, "no_renegotiation", true),
67     MISSING_EXTENSION       ((byte)109, "missing_extension", true),
68     UNSUPPORTED_EXTENSION   ((byte)110, "unsupported_extension", true),
69     CERT_UNOBTAINABLE       ((byte)111, "certificate_unobtainable", true),
70     UNRECOGNIZED_NAME       ((byte)112, "unrecognized_name", true),
71     BAD_CERT_STATUS_RESPONSE((byte)113,
72                                     "bad_certificate_status_response", true),
73     BAD_CERT_HASH_VALUE     ((byte)114, "bad_certificate_hash_value", true),
74     UNKNOWN_PSK_IDENTITY    ((byte)115, "unknown_psk_identity", true),
75     CERTIFICATE_REQUIRED    ((byte)116, "certificate_required", true),
76     NO_APPLICATION_PROTOCOL ((byte)120, "no_application_protocol", true);
77 
78     // ordinal value of the Alert
79     final byte id;
80 
81     // description of the Alert
82     final String description;
83 
84     // Does tha alert happen during handshake only?
85     final boolean handshakeOnly;
86 
87     // Alert message consumer
88     static final SSLConsumer alertConsumer = new AlertConsumer();
89 
Alert(byte id, String description, boolean handshakeOnly)90     private Alert(byte id, String description, boolean handshakeOnly) {
91         this.id = id;
92         this.description = description;
93         this.handshakeOnly = handshakeOnly;
94     }
95 
valueOf(byte id)96     static Alert valueOf(byte id) {
97         for (Alert al : Alert.values()) {
98             if (al.id == id) {
99                 return al;
100             }
101         }
102 
103         return null;
104     }
105 
nameOf(byte id)106     static String nameOf(byte id) {
107         for (Alert al : Alert.values()) {
108             if (al.id == id) {
109                 return al.description;
110             }
111         }
112 
113         return "UNKNOWN ALERT (" + (id & 0x0FF) + ")";
114     }
115 
createSSLException(String reason)116     SSLException createSSLException(String reason) {
117         return createSSLException(reason, null);
118     }
119 
createSSLException(String reason, Throwable cause)120     SSLException createSSLException(String reason, Throwable cause) {
121         if (reason == null) {
122             reason = (cause != null) ? cause.getMessage() : "";
123         }
124 
125         SSLException ssle;
126         if ((cause != null) && (cause instanceof IOException)) {
127             ssle = new SSLException(reason);
128         } else if ((this == UNEXPECTED_MESSAGE)) {
129             ssle = new SSLProtocolException(reason);
130         } else if (handshakeOnly) {
131             ssle = new SSLHandshakeException(reason);
132         } else {
133             ssle = new SSLException(reason);
134         }
135 
136         if (cause != null) {
137             ssle.initCause(cause);
138         }
139 
140         return ssle;
141     }
142 
143     /**
144      * SSL/(D)TLS Alert level.
145      */
146     enum Level {
147         WARNING ((byte)1, "warning"),
148         FATAL   ((byte)2, "fatal");
149 
150         // ordinal value of the Alert level
151         final byte level;
152 
153         // description of the Alert level
154         final String description;
155 
Level(byte level, String description)156         private Level(byte level, String description) {
157             this.level = level;
158             this.description = description;
159         }
160 
valueOf(byte level)161         static Level valueOf(byte level) {
162             for (Level lv : Level.values()) {
163                 if (lv.level == level) {
164                     return lv;
165                 }
166             }
167 
168             return null;
169         }
170 
nameOf(byte level)171         static String nameOf(byte level) {
172             for (Level lv : Level.values()) {
173                 if (lv.level == level) {
174                     return lv.description;
175                 }
176             }
177 
178             return "UNKNOWN ALERT LEVEL (" + (level & 0x0FF) + ")";
179         }
180     }
181 
182     /**
183      * The Alert message.
184      */
185     private static final class AlertMessage {
186         private final byte level;       // level
187         private final byte id;          // description
188 
AlertMessage(TransportContext context, ByteBuffer m)189         AlertMessage(TransportContext context,
190                 ByteBuffer m) throws IOException {
191             //  struct {
192             //      AlertLevel level;
193             //      AlertDescription description;
194             //  } Alert;
195             if (m.remaining() != 2) {
196                 throw context.fatal(Alert.ILLEGAL_PARAMETER,
197                     "Invalid Alert message: no sufficient data");
198             }
199 
200             this.level = m.get();   // level
201             this.id = m.get();      // description
202         }
203 
204         @Override
toString()205         public String toString() {
206             MessageFormat messageFormat = new MessageFormat(
207                     "\"Alert\": '{'\n" +
208                     "  \"level\"      : \"{0}\",\n" +
209                     "  \"description\": \"{1}\"\n" +
210                     "'}'",
211                     Locale.ENGLISH);
212 
213             Object[] messageFields = {
214                 Level.nameOf(level),
215                 Alert.nameOf(id)
216             };
217 
218             return messageFormat.format(messageFields);
219         }
220     }
221 
222     /**
223      * Consumer of alert messages
224      */
225     private static final class AlertConsumer implements SSLConsumer {
226         // Prevent instantiation of this class.
AlertConsumer()227         private AlertConsumer() {
228             // blank
229         }
230 
231         @Override
consume(ConnectionContext context, ByteBuffer m)232         public void consume(ConnectionContext context,
233                 ByteBuffer m) throws IOException {
234             TransportContext tc = (TransportContext)context;
235 
236             AlertMessage am = new AlertMessage(tc, m);
237             if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
238                 SSLLogger.fine("Received alert message", am);
239             }
240 
241             Level level = Level.valueOf(am.level);
242             Alert alert = Alert.valueOf(am.id);
243             if (alert == Alert.CLOSE_NOTIFY) {
244                 tc.isInputCloseNotified = true;
245                 tc.closeInbound();
246 
247                 if (tc.peerUserCanceled) {
248                     tc.closeOutbound();
249                 } else if (tc.handshakeContext != null) {
250                     throw tc.fatal(Alert.UNEXPECTED_MESSAGE,
251                             "Received close_notify during handshake");
252                 }
253             } else if (alert == Alert.USER_CANCELED) {
254                 if (level == Level.WARNING) {
255                     tc.peerUserCanceled = true;
256                 } else {
257                     throw tc.fatal(alert,
258                             "Received fatal close_notify alert", true, null);
259                 }
260             } else if ((level == Level.WARNING) && (alert != null)) {
261                 // Terminate the connection if an alert with a level of warning
262                 // is received during handshaking, except the no_certificate
263                 // warning.
264                 if (alert.handshakeOnly && (tc.handshakeContext != null)) {
265                     // It's OK to get a no_certificate alert from a client of
266                     // which we requested client authentication.  However,
267                     // if we required it, then this is not acceptable.
268                     if (tc.sslConfig.isClientMode ||
269                             alert != Alert.NO_CERTIFICATE ||
270                             (tc.sslConfig.clientAuthType !=
271                                     ClientAuthType.CLIENT_AUTH_REQUESTED)) {
272                         throw tc.fatal(Alert.HANDSHAKE_FAILURE,
273                             "received handshake warning: " + alert.description);
274                     } else {
275                         // Otherwise ignore the warning but remove the
276                         // Certificate and CertificateVerify handshake
277                         // consumer so the state machine doesn't expect it.
278                         tc.handshakeContext.handshakeConsumers.remove(
279                                 SSLHandshake.CERTIFICATE.id);
280                         tc.handshakeContext.handshakeConsumers.remove(
281                                 SSLHandshake.CERTIFICATE_VERIFY.id);
282                     }
283                 }  // Otherwise, ignore the warning
284             } else {    // fatal or unknown
285                 String diagnostic;
286                 if (alert == null) {
287                     alert = Alert.UNEXPECTED_MESSAGE;
288                     diagnostic = "Unknown alert description (" + am.id + ")";
289                 } else {
290                     diagnostic = "Received fatal alert: " + alert.description;
291                 }
292 
293                 throw tc.fatal(alert, diagnostic, true, null);
294             }
295         }
296     }
297 }
298