1//! Errors returned by OpenSSL library.
2//!
3//! OpenSSL errors are stored in an `ErrorStack`. Most methods in the crate
4//! returns a `Result<T, ErrorStack>` type.
5//!
6//! # Examples
7//!
8//! ```
9//! use openssl::error::ErrorStack;
10//! use openssl::bn::BigNum;
11//!
12//! let an_error = BigNum::from_dec_str("Cannot parse letters");
13//! match an_error {
14//! Ok(_) => (),
15//! Err(e) => println!("Parsing Error: {:?}", e),
16//! }
17//! ```
18use cfg_if::cfg_if;
19use libc::{c_char, c_int};
20use std::borrow::Cow;
21#[cfg(boringssl)]
22use std::convert::TryInto;
23use std::error;
24use std::ffi::CStr;
25use std::fmt;
26use std::io;
27use std::ptr;
28use std::str;
29
30#[cfg(not(boringssl))]
31type ErrType = libc::c_ulong;
32#[cfg(boringssl)]
33type ErrType = libc::c_uint;
34
35/// Collection of [`Error`]s from OpenSSL.
36///
37/// [`Error`]: struct.Error.html
38#[derive(Debug, Clone)]
39pub struct ErrorStack(Vec<Error>);
40
41impl ErrorStack {
42 /// Returns the contents of the OpenSSL error stack.
43 pub fn get() -> ErrorStack {
44 let mut vec: Vec = vec![];
45 while let Some(err: Error) = Error::get() {
46 vec.push(err);
47 }
48 ErrorStack(vec)
49 }
50
51 /// Pushes the errors back onto the OpenSSL error stack.
52 pub fn put(&self) {
53 for error: &Error in self.errors() {
54 error.put();
55 }
56 }
57}
58
59impl ErrorStack {
60 /// Returns the errors in the stack.
61 pub fn errors(&self) -> &[Error] {
62 &self.0
63 }
64}
65
66impl fmt::Display for ErrorStack {
67 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
68 if self.0.is_empty() {
69 return fmt.write_str(data:"OpenSSL error");
70 }
71
72 let mut first: bool = true;
73 for err: &Error in &self.0 {
74 if !first {
75 fmt.write_str(data:", ")?;
76 }
77 write!(fmt, "{}", err)?;
78 first = false;
79 }
80 Ok(())
81 }
82}
83
84impl error::Error for ErrorStack {}
85
86impl From<ErrorStack> for io::Error {
87 fn from(e: ErrorStack) -> io::Error {
88 io::Error::new(kind:io::ErrorKind::Other, error:e)
89 }
90}
91
92impl From<ErrorStack> for fmt::Error {
93 fn from(_: ErrorStack) -> fmt::Error {
94 fmt::Error
95 }
96}
97
98/// An error reported from OpenSSL.
99#[derive(Clone)]
100pub struct Error {
101 code: ErrType,
102 file: ShimStr,
103 line: c_int,
104 func: Option<ShimStr>,
105 data: Option<Cow<'static, str>>,
106}
107
108unsafe impl Sync for Error {}
109unsafe impl Send for Error {}
110
111impl Error {
112 /// Returns the first error on the OpenSSL error stack.
113 pub fn get() -> Option<Error> {
114 unsafe {
115 ffi::init();
116
117 let mut file = ptr::null();
118 let mut line = 0;
119 let mut func = ptr::null();
120 let mut data = ptr::null();
121 let mut flags = 0;
122 match ERR_get_error_all(&mut file, &mut line, &mut func, &mut data, &mut flags) {
123 0 => None,
124 code => {
125 // The memory referenced by data is only valid until that slot is overwritten
126 // in the error stack, so we'll need to copy it off if it's dynamic
127 let data = if flags & ffi::ERR_TXT_STRING != 0 {
128 let bytes = CStr::from_ptr(data as *const _).to_bytes();
129 let data = str::from_utf8(bytes).unwrap();
130 #[cfg(not(boringssl))]
131 let data = if flags & ffi::ERR_TXT_MALLOCED != 0 {
132 Cow::Owned(data.to_string())
133 } else {
134 Cow::Borrowed(data)
135 };
136 #[cfg(boringssl)]
137 let data = Cow::Borrowed(data);
138 Some(data)
139 } else {
140 None
141 };
142
143 let file = ShimStr::new(file);
144
145 let func = if func.is_null() {
146 None
147 } else {
148 Some(ShimStr::new(func))
149 };
150
151 Some(Error {
152 code,
153 file,
154 line,
155 func,
156 data,
157 })
158 }
159 }
160 }
161 }
162
163 /// Pushes the error back onto the OpenSSL error stack.
164 pub fn put(&self) {
165 self.put_error();
166
167 unsafe {
168 let data = match self.data {
169 Some(Cow::Borrowed(data)) => Some((data.as_ptr() as *mut c_char, 0)),
170 Some(Cow::Owned(ref data)) => {
171 let ptr = ffi::CRYPTO_malloc(
172 (data.len() + 1) as _,
173 concat!(file!(), "\0").as_ptr() as _,
174 line!() as _,
175 ) as *mut c_char;
176 if ptr.is_null() {
177 None
178 } else {
179 ptr::copy_nonoverlapping(data.as_ptr(), ptr as *mut u8, data.len());
180 *ptr.add(data.len()) = 0;
181 Some((ptr, ffi::ERR_TXT_MALLOCED))
182 }
183 }
184 None => None,
185 };
186 if let Some((ptr, flags)) = data {
187 ffi::ERR_set_error_data(ptr, flags | ffi::ERR_TXT_STRING);
188 }
189 }
190 }
191
192 #[cfg(ossl300)]
193 fn put_error(&self) {
194 unsafe {
195 ffi::ERR_new();
196 ffi::ERR_set_debug(
197 self.file.as_ptr(),
198 self.line,
199 self.func.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
200 );
201 ffi::ERR_set_error(self.library_code(), self.reason_code(), ptr::null());
202 }
203 }
204
205 #[cfg(not(ossl300))]
206 fn put_error(&self) {
207 #[cfg(not(boringssl))]
208 let line = self.line;
209 #[cfg(boringssl)]
210 let line = self.line.try_into().unwrap();
211 unsafe {
212 ffi::ERR_put_error(
213 self.library_code(),
214 ffi::ERR_GET_FUNC(self.code),
215 self.reason_code(),
216 self.file.as_ptr(),
217 line,
218 );
219 }
220 }
221
222 /// Returns the raw OpenSSL error code for this error.
223 pub fn code(&self) -> ErrType {
224 self.code
225 }
226
227 /// Returns the name of the library reporting the error, if available.
228 pub fn library(&self) -> Option<&'static str> {
229 unsafe {
230 let cstr = ffi::ERR_lib_error_string(self.code);
231 if cstr.is_null() {
232 return None;
233 }
234 let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
235 Some(str::from_utf8(bytes).unwrap())
236 }
237 }
238
239 /// Returns the raw OpenSSL error constant for the library reporting the
240 /// error.
241 // On BoringSSL ERR_GET_{LIB,FUNC,REASON} are `unsafe`, but on
242 // OpenSSL/LibreSSL they're safe.
243 #[allow(unused_unsafe)]
244 pub fn library_code(&self) -> libc::c_int {
245 unsafe { ffi::ERR_GET_LIB(self.code) }
246 }
247
248 /// Returns the name of the function reporting the error.
249 pub fn function(&self) -> Option<RetStr<'_>> {
250 self.func.as_ref().map(|s| s.as_str())
251 }
252
253 /// Returns the reason for the error.
254 pub fn reason(&self) -> Option<&'static str> {
255 unsafe {
256 let cstr = ffi::ERR_reason_error_string(self.code);
257 if cstr.is_null() {
258 return None;
259 }
260 let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
261 Some(str::from_utf8(bytes).unwrap())
262 }
263 }
264
265 /// Returns the raw OpenSSL error constant for the reason for the error.
266 // On BoringSSL ERR_GET_{LIB,FUNC,REASON} are `unsafe`, but on
267 // OpenSSL/LibreSSL they're safe.
268 #[allow(unused_unsafe)]
269 pub fn reason_code(&self) -> libc::c_int {
270 unsafe { ffi::ERR_GET_REASON(self.code) }
271 }
272
273 /// Returns the name of the source file which encountered the error.
274 pub fn file(&self) -> RetStr<'_> {
275 self.file.as_str()
276 }
277
278 /// Returns the line in the source file which encountered the error.
279 pub fn line(&self) -> u32 {
280 self.line as u32
281 }
282
283 /// Returns additional data describing the error.
284 #[allow(clippy::option_as_ref_deref)]
285 pub fn data(&self) -> Option<&str> {
286 self.data.as_ref().map(|s| &**s)
287 }
288}
289
290impl fmt::Debug for Error {
291 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
292 let mut builder: DebugStruct<'_, '_> = fmt.debug_struct(name:"Error");
293 builder.field(name:"code", &self.code());
294 if let Some(library: &str) = self.library() {
295 builder.field(name:"library", &library);
296 }
297 if let Some(function: &str) = self.function() {
298 builder.field(name:"function", &function);
299 }
300 if let Some(reason: &str) = self.reason() {
301 builder.field(name:"reason", &reason);
302 }
303 builder.field(name:"file", &self.file());
304 builder.field(name:"line", &self.line());
305 if let Some(data: &str) = self.data() {
306 builder.field(name:"data", &data);
307 }
308 builder.finish()
309 }
310}
311
312impl fmt::Display for Error {
313 // On BoringSSL ERR_GET_{LIB,FUNC,REASON} are `unsafe`, but on
314 // OpenSSL/LibreSSL they're safe.
315 #[allow(unused_unsafe)]
316 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
317 write!(fmt, "error:{:08X}", self.code())?;
318 match self.library() {
319 Some(l) => write!(fmt, ":{}", l)?,
320 None => write!(fmt, ":lib({})", self.library_code())?,
321 }
322 match self.function() {
323 Some(f) => write!(fmt, ":{}", f)?,
324 None => write!(fmt, ":func({})", unsafe { ffi::ERR_GET_FUNC(self.code()) })?,
325 }
326 match self.reason() {
327 Some(r) => write!(fmt, ":{}", r)?,
328 None => write!(fmt, ":reason({})", self.reason_code())?,
329 }
330 write!(
331 fmt,
332 ":{}:{}:{}",
333 self.file(),
334 self.line(),
335 self.data().unwrap_or("")
336 )
337 }
338}
339
340impl error::Error for Error {}
341
342cfg_if! {
343 if #[cfg(ossl300)] {
344 use std::ffi::{CString};
345 use ffi::ERR_get_error_all;
346
347 type RetStr<'a> = &'a str;
348
349 #[derive(Clone)]
350 struct ShimStr(CString);
351
352 impl ShimStr {
353 unsafe fn new(s: *const c_char) -> Self {
354 ShimStr(CStr::from_ptr(s).to_owned())
355 }
356
357 fn as_ptr(&self) -> *const c_char {
358 self.0.as_ptr()
359 }
360
361 fn as_str(&self) -> &str {
362 self.0.to_str().unwrap()
363 }
364 }
365 } else {
366 #[allow(bad_style)]
367 unsafe extern "C" fn ERR_get_error_all(
368 file: *mut *const c_char,
369 line: *mut c_int,
370 func: *mut *const c_char,
371 data: *mut *const c_char,
372 flags: *mut c_int,
373 ) -> ErrType {
374 let code = ffi::ERR_get_error_line_data(file, line, data, flags);
375 *func = ffi::ERR_func_error_string(code);
376 code
377 }
378
379 type RetStr<'a> = &'static str;
380
381 #[derive(Clone)]
382 struct ShimStr(*const c_char);
383
384 impl ShimStr {
385 unsafe fn new(s: *const c_char) -> Self {
386 ShimStr(s)
387 }
388
389 fn as_ptr(&self) -> *const c_char {
390 self.0
391 }
392
393 fn as_str(&self) -> &'static str {
394 unsafe {
395 CStr::from_ptr(self.0).to_str().unwrap()
396 }
397 }
398 }
399 }
400}
401
402#[cfg(test)]
403mod tests {
404 #[cfg(not(ossl310))]
405 use crate::nid::Nid;
406
407 #[test]
408 // Due to a bug in OpenSSL 3.1.0, this test can hang there. Skip for now.
409 #[cfg(not(ossl310))]
410 fn test_error_library_code() {
411 let stack = Nid::create("not-an-oid", "invalid", "invalid").unwrap_err();
412 let errors = stack.errors();
413 #[cfg(not(boringssl))]
414 assert_eq!(errors[0].library_code(), ffi::ERR_LIB_ASN1);
415 #[cfg(boringssl)]
416 assert_eq!(errors[0].library_code(), ffi::ERR_LIB_OBJ as libc::c_int);
417 }
418}
419