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