1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2007-2020. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20 21%% 22%%---------------------------------------------------------------------- 23%% Purpose: Handles an ssl connection, e.i. both the setup 24%% e.i. SSL-Handshake, SSL-Alert and SSL-Cipher protocols and delivering 25%% data to the application. All data on the connectinon is received and 26%% sent according to the SSL-record protocol. 27%% %%---------------------------------------------------------------------- 28 29-module(ssl_alert). 30 31-include("ssl_alert.hrl"). 32-include("ssl_record.hrl"). 33-include("ssl_internal.hrl"). 34 35-export([decode/1, 36 own_alert_format/4, 37 alert_format/4, 38 reason_code/4]). 39 40%%==================================================================== 41%% Internal application API 42%%==================================================================== 43 44%%-------------------------------------------------------------------- 45-spec decode(binary()) -> [#alert{}] | #alert{}. 46%% 47%% Description: Decode alert(s), will return a singel own alert if peer 48%% sends garbage or too many warning alerts. 49%%-------------------------------------------------------------------- 50decode(Bin) -> 51 decode(Bin, [], 0). 52 53%%-------------------------------------------------------------------- 54-spec reason_code(#alert{}, client | server, ProtocolName::string(), StateName::atom()) -> 55 {tls_alert, {atom(), unicode:chardata()}} | closed. 56%% 57%% Description: Returns the error reason that will be returned to the 58%% user. 59%%-------------------------------------------------------------------- 60 61reason_code(#alert{description = ?CLOSE_NOTIFY}, _, _, _) -> 62 closed; 63reason_code(#alert{description = Description, role = Role} = Alert, Role, ProtocolName, StateName) -> 64 {PrefixFmt, PrefixArgs} = alert_prefix_format(ProtocolName, Role, StateName), 65 {Fmt, Args} = own_alert_format_depth(Alert), 66 Txt = lists:flatten(io_lib:format(PrefixFmt ++ Fmt, PrefixArgs ++ Args)), 67 {tls_alert, {description_atom(Description), Txt}}; 68reason_code(#alert{description = Description} = Alert, Role, ProtocolName, StateName) -> 69 {Fmt, Args} = alert_format(ProtocolName, Role, StateName, Alert), 70 Txt = lists:flatten(io_lib:format(Fmt, Args)), 71 {tls_alert, {description_atom(Description), Txt}}. 72 73%%-------------------------------------------------------------------- 74-spec own_alert_format(string(), server | client, StateNam::atom(), #alert{}) -> {io:format(), list()}. 75%% 76%% Description: Generates alert text for log or string part of error return. 77%%-------------------------------------------------------------------- 78own_alert_format(ProtocolName, Role, StateName, Alert) -> 79 {PrfixFmt, PrefixArgs} = alert_prefix_format(ProtocolName, Role, StateName), 80 {Fmt, Args} = own_alert_format(Alert), 81 {PrfixFmt ++ Fmt, PrefixArgs ++ Args}. 82 83%%-------------------------------------------------------------------- 84-spec alert_format(string(), server | client, StateNam::atom(), #alert{}) -> {io:format(), list()}. 85%% 86%% Description: Generates alert text for log or string part of error return. 87%%-------------------------------------------------------------------- 88alert_format(ProtocolName, Role, StateName, Alert) -> 89 {PrfixFmt, PrefixArgs} = alert_prefix_format(ProtocolName, Role, StateName), 90 {Fmt, Args} = alert_format(Alert), 91 {PrfixFmt ++ Fmt, PrefixArgs ++ Args}. 92 93%%-------------------------------------------------------------------- 94%%% Internal functions 95%%-------------------------------------------------------------------- 96alert_prefix_format(ProtocolName, Role, StateName) -> 97 {"~s ~p: In state ~p", [ProtocolName, Role, StateName]}. 98 99own_alert_format(#alert{reason = Reason} = Alert) -> 100 Txt = own_alert_txt(Alert), 101 case Reason of 102 undefined -> 103 {" ~s\n", [Txt]}; 104 Reason -> 105 {" ~s\n - ~p", [Txt, Reason]} 106 end. 107own_alert_format_depth(#alert{reason = Reason} = Alert) -> 108 Txt = own_alert_txt(Alert), 109 case Reason of 110 undefined -> 111 {" ~s\n", [Txt]}; 112 Reason -> 113 {" ~s\n ~P", [Txt, Reason, ?DEPTH]} 114 end. 115 116own_alert_txt(#alert{level = Level, description = Description, where = #{line := Line, file := Mod}, role = Role}) -> 117 "at " ++ Mod ++ ":" ++ integer_to_list(Line) ++ " generated " ++ string:uppercase(atom_to_list(Role)) ++ " ALERT: " ++ 118 level_txt(Level) ++ description_txt(Description). 119 120alert_format(Alert) -> 121 Txt = alert_txt(Alert), 122 {" ~s\n", [Txt]}. 123 124alert_txt(#alert{level = Level, description = Description, role = Role}) -> 125 "received " ++ string:uppercase(atom_to_list(Role)) ++ " ALERT: " ++ 126 level_txt(Level) ++ description_txt(Description). 127 128%% It is very unlikely that an correct implementation will send more than one alert at the time 129%% So it there is more than 10 warning alerts we consider it an error 130decode(<<?BYTE(Level), ?BYTE(_), _/binary>>, _, N) when Level == ?WARNING, N > ?MAX_ALERTS -> 131 ?ALERT_REC(?FATAL, ?DECODE_ERROR, too_many_remote_alerts); 132decode(<<?BYTE(Level), ?BYTE(Description), Rest/binary>>, Acc, N) when Level == ?WARNING -> 133 Alert = ?ALERT_REC(Level, Description), 134 decode(Rest, [Alert | Acc], N + 1); 135decode(<<?BYTE(Level), ?BYTE(Description), _Rest/binary>>, Acc, _) when Level == ?FATAL-> 136 Alert = ?ALERT_REC(Level, Description), 137 lists:reverse([Alert | Acc]); %% No need to decode rest fatal alert will end the connection 138decode(<<?BYTE(_Level), _/binary>>, _, _) -> 139 ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER, failed_to_decode_remote_alert); 140decode(<<>>, Acc, _) -> 141 lists:reverse(Acc, []). 142 143level_txt(?WARNING) -> 144 "Warning - "; 145level_txt(?FATAL) -> 146 "Fatal - ". 147 148description_txt(?CLOSE_NOTIFY) -> 149 "Close Notify"; 150description_txt(?UNEXPECTED_MESSAGE) -> 151 "Unexpected Message"; 152description_txt(?BAD_RECORD_MAC) -> 153 "Bad Record MAC"; 154description_txt(?DECRYPTION_FAILED_RESERVED) -> 155 "Decryption Failed Reserved"; 156description_txt(?RECORD_OVERFLOW) -> 157 "Record Overflow"; 158description_txt(?DECOMPRESSION_FAILURE) -> 159 "Decompression Failure"; 160description_txt(?HANDSHAKE_FAILURE) -> 161 "Handshake Failure"; 162description_txt(?NO_CERTIFICATE_RESERVED) -> 163 "No Certificate Reserved"; 164description_txt(?BAD_CERTIFICATE) -> 165 "Bad Certificate"; 166description_txt(?UNSUPPORTED_CERTIFICATE) -> 167 "Unsupported Certificate"; 168description_txt(?CERTIFICATE_REVOKED) -> 169 "Certificate Revoked"; 170description_txt(?CERTIFICATE_EXPIRED) -> 171 "Certificate Expired"; 172description_txt(?CERTIFICATE_UNKNOWN) -> 173 "Certificate Unknown"; 174description_txt(?ILLEGAL_PARAMETER) -> 175 "Illegal Parameter"; 176description_txt(?UNKNOWN_CA) -> 177 "Unknown CA"; 178description_txt(?ACCESS_DENIED) -> 179 "Access Denied"; 180description_txt(?DECODE_ERROR) -> 181 "Decode Error"; 182description_txt(?DECRYPT_ERROR) -> 183 "Decrypt Error"; 184description_txt(?EXPORT_RESTRICTION) -> 185 "Export Restriction"; 186description_txt(?PROTOCOL_VERSION) -> 187 "Protocol Version"; 188description_txt(?INSUFFICIENT_SECURITY) -> 189 "Insufficient Security"; 190description_txt(?INTERNAL_ERROR) -> 191 "Internal Error"; 192description_txt(?INAPPROPRIATE_FALLBACK) -> 193 "Inappropriate Fallback"; 194description_txt(?USER_CANCELED) -> 195 "User Canceled"; 196description_txt(?NO_RENEGOTIATION) -> 197 "No Renegotiation"; 198description_txt(?MISSING_EXTENSION) -> 199 "Missing extension"; 200description_txt(?UNSUPPORTED_EXTENSION) -> 201 "Unsupported Extension"; 202description_txt(?CERTIFICATE_UNOBTAINABLE) -> 203 "Certificate Unobtainable"; 204description_txt(?UNRECOGNIZED_NAME) -> 205 "Unrecognized Name"; 206description_txt(?BAD_CERTIFICATE_STATUS_RESPONSE) -> 207 "Bad Certificate Status Response"; 208description_txt(?BAD_CERTIFICATE_HASH_VALUE) -> 209 "Bad Certificate Hash Value"; 210description_txt(?UNKNOWN_PSK_IDENTITY) -> 211 "Unknown Psk Identity"; 212description_txt(?CERTIFICATE_REQUIRED) -> 213 "Certificate required"; 214description_txt(?NO_APPLICATION_PROTOCOL) -> 215 "No application protocol"; 216description_txt(Enum) -> 217 lists:flatten(io_lib:format("unsupported/unknown alert: ~p", [Enum])). 218 219description_atom(?CLOSE_NOTIFY) -> 220 close_notify; 221description_atom(?UNEXPECTED_MESSAGE) -> 222 unexpected_message; 223description_atom(?BAD_RECORD_MAC) -> 224 bad_record_mac; 225description_atom(?DECRYPTION_FAILED_RESERVED) -> 226 decryption_failed_reserved; 227description_atom(?RECORD_OVERFLOW) -> 228 record_overflow; 229description_atom(?DECOMPRESSION_FAILURE) -> 230 decompression_failure; 231description_atom(?HANDSHAKE_FAILURE) -> 232 handshake_failure; 233description_atom(?NO_CERTIFICATE_RESERVED) -> 234 no_certificate_reserved; 235description_atom(?BAD_CERTIFICATE) -> 236 bad_certificate; 237description_atom(?UNSUPPORTED_CERTIFICATE) -> 238 unsupported_certificate; 239description_atom(?CERTIFICATE_REVOKED) -> 240 certificate_revoked; 241description_atom(?CERTIFICATE_EXPIRED) -> 242 certificate_expired; 243description_atom(?CERTIFICATE_UNKNOWN) -> 244 certificate_unknown; 245description_atom(?ILLEGAL_PARAMETER) -> 246 illegal_parameter; 247description_atom(?UNKNOWN_CA) -> 248 unknown_ca; 249description_atom(?ACCESS_DENIED) -> 250 access_denied; 251description_atom(?DECODE_ERROR) -> 252 decode_error; 253description_atom(?DECRYPT_ERROR) -> 254 decrypt_error; 255description_atom(?EXPORT_RESTRICTION) -> 256 export_restriction; 257description_atom(?PROTOCOL_VERSION) -> 258 protocol_version; 259description_atom(?INSUFFICIENT_SECURITY) -> 260 insufficient_security; 261description_atom(?INTERNAL_ERROR) -> 262 internal_error; 263description_atom(?INAPPROPRIATE_FALLBACK) -> 264 inappropriate_fallback; 265description_atom(?USER_CANCELED) -> 266 user_canceled; 267description_atom(?NO_RENEGOTIATION) -> 268 no_renegotiation; 269description_atom(?MISSING_EXTENSION) -> 270 missing_extension; 271description_atom(?UNSUPPORTED_EXTENSION) -> 272 unsupported_extension; 273description_atom(?CERTIFICATE_UNOBTAINABLE) -> 274 certificate_unobtainable; 275description_atom(?UNRECOGNIZED_NAME) -> 276 unrecognized_name; 277description_atom(?BAD_CERTIFICATE_STATUS_RESPONSE) -> 278 bad_certificate_status_response; 279description_atom(?BAD_CERTIFICATE_HASH_VALUE) -> 280 bad_certificate_hash_value; 281description_atom(?UNKNOWN_PSK_IDENTITY) -> 282 unknown_psk_identity; 283description_atom(?CERTIFICATE_REQUIRED) -> 284 certificate_required; 285description_atom(?NO_APPLICATION_PROTOCOL) -> 286 no_application_protocol; 287description_atom(_) -> 288 'unsupported/unknown_alert'. 289