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 
270 pub(crate) struct VerifyError {
271     kind: VerifyKind,
272     cause: Reason,
273 }
274 
275 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
276 pub(crate) enum VerifyKind {
277     PubKeyPinning,
278 }
279 
280 pub(crate) enum Reason {
281     Msg(&'static str),
282 }
283 
284 impl VerifyError {
from_msg(kind: VerifyKind, msg: &'static str) -> Self285     pub(crate) fn from_msg(kind: VerifyKind, msg: &'static str) -> Self {
286         Self {
287             kind,
288             cause: Reason::Msg(msg),
289         }
290     }
291 }
292 
293 impl VerifyKind {
as_str(&self) -> &'static str294     pub fn as_str(&self) -> &'static str {
295         match self {
296             Self::PubKeyPinning => "Public Key Pinning Error",
297         }
298     }
299 }
300 
301 impl fmt::Debug for VerifyError {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result302     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
303         let mut builder = f.debug_struct("VerifyError");
304         builder.field("ErrorKind", &self.kind);
305         builder.field("Cause", &self.cause);
306         builder.finish()
307     }
308 }
309 
310 impl fmt::Display for VerifyError {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result311     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
312         f.write_str(self.kind.as_str())?;
313         write!(f, ": {}", self.cause)?;
314         Ok(())
315     }
316 }
317 
318 impl fmt::Debug for Reason {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result319     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
320         match self {
321             Self::Msg(msg) => write!(f, "{}", msg),
322         }
323     }
324 }
325 
326 impl fmt::Display for Reason {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result327     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
328         match self {
329             Self::Msg(msg) => write!(f, "{}", msg),
330         }
331     }
332 }
333 
334 impl Error for VerifyError {}
335 
336 #[cfg(test)]
337 mod ut_c_openssl_error {
338     use crate::util::c_openssl::error::{VerifyError, VerifyKind};
339 
340     /// UT test cases for `VerifyKind::as_str`.
341     ///
342     /// # Brief
343     /// 1. Transfer ErrorKind to str a by calling `VerifyKind::as_str`.
344     /// 2. Checks if the results are correct.
345     #[test]
ut_verify_err_as_str()346     fn ut_verify_err_as_str() {
347         assert_eq!(
348             VerifyKind::PubKeyPinning.as_str(),
349             "Public Key Pinning Error"
350         );
351     }
352 
353     /// UT test cases for `VerifyKind::from` function.
354     ///
355     /// # Brief
356     /// 1. Calls `VerifyKind::from`.
357     /// 2. Checks if the results are correct.
358     #[test]
ut_verify_err_from()359     fn ut_verify_err_from() {
360         let error = VerifyError::from_msg(VerifyKind::PubKeyPinning, "error");
361         assert_eq!(
362             format!("{:?}", error),
363             "VerifyError { ErrorKind: PubKeyPinning, Cause: error }"
364         );
365         assert_eq!(format!("{error}"), "Public Key Pinning Error: error");
366     }
367 }
368