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 //! ```
18 use libc::{c_char, c_int, c_ulong};
19 use std::borrow::Cow;
20 use std::error;
21 use std::ffi::CStr;
22 use std::fmt;
23 use std::io;
24 use std::ptr;
25 use std::str;
26 
27 use ffi;
28 
29 /// Collection of [`Error`]s from OpenSSL.
30 ///
31 /// [`Error`]: struct.Error.html
32 #[derive(Debug, Clone)]
33 pub struct ErrorStack(Vec<Error>);
34 
35 impl ErrorStack {
36     /// Returns the contents of the OpenSSL error stack.
get() -> ErrorStack37     pub fn get() -> ErrorStack {
38         let mut vec = vec![];
39         while let Some(err) = Error::get() {
40             vec.push(err);
41         }
42         ErrorStack(vec)
43     }
44 
45     /// Pushes the errors back onto the OpenSSL error stack.
put(&self)46     pub fn put(&self) {
47         for error in self.errors() {
48             error.put();
49         }
50     }
51 }
52 
53 impl ErrorStack {
54     /// Returns the errors in the stack.
errors(&self) -> &[Error]55     pub fn errors(&self) -> &[Error] {
56         &self.0
57     }
58 }
59 
60 impl fmt::Display for ErrorStack {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result61     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
62         if self.0.is_empty() {
63             return fmt.write_str("OpenSSL error");
64         }
65 
66         let mut first = true;
67         for err in &self.0 {
68             if !first {
69                 fmt.write_str(", ")?;
70             }
71             write!(fmt, "{}", err)?;
72             first = false;
73         }
74         Ok(())
75     }
76 }
77 
78 impl error::Error for ErrorStack {}
79 
80 impl From<ErrorStack> for io::Error {
from(e: ErrorStack) -> io::Error81     fn from(e: ErrorStack) -> io::Error {
82         io::Error::new(io::ErrorKind::Other, e)
83     }
84 }
85 
86 impl From<ErrorStack> for fmt::Error {
from(_: ErrorStack) -> fmt::Error87     fn from(_: ErrorStack) -> fmt::Error {
88         fmt::Error
89     }
90 }
91 
92 /// An error reported from OpenSSL.
93 #[derive(Clone)]
94 pub struct Error {
95     code: c_ulong,
96     file: *const c_char,
97     line: c_int,
98     data: Option<Cow<'static, str>>,
99 }
100 
101 unsafe impl Sync for Error {}
102 unsafe impl Send for Error {}
103 
104 impl Error {
105     /// Returns the first error on the OpenSSL error stack.
get() -> Option<Error>106     pub fn get() -> Option<Error> {
107         unsafe {
108             ffi::init();
109 
110             let mut file = ptr::null();
111             let mut line = 0;
112             let mut data = ptr::null();
113             let mut flags = 0;
114             match ffi::ERR_get_error_line_data(&mut file, &mut line, &mut data, &mut flags) {
115                 0 => None,
116                 code => {
117                     // The memory referenced by data is only valid until that slot is overwritten
118                     // in the error stack, so we'll need to copy it off if it's dynamic
119                     let data = if flags & ffi::ERR_TXT_STRING != 0 {
120                         let bytes = CStr::from_ptr(data as *const _).to_bytes();
121                         let data = str::from_utf8(bytes).unwrap();
122                         let data = if flags & ffi::ERR_TXT_MALLOCED != 0 {
123                             Cow::Owned(data.to_string())
124                         } else {
125                             Cow::Borrowed(data)
126                         };
127                         Some(data)
128                     } else {
129                         None
130                     };
131                     Some(Error {
132                         code,
133                         file,
134                         line,
135                         data,
136                     })
137                 }
138             }
139         }
140     }
141 
142     /// Pushes the error back onto the OpenSSL error stack.
put(&self)143     pub fn put(&self) {
144         unsafe {
145             ffi::ERR_put_error(
146                 ffi::ERR_GET_LIB(self.code),
147                 ffi::ERR_GET_FUNC(self.code),
148                 ffi::ERR_GET_REASON(self.code),
149                 self.file,
150                 self.line,
151             );
152             let data = match self.data {
153                 Some(Cow::Borrowed(data)) => Some((data.as_ptr() as *mut c_char, 0)),
154                 Some(Cow::Owned(ref data)) => {
155                     let ptr = ffi::CRYPTO_malloc(
156                         (data.len() + 1) as _,
157                         concat!(file!(), "\0").as_ptr() as _,
158                         line!() as _,
159                     ) as *mut c_char;
160                     if ptr.is_null() {
161                         None
162                     } else {
163                         ptr::copy_nonoverlapping(data.as_ptr(), ptr as *mut u8, data.len());
164                         *ptr.add(data.len()) = 0;
165                         Some((ptr, ffi::ERR_TXT_MALLOCED))
166                     }
167                 }
168                 None => None,
169             };
170             if let Some((ptr, flags)) = data {
171                 ffi::ERR_set_error_data(ptr, flags | ffi::ERR_TXT_STRING);
172             }
173         }
174     }
175 
176     /// Returns the raw OpenSSL error code for this error.
code(&self) -> c_ulong177     pub fn code(&self) -> c_ulong {
178         self.code
179     }
180 
181     /// Returns the name of the library reporting the error, if available.
library(&self) -> Option<&'static str>182     pub fn library(&self) -> Option<&'static str> {
183         unsafe {
184             let cstr = ffi::ERR_lib_error_string(self.code);
185             if cstr.is_null() {
186                 return None;
187             }
188             let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
189             Some(str::from_utf8(bytes).unwrap())
190         }
191     }
192 
193     /// Returns the name of the function reporting the error.
function(&self) -> Option<&'static str>194     pub fn function(&self) -> Option<&'static str> {
195         unsafe {
196             let cstr = ffi::ERR_func_error_string(self.code);
197             if cstr.is_null() {
198                 return None;
199             }
200             let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
201             Some(str::from_utf8(bytes).unwrap())
202         }
203     }
204 
205     /// Returns the reason for the error.
reason(&self) -> Option<&'static str>206     pub fn reason(&self) -> Option<&'static str> {
207         unsafe {
208             let cstr = ffi::ERR_reason_error_string(self.code);
209             if cstr.is_null() {
210                 return None;
211             }
212             let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
213             Some(str::from_utf8(bytes).unwrap())
214         }
215     }
216 
217     /// Returns the name of the source file which encountered the error.
file(&self) -> &'static str218     pub fn file(&self) -> &'static str {
219         unsafe {
220             assert!(!self.file.is_null());
221             let bytes = CStr::from_ptr(self.file as *const _).to_bytes();
222             str::from_utf8(bytes).unwrap()
223         }
224     }
225 
226     /// Returns the line in the source file which encountered the error.
line(&self) -> u32227     pub fn line(&self) -> u32 {
228         self.line as u32
229     }
230 
231     /// Returns additional data describing the error.
232     #[allow(clippy::option_as_ref_deref)]
data(&self) -> Option<&str>233     pub fn data(&self) -> Option<&str> {
234         self.data.as_ref().map(|s| &**s)
235     }
236 }
237 
238 impl fmt::Debug for Error {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result239     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
240         let mut builder = fmt.debug_struct("Error");
241         builder.field("code", &self.code());
242         if let Some(library) = self.library() {
243             builder.field("library", &library);
244         }
245         if let Some(function) = self.function() {
246             builder.field("function", &function);
247         }
248         if let Some(reason) = self.reason() {
249             builder.field("reason", &reason);
250         }
251         builder.field("file", &self.file());
252         builder.field("line", &self.line());
253         if let Some(data) = self.data() {
254             builder.field("data", &data);
255         }
256         builder.finish()
257     }
258 }
259 
260 impl fmt::Display for Error {
fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result261     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
262         write!(fmt, "error:{:08X}", self.code())?;
263         match self.library() {
264             Some(l) => write!(fmt, ":{}", l)?,
265             None => write!(fmt, ":lib({})", ffi::ERR_GET_LIB(self.code()))?,
266         }
267         match self.function() {
268             Some(f) => write!(fmt, ":{}", f)?,
269             None => write!(fmt, ":func({})", ffi::ERR_GET_FUNC(self.code()))?,
270         }
271         match self.reason() {
272             Some(r) => write!(fmt, ":{}", r)?,
273             None => write!(fmt, ":reason({})", ffi::ERR_GET_REASON(self.code()))?,
274         }
275         write!(
276             fmt,
277             ":{}:{}:{}",
278             self.file(),
279             self.line(),
280             self.data().unwrap_or("")
281         )
282     }
283 }
284 
285 impl error::Error for Error {}
286