b4456574529d43d2b1f8f1bacd6134ed157c6c06
[folly.git] / folly / io / async / ssl / SSLErrors.cpp
1 /*
2  * Copyright 2017 Facebook, Inc.
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 #include <folly/io/async/ssl/SSLErrors.h>
17
18 #include <folly/Range.h>
19 #include <openssl/err.h>
20 #include <openssl/ssl.h>
21
22 using namespace folly;
23
24 namespace {
25
26 std::string decodeOpenSSLError(
27     int sslError,
28     unsigned long errError,
29     int sslOperationReturnValue) {
30   if (sslError == SSL_ERROR_SYSCALL && errError == 0) {
31     if (sslOperationReturnValue == 0) {
32       return "Connection EOF";
33     } else {
34       // In this case errno is set, AsyncSocketException will add it.
35       return "Network error";
36     }
37   } else if (sslError == SSL_ERROR_ZERO_RETURN) {
38     // This signifies a TLS closure alert.
39     return "SSL connection closed normally";
40   } else {
41     std::array<char, 256> buf;
42     ERR_error_string_n(errError, buf.data(), buf.size());
43     // OpenSSL will null terminate the string.
44     return std::string(buf.data());
45   }
46 }
47
48 const StringPiece getSSLErrorString(SSLError error) {
49   StringPiece ret;
50   switch (error) {
51     case SSLError::CLIENT_RENEGOTIATION:
52       ret = "Client tried to renegotiate with server";
53       break;
54     case SSLError::INVALID_RENEGOTIATION:
55       ret = "Attempt to start renegotiation, but unsupported";
56       break;
57     case SSLError::EARLY_WRITE:
58       ret = "Attempt to write before SSL connection established";
59       break;
60     case SSLError::SSL_ERROR:
61       ret = "SSL error";
62       break;
63     case SSLError::NETWORK_ERROR:
64       ret = "Network error";
65       break;
66     case SSLError::EOF_ERROR:
67       ret = "SSL connection closed normally";
68       break;
69   }
70   return ret;
71 }
72
73 AsyncSocketException::AsyncSocketExceptionType exTypefromSSLErrInfo(
74     int sslErr,
75     unsigned long errError,
76     int sslOperationReturnValue) {
77   if (sslErr == SSL_ERROR_ZERO_RETURN) {
78     return AsyncSocketException::END_OF_FILE;
79   } else if (sslErr == SSL_ERROR_SYSCALL) {
80     if (errError == 0 && sslOperationReturnValue == 0) {
81       return AsyncSocketException::END_OF_FILE;
82     } else {
83       return AsyncSocketException::NETWORK_ERROR;
84     }
85   } else {
86     // Assume an actual SSL error
87     return AsyncSocketException::SSL_ERROR;
88   }
89 }
90
91 AsyncSocketException::AsyncSocketExceptionType exTypefromSSLErr(SSLError err) {
92   switch (err) {
93     case SSLError::EOF_ERROR:
94       return AsyncSocketException::END_OF_FILE;
95     case SSLError::NETWORK_ERROR:
96       return AsyncSocketException::NETWORK_ERROR;
97     default:
98       // everything else is a SSL_ERROR
99       return AsyncSocketException::SSL_ERROR;
100   }
101 }
102 }
103
104 namespace folly {
105
106 SSLException::SSLException(
107     int sslErr,
108     unsigned long errError,
109     int sslOperationReturnValue,
110     int errno_copy)
111     : AsyncSocketException(
112           exTypefromSSLErrInfo(sslErr, errError, sslOperationReturnValue),
113           decodeOpenSSLError(sslErr, errError, sslOperationReturnValue),
114           sslErr == SSL_ERROR_SYSCALL ? errno_copy : 0) {
115   if (sslErr == SSL_ERROR_ZERO_RETURN) {
116     sslError = SSLError::EOF_ERROR;
117   } else if (sslErr == SSL_ERROR_SYSCALL) {
118     sslError = SSLError::NETWORK_ERROR;
119   } else {
120     // Conservatively assume that this is an SSL error
121     sslError = SSLError::SSL_ERROR;
122   }
123 }
124
125 SSLException::SSLException(SSLError error)
126     : AsyncSocketException(
127           exTypefromSSLErr(error),
128           getSSLErrorString(error).str(),
129           0),
130       sslError(error) {}
131 }