| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
package sun.security.ssl;  | 
 | 
 | 
 | 
import java.io.IOException;  | 
 | 
import java.nio.ByteBuffer;  | 
 | 
import java.text.MessageFormat;  | 
 | 
import java.util.Locale;  | 
 | 
import javax.net.ssl.SSLException;  | 
 | 
import javax.net.ssl.SSLHandshakeException;  | 
 | 
import javax.net.ssl.SSLProtocolException;  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
enum Alert { | 
 | 
    // Please refer to TLS Alert Registry for the latest TLS Alert values:  | 
 | 
      | 
 | 
    CLOSE_NOTIFY            ((byte)0,   "close_notify", false),  | 
 | 
    UNEXPECTED_MESSAGE      ((byte)10,  "unexpected_message", false),  | 
 | 
    BAD_RECORD_MAC          ((byte)20,  "bad_record_mac", false),  | 
 | 
    DECRYPTION_FAILED       ((byte)21,  "decryption_failed", false),  | 
 | 
    RECORD_OVERFLOW         ((byte)22,  "record_overflow", false),  | 
 | 
    DECOMPRESSION_FAILURE   ((byte)30,  "decompression_failure", false),  | 
 | 
    HANDSHAKE_FAILURE       ((byte)40,  "handshake_failure", true),  | 
 | 
    NO_CERTIFICATE          ((byte)41,  "no_certificate", true),  | 
 | 
    BAD_CERTIFICATE         ((byte)42,  "bad_certificate", true),  | 
 | 
    UNSUPPORTED_CERTIFICATE ((byte)43,  "unsupported_certificate", true),  | 
 | 
    CERTIFICATE_REVOKED     ((byte)44,  "certificate_revoked", true),  | 
 | 
    CERTIFICATE_EXPIRED     ((byte)45,  "certificate_expired", true),  | 
 | 
    CERTIFICATE_UNKNOWN     ((byte)46,  "certificate_unknown", true),  | 
 | 
    ILLEGAL_PARAMETER       ((byte)47,  "illegal_parameter", true),  | 
 | 
    UNKNOWN_CA              ((byte)48,  "unknown_ca", true),  | 
 | 
    ACCESS_DENIED           ((byte)49,  "access_denied", true),  | 
 | 
    DECODE_ERROR            ((byte)50,  "decode_error", true),  | 
 | 
    DECRYPT_ERROR           ((byte)51,  "decrypt_error", true),  | 
 | 
    EXPORT_RESTRICTION      ((byte)60,  "export_restriction", true),  | 
 | 
    PROTOCOL_VERSION        ((byte)70,  "protocol_version", true),  | 
 | 
    INSUFFICIENT_SECURITY   ((byte)71,  "insufficient_security", true),  | 
 | 
    INTERNAL_ERROR          ((byte)80,  "internal_error", false),  | 
 | 
    INAPPROPRIATE_FALLBACK  ((byte)86,  "inappropriate_fallback", false),  | 
 | 
    USER_CANCELED           ((byte)90,  "user_canceled", false),  | 
 | 
    NO_RENEGOTIATION        ((byte)100, "no_renegotiation", true),  | 
 | 
    MISSING_EXTENSION       ((byte)109, "missing_extension", true),  | 
 | 
    UNSUPPORTED_EXTENSION   ((byte)110, "unsupported_extension", true),  | 
 | 
    CERT_UNOBTAINABLE       ((byte)111, "certificate_unobtainable", true),  | 
 | 
    UNRECOGNIZED_NAME       ((byte)112, "unrecognized_name", true),  | 
 | 
    BAD_CERT_STATUS_RESPONSE((byte)113,  | 
 | 
                                    "bad_certificate_status_response", true),  | 
 | 
    BAD_CERT_HASH_VALUE     ((byte)114, "bad_certificate_hash_value", true),  | 
 | 
    UNKNOWN_PSK_IDENTITY    ((byte)115, "unknown_psk_identity", true),  | 
 | 
    CERTIFICATE_REQUIRED    ((byte)116, "certificate_required", true),  | 
 | 
    NO_APPLICATION_PROTOCOL ((byte)120, "no_application_protocol", true);  | 
 | 
 | 
 | 
      | 
 | 
    final byte id;  | 
 | 
 | 
 | 
      | 
 | 
    final String description;  | 
 | 
 | 
 | 
      | 
 | 
    final boolean handshakeOnly;  | 
 | 
 | 
 | 
      | 
 | 
    static final SSLConsumer alertConsumer = new AlertConsumer();  | 
 | 
 | 
 | 
    private Alert(byte id, String description, boolean handshakeOnly) { | 
 | 
        this.id = id;  | 
 | 
        this.description = description;  | 
 | 
        this.handshakeOnly = handshakeOnly;  | 
 | 
    }  | 
 | 
 | 
 | 
    static Alert valueOf(byte id) { | 
 | 
        for (Alert al : Alert.values()) { | 
 | 
            if (al.id == id) { | 
 | 
                return al;  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        return null;  | 
 | 
    }  | 
 | 
 | 
 | 
    static String nameOf(byte id) { | 
 | 
        for (Alert al : Alert.values()) { | 
 | 
            if (al.id == id) { | 
 | 
                return al.description;  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        return "UNKNOWN ALERT (" + (id & 0x0FF) + ")"; | 
 | 
    }  | 
 | 
 | 
 | 
    SSLException createSSLException(String reason) { | 
 | 
        return createSSLException(reason, null);  | 
 | 
    }  | 
 | 
 | 
 | 
    SSLException createSSLException(String reason, Throwable cause) { | 
 | 
        if (reason == null) { | 
 | 
            reason = (cause != null) ? cause.getMessage() : "";  | 
 | 
        }  | 
 | 
 | 
 | 
        SSLException ssle;  | 
 | 
        if ((cause != null) && (cause instanceof IOException)) { | 
 | 
            ssle = new SSLException(reason);  | 
 | 
        } else if ((this == UNEXPECTED_MESSAGE)) { | 
 | 
            ssle = new SSLProtocolException(reason);  | 
 | 
        } else if (handshakeOnly) { | 
 | 
            ssle = new SSLHandshakeException(reason);  | 
 | 
        } else { | 
 | 
            ssle = new SSLException(reason);  | 
 | 
        }  | 
 | 
 | 
 | 
        if (cause != null) { | 
 | 
            ssle.initCause(cause);  | 
 | 
        }  | 
 | 
 | 
 | 
        return ssle;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    enum Level { | 
 | 
        WARNING ((byte)1, "warning"),  | 
 | 
        FATAL   ((byte)2, "fatal");  | 
 | 
 | 
 | 
          | 
 | 
        final byte level;  | 
 | 
 | 
 | 
          | 
 | 
        final String description;  | 
 | 
 | 
 | 
        private Level(byte level, String description) { | 
 | 
            this.level = level;  | 
 | 
            this.description = description;  | 
 | 
        }  | 
 | 
 | 
 | 
        static Level valueOf(byte level) { | 
 | 
            for (Level lv : Level.values()) { | 
 | 
                if (lv.level == level) { | 
 | 
                    return lv;  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            return null;  | 
 | 
        }  | 
 | 
 | 
 | 
        static String nameOf(byte level) { | 
 | 
            for (Level lv : Level.values()) { | 
 | 
                if (lv.level == level) { | 
 | 
                    return lv.description;  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            return "UNKNOWN ALERT LEVEL (" + (level & 0x0FF) + ")"; | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private static final class AlertMessage { | 
 | 
        private final byte level;         | 
 | 
        private final byte id;            | 
 | 
 | 
 | 
        AlertMessage(TransportContext context,  | 
 | 
                ByteBuffer m) throws IOException { | 
 | 
            //  struct { | 
 | 
            //      AlertLevel level;  | 
 | 
            //      AlertDescription description;  | 
 | 
              | 
 | 
            if (m.remaining() != 2) { | 
 | 
                throw context.fatal(Alert.ILLEGAL_PARAMETER,  | 
 | 
                    "Invalid Alert message: no sufficient data");  | 
 | 
            }  | 
 | 
 | 
 | 
            this.level = m.get();     | 
 | 
            this.id = m.get();        | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public String toString() { | 
 | 
            MessageFormat messageFormat = new MessageFormat(  | 
 | 
                    "\"Alert\": '{'\n" + | 
 | 
                    "  \"level\"      : \"{0}\",\n" + | 
 | 
                    "  \"description\": \"{1}\"\n" + | 
 | 
                    "'}'",  | 
 | 
                    Locale.ENGLISH);  | 
 | 
 | 
 | 
            Object[] messageFields = { | 
 | 
                Level.nameOf(level),  | 
 | 
                Alert.nameOf(id)  | 
 | 
            };  | 
 | 
 | 
 | 
            return messageFormat.format(messageFields);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private static final class AlertConsumer implements SSLConsumer { | 
 | 
          | 
 | 
        private AlertConsumer() { | 
 | 
            // blank  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public void consume(ConnectionContext context,  | 
 | 
                ByteBuffer m) throws IOException { | 
 | 
            TransportContext tc = (TransportContext)context;  | 
 | 
 | 
 | 
            AlertMessage am = new AlertMessage(tc, m);  | 
 | 
            if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { | 
 | 
                SSLLogger.fine("Received alert message", am); | 
 | 
            }  | 
 | 
 | 
 | 
            Level level = Level.valueOf(am.level);  | 
 | 
            Alert alert = Alert.valueOf(am.id);  | 
 | 
            if (alert == Alert.CLOSE_NOTIFY) { | 
 | 
                tc.isInputCloseNotified = true;  | 
 | 
                tc.closeInbound();  | 
 | 
 | 
 | 
                if (tc.peerUserCanceled) { | 
 | 
                    tc.closeOutbound();  | 
 | 
                } else if (tc.handshakeContext != null) { | 
 | 
                    throw tc.fatal(Alert.UNEXPECTED_MESSAGE,  | 
 | 
                            "Received close_notify during handshake");  | 
 | 
                }  | 
 | 
            } else if (alert == Alert.USER_CANCELED) { | 
 | 
                if (level == Level.WARNING) { | 
 | 
                    tc.peerUserCanceled = true;  | 
 | 
                } else { | 
 | 
                    throw tc.fatal(alert,  | 
 | 
                            "Received fatal close_notify alert", true, null);  | 
 | 
                }  | 
 | 
            } else if ((level == Level.WARNING) && (alert != null)) { | 
 | 
                // Terminate the connection if an alert with a level of warning  | 
 | 
                // is received during handshaking, except the no_certificate  | 
 | 
                  | 
 | 
                if (alert.handshakeOnly && (tc.handshakeContext != null)) { | 
 | 
                    // It's OK to get a no_certificate alert from a client of  | 
 | 
                    // which we requested client authentication.  However,  | 
 | 
                      | 
 | 
                    if (tc.sslConfig.isClientMode ||  | 
 | 
                            alert != Alert.NO_CERTIFICATE ||  | 
 | 
                            (tc.sslConfig.clientAuthType !=  | 
 | 
                                    ClientAuthType.CLIENT_AUTH_REQUESTED)) { | 
 | 
                        throw tc.fatal(Alert.HANDSHAKE_FAILURE,  | 
 | 
                            "received handshake warning: " + alert.description);  | 
 | 
                    } else { | 
 | 
                        // Otherwise ignore the warning but remove the  | 
 | 
                        // Certificate and CertificateVerify handshake  | 
 | 
                          | 
 | 
                        tc.handshakeContext.handshakeConsumers.remove(  | 
 | 
                                SSLHandshake.CERTIFICATE.id);  | 
 | 
                        tc.handshakeContext.handshakeConsumers.remove(  | 
 | 
                                SSLHandshake.CERTIFICATE_VERIFY.id);  | 
 | 
                    }  | 
 | 
                }  // Otherwise, ignore the warning  | 
 | 
            } else {     | 
 | 
                String diagnostic;  | 
 | 
                if (alert == null) { | 
 | 
                    alert = Alert.UNEXPECTED_MESSAGE;  | 
 | 
                    diagnostic = "Unknown alert description (" + am.id + ")"; | 
 | 
                } else { | 
 | 
                    diagnostic = "Received fatal alert: " + alert.description;  | 
 | 
                }  | 
 | 
 | 
 | 
                throw tc.fatal(alert, diagnostic, true, null);  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
}  |