1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::error::*;
use nss_sys::*;
use std::{convert::TryFrom, ffi::CString, os::raw::c_char, sync::Once};
// This is the NSS version that this crate is claiming to be compatible with.
// We check it at runtime using `NSS_VersionCheck`.
pub const COMPATIBLE_NSS_VERSION: &str = "3.26";
static NSS_INIT: Once = Once::new();
pub fn ensure_nss_initialized() {
NSS_INIT.call_once(|| {
let version_ptr = CString::new(COMPATIBLE_NSS_VERSION).unwrap();
if unsafe { NSS_VersionCheck(version_ptr.as_ptr()) == PR_FALSE } {
panic!("Incompatible NSS version!")
}
let empty = CString::default();
let flags = NSS_INIT_READONLY
| NSS_INIT_NOCERTDB
| NSS_INIT_NOMODDB
| NSS_INIT_FORCEOPEN
| NSS_INIT_OPTIMIZESPACE;
let context = unsafe {
NSS_InitContext(
empty.as_ptr(),
empty.as_ptr(),
empty.as_ptr(),
empty.as_ptr(),
std::ptr::null_mut(),
flags,
)
};
if context.is_null() {
let error = get_last_error();
panic!("Could not initialize NSS: {}", error);
}
})
}
pub fn map_nss_secstatus<F>(callback: F) -> Result<()>
where
F: FnOnce() -> SECStatus,
{
if callback() == SECStatus::SECSuccess {
return Ok(());
}
Err(get_last_error())
}
/// Retrieve and wrap the last NSS/NSPR error in the current thread.
#[cold]
pub fn get_last_error() -> Error {
let error_code = unsafe { PR_GetError() };
let error_text: String = usize::try_from(unsafe { PR_GetErrorTextLength() })
.map(|error_text_len| {
let mut out_str = vec![0u8; error_text_len + 1];
unsafe { PR_GetErrorText(out_str.as_mut_ptr() as *mut c_char) };
CString::new(&out_str[0..error_text_len])
.unwrap_or_else(|_| CString::default())
.to_str()
.unwrap_or_else(|_| "")
.to_owned()
})
.unwrap_or_else(|_| "".to_string());
ErrorKind::NSSError(error_code, error_text).into()
}
pub(crate) trait ScopedPtr
where
Self: std::marker::Sized,
{
type RawType;
unsafe fn from_ptr(ptr: *mut Self::RawType) -> Result<Self>;
fn as_ptr(&self) -> *const Self::RawType;
fn as_mut_ptr(&self) -> *mut Self::RawType;
}
// The macro defines a wrapper around pointers refering to types allocated by NSS,
// calling their NSS destructor method when they go out of scope to avoid memory leaks.
// The `as_ptr`/`as_mut_ptr` are provided to retrieve the raw pointers to pass to
// NSS functions that consume them.
#[macro_export]
macro_rules! scoped_ptr {
($scoped:ident, $target:ty, $dtor:path) => {
pub struct $scoped {
ptr: *mut $target,
}
impl crate::util::ScopedPtr for $scoped {
type RawType = $target;
#[allow(dead_code)]
unsafe fn from_ptr(ptr: *mut $target) -> crate::error::Result<$scoped> {
if !ptr.is_null() {
Ok($scoped { ptr })
} else {
Err(crate::error::ErrorKind::InternalError.into())
}
}
#[inline]
fn as_ptr(&self) -> *const $target {
self.ptr
}
#[inline]
fn as_mut_ptr(&self) -> *mut $target {
self.ptr
}
}
impl Drop for $scoped {
fn drop(&mut self) {
assert!(!self.ptr.is_null());
unsafe { $dtor(self.ptr) };
}
}
};
}
pub(crate) unsafe fn sec_item_as_slice(sec_item: &mut SECItem) -> Result<&mut [u8]> {
let sec_item_buf_len = usize::try_from(sec_item.len)?;
let buf = std::slice::from_raw_parts_mut(sec_item.data, sec_item_buf_len);
Ok(buf)
}
|