Coverage Report

Created: 2025-10-29 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/openssl-0.10.62/src/error.rs
Line
Count
Source
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.
43
0
    pub fn get() -> ErrorStack {
44
0
        let mut vec = vec![];
45
0
        while let Some(err) = Error::get() {
46
0
            vec.push(err);
47
0
        }
48
0
        ErrorStack(vec)
49
0
    }
50
51
    /// Pushes the errors back onto the OpenSSL error stack.
52
0
    pub fn put(&self) {
53
0
        for error in self.errors() {
54
0
            error.put();
55
0
        }
56
0
    }
57
}
58
59
impl ErrorStack {
60
    /// Returns the errors in the stack.
61
0
    pub fn errors(&self) -> &[Error] {
62
0
        &self.0
63
0
    }
64
}
65
66
impl fmt::Display for ErrorStack {
67
0
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
68
0
        if self.0.is_empty() {
69
0
            return fmt.write_str("OpenSSL error");
70
0
        }
71
72
0
        let mut first = true;
73
0
        for err in &self.0 {
74
0
            if !first {
75
0
                fmt.write_str(", ")?;
76
0
            }
77
0
            write!(fmt, "{}", err)?;
78
0
            first = false;
79
        }
80
0
        Ok(())
81
0
    }
82
}
83
84
impl error::Error for ErrorStack {}
85
86
impl From<ErrorStack> for io::Error {
87
0
    fn from(e: ErrorStack) -> io::Error {
88
0
        io::Error::new(io::ErrorKind::Other, e)
89
0
    }
90
}
91
92
impl From<ErrorStack> for fmt::Error {
93
0
    fn from(_: ErrorStack) -> fmt::Error {
94
0
        fmt::Error
95
0
    }
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.
113
0
    pub fn get() -> Option<Error> {
114
        unsafe {
115
0
            ffi::init();
116
117
0
            let mut file = ptr::null();
118
0
            let mut line = 0;
119
0
            let mut func = ptr::null();
120
0
            let mut data = ptr::null();
121
0
            let mut flags = 0;
122
0
            match ERR_get_error_all(&mut file, &mut line, &mut func, &mut data, &mut flags) {
123
0
                0 => None,
124
0
                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
0
                    let data = if flags & ffi::ERR_TXT_STRING != 0 {
128
0
                        let bytes = CStr::from_ptr(data as *const _).to_bytes();
129
0
                        let data = str::from_utf8(bytes).unwrap();
130
                        #[cfg(not(boringssl))]
131
0
                        let data = if flags & ffi::ERR_TXT_MALLOCED != 0 {
132
0
                            Cow::Owned(data.to_string())
133
                        } else {
134
0
                            Cow::Borrowed(data)
135
                        };
136
                        #[cfg(boringssl)]
137
                        let data = Cow::Borrowed(data);
138
0
                        Some(data)
139
                    } else {
140
0
                        None
141
                    };
142
143
0
                    let file = ShimStr::new(file);
144
145
0
                    let func = if func.is_null() {
146
0
                        None
147
                    } else {
148
0
                        Some(ShimStr::new(func))
149
                    };
150
151
0
                    Some(Error {
152
0
                        code,
153
0
                        file,
154
0
                        line,
155
0
                        func,
156
0
                        data,
157
0
                    })
158
                }
159
            }
160
        }
161
0
    }
162
163
    /// Pushes the error back onto the OpenSSL error stack.
164
0
    pub fn put(&self) {
165
0
        self.put_error();
166
167
        unsafe {
168
0
            let data = match self.data {
169
0
                Some(Cow::Borrowed(data)) => Some((data.as_ptr() as *mut c_char, 0)),
170
0
                Some(Cow::Owned(ref data)) => {
171
0
                    let ptr = ffi::CRYPTO_malloc(
172
0
                        (data.len() + 1) as _,
173
0
                        concat!(file!(), "\0").as_ptr() as _,
174
0
                        line!() as _,
175
0
                    ) as *mut c_char;
176
0
                    if ptr.is_null() {
177
0
                        None
178
                    } else {
179
0
                        ptr::copy_nonoverlapping(data.as_ptr(), ptr as *mut u8, data.len());
180
0
                        *ptr.add(data.len()) = 0;
181
0
                        Some((ptr, ffi::ERR_TXT_MALLOCED))
182
                    }
183
                }
184
0
                None => None,
185
            };
186
0
            if let Some((ptr, flags)) = data {
187
0
                ffi::ERR_set_error_data(ptr, flags | ffi::ERR_TXT_STRING);
188
0
            }
189
        }
190
0
    }
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
0
    fn put_error(&self) {
207
        #[cfg(not(boringssl))]
208
0
        let line = self.line;
209
        #[cfg(boringssl)]
210
        let line = self.line.try_into().unwrap();
211
0
        unsafe {
212
0
            ffi::ERR_put_error(
213
0
                self.library_code(),
214
0
                ffi::ERR_GET_FUNC(self.code),
215
0
                self.reason_code(),
216
0
                self.file.as_ptr(),
217
0
                line,
218
0
            );
219
0
        }
220
0
    }
