dmsetup, losetup and mount
Author
Commands
#!/bin/bash ldev=/dev/loop/0 mdev=mdev pass="my_sekkrit_password" # create empty filesystem image dd if=/dev/zero of=image bs=1M count=10 # make a block device out of it using the loopback driver losetup $ldev image # get the exact size of the loopback block device blksize=$(blockdev --getsize $ldev) echo "blksize= $blksize" # create a hexadecimal key 128 bits long out of the passphrase key=$(echo "$pass" | md5sum | cut -d" " -f1) echo "key= $key" # create an encrypted block device representation of the image file # using the aes cipher with the 128 bit key we generated above echo "0 $blksize crypt aes-plain $key 0 $ldev 0" | dmsetup create $mdev # create an ext filesystem through the encrypted block device mkfs -t ext2 /dev/mapper/${mdev} # mount the filesystem mkdir mnt mount /dev/mapper/${mdev} ./mnt # spawn a subshell so the user can create some files whose contents # will then be encrypted in the image file. echo "write some files to mnt, then exit from this subshell" bash # undo mount, device mapping, and loopback. umount mnt rmdir mnt dmsetup remove $mdev losetup -d $ldev
Summary
We extend the previous article (../dmsetup_and_losetup) by creating a filesystem inside the encrypted image file.
Discussion
This script is only a little more complicated than that of the previous article: It creates an ext2 filesystem inside the image file and mounts it. It is somewhat better parameterized (using ldev, mdev, and pass).
This line:
key=$(echo "$pass" | md5sum | cut -d" " -f1)
derives a 128 bit hexadecimal key, suitable for use in the dmsetup command with the aes cipher, from an arbitrary passphrase by calculaing the md5 checksum of the passphrase. The md5sum command can be run on standard in or on named files. For example, running it on a file named tmp.txt:
$ md5sum tmp.txt
generates the following output:
8448af516bf24b00065a60018992a91a *tmp.txt
In other words, md5sum's output has the form:
<128 bit hexadecimal checksum> <filename>
We discard everything after the checksum using the "cut" command. Our particular "cut" command is saying: assume the input is divided into columns by space characters, and only write out the first column.
Finally, by wrapping the whole command pipe in the bash command quoting construct, "$()", we capture the output in the variable "key".
This line:
mkfs -t ext2 /dev/mapper/${mdev}
creates the filesystem on the mapper device, causing the corresponding cipher text of the filesystem image to be written to the underlying file image_file.
The script then mounts the mapper device at ./mnt and spawns a subshell (the line "bash") to allow the user to create files under mnt. Once finished creating files, the user types "exit", and the script continues executing after the "bash" line. This, by the way, is a useful trick to use when debugging bash scripts with complicated setup sequences: invoke bash in the script right after the complicated setup sequence so the user can determine if the sequence worked.
The final four lines undo mount, loopback, crypto mappings leaving the encrypted filesystem image, image_file, inscrutable to enemey eyes.
To verify that the crypto mapping wasn't a *complete and utter fraud*, you can try mounting image_file without the crypto mapping like this:
mount -o loop image_file ./mnt
To mount the image_file properly, and to view the same files created inside the filesystem during the first run, simply comment out the "dd" command in the above script and run it again. From inside the subshell, you should be able to see the previously created files.
Security Considerations
When building scripts that are serious about protecting data from prying eyes, you need to consider at least two additional factors.
-
The passphrase should be obtained by having the script read keystrokes directly from "/dev/tty" instead of using script command line arguments. One problem with reading key material from command line arguments is that command lines typed from most shells are typically written to the file ".history" in the user account the shell was running from (which would typically reside on an unencrypted volume). Another problem with using command line arguments is that while the command is running, the full text of the command line can be viewed by any local user by running the "ps" command to get a process list. You might object that once the key is in a variable in a bash session, a command like:echo "... $key ..." | dmsetup create some_dev would reveal the key through the process list. It certainly would, if the "echo" command run in this case were a normal unix command. It is, in our case, a bash built-in command, however, and it's text will not appear in the process list (what will appear in the process list is simply "bash").
-
Although we have kept the key string out of any unencrypted file and out of the process list, it can still wind up being written to any swap files or partitions enabled on the system. You might think it would be hard to recover a 128 bit hexadecimal string from regions of memory written to a swap file but you would be suprised how easy that is to do. There are two ways to address this: disable any swap devices, or only enable swap devices that have been crypto mapped using dmsetup. The procedure for that uses the same dmsetup command highlighted in this series of articles.