• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2023 Huawei Device Co., Ltd.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 //     http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 use core::ffi::CStr;
15 use core::{ptr, str};
16 use std::borrow::Cow;
17 use std::error::Error;
18 #[cfg(feature = "c_openssl_3_0")]
19 use std::ffi::CString;
20 use std::fmt;
21 
22 #[cfg(feature = "c_openssl_1_1")]
23 use libc::c_char;
24 use libc::{c_int, c_ulong};
25 
26 use super::ssl_init;
27 #[cfg(feature = "c_openssl_3_0")]
28 use crate::util::c_openssl::ffi::err::ERR_get_error_all;
29 #[cfg(feature = "c_openssl_1_1")]
30 use crate::util::c_openssl::ffi::err::{ERR_func_error_string, ERR_get_error_line_data};
31 use crate::util::c_openssl::ffi::err::{ERR_lib_error_string, ERR_reason_error_string};
32 
33 const ERR_TXT_MALLOCED: c_int = 0x01;
34 const ERR_TXT_STRING: c_int = 0x02;
35 
36 /// An error reported from OpenSSL.
37 #[derive(Debug)]
38 pub(crate) struct StackError {
39     code: c_ulong,
40     #[cfg(feature = "c_openssl_1_1")]
41     file: *const c_char,
42     #[cfg(feature = "c_openssl_3_0")]
43     file: CString,
44     line: c_int,
45     #[cfg(feature = "c_openssl_3_0")]
46     func: Option<CString>,
47     data: Option<Cow<'static, str>>,
48 }
49 
50 impl Clone for StackError {
clone(&self) -> Self51     fn clone(&self) -> Self {
52         Self {
53             code: self.code,
54             #[cfg(feature = "c_openssl_1_1")]
55             file: self.file,
56             #[cfg(feature = "c_openssl_3_0")]
57             file: self.file.clone(),
58             line: self.line,
59             #[cfg(feature = "c_openssl_3_0")]
60             func: self.func.clone(),
61             data: self.data.clone(),
62         }
63     }
64 }
65 
66 impl StackError {
67     /// Returns the first error on the OpenSSL error stack.
get() -> Option<StackError>68     fn get() -> Option<StackError> {
69         unsafe {
70             ssl_init();
71 
72             let mut file = ptr::null();
73             let mut line = 0;
74             #[cfg(feature = "c_openssl_3_0")]
75             let mut func = ptr::null();
76             let mut data = ptr::null();
77             let mut flags = 0;
78 
79             #[cfg(feature = "c_openssl_1_1")]
80             match ERR_get_error_line_data(&mut file, &mut line, &mut data, &mut flags) {
81                 0 => None,
82                 code => {
83                     let data = if flags & ERR_TXT_STRING != 0 {
84                         let bytes = CStr::from_ptr(data as *const _).to_bytes();
85                         let data = str::from_utf8(bytes).unwrap_or("");
86                         let data = if flags & ERR_TXT_MALLOCED != 0 {
87                             Cow::Owned(data.to_string())
88                         } else {
89                             Cow::Borrowed(data)
90                         };
91                         Some(data)
92                     } else {
93                         None
94                     };
95                     Some(StackError {
96                         code,
97                         file,
98                         line,
99                         data,
100                     })
101                 }
102             }
103 
104             #[cfg(feature = "c_openssl_3_0")]
105             match ERR_get_error_all(&mut file, &mut line, &mut func, &mut data, &mut flags) {
106                 0 => None,
107                 code => {
108                     let data = if flags & ERR_TXT_STRING != 0 {
109                         let bytes = CStr::from_ptr(data as *const _).to_bytes();
110                         let data = str::from_utf8(bytes).unwrap();
111                         let data = if flags & ERR_TXT_MALLOCED != 0 {
112                             Cow::Owned(data.to_string())
113                         } else {
114                             Cow::Borrowed(data)
115                         };
116                         Some(data)
117                     } else {
118                         None
119                     };
120 
121                     let file = CStr::from_ptr(file).to_owned();
122                     let func = if func.is_null() {
123                         None
124                     } else {
125                         Some(CStr::from_ptr(func).to_owned())
126                     };
127                     Some(StackError {
128                         code,
129                         file,
130                         line,
131                         func,
132                         data,
133                     })
134                 }
135             }
136         }
137     }
138 
139     /// Returns the raw OpenSSL error code for this error.
code(&self) -> c_ulong140     fn code(&self) -> c_ulong {
141         self.code
142     }
143 }
144 
145 impl fmt::Display for StackError {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result146     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147         write!(f, "error:{:08X}", self.code())?;
148         unsafe {
149             let lib_error = ERR_lib_error_string(self.code);
150             if !lib_error.is_null() {
151                 let bytes = CStr::from_ptr(lib_error as *const _).to_bytes();
152                 write!(f, "lib: ({}), ", str::from_utf8(bytes).unwrap_or_default())?;
153             } else {
154                 write!(f, "lib: ({}), ", error_get_lib(self.code))?;
155             }
156         }
157 
158         #[cfg(feature = "c_openssl_1_1")]
159         {
160             let func_error = unsafe { ERR_func_error_string(self.code) };
161             if !func_error.is_null() {
162                 let bytes = unsafe { core::ffi::CStr::from_ptr(func_error as *const _).to_bytes() };
163                 write!(f, "func: ({}), ", str::from_utf8(bytes).unwrap_or_default())?;
164             } else {
165                 write!(f, "func: ({}), ", error_get_func(self.code))?;
166             }
167         }
168 
169         #[cfg(feature = "c_openssl_3_0")]
170         {
171             let func_error = self.func.as_ref().map(|s| s.to_str().unwrap_or_default());
172             match func_error {
173                 Some(s) => write!(f, ":{s}")?,
174                 None => write!(f, ":func({})", error_get_func(self.code))?,
175             }
176         }
177 
178         unsafe {
179             let reason_error = ERR_reason_error_string(self.code);
180             if !reason_error.is_null() {
181                 let bytes = CStr::from_ptr(reason_error as *const _).to_bytes();
182                 write!(
183                     f,
184                     "reason: ({}), ",
185                     str::from_utf8(bytes).unwrap_or_default()
186                 )?;
187             } else {
188                 write!(f, "reason: ({}), ", error_get_reason(self.code))?;
189             }
190         }
191         write!(
192             f,
193             ":{:?}:{}:{}",
194             self.file,
195             self.line,
196             self.data.as_deref().unwrap_or("")
197         )
198     }
199 }
200 
201 unsafe impl Sync for StackError {}
202 unsafe impl Send for StackError {}
203 
204 #[derive(Clone, Debug)]
205 pub struct ErrorStack(Vec<StackError>);
206 
207 impl fmt::Display for ErrorStack {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result208     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
209         if self.0.is_empty() {
210             return fmt.write_str("Error happened in OpenSSL");
211         }
212 
213         for err in &self.0 {
214             write!(fmt, "{err} ")?;
215         }
216         Ok(())
217     }
218 }
219 
220 impl Error for ErrorStack {}
221 
222 impl ErrorStack {
get() -> ErrorStack223     pub(crate) fn get() -> ErrorStack {
224         let mut vec = vec![];
225         while let Some(err) = StackError::get() {
226             vec.push(err);
227         }
228         ErrorStack(vec)
229     }
230 
errors(&self) -> &[StackError]231     pub(crate) fn errors(&self) -> &[StackError] {
232         &self.0
233     }
234 }
235 
236 #[cfg(feature = "c_openssl_3_0")]
237 const ERR_SYSTEM_FLAG: c_ulong = c_int::max_value() as c_ulong + 1;
238 #[cfg(feature = "c_openssl_3_0")]
error_system_error(code: c_ulong) -> bool239 const fn error_system_error(code: c_ulong) -> bool {
240     code & ERR_SYSTEM_FLAG != 0
241 }
242 
error_get_lib(code: c_ulong) -> c_int243 pub(crate) const fn error_get_lib(code: c_ulong) -> c_int {
244     #[cfg(feature = "c_openssl_1_1")]
245     return ((code >> 24) & 0x0FF) as c_int;
246 
247     #[cfg(feature = "c_openssl_3_0")]
248     return ((2 as c_ulong * (error_system_error(code) as c_ulong))
249         | (((code >> 23) & 0xFF) * (!error_system_error(code) as c_ulong))) as c_int;
250 }
251 
252 #[allow(unused_variables)]
error_get_func(code: c_ulong) -> c_int253 const fn error_get_func(code: c_ulong) -> c_int {
254     #[cfg(feature = "c_openssl_1_1")]
255     return ((code >> 12) & 0xFFF) as c_int;
256 
257     #[cfg(feature = "c_openssl_3_0")]
258     0
259 }
260 
error_get_reason(code: c_ulong) -> c_int261 pub(crate) const fn error_get_reason(code: c_ulong) -> c_int {
262     #[cfg(feature = "c_openssl_1_1")]
263     return (code & 0xFFF) as c_int;
264 
265     #[cfg(feature = "c_openssl_3_0")]
266     return ((2 as c_ulong * (error_system_error(code) as c_ulong))
267         | ((code & 0x7FFFFF) * (!error_system_error(code) as c_ulong))) as c_int;
268 }
269