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 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
|
package core
import (
"errors"
"sync"
"github.com/awnumar/memcall"
)
var (
buffers = new(bufferList)
)
// ErrNullBuffer is returned when attempting to construct a buffer of size less than one.
var ErrNullBuffer = errors.New("<memguard::core::ErrNullBuffer> buffer size must be greater than zero")
// ErrBufferExpired is returned when attempting to perform an operation on or with a buffer that has been destroyed.
var ErrBufferExpired = errors.New("<memguard::core::ErrBufferExpired> buffer has been purged from memory and can no longer be used")
/*
Buffer is a structure that holds raw sensitive data.
The number of Buffers that can exist at one time is limited by how much memory your system's kernel allows each process to mlock/VirtualLock. Therefore you should call DestroyBuffer on Buffers that you no longer need, ideally defering a Destroy call after creating a new one.
*/
type Buffer struct {
sync.RWMutex // Local mutex lock // TODO: this does not protect 'data' field
alive bool // Signals that destruction has not come
mutable bool // Mutability state of underlying memory
data []byte // Portion of memory holding the data
memory []byte // Entire allocated memory region
preguard []byte // Guard page addressed before the data
inner []byte // Inner region between the guard pages
postguard []byte // Guard page addressed after the data
canary []byte // Value written behind data to detect spillage
}
/*
NewBuffer is a raw constructor for the Buffer object.
*/
func NewBuffer(size int) (*Buffer, error) {
var err error
if size < 1 {
return nil, ErrNullBuffer
}
b := new(Buffer)
// Allocate the total needed memory
innerLen := roundToPageSize(size)
b.memory, err = memcall.Alloc((2 * pageSize) + innerLen)
if err != nil {
Panic(err)
}
// Construct slice reference for data buffer.
b.data = getBytes(&b.memory[pageSize+innerLen-size], size)
// Construct slice references for page sectors.
b.preguard = getBytes(&b.memory[0], pageSize)
b.inner = getBytes(&b.memory[pageSize], innerLen)
b.postguard = getBytes(&b.memory[pageSize+innerLen], pageSize)
// Construct slice reference for canary portion of inner page.
b.canary = getBytes(&b.memory[pageSize], len(b.inner)-len(b.data))
// Lock the pages that will hold sensitive data.
if err := memcall.Lock(b.inner); err != nil {
Panic(err)
}
// Initialise the canary value and reference regions.
if err := Scramble(b.canary); err != nil {
Panic(err)
}
Copy(b.preguard, b.canary)
Copy(b.postguard, b.canary)
// Make the guard pages inaccessible.
if err := memcall.Protect(b.preguard, memcall.NoAccess()); err != nil {
Panic(err)
}
if err := memcall.Protect(b.postguard, memcall.NoAccess()); err != nil {
Panic(err)
}
// Set remaining properties
b.alive = true
b.mutable = true
// Append the container to list of active buffers.
buffers.add(b)
// Return the created Buffer to the caller.
return b, nil
}
// Data returns a byte slice representing the memory region containing the data.
func (b *Buffer) Data() []byte {
return b.data
}
// Inner returns a byte slice representing the entire inner memory pages. This should NOT be used unless you have a specific need.
func (b *Buffer) Inner() []byte {
return b.inner
}
// Freeze makes the underlying memory of a given buffer immutable. This will do nothing if the Buffer has been destroyed.
func (b *Buffer) Freeze() {
if err := b.freeze(); err != nil {
Panic(err)
}
}
func (b *Buffer) freeze() error {
b.Lock()
defer b.Unlock()
if !b.alive {
return nil
}
if b.mutable {
if err := memcall.Protect(b.inner, memcall.ReadOnly()); err != nil {
return err
}
b.mutable = false
}
return nil
}
// Melt makes the underlying memory of a given buffer mutable. This will do nothing if the Buffer has been destroyed.
func (b *Buffer) Melt() {
if err := b.melt(); err != nil {
Panic(err)
}
}
func (b *Buffer) melt() error {
b.Lock()
defer b.Unlock()
if !b.alive {
return nil
}
if !b.mutable {
if err := memcall.Protect(b.inner, memcall.ReadWrite()); err != nil {
return err
}
b.mutable = true
}
return nil
}
// Scramble attempts to overwrite the data with cryptographically-secure random bytes.
func (b *Buffer) Scramble() {
if err := b.scramble(); err != nil {
Panic(err)
}
}
func (b *Buffer) scramble() error {
b.Lock()
defer b.Unlock()
return Scramble(b.Data())
}
/*
Destroy performs some security checks, securely wipes the contents of, and then releases a Buffer's memory back to the OS. If a security check fails, the process will attempt to wipe all it can before safely panicking.
If the Buffer has already been destroyed, the function does nothing and returns nil.
*/
func (b *Buffer) Destroy() {
if err := b.destroy(); err != nil {
Panic(err)
}
// Remove this one from global slice.
buffers.remove(b)
}
func (b *Buffer) destroy() error {
if b == nil {
return nil
}
// Attain a mutex lock on this Buffer.
b.Lock()
defer b.Unlock()
// Return if it's already destroyed.
if !b.alive {
return nil
}
// Make all of the memory readable and writable.
if err := memcall.Protect(b.memory, memcall.ReadWrite()); err != nil {
return err
}
b.mutable = true
// Wipe data field.
Wipe(b.data)
// Verify the canary
if !Equal(b.preguard, b.postguard) || !Equal(b.preguard[:len(b.canary)], b.canary) {
return errors.New("<memguard::core::buffer> canary verification failed; buffer overflow detected")
}
// Wipe the memory.
Wipe(b.memory)
// Unlock pages locked into memory.
if err := memcall.Unlock(b.inner); err != nil {
return err
}
// Free all related memory.
if err := memcall.Free(b.memory); err != nil {
return err
}
// Reset the fields.
b.alive = false
b.mutable = false
b.data = nil
b.memory = nil
b.preguard = nil
b.inner = nil
b.postguard = nil
b.canary = nil
return nil
}
// Alive returns true if the buffer has not been destroyed.
func (b *Buffer) Alive() bool {
b.RLock()
defer b.RUnlock()
return b.alive
}
// Mutable returns true if the buffer is mutable.
func (b *Buffer) Mutable() bool {
b.RLock()
defer b.RUnlock()
return b.mutable
}
// BufferList stores a list of buffers in a thread-safe manner.
type bufferList struct {
sync.RWMutex
list []*Buffer
}
// Add appends a given Buffer to the list.
func (l *bufferList) add(b ...*Buffer) {
l.Lock()
defer l.Unlock()
l.list = append(l.list, b...)
}
// Copy returns an instantaneous snapshot of the list.
func (l *bufferList) copy() []*Buffer {
l.Lock()
defer l.Unlock()
list := make([]*Buffer, len(l.list))
copy(list, l.list)
return list
}
// Remove removes a given Buffer from the list.
func (l *bufferList) remove(b *Buffer) {
l.Lock()
defer l.Unlock()
for i, v := range l.list {
if v == b {
l.list = append(l.list[:i], l.list[i+1:]...)
break
}
}
}
// Exists checks if a given buffer is in the list.
func (l *bufferList) exists(b *Buffer) bool {
l.RLock()
defer l.RUnlock()
for _, v := range l.list {
if b == v {
return true
}
}
return false
}
// Flush clears the list and returns its previous contents.
func (l *bufferList) flush() []*Buffer {
l.Lock()
defer l.Unlock()
list := make([]*Buffer, len(l.list))
copy(list, l.list)
l.list = nil
return list
}
|