diff options
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/power/Kconfig | 12 | ||||
| -rw-r--r-- | kernel/power/swsusp.c | 164 |
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 | ||
| 75 | config 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 | |||
| 75 | config SUSPEND_SMP | 87 | config 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 */ |
| 77 | extern const void __nosave_begin, __nosave_end; | 88 | extern 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 | ||
| 105 | static struct swsusp_header { | 116 | static 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; | |||
| 129 | static unsigned short swapfile_used[MAX_SWAPFILES]; | 141 | static unsigned short swapfile_used[MAX_SWAPFILES]; |
| 130 | static unsigned short root_swap; | 142 | static unsigned short root_swap; |
| 131 | 143 | ||
| 144 | static int write_page(unsigned long addr, swp_entry_t * loc); | ||
| 145 | static int bio_read_page(pgoff_t page_off, void * page); | ||
| 146 | |||
| 147 | static u8 key_iv[MAXKEY+MAXIV]; | ||
| 148 | |||
| 149 | #ifdef CONFIG_SWSUSP_ENCRYPT | ||
| 150 | |||
| 151 | static 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 | |||
| 200 | fail: crypto_free_tfm(tfm); | ||
| 201 | out: return error; | ||
| 202 | } | ||
| 203 | |||
| 204 | static __inline__ void crypto_exit(void *mem) | ||
| 205 | { | ||
| 206 | crypto_free_tfm((struct crypto_tfm *)mem); | ||
| 207 | } | ||
| 208 | |||
| 209 | static __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 | |||
| 230 | static __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 | ||
| 249 | static __inline__ int crypto_init(int mode, void *mem) | ||
| 250 | { | ||
| 251 | return 0; | ||
| 252 | } | ||
| 253 | |||
| 254 | static __inline__ void crypto_exit(void *mem) | ||
| 255 | { | ||
| 256 | } | ||
| 257 | |||
| 258 | static __inline__ int crypto_write(struct pbe *p, void *mem) | ||
| 259 | { | ||
| 260 | return write_page(p->address, &(p->swap_address)); | ||
| 261 | } | ||
| 262 | |||
| 263 | static __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 | |||
| 132 | static int mark_swapfiles(swp_entry_t prev) | 269 | static 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"); |
