• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 cfg_if::cfg_if;
19 use libc::{c_char, c_int};
20 use std::borrow::Cow;
21 #[cfg(boringssl)]
22 use std::convert::TryInto;
23 use std::error;
24 use std::ffi::CStr;
25 use std::fmt;
26 use std::io;
27 use std::ptr;
28 use std::str;
29 
30 #[cfg(not(boringssl))]
31 type ErrType = libc::c_ulong;
32 #[cfg(boringssl)]
33 type ErrType = libc::c_uint;
34 
35 /// Collection of [`Error`]s from OpenSSL.
36 ///
37 /// [`Error`]: struct.Error.html
38 #[derive(Debug, Clone)]
39 pub struct ErrorStack(Vec<Error>);
40 
41 impl ErrorStack {
42     /// Returns the contents of the OpenSSL error stack.
get() -> ErrorStack43     pub fn get() -> ErrorStack {
44         let mut vec = vec![];
45         while let Some(err) = Error::get() {
46             vec.push(err);
47         }
48         ErrorStack(vec)
49     }
50 
51     /// Pushes the errors back onto the OpenSSL error stack.
put(&self)52     pub fn put(&self) {
53         for error in self.errors() {
54             error.put();
55         }
56     }
57 }
58 
59 impl ErrorStack {
60     /// Returns the errors in the stack.
errors(&self) -> &[Error]61     pub fn errors(&self) -> &[Error] {
62         &self.0
63     }
64 }
65 
66 impl fmt::Display for ErrorStack {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result67     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
68         if self.0.is_empty() {
69             return fmt.write_str("OpenSSL error");
70         }
71 
72         let mut first = true;
73         for err in &self.0 {
74             if !first {
75                 fmt.write_str(", ")?;
76             }
77             write!(fmt, "{}", err)?;
78             first = false;
79         }
80         Ok(())
81     }
82 }
83 
84 impl error::Error for ErrorStack {}
85 
86 impl From<ErrorStack> for io::Error {
from(e: ErrorStack) -> io::Error87     fn from(e: ErrorStack) -> io::Error {
88         io::Error::new(io::ErrorKind::Other, e)
89     }
90 }
91 
92 impl From<ErrorStack> for fmt::Error {
from(_: ErrorStack) -> fmt::Error93     fn from(_: ErrorStack) -> fmt::Error {
94         fmt::Error
95     }
96 }
97 
98 /// An error reported from OpenSSL.
99 #[derive(Clone)]
100 pub struct Error {
101     code: ErrType,
102     file: ShimStr,
103     line: c_int,
104     func: Option<ShimStr>,
105     data: Option<Cow<'static, str>>,
106 }
107 
108 unsafe impl Sync for Error {}
109 unsafe impl Send for Error {}
110 
111 impl Error {
112     /// Returns the first error on the OpenSSL error stack.
get() -> Option<Error>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.
put(&self)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)]
put_error(&self)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))]
put_error(&self)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.
code(&self) -> ErrType223     pub fn code(&self) -> ErrType {
224         self.code
225     }
226 
227     /// Returns the name of the library reporting the error, if available.
library(&self) -> Option<&'static str>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)]
library_code(&self) -> libc::c_int244     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.
function(&self) -> Option<RetStr<'_>>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.
reason(&self) -> Option<&'static str>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)]
reason_code(&self) -> libc::c_int269     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.
file(&self) -> RetStr<'_>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.
line(&self) -> u32279     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)]
data(&self) -> Option<&str>285     pub fn data(&self) -> Option<&str> {
286         self.data.as_ref().map(|s| &**s)
287     }
288 }
289 
290 impl fmt::Debug for Error {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result291     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
292         let mut builder = fmt.debug_struct("Error");
293         builder.field("code", &self.code());
294         if let Some(library) = self.library() {
295             builder.field("library", &library);
296         }
297         if let Some(function) = self.function() {
298             builder.field("function", &function);
299         }
300         if let Some(reason) = self.reason() {
301             builder.field("reason", &reason);
302         }
303         builder.field("file", &self.file());
304         builder.field("line", &self.line());
305         if let Some(data) = self.data() {
306             builder.field("data", &data);
307         }
308         builder.finish()
309     }
310 }
311 
312 impl 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)]
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result316     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 
340 impl error::Error for Error {}
341 
342 cfg_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)]
403 mod tests {
404     use crate::nid::Nid;
405 
406     #[test]
test_error_library_code()407     fn test_error_library_code() {
408         let stack = Nid::create("not-an-oid", "invalid", "invalid").unwrap_err();
409         let errors = stack.errors();
410         #[cfg(not(boringssl))]
411         assert_eq!(errors[0].library_code(), ffi::ERR_LIB_ASN1);
412         #[cfg(boringssl)]
413         assert_eq!(errors[0].library_code(), ffi::ERR_LIB_OBJ as libc::c_int);
414     }
415 }
416