Skip to content

Commit 671d0ca

Browse files
mattludwigsfhunleth
authored andcommitted
Add support for decrypting non-rootfs partitions
1 parent 291d5fa commit 671d0ca

5 files changed

Lines changed: 158 additions & 13 deletions

File tree

README.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ variables have special uses:
5454

5555
Variable | Description
5656
-------------------|-------------
57-
rootfs.fstype | Root filesystem time. Defaults to "squashfs"
57+
rootfs.fstype | Root filesystem type. Defaults to "squashfs"
5858
rootfs.path | Root filesystem path. Defaults to "/dev/mmcblk0p2"
5959
rootfs.encrypted | True if the filesystem is encrypted. Defaults to `false`
6060
rootfs.cipher | The cipher used to encrypt the filesystem. For example, "aes-cbc-plain"
@@ -65,6 +65,11 @@ uboot_env.modified | True if something has modified the U-Boot block and it diff
6565
uboot_env.start | The block offset of the U-Boot environment. (512 byte blocks)
6666
uboot_env.count | The number of blocks in the environment. Defaults to 256.
6767
run_repl | True to run a REPL before booting. This is useful for debug. Defaults to `false`
68+
dm_crypt.n.path | The extra encrypted filesystem path.Where `n` is the index of the extra filesystem.
69+
dm_crypt.n.cipher | The cipher used to encrypt the extra filesystem. Where `n` is the index of the extra filesystem.
70+
dm_crypt.n.secret | The secret key as hex digits for the extra filesystem. Where `n` is the index of the extra filesystem.
71+
72+
_for more information about configuring extra encrypted filesystems see [Mounting extra encrypted filesystems](#mounting-extra-encrypted-file-systems)_
6873

6974
It's also possible to call built-in functions:
7075

@@ -180,3 +185,22 @@ This is illustrative, but obviously quite insecure. The current route to
180185
obtaining the secret key is to edit the C code to this project to integrate it
181186
with platform-specific way of keeping or hiding secrets. It is hoped that
182187
alternatives can be shared in the future.
188+
189+
### Mounting extra encrypted file systems
190+
191+
If you want to mount more encrypted file systems outside of the `rootfs` you
192+
can use the `dm_crypt` variable to configure the extra filesystems. The
193+
`dm_crypt` variable works using a number to under the `dm_crypt` variable
194+
namespace like so: `dm_crypt.n.path`.
195+
196+
Here's an example configuration file with configuration two more filesystems:
197+
198+
```config
199+
dm_crypt.1.path = "/dev/mmcblk0p3"
200+
dm_crypt.1.cipher = "aes-cbc-plain"
201+
dm_crypt.1.secret = "8e9c0780fd7f5d00c18a30812fe960cfce71f6074dd9cded6aab2897568cc856"
202+
203+
dm_crypt.1.path = "/dev/mmcblk0p4"
204+
dm_crypt.2.cipher = "aes-cbc-plain"
205+
dm_crypt.2.secret = "4e9c781fd7f5d00c18a30812fe970cfce56f6064dd9cded6aab2897575cc861"
206+
```

src/nerves_initramfs.c

Lines changed: 81 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
#include "linenoise.h"
2020
#include "script.h"
2121

22+
#define MAX_EXTRA_ENCRYPTED_DEVS 8
23+
2224
// Global U-Boot environment data
2325
struct uboot_env working_uboot_env;
2426

@@ -38,19 +40,26 @@ static int retry_open(const char *path, int flags)
3840

3941
return fd;
4042
}
41-
static int losetup(int rootfs_fd)
43+
static int losetup(int rootfs_fd, int loop_ix)
4244
{
45+
char loop_devname[16];
46+
snprintf(loop_devname, sizeof(loop_devname), "/dev/loop%d", loop_ix);
47+
4348
// Assume loop0 is available since who else would be able to take it?
44-
int loop_fd = open("/dev/loop0", O_RDONLY);
49+
int loop_fd = open(loop_devname, O_RDONLY);
4550
if (loop_fd < 0)
46-
fatal("Enable CONFIG_BLK_DEV_LOOP in kernel");
51+
fatal("Enable CONFIG_BLK_DEV_LOOP in kernel and check that at least %d loop devices", loop_ix + 1);
4752

4853
OK_OR_FATAL(ioctl(loop_fd, LOOP_SET_FD, rootfs_fd), "LOOP_SET_FD failed");
4954

5055
return loop_fd;
5156
}
5257

