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;