221
222
    /// Returns the raw OpenSSL error code for this error.
223
0
    pub fn code(&self) -> ErrType {
224
0
        self.code
225
0
    }
226
227
    /// Returns the name of the library reporting the error, if available.
228
0
    pub fn library(&self) -> Option<&'static str> {
229
        unsafe {
230
0
            let cstr = ffi::ERR_lib_error_string(self.code);
231
0
            if cstr.is_null() {
232
0
                return None;
233
0
            }
234
0
            let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
235
0
            Some(str::from_utf8(bytes).unwrap())
236
        }
237
0
    }
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
0
    pub fn library_code(&self) -> libc::c_int {
245
0
        unsafe { ffi::ERR_GET_LIB(self.code) }
246
0
    }
247
248
    /// Returns the name of the function reporting the error.
249
0
    pub fn function(&self) -> Option<RetStr<'_>> {
250
0
        self.func.as_ref().map(|s| s.as_str())
251
0
    }
252
253
    /// Returns the reason for the error.
254
0
    pub fn reason(&self) -> Option<&'static str> {
255
        unsafe {
256
0
            let cstr = ffi::ERR_reason_error_string(self.code);
257
0
            if cstr.is_null() {
258
0
                return None;
259
0
            }
260
0
            let bytes = CStr::from_ptr(cstr as *const _).to_bytes();
261
0
            Some(str::from_utf8(bytes).unwrap())
262
        }
263
0
    }
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
0
    pub fn reason_code(&self) -> libc::c_int {
270
0
        unsafe { ffi::ERR_GET_REASON(self.code) }
271
0
    }
272
273
    /// Returns the name of the source file which encountered the error.
274
0
    pub fn file(&self) -> RetStr<'_> {
275
0
        self.file.as_str()
276
0
    }
277
278
    /// Returns the line in the source file which encountered the error.
279
0
    pub fn line(&self) -> u32 {
280
0
        self.line as u32
281
0
    }
282
283
    /// Returns additional data describing the error.
284
    #[allow(clippy::option_as_ref_deref)]
285
0
    pub fn data(&self) -> Option<&str> {
286
0
        self.data.as_ref().map(|s| &**s)
287
0
    }
288
}
289
290
impl fmt::Debug for Error {
291
0
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
292
0
        let mut builder = fmt.debug_struct("Error");
293
0
        builder.field("code", &self.code());
294
0
        if let Some(library) = self.library() {
295
0
            builder.field("library", &library);
296
0
        }
297
0
        if let Some(function) = self.function() {
298
0
            builder.field("function", &function);
299
0
        }
300
0
        if let Some(reason) = self.reason() {
301
0
            builder.field("reason", &reason);
302
0
        }
303
0
        builder.field("file", &self.file());
304
0
        builder.field("line", &self.line());
305
0
        if let Some(data) = self.data() {
306
0
            builder.field("data", &data);
307
0
        }
308
0
        builder.finish()
309
0
    }
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)]
316
0
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
317
0
        write!(fmt, "error:{:08X}", self.code())?;
318
0
        match self.library() {
319
0
            Some(l) => write!(fmt, ":{}", l)?,
320
0
            None => write!(fmt, ":lib({})", self.library_code())?,
321
        }
322
0
        match self.function() {
323
0
            Some(f) => write!(fmt, ":{}", f)?,
324
0
            None => write!(fmt, ":func({})", unsafe { ffi::ERR_GET_FUNC(self.code()) })?,
325
        }
326
0
        match self.reason() {
327
0
            Some(r) => write!(fmt, ":{}", r)?,
328
0
            None => write!(fmt, ":reason({})", self.reason_code())?,
329
        }
330
0
        write!(
331
0
            fmt,
332
0
            ":{}:{}:{}",
333
0
            self.file(),
334
0
            self.line(),
335
0
            self.data().unwrap_or("")
336
        )
337
0
    }
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
0
        unsafe extern "C" fn ERR_get_error_all(
368
0
            file: *mut *const c_char,
369
0
            line: *mut c_int,
370
0
            func: *mut *const c_char,
371
0
            data: *mut *const c_char,
372
0
            flags: *mut c_int,
373
0
        ) -> ErrType {
374
0
            let code = ffi::ERR_get_error_line_data(file, line, data, flags);
375
0
            *func = ffi::ERR_func_error_string(code);
376
0
            code
377
0
        }
378
379
        type RetStr<'a> = &'static str;
380
381
        #[derive(Clone)]
382
        struct ShimStr(*const c_char);
383
384
        impl ShimStr {
385
0
            unsafe fn new(s: *const c_char) -> Self {
386
0
                ShimStr(s)
387
0
            }
388
389
0
            fn as_ptr(&self) -> *const c_char {
390
0
                self.0
391
0
            }
392
393
0
            fn as_str(&self) -> &'static str {
394
                unsafe {
395
0
                    CStr::from_ptr(self.0).to_str().unwrap()
396
                }
397
0
            }
398
        }
399
    }
400
}
401
402
#[cfg(test)]
403
mod 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
}