Kees Cook | 498977a | 2012-02-27 12:43:15 -0800 | [diff] [blame] | 1 | /* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| 2 | * Use of this source code is governed by a BSD-style license that can be |
| 3 | * found in the LICENSE file. |
| 4 | * |
| 5 | * This is a collection of helper utilities for use with the "mount-encrypted" |
| 6 | * utility. |
| 7 | * |
| 8 | */ |
| 9 | #define _GNU_SOURCE |
| 10 | #define _FILE_OFFSET_BITS 64 |
| 11 | #include <stdint.h> |
| 12 | #include <stdio.h> |
| 13 | #include <stdlib.h> |
| 14 | #include <unistd.h> |
| 15 | #include <string.h> |
| 16 | #include <errno.h> |
| 17 | #include <fcntl.h> |
| 18 | #include <sys/ioctl.h> |
| 19 | #include <sys/stat.h> |
| 20 | #include <sys/statvfs.h> |
| 21 | #include <sys/time.h> |
| 22 | #include <sys/types.h> |
| 23 | #include <sys/mount.h> |
| 24 | #include <linux/fs.h> |
| 25 | #include <linux/loop.h> |
| 26 | |
| 27 | #include <glib.h> |
| 28 | #include <glib/gstdio.h> |
| 29 | |
| 30 | #include <openssl/evp.h> |
| 31 | |
| 32 | #include "mount-encrypted.h" |
| 33 | #include "mount-helpers.h" |
| 34 | |
| 35 | static const gchar * const kRootDir = "/"; |
| 36 | static const gchar * const kLoopTemplate = "/dev/loop%d"; |
| 37 | static const int kLoopMajor = 7; |
| 38 | static const int kLoopMax = 8; |
| 39 | static const unsigned int kResizeStepSeconds = 2; |
| 40 | static const size_t kResizeBlocks = 32768 * 10; |
| 41 | static const gchar * const kExt4ExtendedOptions = "discard,lazy_itable_init"; |
| 42 | |
| 43 | int remove_tree(const char *tree) |
| 44 | { |
| 45 | const gchar *rm[] = { |
| 46 | "/bin/rm", "-rf", tree, |
| 47 | NULL |
| 48 | }; |
| 49 | |
| 50 | return runcmd(rm, NULL); |
| 51 | } |
| 52 | |
| 53 | size_t get_sectors(const char *device) |
| 54 | { |
| 55 | size_t sectors; |
| 56 | int fd; |
| 57 | if ((fd = open(device, O_RDONLY | O_NOFOLLOW)) < 0) { |
| 58 | PERROR("open(%s)", device); |
| 59 | return 0; |
| 60 | } |
| 61 | if (ioctl(fd, BLKGETSIZE, §ors)) { |
| 62 | PERROR("ioctl(%s, BLKGETSIZE)", device); |
| 63 | return 0; |
| 64 | } |
| 65 | close(fd); |
| 66 | return sectors; |
| 67 | } |
| 68 | |
| 69 | int runcmd(const gchar *argv[], gchar **output) |
| 70 | { |
| 71 | gint rc; |
| 72 | gchar *out = NULL, *errout = NULL; |
| 73 | GError *err = NULL; |
| 74 | |
| 75 | g_spawn_sync(kRootDir, (gchar **)argv, NULL, 0, NULL, NULL, |
| 76 | &out, &errout, &rc, &err); |
| 77 | if (err) { |
| 78 | ERROR("%s: %s", argv[0], err->message); |
| 79 | g_error_free(err); |
| 80 | return -1; |
| 81 | } |
| 82 | |
| 83 | if (rc) |
| 84 | ERROR("%s failed (%d)\n%s\n%s", argv[0], rc, out, errout); |
| 85 | |
| 86 | if (output) |
| 87 | *output = out; |
| 88 | else |
| 89 | g_free(out); |
| 90 | g_free(errout); |
| 91 | |
| 92 | return rc; |
| 93 | } |
| 94 | |
| 95 | int same_vfs(const char *mnt_a, const char *mnt_b) |
| 96 | { |
| 97 | struct statvfs stat_a, stat_b; |
| 98 | |
| 99 | if (statvfs(mnt_a, &stat_a)) { |
| 100 | PERROR("statvfs(%s)", mnt_a); |
| 101 | exit(1); |
| 102 | } |
| 103 | if (statvfs(mnt_b, &stat_b)) { |
| 104 | PERROR("statvfs(%s)", mnt_b); |
| 105 | exit(1); |
| 106 | } |
| 107 | return (stat_a.f_fsid == stat_b.f_fsid); |
| 108 | } |
| 109 | |
| 110 | /* Returns allocated string that holds [length]*2 + 1 characters. */ |
| 111 | char *stringify_hex(uint8_t *binary, size_t length) |
| 112 | { |
| 113 | char *string; |
| 114 | size_t i; |
| 115 | |
| 116 | string = malloc(length * 2 + 1); |
| 117 | if (!string) { |
| 118 | PERROR("malloc"); |
| 119 | return NULL; |
| 120 | } |
| 121 | for (i = 0; i < length; ++i) |
| 122 | sprintf(string + (i * 2), "%02x", binary[i]); |
| 123 | string[length * 2] = '\0'; |
| 124 | |
| 125 | return string; |
| 126 | } |
| 127 | |
| 128 | /* Returns allocated byte array that holds strlen([string])/2 bytes. */ |
| 129 | uint8_t *hexify_string(char *string, uint8_t *binary, size_t length) |
| 130 | { |
| 131 | size_t bytes, i; |
| 132 | |
| 133 | bytes = strlen(string) / 2; |
| 134 | if (bytes > length) { |
| 135 | ERROR("Hex string too long (%zu) for byte array (%zu)", |
| 136 | bytes, length); |
| 137 | return NULL; |
| 138 | } |
| 139 | |
| 140 | for (i = 0; i < bytes; ++i) { |
| 141 | if (sscanf(&string[i * 2], "%2hhx", &binary[i]) != 1) { |
| 142 | ERROR("Invalid hex code at byte %zu.", i); |
| 143 | return NULL; |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | return binary; |
| 148 | } |
| 149 | |
| 150 | static int is_loop_device(int fd) |
| 151 | { |
| 152 | struct stat info; |
| 153 | |
| 154 | return (fstat(fd, &info) == 0 && S_ISBLK(info.st_mode) && |
| 155 | major(info.st_rdev) == kLoopMajor); |
| 156 | } |
| 157 | |
Kees Cook | e97760c | 2012-05-04 16:52:02 -0700 | [diff] [blame] | 158 | static int loop_is_attached(int fd, struct loop_info64 *info) |
Kees Cook | 498977a | 2012-02-27 12:43:15 -0800 | [diff] [blame] | 159 | { |
Kees Cook | e97760c | 2012-05-04 16:52:02 -0700 | [diff] [blame] | 160 | struct loop_info64 local_info; |
Kees Cook | 498977a | 2012-02-27 12:43:15 -0800 | [diff] [blame] | 161 | |
Kees Cook | e97760c | 2012-05-04 16:52:02 -0700 | [diff] [blame] | 162 | return ioctl(fd, LOOP_GET_STATUS64, info ? info : &local_info) == 0; |
Kees Cook | 498977a | 2012-02-27 12:43:15 -0800 | [diff] [blame] | 163 | } |
| 164 | |
Kees Cook | e97760c | 2012-05-04 16:52:02 -0700 | [diff] [blame] | 165 | /* Returns either the matching loopback name, or next available, if NULL. */ |
| 166 | static int loop_locate(gchar **loopback, const char *name) |
Kees Cook | 498977a | 2012-02-27 12:43:15 -0800 | [diff] [blame] | 167 | { |
Kees Cook | e97760c | 2012-05-04 16:52:02 -0700 | [diff] [blame] | 168 | int i, fd, namelen = 0; |
| 169 | |
| 170 | if (name) { |
| 171 | namelen = strlen(name); |
Kees Cook | 655cc11 | 2012-05-23 16:35:56 -0700 | [diff] [blame] | 172 | if (namelen >= LO_NAME_SIZE) { |
| 173 | ERROR("'%s' too long (>= %d)", name, LO_NAME_SIZE); |
Kees Cook | e97760c | 2012-05-04 16:52:02 -0700 | [diff] [blame] | 174 | return -1; |
Kees Cook | 655cc11 | 2012-05-23 16:35:56 -0700 | [diff] [blame] | 175 | } |
Kees Cook | e97760c | 2012-05-04 16:52:02 -0700 | [diff] [blame] | 176 | } |
Kees Cook | 498977a | 2012-02-27 12:43:15 -0800 | [diff] [blame] | 177 | |
| 178 | *loopback = NULL; |
| 179 | for (i = 0; i < kLoopMax; ++i) { |
Kees Cook | e97760c | 2012-05-04 16:52:02 -0700 | [diff] [blame] | 180 | struct loop_info64 info; |
| 181 | int attached; |
| 182 | |
Kees Cook | 498977a | 2012-02-27 12:43:15 -0800 | [diff] [blame] | 183 | g_free(*loopback); |
| 184 | *loopback = g_strdup_printf(kLoopTemplate, i); |
| 185 | if (!*loopback) { |
| 186 | PERROR("g_strdup_printf"); |
| 187 | return -1; |
| 188 | } |
| 189 | |
| 190 | fd = open(*loopback, O_RDONLY | O_NOFOLLOW); |
| 191 | if (fd < 0) { |
| 192 | PERROR("open(%s)", *loopback); |
| 193 | goto failed; |
| 194 | } |
Kees Cook | e97760c | 2012-05-04 16:52:02 -0700 | [diff] [blame] | 195 | if (!is_loop_device(fd)) { |
Kees Cook | 498977a | 2012-02-27 12:43:15 -0800 | [diff] [blame] | 196 | close(fd); |
Kees Cook | e97760c | 2012-05-04 16:52:02 -0700 | [diff] [blame] | 197 | continue; |
| 198 | } |
| 199 | |
| 200 | memset(&info, 0, sizeof(info)); |
| 201 | attached = loop_is_attached(fd, &info); |
| 202 | close(fd); |
| 203 | |
Kees Cook | 655cc11 | 2012-05-23 16:35:56 -0700 | [diff] [blame] | 204 | if (attached) |
| 205 | DEBUG("Saw %s on %s", info.lo_file_name, *loopback); |
| 206 | |
Kees Cook | e97760c | 2012-05-04 16:52:02 -0700 | [diff] [blame] | 207 | if ((attached && name && |
| 208 | strncmp((char *)info.lo_file_name, name, namelen) == 0) || |
| 209 | (!attached && !name)) { |
Kees Cook | 655cc11 | 2012-05-23 16:35:56 -0700 | [diff] [blame] | 210 | DEBUG("Using %s", *loopback); |
Kees Cook | e97760c | 2012-05-04 16:52:02 -0700 | [diff] [blame] | 211 | /* Reopen for working on it. */ |
Kees Cook | 498977a | 2012-02-27 12:43:15 -0800 | [diff] [blame] | 212 | fd = open(*loopback, O_RDWR | O_NOFOLLOW); |
Kees Cook | e97760c | 2012-05-04 16:52:02 -0700 | [diff] [blame] | 213 | if (is_loop_device(fd) && |
| 214 | loop_is_attached(fd, NULL) == attached) |
Kees Cook | 498977a | 2012-02-27 12:43:15 -0800 | [diff] [blame] | 215 | return fd; |
| 216 | } |
Kees Cook | 498977a | 2012-02-27 12:43:15 -0800 | [diff] [blame] | 217 | } |
| 218 | ERROR("Ran out of loopback devices"); |
| 219 | |
| 220 | failed: |
| 221 | g_free(*loopback); |
| 222 | *loopback = NULL; |
| 223 | return -1; |
| 224 | } |
| 225 | |
Kees Cook | e97760c | 2012-05-04 16:52:02 -0700 | [diff] [blame] | 226 | static int loop_detach_fd(int fd) |
| 227 | { |
| 228 | if (ioctl(fd, LOOP_CLR_FD, 0)) { |
| 229 | PERROR("LOOP_CLR_FD"); |
| 230 | return 0; |
| 231 | } |
| 232 | return 1; |
| 233 | } |
| 234 | |
Kees Cook | 498977a | 2012-02-27 12:43:15 -0800 | [diff] [blame] | 235 | int loop_detach(const gchar *loopback) |
| 236 | { |
Kees Cook | e97760c | 2012-05-04 16:52:02 -0700 | [diff] [blame] | 237 | int fd, rc = 1; |
Kees Cook | 498977a | 2012-02-27 12:43:15 -0800 | [diff] [blame] | 238 | |
| 239 | fd = open(loopback, O_RDONLY | O_NOFOLLOW); |
| 240 | if (fd < 0) { |
| 241 | PERROR("open(%s)", loopback); |
| 242 | return 0; |
| 243 | } |
Kees Cook | e97760c | 2012-05-04 16:52:02 -0700 | [diff] [blame] | 244 | if (!is_loop_device(fd) || !loop_is_attached(fd, NULL) || |
| 245 | !loop_detach_fd(fd)) |
| 246 | rc = 0; |
Kees Cook | 498977a | 2012-02-27 12:43:15 -0800 | [diff] [blame] | 247 | |
| 248 | close (fd); |
Kees Cook | e97760c | 2012-05-04 16:52:02 -0700 | [diff] [blame] | 249 | return rc; |
Kees Cook | 498977a | 2012-02-27 12:43:15 -0800 | [diff] [blame] | 250 | } |
| 251 | |
Kees Cook | e97760c | 2012-05-04 16:52:02 -0700 | [diff] [blame] | 252 | int loop_detach_name(const char *name) |
| 253 | { |
| 254 | gchar *loopback = NULL; |
| 255 | int loopfd, rc; |
| 256 | |
| 257 | loopfd = loop_locate(&loopback, name); |
| 258 | if (loopfd < 0) |
| 259 | return 0; |
| 260 | rc = loop_detach_fd(loopfd); |
| 261 | |
| 262 | close(loopfd); |
| 263 | g_free(loopback); |
| 264 | return rc; |
| 265 | } |
| 266 | |
| 267 | /* Closes fd, returns name of loopback device pathname. */ |
Kees Cook | 498977a | 2012-02-27 12:43:15 -0800 | [diff] [blame] | 268 | gchar *loop_attach(int fd, const char *name) |
| 269 | { |
| 270 | gchar *loopback = NULL; |
| 271 | int loopfd; |
| 272 | struct loop_info64 info; |
| 273 | |
Kees Cook | e97760c | 2012-05-04 16:52:02 -0700 | [diff] [blame] | 274 | loopfd = loop_locate(&loopback, NULL); |
Kees Cook | 498977a | 2012-02-27 12:43:15 -0800 | [diff] [blame] | 275 | if (loopfd < 0) |
| 276 | return NULL; |
| 277 | if (ioctl(loopfd, LOOP_SET_FD, fd) < 0) { |
| 278 | PERROR("LOOP_SET_FD"); |
| 279 | goto failed; |
| 280 | } |
| 281 | |
| 282 | memset(&info, 0, sizeof(info)); |
| 283 | strncpy((char*)info.lo_file_name, name, LO_NAME_SIZE); |
| 284 | if (ioctl(loopfd, LOOP_SET_STATUS64, &info)) { |
| 285 | PERROR("LOOP_SET_STATUS64"); |
| 286 | goto failed; |
| 287 | } |
| 288 | |
| 289 | close(loopfd); |
| 290 | close(fd); |
| 291 | return loopback; |
| 292 | failed: |
| 293 | close(loopfd); |
| 294 | close(fd); |
| 295 | g_free(loopback); |
| 296 | return 0; |
| 297 | } |
| 298 | |
| 299 | int dm_setup(size_t sectors, const gchar *encryption_key, const char *name, |
| 300 | const gchar *device, const char *path) |
| 301 | { |
| 302 | /* Mount loopback device with dm-crypt using the encryption key. */ |
| 303 | gchar *table = g_strdup_printf("0 %zu crypt " \ |
| 304 | "aes-cbc-essiv:sha256 %s " \ |
| 305 | "0 %s 0 " \ |
| 306 | "1 allow_discards", |
| 307 | sectors, |
| 308 | encryption_key, |
| 309 | device); |
| 310 | if (!table) { |
| 311 | PERROR("g_strdup_printf"); |
| 312 | return 0; |
| 313 | } |
| 314 | |
| 315 | const gchar *argv[] = { |
| 316 | "/sbin/dmsetup", |
| 317 | "create", name, |
| 318 | "--noudevrules", "--noudevsync", |
| 319 | "--table", table, |
| 320 | NULL |
| 321 | }; |
| 322 | |
| 323 | /* TODO(keescook): replace with call to libdevmapper. */ |
| 324 | if (runcmd(argv, NULL) != 0) { |
| 325 | g_free(table); |
| 326 | return 0; |
| 327 | } |
| 328 | g_free(table); |
| 329 | |
| 330 | /* Make sure the dm-crypt device showed up. */ |
| 331 | if (access(path, R_OK)) { |
| 332 | ERROR("%s does not exist", path); |
| 333 | return 0; |
| 334 | } |
| 335 | |
| 336 | return 1; |
| 337 | } |
| 338 | |
Kees Cook | e97760c | 2012-05-04 16:52:02 -0700 | [diff] [blame] | 339 | int dm_teardown(const gchar *device) |
Kees Cook | 498977a | 2012-02-27 12:43:15 -0800 | [diff] [blame] | 340 | { |
| 341 | const char *argv[] = { |
| 342 | "/sbin/dmsetup", |
| 343 | "remove", device, |
| 344 | "--noudevrules", "--noudevsync", |
| 345 | NULL |
| 346 | }; |
| 347 | /* TODO(keescook): replace with call to libdevmapper. */ |
Kees Cook | e97760c | 2012-05-04 16:52:02 -0700 | [diff] [blame] | 348 | if (runcmd(argv, NULL) != 0) |
| 349 | return 0; |
| 350 | return 1; |
Kees Cook | 498977a | 2012-02-27 12:43:15 -0800 | [diff] [blame] | 351 | } |
| 352 | |
| 353 | char *dm_get_key(const gchar *device) |
| 354 | { |
| 355 | gchar *output = NULL; |
| 356 | char *key; |
| 357 | int i; |
| 358 | const char *argv[] = { |
| 359 | "/sbin/dmsetup", |
| 360 | "table", "--showkeys", |
| 361 | device, |
| 362 | NULL |
| 363 | }; |
| 364 | /* TODO(keescook): replace with call to libdevmapper. */ |
| 365 | if (runcmd(argv, &output) != 0) |
| 366 | return NULL; |
| 367 | |
| 368 | /* Key is 4th field in the output. */ |
| 369 | for (i = 0, key = strtok(output, " "); |
| 370 | i < 4 && key; |
| 371 | ++i, key = strtok(NULL, " ")) { } |
| 372 | |
| 373 | /* Create a copy of the key and free the output buffer. */ |
| 374 | if (key) { |
| 375 | key = strdup(key); |
| 376 | g_free(output); |
| 377 | } |
| 378 | |
| 379 | return key; |
| 380 | } |
| 381 | |
| 382 | int sparse_create(const char *path, size_t size) |
| 383 | { |
| 384 | int sparsefd; |
| 385 | |
| 386 | sparsefd = open(path, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, |
| 387 | S_IRUSR | S_IWUSR); |
| 388 | if (sparsefd < 0) |
| 389 | goto out; |
| 390 | |
| 391 | if (ftruncate(sparsefd, size)) { |
| 392 | int saved_errno = errno; |
| 393 | |
| 394 | close(sparsefd); |
| 395 | unlink(path); |
| 396 | errno = saved_errno; |
| 397 | |
| 398 | sparsefd = -1; |
| 399 | } |
| 400 | |
| 401 | out: |
| 402 | return sparsefd; |
| 403 | } |
| 404 | |
| 405 | int filesystem_build(const char *device, size_t block_bytes, size_t blocks_min, |
| 406 | size_t blocks_max) |
| 407 | { |
| 408 | int rc = 0; |
| 409 | |
| 410 | gchar *blocksize = g_strdup_printf("%zu", block_bytes); |
| 411 | if (!blocksize) { |
| 412 | PERROR("g_strdup_printf"); |
| 413 | goto out; |
| 414 | } |
| 415 | |
| 416 | gchar *blocks_str; |
| 417 | blocks_str = g_strdup_printf("%zu", blocks_min); |
| 418 | if (!blocks_str) { |
| 419 | PERROR("g_strdup_printf"); |
| 420 | goto free_blocksize; |
| 421 | } |
| 422 | |
| 423 | gchar *extended; |
| 424 | if (blocks_min < blocks_max) { |
| 425 | extended = g_strdup_printf("%s,resize=%zu", |
| 426 | kExt4ExtendedOptions, blocks_max); |
| 427 | } else { |
| 428 | extended = g_strdup_printf("%s", kExt4ExtendedOptions); |
| 429 | } |
| 430 | if (!extended) { |
| 431 | PERROR("g_strdup_printf"); |
| 432 | goto free_blocks_str; |
| 433 | } |
| 434 | |
| 435 | const gchar *mkfs[] = { |
| 436 | "/sbin/mkfs.ext4", |
| 437 | "-T", "default", |
| 438 | "-b", blocksize, |
| 439 | "-m", "0", |
| 440 | "-O", "^huge_file,^flex_bg", |
| 441 | "-E", extended, |
| 442 | device, |
| 443 | blocks_str, |
| 444 | NULL |
| 445 | }; |
| 446 | |
| 447 | rc = (runcmd(mkfs, NULL) == 0); |
| 448 | if (!rc) |
| 449 | goto free_extended; |
| 450 | |
| 451 | const gchar *tune2fs[] = { |
| 452 | "/sbin/tune2fs", |
| 453 | "-c", "0", |
| 454 | "-i", "0", |
| 455 | device, |
| 456 | NULL |
| 457 | }; |
| 458 | rc = (runcmd(tune2fs, NULL) == 0); |
| 459 | |
| 460 | free_extended: |
| 461 | g_free(extended); |
| 462 | free_blocks_str: |
| 463 | g_free(blocks_str); |
| 464 | free_blocksize: |
| 465 | g_free(blocksize); |
| 466 | out: |
| 467 | return rc; |
| 468 | } |
| 469 | |
| 470 | /* Spawns a filesystem resizing process. */ |
Kees Cook | f9e82e9 | 2012-04-03 11:57:04 -0700 | [diff] [blame] | 471 | int filesystem_resize(const char *device, size_t blocks, size_t blocks_max) |
Kees Cook | 498977a | 2012-02-27 12:43:15 -0800 | [diff] [blame] | 472 | { |
Kees Cook | 498977a | 2012-02-27 12:43:15 -0800 | [diff] [blame] | 473 | /* Ignore resizing if we know the filesystem was built to max size. */ |
Kees Cook | 655cc11 | 2012-05-23 16:35:56 -0700 | [diff] [blame] | 474 | if (blocks >= blocks_max) { |
| 475 | INFO("Resizing aborted. blocks:%zu >= blocks_max:%zu", |
| 476 | blocks, blocks_max); |
Kees Cook | f9e82e9 | 2012-04-03 11:57:04 -0700 | [diff] [blame] | 477 | return 1; |
Kees Cook | 655cc11 | 2012-05-23 16:35:56 -0700 | [diff] [blame] | 478 | } |
Kees Cook | 498977a | 2012-02-27 12:43:15 -0800 | [diff] [blame] | 479 | |
| 480 | /* TODO(keescook): Read superblock to find out the current size of |
| 481 | * the filesystem (since statvfs does not report the correct value). |
| 482 | * For now, instead of doing multi-step resizing, just resize to the |
| 483 | * full size of the block device in one step. |
| 484 | */ |
| 485 | blocks = blocks_max; |
| 486 | |
Kees Cook | 655cc11 | 2012-05-23 16:35:56 -0700 | [diff] [blame] | 487 | INFO("Resizing started in %d second steps.", kResizeStepSeconds); |
Kees Cook | 498977a | 2012-02-27 12:43:15 -0800 | [diff] [blame] | 488 | |
| 489 | do { |
| 490 | gchar *blocks_str; |
| 491 | |
| 492 | sleep(kResizeStepSeconds); |
| 493 | |
| 494 | blocks += kResizeBlocks; |
| 495 | if (blocks > blocks_max) |
| 496 | blocks = blocks_max; |
| 497 | |
| 498 | blocks_str = g_strdup_printf("%zu", blocks); |
| 499 | if (!blocks_str) { |
| 500 | PERROR("g_strdup_printf"); |
Kees Cook | f9e82e9 | 2012-04-03 11:57:04 -0700 | [diff] [blame] | 501 | return 0; |
Kees Cook | 498977a | 2012-02-27 12:43:15 -0800 | [diff] [blame] | 502 | } |
| 503 | |
| 504 | const gchar *resize[] = { |
| 505 | "/sbin/resize2fs", |
| 506 | "-f", |
| 507 | device, |
| 508 | blocks_str, |
| 509 | NULL |
| 510 | }; |
| 511 | |
| 512 | INFO("Resizing filesystem on %s to %zu.", device, blocks); |
| 513 | if (runcmd(resize, NULL)) { |
| 514 | ERROR("resize2fs failed"); |
Kees Cook | f9e82e9 | 2012-04-03 11:57:04 -0700 | [diff] [blame] | 515 | return 0; |
Kees Cook | 498977a | 2012-02-27 12:43:15 -0800 | [diff] [blame] | 516 | } |
| 517 | g_free(blocks_str); |
| 518 | } while (blocks < blocks_max); |
| 519 | |
| 520 | INFO("Resizing finished."); |
Kees Cook | f9e82e9 | 2012-04-03 11:57:04 -0700 | [diff] [blame] | 521 | return 1; |
Kees Cook | 498977a | 2012-02-27 12:43:15 -0800 | [diff] [blame] | 522 | } |
| 523 | |
| 524 | char *keyfile_read(const char *keyfile, uint8_t *system_key) |
| 525 | { |
| 526 | char *key = NULL; |
| 527 | unsigned char *cipher = NULL; |
| 528 | gsize length; |
| 529 | uint8_t *plain = NULL; |
| 530 | int plain_length, final_len; |
| 531 | GError *error = NULL; |
| 532 | EVP_CIPHER_CTX ctx; |
| 533 | const EVP_CIPHER *algo = EVP_aes_256_cbc(); |
| 534 | |
| 535 | DEBUG("Reading keyfile %s", keyfile); |
| 536 | if (EVP_CIPHER_key_length(algo) != DIGEST_LENGTH) { |
| 537 | ERROR("cipher key size mismatch (got %d, want %d)", |
| 538 | EVP_CIPHER_key_length(algo), DIGEST_LENGTH); |
| 539 | goto out; |
| 540 | } |
| 541 | |
| 542 | if (access(keyfile, R_OK)) { |
| 543 | /* This file being missing is handled in caller, so |
| 544 | * do not emit error message. |
| 545 | */ |
| 546 | INFO("%s does not exist.", keyfile); |
| 547 | goto out; |
| 548 | } |
| 549 | |
| 550 | if (!g_file_get_contents(keyfile, (gchar **)&cipher, &length, |
| 551 | &error)) { |
| 552 | ERROR("Unable to read %s: %s", keyfile, error->message); |
| 553 | g_error_free(error); |
| 554 | goto out; |
| 555 | } |
| 556 | plain = malloc(length); |
| 557 | if (!plain) { |
| 558 | PERROR("malloc"); |
| 559 | goto free_cipher; |
| 560 | } |
| 561 | |
| 562 | DEBUG("Decrypting keyfile %s", keyfile); |
| 563 | /* Use the default IV. */ |
| 564 | if (!EVP_DecryptInit(&ctx, algo, system_key, NULL)) { |
| 565 | SSL_ERROR("EVP_DecryptInit"); |
| 566 | goto free_plain; |
| 567 | } |
| 568 | /* TODO(keescook): this is a heap overflow -- file size not checked. */ |
| 569 | if (!EVP_DecryptUpdate(&ctx, plain, &plain_length, cipher, length)) { |
| 570 | SSL_ERROR("EVP_DecryptUpdate"); |
| 571 | goto free_ctx; |
| 572 | } |
| 573 | if (!EVP_DecryptFinal(&ctx, plain+plain_length, &final_len)) { |
| 574 | SSL_ERROR("EVP_DecryptFinal"); |
| 575 | goto free_ctx; |
| 576 | } |
| 577 | plain_length += final_len; |
| 578 | |
| 579 | if (plain_length != DIGEST_LENGTH) { |
| 580 | ERROR("Decrypted encryption key length (%d) is not %d.", |
| 581 | plain_length, DIGEST_LENGTH); |
| 582 | goto free_ctx; |
| 583 | } |
| 584 | |
| 585 | debug_dump_hex("encryption key", plain, DIGEST_LENGTH); |
| 586 | |
| 587 | key = stringify_hex(plain, DIGEST_LENGTH); |
| 588 | |
| 589 | free_ctx: |
| 590 | EVP_CIPHER_CTX_cleanup(&ctx); |
| 591 | free_plain: |
| 592 | free(plain); |
| 593 | free_cipher: |
| 594 | g_free(cipher); |
| 595 | out: |
| 596 | DEBUG("key:%p", key); |
| 597 | return key; |
| 598 | } |
| 599 | |
| 600 | int keyfile_write(const char *keyfile, uint8_t *system_key, char *string) |
| 601 | { |
| 602 | int rc = 0; |
| 603 | size_t length; |
| 604 | uint8_t plain[DIGEST_LENGTH]; |
| 605 | uint8_t *cipher = NULL; |
| 606 | int cipher_length, final_len; |
| 607 | GError *error = NULL; |
| 608 | EVP_CIPHER_CTX ctx; |
| 609 | const EVP_CIPHER *algo = EVP_aes_256_cbc(); |
| 610 | |
| 611 | DEBUG("Staring to process keyfile %s", keyfile); |
| 612 | if (EVP_CIPHER_key_length(algo) != DIGEST_LENGTH) { |
| 613 | ERROR("cipher key size mismatch (got %d, want %d)", |
| 614 | EVP_CIPHER_key_length(algo), DIGEST_LENGTH); |
| 615 | goto out; |
| 616 | } |
| 617 | |
| 618 | if (access(keyfile, R_OK) == 0) { |
| 619 | ERROR("%s already exists.", keyfile); |
| 620 | goto out; |
| 621 | } |
| 622 | |
| 623 | length = strlen(string); |
| 624 | if (length != sizeof(plain) * 2) { |
| 625 | ERROR("Encryption key string length (%zu) is not %zu.", |
| 626 | length, sizeof(plain) * 2); |
| 627 | goto out; |
| 628 | } |
| 629 | |
| 630 | length = sizeof(plain); |
| 631 | if (!hexify_string(string, plain, length)) { |
| 632 | ERROR("Failed to convert encryption key to byte array"); |
| 633 | goto out; |
| 634 | } |
| 635 | |
| 636 | debug_dump_hex("encryption key", plain, DIGEST_LENGTH); |
| 637 | |
| 638 | cipher = malloc(length + EVP_CIPHER_block_size(algo)); |
| 639 | if (!cipher) { |
| 640 | PERROR("malloc"); |
| 641 | goto out; |
| 642 | } |
| 643 | |
| 644 | DEBUG("Encrypting keyfile %s", keyfile); |
| 645 | /* Use the default IV. */ |
| 646 | if (!EVP_EncryptInit(&ctx, algo, system_key, NULL)) { |
| 647 | SSL_ERROR("EVP_EncryptInit"); |
| 648 | goto free_cipher; |
| 649 | } |
| 650 | if (!EVP_EncryptUpdate(&ctx, cipher, &cipher_length, |
| 651 | (unsigned char *)plain, length)) { |
| 652 | SSL_ERROR("EVP_EncryptUpdate"); |
| 653 | goto free_ctx; |
| 654 | } |
| 655 | if (!EVP_EncryptFinal(&ctx, cipher+cipher_length, &final_len)) { |
| 656 | SSL_ERROR("EVP_EncryptFinal"); |
| 657 | goto free_ctx; |
| 658 | } |
| 659 | length = cipher_length + final_len; |
| 660 | |
Kees Cook | 6a31226 | 2012-06-15 13:55:24 -0700 | [diff] [blame] | 661 | DEBUG("Writing %zu bytes to %s", length, keyfile); |
Kees Cook | beb3be1 | 2012-04-05 10:15:47 -0700 | [diff] [blame] | 662 | /* TODO(keescook): replace this with a mode-400 writer. */ |
Kees Cook | 498977a | 2012-02-27 12:43:15 -0800 | [diff] [blame] | 663 | if (!g_file_set_contents(keyfile, (gchar *)cipher, length, &error)) { |
| 664 | ERROR("Unable to write %s: %s", keyfile, error->message); |
| 665 | g_error_free(error); |
| 666 | goto free_ctx; |
| 667 | } |
| 668 | |
| 669 | rc = 1; |
| 670 | |
| 671 | free_ctx: |
| 672 | EVP_CIPHER_CTX_cleanup(&ctx); |
| 673 | free_cipher: |
| 674 | free(cipher); |
| 675 | out: |
Kees Cook | beb3be1 | 2012-04-05 10:15:47 -0700 | [diff] [blame] | 676 | DEBUG("keyfile write rc:%d", rc); |
Kees Cook | 498977a | 2012-02-27 12:43:15 -0800 | [diff] [blame] | 677 | return rc; |
| 678 | } |