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 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
//! SecBox. //! //! This crate provides a security primitive, `SecBox`, which tries to limit the damage //! of common vulnerabilities. //! //! # Vulerabilities //! //! - **Stack or local out-of-bound indexing:** You can usually use buffer overflow to read the //! stack, but if you need to deref the element to get the data, you often cannot know how much to //! offset by (that is, you don't know at what address the array starts). Scanning linearly is //! unproductive (especially since the data doesn't line up) and quickly results in segfault. //! //! - **Partial memory dumps:** Partial memory dumps (e.g. page dumps or CPU cache dumps) are //! avoided by discontinuity, which means that partial memory segments would rarely contain //! interesting data. //! //! - **Swap RAM data leaks:** To avoid the memory being written to persistent memory (and thus //! easier to access), we memlock the internal data, making sure that the data never leaves the //! temporary memory. //! //! - **Read of uninitialized data:** Uninitialized reads is a rare bug in Rust, but it is common //! in C and C++ and thus Rust bindings to libraries written in those. For this reason, we make //! sure that the data overwritten it with zeros, and thus made unaccessible after free. //! //! - **Crash dump data leaks:** Due to zeroing data, crash dumps are often limited in exposure of //! sensitive data. //! //! # NB! //! //! `SecBox` doesn't mean that the inner data is completely protected. You still need to make sure //! it is handled properly and not leaked by other means. #![feature(box_syntax, unique, core_intrinsics)] #![warn(missing_docs)] extern crate libc; use std::ptr::{self, Unique}; use std::{mem, intrinsics, ops, fmt}; /// A secure box. /// /// This will make sure the internal memory is memlocked, and cleared when dropped. /// /// While this is slower than e.g. having a secure string, it allows for better security due to /// obfustication as well as no unsecure reallocation. /// /// # Security measures /// /// 1. Memlocking. This memlocks the inner data making sure the dataresident in memory. /// 2. Volatile zeroing. This makes sure the data is overwritten when dropped, making it impossible /// to read afterwards. /// 3. Non linearity. If you have a vector of `SecBox`es, they will not necessarily be lined up, /// which mean that if an attacker can read some part of the memory, it will rarely make sense. /// /// # An important note /// /// Wrapping a primitive doesn't necessarily affect the inner data. Many primitves (like `Vec` and /// `Box`) are simply wrappers around a pointer to the inner data. For this reason you need to wrap /// the inner data (e.g. `Vec<SecBox<T>>` instaed of `SecBox<Vec<T>>`). pub struct SecBox<T: ?Sized> { /// The inner pointer. /// /// We use a raw pointer so that we can handle the destructor manually. inner: Unique<T>, } impl<T: ?Sized> SecBox<T> { /// Create a new `SecBox`. /// /// If you want to construct a unsized SecBox, you should convert a `Box` through the `From` /// trait. #[inline(always)] pub fn new(inner: T) -> SecBox<T> where T: Sized { let res = SecBox { inner: unsafe { Unique::new(Box::into_raw(box mem::uninitialized::<T>())) }, }; // Lock the data. res.memlock(); // We set the inner data after the memlock to make sure that the data doesn't leave the memory. unsafe { ptr::write(*res.inner, inner); } res } /// Get the inner value of this `SecBox`. /// /// Take care. This moves the value from a secure space to the stack, allowing the data to /// reside in swap RAM. pub fn into_inner(self) -> T where T: Sized { unsafe { // Read the inner. let res = ptr::read(*self.inner); // Zero it. ptr::write_volatile(*self.inner, mem::zeroed()); // Unlock the memory. self.memunlock(); res } } /// Memlock the inner data. fn memlock(&self) { unsafe { libc::mlock(&**self as *const T as *const libc::c_void, mem::size_of_val(&**self) as libc::size_t); }; } /// Memunlock the inner data. fn memunlock(&self) { unsafe { libc::munlock(&**self as *const T as *const libc::c_void, mem::size_of_val(&**self) as libc::size_t); }; } } impl<T: ?Sized + Clone> Clone for SecBox<T> { fn clone(&self) -> SecBox<T> { unsafe { let mut bx = SecBox::new(mem::uninitialized::<T>()); // To avoid getting it outside the secure space, we clone inplace. bx.clone_from(self); bx } } fn clone_from(&mut self, src: &SecBox<T>) { (&mut **self).clone_from(src); } } impl<T: ?Sized> From<Box<T>> for SecBox<T> { fn from(from: Box<T>) -> SecBox<T> { let res = SecBox { inner: unsafe { Unique::new(Box::into_raw(from)) }, }; // Lock the data. res.memlock(); res } } impl<T: ?Sized> ops::Deref for SecBox<T> { type Target = T; fn deref(&self) -> &T { unsafe { self.inner.get() } } } impl<T: ?Sized> ops::DerefMut for SecBox<T> { fn deref_mut(&mut self) -> &mut T { unsafe { self.inner.get_mut() } } } impl<T: ?Sized> fmt::Display for SecBox<T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "*******") } } impl<T: ?Sized> fmt::Debug for SecBox<T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "*******") } } impl<T: ?Sized> Drop for SecBox<T> { fn drop(&mut self) { unsafe { // Drop the inner. ptr::drop_in_place(*self.inner); // Zero the content. intrinsics::volatile_set_memory(*self.inner as *mut u8, 0, mem::size_of_val(&**self)); // Unlock the memory. self.memunlock(); // Drop the box itself. drop(Box::from_raw(*self.inner)); } } } #[cfg(test)] mod test;