53-
static int dm_create(off_t rootfs_size, const char *cipher, const char *secret)
58+
static int dm_create(int map_ix,
59+
const char *name,
60+
off_t rootfs_size,
61+
const char *cipher,
62+
const char *secret)
5463
{
5564
int dm_control = retry_open("/dev/mapper/control", O_RDWR);
5665

@@ -68,8 +77,8 @@ static int dm_create(off_t rootfs_size, const char *cipher, const char *secret)
6877
dm->flags = 0;
6978
dm->event_nr = 0;
7079
dm->dev = 0;
71-
strcpy(dm->name, "rootfs");
72-
strcpy(dm->uuid, "CRYPT-PLAIN-rootfs");
80+
strcpy(dm->name, name);
81+
snprintf(dm->uuid, sizeof(dm->uuid), "CRYPT-PLAIN-%s", name);
7382

7483
OK_OR_FATAL(ioctl(dm_control, DM_DEV_CREATE, request_buffer), "Enable CONFIG_DM_CRYPT in kernel");
7584

@@ -83,13 +92,13 @@ static int dm_create(off_t rootfs_size, const char *cipher, const char *secret)
8392
dm->open_count = 0;
8493
dm->flags = DM_SECURE_DATA_FLAG;
8594
dm->dev = 0;
86-
strcpy(dm->name, "rootfs");
95+
strcpy(dm->name, name);
8796
struct dm_target_spec *target = (struct dm_target_spec *) &request_buffer[dm->data_start];
8897
memset(target, 0, sizeof(struct dm_target_spec));
8998
target->sector_start = 0;
9099
target->length = rootfs_size;
91100
strcpy(target->target_type, "crypt");
92-
sprintf((char *) &request_buffer[dm->data_start + sizeof(struct dm_target_spec)], "%s %s 0 /dev/loop0 0", cipher, secret);
101+
sprintf((char *) &request_buffer[dm->data_start + sizeof(struct dm_target_spec)], "%s %s %d /dev/loop%d 0", cipher, secret, map_ix, map_ix);
93102
OK_OR_FATAL(ioctl(dm_control, DM_TABLE_LOAD, request_buffer), "Check CONFIG_DM_CRYPT and crypto algs enabled");
94103

95104

@@ -101,7 +110,7 @@ static int dm_create(off_t rootfs_size, const char *cipher, const char *secret)
101110
dm->data_start = sizeof(struct dm_ioctl);
102111
dm->target_count = 0;
103112
dm->flags = DM_SECURE_DATA_FLAG;
104-
strcpy(dm->name, "rootfs");
113+
strcpy(dm->name, name);
105114
OK_OR_FATAL(ioctl(dm_control, DM_DEV_SUSPEND, request_buffer), "DM_DEV_SUSPEND failed");
106115

107116
close(dm_control);
@@ -188,17 +197,76 @@ static void mount_encrypted_fs(const char *rootfs, const char *rootfs_type, cons
188197
off_t rootfs_size = lseek(rootfs_fd, 0, SEEK_END);
189198
(void) lseek(rootfs_fd, 0, SEEK_SET);
190199

191-
int loop_fd = losetup(rootfs_fd);
200+
int loop_fd = losetup(rootfs_fd, 0);
192201
close(rootfs_fd);
193202

194-
dm_create(rootfs_size, cipher, secret);
203+
dm_create(0, "rootfs", rootfs_size, cipher, secret);
195204

196205
OK_OR_FATAL(mount("/dev/dm-0", "/mnt", rootfs_type, MS_RDONLY, NULL), "Expecting %s filesystem on %s", rootfs_type, rootfs);
197206

198207
// It's ok to close loop_fd now that the mount happened.
199208
close(loop_fd);
200209
}
201210

211+
static void map_encrypted_device(int map_ix, const char *path, const char *name, const char *cipher, const char *secret)
212+
{
213+
// Wait for the device to appear
214+
int fd = retry_open(path, O_RDONLY);
215+
off_t size = lseek(fd, 0, SEEK_END);
216+
(void) lseek(fd, 0, SEEK_SET);
217+
218+
int loop_fd = losetup(fd, map_ix);
219+
close(fd);
220+
221+
char dm_name[16];
222+
snprintf(dm_name, sizeof(dm_name), "/dev/dm-%d", map_ix);
223+
224+
char link_name[32];
225+
snprintf(link_name, sizeof(link_name), "/dev/mapper/%s", name);
226+
227+
dm_create(map_ix, name, size, cipher, secret);
228+
229+
OK_OR_WARN(symlink(link_name, dm_name), "Could not symlink %s -> %s", link_name, dm_name);
230+
231+
// Hmmmmm.... It's ok to close loop_fd now that the mount happened.
232+
close(loop_fd);
233+
}
234+
235+
static void map_extra_encrypted_devs()
236+
{
237+
for (int ix = 1; ix <= MAX_EXTRA_ENCRYPTED_DEVS; ix++) {
238+
char base[12];
239+
snprintf(base, sizeof(base), "dm_crypt.%d", ix);
240+
241+
char path_var[20];
242+
char name_var[20];
243+
char cipher_var[20];
244+
char secret_var[20];
245+
246+
snprintf(path_var, sizeof(path_var), "%s.path", base);
247+
snprintf(name_var, sizeof(name_var), "%s.name", base);
248+
snprintf(cipher_var, sizeof(cipher_var), "%s.cipher", base);
249+
snprintf(secret_var, sizeof(secret_var), "%s.secret", base);
250+
251+
const char *path = get_variable_as_string(path_var);
252+
const char *name = get_variable_as_string(name_var);
253+
const char *cipher = get_variable_as_string(cipher_var);
254+
const char *secret = get_variable_as_string(secret_var);
255+
256+
if (*path != '\0') {
257+
if (*name == '\0' ||
258+
*cipher == '\0' ||
259+
*secret == '\0') {
260+
info("Not mapping %s. Make sure that %s.name, %s.cipher, and %s.secret are set",
261+
path, base, base, base);
262+
continue;
263+
}
264+
info("mapping other encrypted block device %s", path);
265+
map_encrypted_device(ix, path, name, cipher, secret);
266+
}
267+
}
268+
}
269+
202270
static void repl()
203271
{
204272
char *line;
@@ -263,6 +331,8 @@ int main(int argc, char *argv[])
263331
else
264332
mount_fs(rootfs_path, rootfs_fstype);
265333

334+
map_extra_encrypted_devs();
335+
266336
switch_root();
267337

268338
// Launch the real init. It's always /sbin/init with Buildroot.

tests/006_extra_encrypted_devices

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#!/bin/sh
2+
3+
#
4+
# Test adding 1 or more encrypted devices
5+
#
6+
7+
cat >$CONFIG <<EOF
8+
dm_crypt.1.path = "/dev/mmcblk0p3"
9+
dm_crypt.1.name = "app"
10+
dm_crypt.1.cipher = "aes-cbc-plain"
11+
dm_crypt.1.secret = "8e9c0780fd7f5d00c18a30812fe960cfce71f6074dd9cded6aab2897568cc856"
12+
13+
dm_crypt.2.path = "/dev/mmcblk0p4"
14+
dm_crypt.2.name = "provisioning"
15+
dm_crypt.2.cipher = "aes-cbc-plain"
16+
dm_crypt.2.secret = "4e9c781fd7f5d00c18a30812fe970cfce56f6064dd9cded6aab2897575cc861"
17+
EOF
18+
19+
20+
cat >$EXPECTED <<EOF
21+
fixture: mount("devtmpfs", "/dev", "devtmpfs", 10, data)
22+
fixture: mkdir("/mnt", 777)
23+
fixture: mount("/dev/mmcblk0p2", "/mnt", "squashfs", 1, data)
24+
nerves_initramfs: mapping other encrypted block device /dev/mmcblk0p3
25+
fixture: ioctl(LOOP_SET_FD)
26+
fixture: ioctl(DM_DEV_CREATE, data_size=16384, data_start=312, target_count=0, open_count=0, flags=0, event_nr=0, dev=0x0, name=app, uuid=CRYPT-PLAIN-app
27+
fixture: ioctl(DM_TABLE_LOAD, data_size=16384, data_start=312, target_count=1, open_count=0, flags=32768, event_nr=0, dev=0x0, name=app, uuid=, target=0,393216,crypt (aes-cbc-plain 8e9c0780fd7f5d00c18a30812fe960cfce71f6074dd9cded6aab2897568cc856 1 /dev/loop1 0)
28+
fixture: ioctl(DM_DEV_SUSPEND, data_size=16384, data_start=312, target_count=0, open_count=0, flags=32768, event_nr=0, dev=0x0, name=app, uuid=
29+
fixture: symlink("/dev/mapper/app, /dev/dm-1")
30+
nerves_initramfs: mapping other encrypted block device /dev/mmcblk0p4
31+
fixture: ioctl(LOOP_SET_FD)
32+
fixture: ioctl(DM_DEV_CREATE, data_size=16384, data_start=312, target_count=0, open_count=0, flags=0, event_nr=0, dev=0x0, name=provisioning, uuid=CRYPT-PLAIN-provisioning
33+
fixture: ioctl(DM_TABLE_LOAD, data_size=16384, data_start=312, target_count=1, open_count=0, flags=32768, event_nr=0, dev=0x0, name=provisioning, uuid=, target=0,655360,crypt (aes-cbc-plain 4e9c781fd7f5d00c18a30812fe970cfce56f6064dd9cded6aab2897575cc861 2 /dev/loop2 0)
34+
fixture: ioctl(DM_DEV_SUSPEND, data_size=16384, data_start=312, target_count=0, open_count=0, flags=32768, event_nr=0, dev=0x0, name=provisioning, uuid=
35+
fixture: symlink("/dev/mapper/provisioning, /dev/dm-2")
36+
fixture: unlink("/init")
37+
fixture: rmdir("/root")
38+
fixture: mount("/dev", "/mnt/dev", "(null)", 8192, data)
39+
fixture: mount(".", "/", "(null)", 8192, data)
40+
fixture: chroot(.)
41+
Hello from the chained /sbin/init
42+
EOF

tests/fixture/init_fixture.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ OVERRIDE(int, dup2, (int oldfd, int newfd))
290290

291291
OVERRIDE(int, symlink, (const char *target, const char *linkpath))
292292
{
293+
log("symlink(\"%s, %s\")", target, linkpath);
293294
char new_target[PATH_MAX];
294295
if (fixup_path(target, new_target) < 0)
295296
return -1;

tests/run_tests_impl.sh

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,15 @@ run() {
5151
ln -s $INIT $WORK/init
5252
mkdir -p $WORK/sbin
5353
ln -s $CHAINED_INIT $WORK/sbin/init
54+
ln -s /dev/null $WORK/dev/null
5455

5556
# Create the device containing a root filesystem
5657
dd if=/dev/zero of=$WORK/dev/mmcblk0p2 bs=512 count=0 seek=1024 2>/dev/null
57-
ln -s /dev/null $WORK/dev/null
58+
59+
# Create devices for other partitions
60+
dd if=/dev/zero of=$WORK/dev/mmcblk0p1 bs=512 count=0 seek=512 2>/dev/null
61+
dd if=/dev/zero of=$WORK/dev/mmcblk0p3 bs=512 count=0 seek=768 2>/dev/null
62+
dd if=/dev/zero of=$WORK/dev/mmcblk0p4 bs=512 count=0 seek=1280 2>/dev/null
5863

5964
# Fake active console
6065
ln -s $(tty) $WORK/dev/ttyF1
@@ -65,6 +70,9 @@ run() {
6570
mkdir -p $WORK/dev/mapper
6671
touch $WORK/dev/mapper/control
6772
touch $WORK/dev/loop0
73+
touch $WORK/dev/loop1
74+
touch $WORK/dev/loop2
75+
touch $WORK/dev/loop3
6876

6977
# Run the test script to setup files for the test
7078
source "$TESTS_DIR/$TEST"

0 commit comments

Comments
 (0)