![]() |
|
the first is to do some code analysis to figure out where privileges in a program is needed. for example, if at a point in the codepath, no future potential codepaths require privileged operations, you can drop privileges. furthermore, you can make efforts to actually perform some form of privilege separation by separating out priveliged and unpriveliged code. of course, this is very tricky, and would likely be very heuristic (for example, if the result of a priveliged operation is a file descriptor, we need to do file descriptor passing). niels points out that dawn song already did it.
second, to have the compiler generate systrace policies for a binary by inspecting the system calls it makes, and doing some sort of analysis as to what the range of their arguments are. this is the tricky part. whenever the range cannot be figured out, it can resort to some system-wide policy on that specific system call.
-General- We should probably assume that encrypted swap is in place. It's much easier to deal with than to actually attempt to pin memory. -Encryption/Decryption Logistics- 1. Do it in-place. This is where it's done by the application itself, specifically in a loaded shared module. Signalling here will be a little tricky as to not interfere with other application semantics. 2. Do it in the kernel. Very easy. But does encryption belong in the kernel? 3. Map all candidate memory into a dedicated daemon. This makes the encryption and key management much more portable and easier to work with. We'll need some kernel support, however, to map the memory, which should not be too bad. Think about whole-image encryption here, though. It seems like it will be tricky to do that. -The Interface- This is an important part to get right. It has to be easy to use. In terms of the API, perhaps something as simple as: void *memcrypt_malloc(size_t nbytes); void *memcrypt_free(void *p); Should we allow the application to encrypt at will, too? Or just on-demand from the user? int memcrypt_encrypt(void *p); /* NULL to encrypt all memory? */ int memcrypt_decrypt(void *p); /* NULL to decrypt all memory? */ NOTE: passing a NULL pointer to encrypt/decrypt all memory is probably a bad idea from an interface perspective. How about int memcrypt_encrypt_all(void); int memcrypt_decrypt_all(void); -The Dedicated Daemon Approach- There is a daemon that runs on a per-user basis. This daemon handles all key management and cryptographic operations. There is a thin kernel layer that provides a device, /dev/memcrypt. The daemon sleeps (select()) on this device, and the kernel layer passes messages down. The "application" is the client application utilizing the protected memory scheme. The "daemon" is the daemon handling key management and cryptographic operations. When an application allocates protected memory, the memory is first allocated, and the kernel is notified (through a syscall, or ioctl() on /dev/memcrypt). The kernel in turn notifies the daemon that there is a new memory area to be protected, and the daemon requests for that memory to be mapped into its address space. When this process completes and the memory is mapped into the daemon's address space, the system call or ioctl() on /dev/memcrypt from the application returns, and execution resumes as usual. If the daemon dies: All applications with protected memory are stopped, all the protected memory is scribbled, and the applications are killed. When the user requests encryption, the user provides some key material to the daemon, and the daemon SIGSTOPs each client application, and proceeds to encrypt its memory. When the user wishes to decrypt the memory, the user again provides the necessary key material to perform the decryption, and the daemon then proceeds to decrypt the processes' protected memory, and then SIGCONTs each process. The key used for decryption can be stored until the user encrypts again, that way it only has to be provided initially and on each decryption. |