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
|
{-# LANGUAGE ScopedTypeVariables #-}
-- | This module wraps BoringSSL's @OPENSSL_cleanse@, which securely overwrites
-- memory. ("Securely" here means that BoringSSL uses some assembly magic to
-- prevent the compiler from optimizing out the write.) However, the module
-- doesn't actually expose @OPENSSL_cleanse@ directly; instead, it lets you
-- allocate 'ForeignPtr's with cleansing registered as a finalizer. GHC runs all
-- 'ForeignPtr' finalizers prior to program termination, which gives the
-- 'ForeignPtr's allocated this way the approximately same security guarantees
-- as memory allocated through BoringSSL's allocator interface. In particular,
-- unless you exit your program through GHC's foreign function interface, all
-- memory allocated through 'mallocCleansablePtr' will be forcibly cleared prior
-- to program exit.
module Cleanse
( mallocCleansablePtr
) where
import Foreign
(FinalizerPtr, ForeignPtr, Storable(poke, sizeOf),
addForeignPtrFinalizer, mallocForeignPtrBytes, withForeignPtr)
import Foreign.C.Types
import Foreign.ForeignPtr.Compat (plusForeignPtr)
#include <stddef.h>
#include <openssl/mem.h>
-- We implement 'mallocCleansablePtr' using the standard allocator technique of
-- saving the allocated region size immediately before the allocated region.
#def struct __attribute__((__packed__)) Buffer {
size_t size;
char data[];
};
bufferSize :: Int
bufferSize = #size struct Buffer
dataOffset :: Int
dataOffset = #offset struct Buffer, data
mallocCleansablePtr :: forall a. Storable a => IO (ForeignPtr a)
mallocCleansablePtr = do
-- Allocate the buffer.
let dataSize = sizeOf (undefined :: a)
fp <- mallocForeignPtrBytes (bufferSize + dataSize)
-- Save the data size.
withForeignPtr fp $ \p -> poke p (fromIntegral dataSize :: CSize)
-- Now that the size is saved, we can register the cleansing finalizer. This
-- will look at the size and wipe the buffer.
addForeignPtrFinalizer btlsCleansePtr fp
-- Return a pointer to the data region.
return (fp `plusForeignPtr` dataOffset :: ForeignPtr a)
-- The cleansing finalizer itself is totally straightforward.
#def void btlsCleanse(struct Buffer* const p) {
OPENSSL_cleanse(p->data, p->size);
}
foreign import ccall "&btlsCleanse"
btlsCleansePtr :: FinalizerPtr a
|