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