aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorAndreas Steinmetz <ast@domdv.de>2005-09-03 18:56:59 -0400
committerLinus Torvalds <torvalds@evo.osdl.org>2005-09-05 03:06:16 -0400
commitc2ff18f4070f6303a81fd7d9d967d7c9e01b588f (patch)
tree250a50e27885a179d0fee37cef31cf3a4d4627d3 /kernel
parent583a4e88db1eadc52116e1f97b4519de655b2b80 (diff)
[PATCH] encrypt suspend data for easy wiping
The patch protects from leaking sensitive data after resume from suspend. During suspend a temporary key is created and this key is used to encrypt the data written to disk. When, during resume, the data was read back into memory the temporary key is destroyed which simply means that all data written to disk during suspend are then inaccessible so they can't be stolen lateron. Think of the following: you suspend while an application is running that keeps sensitive data in memory. The application itself prevents the data from being swapped out. Suspend, however, must write these data to swap to be able to resume lateron. Without suspend encryption your sensitive data are then stored in plaintext on disk. This means that after resume your sensitive data are accessible to all applications having direct access to the swap device which was used for suspend. If you don't need swap after resume these data can remain on disk virtually forever. Thus it can happen that your system gets broken in weeks later and sensitive data which you thought were encrypted and protected are retrieved and stolen from the swap device. Signed-off-by: Andreas Steinmetz <ast@domdv.de> Acked-by: Pavel Machek <pavel@suse.cz> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/power/Kconfig12
-rw-r--r--kernel/power/swsusp.c164
2 files changed, 171 insertions, 5 deletions
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 2c7121d9bff1..917066a5767c 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -72,6 +72,18 @@ config PM_STD_PARTITION
72 suspended image to. It will simply pick the first available swap 72 suspended image to. It will simply pick the first available swap
73 device. 73 device.
74 74
75config SWSUSP_ENCRYPT
76 bool "Encrypt suspend image"
77 depends on SOFTWARE_SUSPEND && CRYPTO=y && (CRYPTO_AES=y || CRYPTO_AES_586=y || CRYPTO_AES_X86_64=y)
78 default ""
79 ---help---
80 To prevent data gathering from swap after resume you can encrypt
81 the suspend image with a temporary key that is deleted on
82 resume.
83
84 Note that the temporary key is stored unencrypted on disk while the
85 system is suspended.
86
75config SUSPEND_SMP 87config SUSPEND_SMP
76 bool 88 bool
77 depends on HOTPLUG_CPU && X86 && PM 89 depends on HOTPLUG_CPU && X86 && PM
diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c
index 975b1648a806..b041cea2e878 100644
--- a/kernel/power/swsusp.c
+++ b/kernel/power/swsusp.c
@@ -31,6 +31,9 @@
31 * Alex Badea <vampire@go.ro>: 31 * Alex Badea <vampire@go.ro>:
32 * Fixed runaway init 32 * Fixed runaway init
33 * 33 *
34 * Andreas Steinmetz <ast@domdv.de>:
35 * Added encrypted suspend option
36 *
34 * More state savers are welcome. Especially for the scsi layer... 37 * More state savers are welcome. Especially for the scsi layer...
35 * 38 *
36 * For TODOs,FIXMEs also look in Documentation/power/swsusp.txt 39 * For TODOs,FIXMEs also look in Documentation/power/swsusp.txt
@@ -71,8 +74,16 @@
71#include <asm/tlbflush.h> 74#include <asm/tlbflush.h>
72#include <asm/io.h> 75#include <asm/io.h>
73 76
77#include <linux/random.h>
78#include <linux/crypto.h>
79#include <asm/scatterlist.h>
80
74#include "power.h" 81#include "power.h"
75 82
83#define CIPHER "aes"
84#define MAXKEY 32
85#define MAXIV 32
86
76/* References to section boundaries */ 87/* References to section boundaries */
77extern const void __nosave_begin, __nosave_end; 88extern const void __nosave_begin, __nosave_end;
78 89
@@ -103,7 +114,8 @@ static suspend_pagedir_t *pagedir_save;
103#define SWSUSP_SIG "S1SUSPEND" 114#define SWSUSP_SIG "S1SUSPEND"
104 115
105static struct swsusp_header { 116static struct swsusp_header {
106 char reserved[PAGE_SIZE - 20 - sizeof(swp_entry_t)]; 117 char reserved[PAGE_SIZE - 20 - MAXKEY - MAXIV - sizeof(swp_entry_t)];
118 u8 key_iv[MAXKEY+MAXIV];
107 swp_entry_t swsusp_info; 119 swp_entry_t swsusp_info;
108 char orig_sig[10]; 120 char orig_sig[10];
109 char sig[10]; 121 char sig[10];
@@ -129,6 +141,131 @@ static struct swsusp_info swsusp_info;
129static unsigned short swapfile_used[MAX_SWAPFILES]; 141static unsigned short swapfile_used[MAX_SWAPFILES];
130static unsigned short root_swap; 142static unsigned short root_swap;
131 143
144static int write_page(unsigned long addr, swp_entry_t * loc);
145static int bio_read_page(pgoff_t page_off, void * page);
146
147static u8 key_iv[MAXKEY+MAXIV];
148
149#ifdef CONFIG_SWSUSP_ENCRYPT
150
151static int crypto_init(int mode, void **mem)
152{
153 int error = 0;
154 int len;
155 char *modemsg;
156 struct crypto_tfm *tfm;
157
158 modemsg = mode ? "suspend not possible" : "resume not possible";
159
160 tfm = crypto_alloc_tfm(CIPHER, CRYPTO_TFM_MODE_CBC);
161 if(!tfm) {
162 printk(KERN_ERR "swsusp: no tfm, %s\n", modemsg);
163 error = -EINVAL;
164 goto out;
165 }
166
167 if(MAXKEY < crypto_tfm_alg_min_keysize(tfm)) {
168 printk(KERN_ERR "swsusp: key buffer too small, %s\n", modemsg);
169 error = -ENOKEY;
170 goto fail;
171 }
172
173 if (mode)
174 get_random_bytes(key_iv, MAXKEY+MAXIV);
175
176 len = crypto_tfm_alg_max_keysize(tfm);
177 if (len > MAXKEY)
178 len = MAXKEY;
179
180 if (crypto_cipher_setkey(tfm, key_iv, len)) {
181 printk(KERN_ERR "swsusp: key setup failure, %s\n", modemsg);
182 error = -EKEYREJECTED;
183 goto fail;
184 }
185
186 len = crypto_tfm_alg_ivsize(tfm);
187
188 if (MAXIV < len) {
189 printk(KERN_ERR "swsusp: iv buffer too small, %s\n", modemsg);
190 error = -EOVERFLOW;
191 goto fail;
192 }
193
194 crypto_cipher_set_iv(tfm, key_iv+MAXKEY, len);
195
196 *mem=(void *)tfm;
197
198 goto out;
199
200fail: crypto_free_tfm(tfm);
201out: return error;
202}
203
204static __inline__ void crypto_exit(void *mem)
205{
206 crypto_free_tfm((struct crypto_tfm *)mem);
207}
208
209static __inline__ int crypto_write(struct pbe *p, void *mem)
210{
211 int error = 0;
212 struct scatterlist src, dst;
213
214 src.page = virt_to_page(p->address);
215 src.offset = 0;
216 src.length = PAGE_SIZE;
217 dst.page = virt_to_page((void *)&swsusp_header);
218 dst.offset = 0;
219 dst.length = PAGE_SIZE;
220
221 error = crypto_cipher_encrypt((struct crypto_tfm *)mem, &dst, &src,
222 PAGE_SIZE);
223
224 if (!error)
225 error = write_page((unsigned long)&swsusp_header,
226 &(p->swap_address));
227 return error;
228}
229
230static __inline__ int crypto_read(struct pbe *p, void *mem)
231{
232 int error = 0;
233 struct scatterlist src, dst;
234
235 error = bio_read_page(swp_offset(p->swap_address), (void *)p->address);
236 if (!error) {
237 src.offset = 0;
238 src.length = PAGE_SIZE;
239 dst.offset = 0;
240 dst.length = PAGE_SIZE;
241 src.page = dst.page = virt_to_page((void *)p->address);
242
243 error = crypto_cipher_decrypt((struct crypto_tfm *)mem, &dst,
244 &src, PAGE_SIZE);
245 }
246 return error;
247}
248#else
249static __inline__ int crypto_init(int mode, void *mem)
250{
251 return 0;
252}
253
254static __inline__ void crypto_exit(void *mem)
255{
256}
257
258static __inline__ int crypto_write(struct pbe *p, void *mem)
259{
260 return write_page(p->address, &(p->swap_address));
261}
262
263static __inline__ int crypto_read(struct pbe *p, void *mem)
264{
265 return bio_read_page(swp_offset(p->swap_address), (void *)p->address);
266}
267#endif
268
132static int mark_swapfiles(swp_entry_t prev) 269static int mark_swapfiles(swp_entry_t prev)
133{ 270{
134 int error; 271 int error;
@@ -140,6 +277,7 @@ static int mark_swapfiles(swp_entry_t prev)
140 !memcmp("SWAPSPACE2",swsusp_header.sig, 10)) { 277 !memcmp("SWAPSPACE2",swsusp_header.sig, 10)) {
141 memcpy(swsusp_header.orig_sig,swsusp_header.sig, 10); 278 memcpy(swsusp_header.orig_sig,swsusp_header.sig, 10);
142 memcpy(swsusp_header.sig,SWSUSP_SIG, 10); 279 memcpy(swsusp_header.sig,SWSUSP_SIG, 10);
280 memcpy(swsusp_header.key_iv, key_iv, MAXKEY+MAXIV);
143 swsusp_header.swsusp_info = prev; 281 swsusp_header.swsusp_info = prev;
144 error = rw_swap_page_sync(WRITE, 282 error = rw_swap_page_sync(WRITE,
145 swp_entry(root_swap, 0), 283 swp_entry(root_swap, 0),
@@ -286,6 +424,10 @@ static int data_write(void)
286 int error = 0, i = 0; 424 int error = 0, i = 0;
287 unsigned int mod = nr_copy_pages / 100; 425 unsigned int mod = nr_copy_pages / 100;
288 struct pbe *p; 426 struct pbe *p;
427 void *tfm;
428
429 if ((error = crypto_init(1, &tfm)))
430 return error;
289 431
290 if (!mod) 432 if (!mod)
291 mod = 1; 433 mod = 1;
@@ -294,11 +436,14 @@ static int data_write(void)
294 for_each_pbe (p, pagedir_nosave) { 436 for_each_pbe (p, pagedir_nosave) {
295 if (!(i%mod)) 437 if (!(i%mod))
296 printk( "\b\b\b\b%3d%%", i / mod ); 438 printk( "\b\b\b\b%3d%%", i / mod );
297 if ((error = write_page(p->address, &(p->swap_address)))) 439 if ((error = crypto_write(p, tfm))) {
440 crypto_exit(tfm);
298 return error; 441 return error;
442 }
299 i++; 443 i++;
300 } 444 }
301 printk("\b\b\b\bdone\n"); 445 printk("\b\b\b\bdone\n");
446 crypto_exit(tfm);
302 return error; 447 return error;
303} 448}
304 449
@@ -400,6 +545,7 @@ static int write_suspend_image(void)
400 if ((error = close_swap())) 545 if ((error = close_swap()))
401 goto FreePagedir; 546 goto FreePagedir;
402 Done: 547 Done:
548 memset(key_iv, 0, MAXKEY+MAXIV);
403 return error; 549 return error;
404 FreePagedir: 550 FreePagedir:
405 free_pagedir_entries(); 551 free_pagedir_entries();
@@ -1212,6 +1358,8 @@ static int check_sig(void)
1212 return error; 1358 return error;
1213 if (!memcmp(SWSUSP_SIG, swsusp_header.sig, 10)) { 1359 if (!memcmp(SWSUSP_SIG, swsusp_header.sig, 10)) {
1214 memcpy(swsusp_header.sig, swsusp_header.orig_sig, 10); 1360 memcpy(swsusp_header.sig, swsusp_header.orig_sig, 10);
1361 memcpy(key_iv, swsusp_header.key_iv, MAXKEY+MAXIV);
1362 memset(swsusp_header.key_iv, 0, MAXKEY+MAXIV);
1215 1363
1216 /* 1364 /*
1217 * Reset swap signature now. 1365 * Reset swap signature now.
@@ -1239,6 +1387,10 @@ static int data_read(struct pbe *pblist)
1239 int error = 0; 1387 int error = 0;
1240 int i = 0; 1388 int i = 0;
1241 int mod = swsusp_info.image_pages / 100; 1389 int mod = swsusp_info.image_pages / 100;
1390 void *tfm;
1391
1392 if ((error = crypto_init(0, &tfm)))
1393 return error;
1242 1394
1243 if (!mod) 1395 if (!mod)
1244 mod = 1; 1396 mod = 1;
@@ -1250,14 +1402,15 @@ static int data_read(struct pbe *pblist)
1250 if (!(i % mod)) 1402 if (!(i % mod))
1251 printk("\b\b\b\b%3d%%", i / mod); 1403 printk("\b\b\b\b%3d%%", i / mod);
1252 1404
1253 error = bio_read_page(swp_offset(p->swap_address), 1405 if ((error = crypto_read(p, tfm))) {
1254 (void *)p->address); 1406 crypto_exit(tfm);
1255 if (error)
1256 return error; 1407 return error;
1408 }
1257 1409
1258 i++; 1410 i++;
1259 } 1411 }
1260 printk("\b\b\b\bdone\n"); 1412 printk("\b\b\b\bdone\n");
1413 crypto_exit(tfm);
1261 return error; 1414 return error;
1262} 1415}
1263 1416
@@ -1385,6 +1538,7 @@ int swsusp_read(void)
1385 1538
1386 error = read_suspend_image(); 1539 error = read_suspend_image();
1387 blkdev_put(resume_bdev); 1540 blkdev_put(resume_bdev);
1541 memset(key_iv, 0, MAXKEY+MAXIV);
1388 1542
1389 if (!error) 1543 if (!error)
1390 pr_debug("swsusp: Reading resume file was successful\n"); 1544 pr_debug("swsusp: Reading resume file was successful\n");