Updates to cryptfs framework.

Update the enable inplace API to allow the UI to show a progress bar.
Add new command changepw (whichis currently not working)
Internal restructuring of code to support these two features.
Some minor cleanup of the code as well.

Change-Id: I11461fc9ce66965bea6cd0b6bb2ff48bcf607b97
This commit is contained in:
Ken Sumrall 2011-01-17 15:26:29 -08:00
parent 6864b7ec94
commit 8ddbe40a8a
3 changed files with 238 additions and 97 deletions

View file

@ -514,8 +514,6 @@ CommandListener::CryptfsCmd::CryptfsCmd() :
int CommandListener::CryptfsCmd::runCommand(SocketClient *cli, int CommandListener::CryptfsCmd::runCommand(SocketClient *cli,
int argc, char **argv) { int argc, char **argv) {
dumpArgs(argc, argv, -1);
if (argc < 2) { if (argc < 2) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false); cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
return 0; return 0;
@ -528,20 +526,31 @@ int CommandListener::CryptfsCmd::runCommand(SocketClient *cli,
cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs checkpw <passwd>", false); cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs checkpw <passwd>", false);
return 0; return 0;
} }
dumpArgs(argc, argv, 2);
rc = cryptfs_check_passwd(argv[2]); rc = cryptfs_check_passwd(argv[2]);
} else if (!strcmp(argv[1], "restart")) { } else if (!strcmp(argv[1], "restart")) {
if (argc != 2) { if (argc != 2) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs restart", false); cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs restart", false);
return 0; return 0;
} }
dumpArgs(argc, argv, -1);
rc = cryptfs_restart(); rc = cryptfs_restart();
} else if (!strcmp(argv[1], "enablecrypto")) { } else if (!strcmp(argv[1], "enablecrypto")) {
if ( (argc != 4) || (strcmp(argv[2], "wipe") && strcmp(argv[2], "inplace")) ) { if ( (argc != 4) || (strcmp(argv[2], "wipe") && strcmp(argv[2], "inplace")) ) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs enablecrypto <wipe|inplace> <passwd>", false); cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs enablecrypto <wipe|inplace> <passwd>", false);
return 0; return 0;
} }
dumpArgs(argc, argv, 3);
rc = cryptfs_enable(argv[2], argv[3]); rc = cryptfs_enable(argv[2], argv[3]);
} else if (!strcmp(argv[1], "changepw")) {
if (argc != 4) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs changepw <oldpasswd> <newpasswd>", false);
return 0;
}
SLOGD("cryptfs changepw <oldpw> <newpw>");
rc = cryptfs_changepw(argv[2], argv[3]);
} else { } else {
dumpArgs(argc, argv, -1);
cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown cryptfs cmd", false); cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown cryptfs cmd", false);
} }

263
cryptfs.c
View file

@ -33,6 +33,7 @@
#include <string.h> #include <string.h>
#include <sys/mount.h> #include <sys/mount.h>
#include <openssl/evp.h> #include <openssl/evp.h>
#include <openssl/sha.h>
#include <errno.h> #include <errno.h>
#include <sys/reboot.h> #include <sys/reboot.h>
#include "cryptfs.h" #include "cryptfs.h"
@ -41,9 +42,13 @@
#include "cutils/properties.h" #include "cutils/properties.h"
#define DM_CRYPT_BUF_SIZE 4096 #define DM_CRYPT_BUF_SIZE 4096
#define DATA_MNT_POINT "/data"
char *me = "cryptfs"; char *me = "cryptfs";
static unsigned char saved_key_sha1[20] = { '\0' };
static int key_sha1_saved = 0;
static void ioctl_init(struct dm_ioctl *io, size_t dataSize, const char *name, unsigned flags) static void ioctl_init(struct dm_ioctl *io, size_t dataSize, const char *name, unsigned flags)
{ {
memset(io, 0, dataSize); memset(io, 0, dataSize);
@ -150,13 +155,7 @@ static int get_crypt_ftr_and_key(char *real_blk_name, struct crypt_mnt_ftr *cryp
* encryption info footer and key, and plenty of bytes to spare for future * encryption info footer and key, and plenty of bytes to spare for future
* growth. * growth.
*/ */
#if 1 /* The real location, use when the enable code works */
off = ((off64_t)nr_sec * 512) - CRYPT_FOOTER_OFFSET; off = ((off64_t)nr_sec * 512) - CRYPT_FOOTER_OFFSET;
#else
/* For testing, I'm slapping a handbuild header after my 200 megabyte
* /data partition. So my offset if 200 megabytes */
off = 200*1024*1024;
#endif
if (lseek64(fd, off, SEEK_SET) == -1) { if (lseek64(fd, off, SEEK_SET) == -1) {
SLOGE("Cannot seek to real block device footer\n"); SLOGE("Cannot seek to real block device footer\n");
@ -343,36 +342,31 @@ errout:
} }
/* If we need to debug this, look at Devmapper.cpp:dumpState(),
* It does DM_LIST_DEVICES, then iterates on each device and
* calls DM_DEV_STATUS.
*/
#define HASH_COUNT 2000 #define HASH_COUNT 2000
#define KEY_LEN_BYTES 16 #define KEY_LEN_BYTES 16
#define IV_LEN_BYTES 16 #define IV_LEN_BYTES 16
static int create_encrypted_random_key(char *passwd, unsigned char *master_key) static void pbkdf2(char *passwd, unsigned char *ikey)
{ {
int fd;
unsigned char buf[KEY_LEN_BYTES];
unsigned char ikey[32+32] = { 0 }; /* Big enough to hold a 256 bit key and 256 bit IV */
unsigned char salt[32] = { 0 }; unsigned char salt[32] = { 0 };
EVP_CIPHER_CTX e_ctx;
int encrypted_len, final_len;
/* Get some random bits for a key */
fd = open("/dev/urandom", O_RDONLY);
read(fd, buf, sizeof(buf));
close(fd);
/* Now encrypt it with the password */
/* To Do: Make a salt based on some immutable data about this device. /* To Do: Make a salt based on some immutable data about this device.
* IMEI, or MEID, or CPU serial number, or whatever we can find * IMEI, or MEID, or CPU serial number, or whatever we can find
*/ */
/* Turn the password into a key and IV that can decrypt the master key */ /* Turn the password into a key and IV that can decrypt the master key */
PKCS5_PBKDF2_HMAC_SHA1(passwd, strlen(passwd), salt, sizeof(salt), PKCS5_PBKDF2_HMAC_SHA1(passwd, strlen(passwd), salt, sizeof(salt),
HASH_COUNT, KEY_LEN_BYTES+IV_LEN_BYTES, ikey); HASH_COUNT, KEY_LEN_BYTES+IV_LEN_BYTES, ikey);
}
static int encrypt_master_key(char *passwd, unsigned char *decrypted_master_key,
unsigned char *encrypted_master_key)
{
unsigned char ikey[32+32] = { 0 }; /* Big enough to hold a 256 bit key and 256 bit IV */
EVP_CIPHER_CTX e_ctx;
int encrypted_len, final_len;
/* Turn the password into a key and IV that can decrypt the master key */
pbkdf2(passwd, ikey);
/* Initialize the decryption engine */ /* Initialize the decryption engine */
if (! EVP_EncryptInit(&e_ctx, EVP_aes_128_cbc(), ikey, ikey+KEY_LEN_BYTES)) { if (! EVP_EncryptInit(&e_ctx, EVP_aes_128_cbc(), ikey, ikey+KEY_LEN_BYTES)) {
@ -380,13 +374,14 @@ static int create_encrypted_random_key(char *passwd, unsigned char *master_key)
return -1; return -1;
} }
EVP_CIPHER_CTX_set_padding(&e_ctx, 0); /* Turn off padding as our data is block aligned */ EVP_CIPHER_CTX_set_padding(&e_ctx, 0); /* Turn off padding as our data is block aligned */
/* Encrypt the master key */ /* Encrypt the master key */
if (! EVP_EncryptUpdate(&e_ctx, master_key, &encrypted_len, if (! EVP_EncryptUpdate(&e_ctx, encrypted_master_key, &encrypted_len,
buf, KEY_LEN_BYTES)) { decrypted_master_key, KEY_LEN_BYTES)) {
SLOGE("EVP_EncryptUpdate failed\n"); SLOGE("EVP_EncryptUpdate failed\n");
return -1; return -1;
} }
if (! EVP_EncryptFinal(&e_ctx, master_key + encrypted_len, &final_len)) { if (! EVP_EncryptFinal(&e_ctx, encrypted_master_key + encrypted_len, &final_len)) {
SLOGE("EVP_EncryptFinal failed\n"); SLOGE("EVP_EncryptFinal failed\n");
return -1; return -1;
} }
@ -403,16 +398,11 @@ static int decrypt_master_key(char *passwd, unsigned char *encrypted_master_key,
unsigned char *decrypted_master_key) unsigned char *decrypted_master_key)
{ {
unsigned char ikey[32+32] = { 0 }; /* Big enough to hold a 256 bit key and 256 bit IV */ unsigned char ikey[32+32] = { 0 }; /* Big enough to hold a 256 bit key and 256 bit IV */
unsigned char salt[32] = { 0 };
EVP_CIPHER_CTX d_ctx; EVP_CIPHER_CTX d_ctx;
int decrypted_len, final_len; int decrypted_len, final_len;
/* To Do: Make a salt based on some immutable data about this device.
* IMEI, or MEID, or CPU serial number, or whatever we can find
*/
/* Turn the password into a key and IV that can decrypt the master key */ /* Turn the password into a key and IV that can decrypt the master key */
PKCS5_PBKDF2_HMAC_SHA1(passwd, strlen(passwd), salt, sizeof(salt), pbkdf2(passwd, ikey);
HASH_COUNT, KEY_LEN_BYTES+IV_LEN_BYTES, ikey);
/* Initialize the decryption engine */ /* Initialize the decryption engine */
if (! EVP_DecryptInit(&d_ctx, EVP_aes_128_cbc(), ikey, ikey+KEY_LEN_BYTES)) { if (! EVP_DecryptInit(&d_ctx, EVP_aes_128_cbc(), ikey, ikey+KEY_LEN_BYTES)) {
@ -435,6 +425,24 @@ static int decrypt_master_key(char *passwd, unsigned char *encrypted_master_key,
} }
} }
static int create_encrypted_random_key(char *passwd, unsigned char *master_key)
{
int fd;
unsigned char buf[KEY_LEN_BYTES];
unsigned char ikey[32+32] = { 0 }; /* Big enough to hold a 256 bit key and 256 bit IV */
unsigned char salt[32] = { 0 };
EVP_CIPHER_CTX e_ctx;
int encrypted_len, final_len;
/* Get some random bits for a key */
fd = open("/dev/urandom", O_RDONLY);
read(fd, buf, sizeof(buf));
close(fd);
/* Now encrypt it with the password */
return encrypt_master_key(passwd, buf, master_key);
}
static int get_orig_mount_parms(char *mount_point, char *fs_type, char *real_blkdev, static int get_orig_mount_parms(char *mount_point, char *fs_type, char *real_blkdev,
unsigned long *mnt_flags, char *fs_options) unsigned long *mnt_flags, char *fs_options)
{ {
@ -482,6 +490,36 @@ static int wait_and_unmount(char *mountpoint)
return rc; return rc;
} }
#define DATA_PREP_TIMEOUT 100
static int prep_data_fs(void)
{
int i;
/* Do the prep of the /data filesystem */
property_set("vold.post_fs_data_done", "0");
property_set("vold.decrypt", "trigger_post_fs_data");
SLOGD("Just triggered post_fs_data\n");
/* Wait a max of 25 seconds, hopefully it takes much less */
for (i=0; i<DATA_PREP_TIMEOUT; i++) {
char p[16];;
property_get("vold.post_fs_data_done", p, "0");
if (*p == '1') {
break;
} else {
usleep(250000);
}
}
if (i == DATA_PREP_TIMEOUT) {
/* Ugh, we failed to prep /data in time. Bail. */
return -1;
} else {
SLOGD("post_fs_data done\n");
return 0;
}
}
int cryptfs_restart(void) int cryptfs_restart(void)
{ {
char fs_type[32]; char fs_type[32];
@ -491,7 +529,6 @@ int cryptfs_restart(void)
unsigned long mnt_flags; unsigned long mnt_flags;
struct stat statbuf; struct stat statbuf;
int rc = -1, i; int rc = -1, i;
#define DATA_PREP_TIMEOUT 100
/* Here is where we shut down the framework. The init scripts /* Here is where we shut down the framework. The init scripts
* start all services in one of three classes: core, main or late_start. * start all services in one of three classes: core, main or late_start.
@ -523,31 +560,15 @@ int cryptfs_restart(void)
return -1; return -1;
} }
if (! get_orig_mount_parms("/data", fs_type, real_blkdev, &mnt_flags, fs_options)) { if (! get_orig_mount_parms(DATA_MNT_POINT, fs_type, real_blkdev, &mnt_flags, fs_options)) {
SLOGD("Just got orig mount parms\n"); SLOGD("Just got orig mount parms\n");
if (! (rc = wait_and_unmount("/data")) ) { if (! (rc = wait_and_unmount(DATA_MNT_POINT)) ) {
/* If that succeeded, then mount the decrypted filesystem */ /* If that succeeded, then mount the decrypted filesystem */
mount(crypto_blkdev, "/data", fs_type, mnt_flags, fs_options); mount(crypto_blkdev, DATA_MNT_POINT, fs_type, mnt_flags, fs_options);
/* Do the prep of the /data filesystem */ /* Create necessary paths on /data */
property_set("vold.post_fs_data_done", "0"); if (prep_data_fs()) {
property_set("vold.decrypt", "trigger_post_fs_data");
SLOGD("Just triggered post_fs_data\n");
/* Wait a max of 25 seconds, hopefully it takes much less */
for (i=0; i<DATA_PREP_TIMEOUT; i++) {
char p[16];;
property_get("vold.post_fs_data_done", p, "0");
if (*p == '1') {
break;
} else {
usleep(250000);
}
}
if (i == DATA_PREP_TIMEOUT) {
/* Ugh, we failed to prep /data in time. Bail. */
return -1; return -1;
} }
@ -633,6 +654,12 @@ static int test_mount_encrypted_fs(char *passwd, char *mount_point)
* so we can mount it when restarting the framework. * so we can mount it when restarting the framework.
*/ */
property_set("ro.crypto.fs_crypto_blkdev", crypto_blkdev); property_set("ro.crypto.fs_crypto_blkdev", crypto_blkdev);
/* Also save a SHA1 of the master key so we can know if we
* successfully decrypted the key when we want to change the
* password on it.
*/
SHA1(decrypted_master_key, KEY_LEN_BYTES, saved_key_sha1);
key_sha1_saved = 1;
rc = 0; rc = 0;
} }
@ -643,7 +670,7 @@ int cryptfs_check_passwd(char *passwd)
{ {
int rc = -1; int rc = -1;
rc = test_mount_encrypted_fs(passwd, "/data"); rc = test_mount_encrypted_fs(passwd, DATA_MNT_POINT);
return rc; return rc;
} }
@ -707,6 +734,7 @@ static int cryptfs_enable_inplace(char *crypto_blkdev, char *real_blkdev, off64_
char *buf[CRYPT_INPLACE_BUFSIZE]; char *buf[CRYPT_INPLACE_BUFSIZE];
int rc = -1; int rc = -1;
off64_t numblocks, i, remainder; off64_t numblocks, i, remainder;
off64_t one_pct, cur_pct, new_pct;
if ( (realfd = open(real_blkdev, O_RDONLY)) < 0) { if ( (realfd = open(real_blkdev, O_RDONLY)) < 0) {
SLOGE("Error opening real_blkdev %s for inplace encrypt\n", real_blkdev); SLOGE("Error opening real_blkdev %s for inplace encrypt\n", real_blkdev);
@ -729,11 +757,18 @@ static int cryptfs_enable_inplace(char *crypto_blkdev, char *real_blkdev, off64_
SLOGE("Encrypting filesystem in place..."); SLOGE("Encrypting filesystem in place...");
one_pct = numblocks / 100;
cur_pct = 0;
/* process the majority of the filesystem in blocks */ /* process the majority of the filesystem in blocks */
for (i=0; i<numblocks; i++) { for (i=0; i<numblocks; i++) {
if ( ! (i % 65536)) { //KEN new_pct = i / one_pct;
SLOGE("|"); //KEN if (new_pct > cur_pct) {
} //KEN char buf[8];
cur_pct = new_pct;
snprintf(buf, sizeof(buf), "%lld", cur_pct);
property_set("vold.encrypt_progress", buf);
}
if (unix_read(realfd, buf, CRYPT_INPLACE_BUFSIZE) <= 0) { if (unix_read(realfd, buf, CRYPT_INPLACE_BUFSIZE) <= 0) {
SLOGE("Error reading real_blkdev %s for inplace encrypt\n", crypto_blkdev); SLOGE("Error reading real_blkdev %s for inplace encrypt\n", crypto_blkdev);
goto errout; goto errout;
@ -756,6 +791,8 @@ static int cryptfs_enable_inplace(char *crypto_blkdev, char *real_blkdev, off64_
} }
} }
property_set("vold.encrypt_progress", "100");
rc = 0; rc = 0;
errout: errout:
@ -767,6 +804,9 @@ errout:
#define CRYPTO_ENABLE_WIPE 1 #define CRYPTO_ENABLE_WIPE 1
#define CRYPTO_ENABLE_INPLACE 2 #define CRYPTO_ENABLE_INPLACE 2
#define FRAMEWORK_BOOT_WAIT 60
int cryptfs_enable(char *howarg, char *passwd) int cryptfs_enable(char *howarg, char *passwd)
{ {
int how = 0; int how = 0;
@ -774,8 +814,9 @@ int cryptfs_enable(char *howarg, char *passwd)
char fs_type[32], fs_options[256], mount_point[32]; char fs_type[32], fs_options[256], mount_point[32];
unsigned long mnt_flags, nr_sec; unsigned long mnt_flags, nr_sec;
unsigned char master_key[16], decrypted_master_key[16]; unsigned char master_key[16], decrypted_master_key[16];
int rc, fd; int rc=-1, fd, i;
struct crypt_mnt_ftr crypt_ftr; struct crypt_mnt_ftr crypt_ftr;
char tmpfs_options[80];
if (!strcmp(howarg, "wipe")) { if (!strcmp(howarg, "wipe")) {
how = CRYPTO_ENABLE_WIPE; how = CRYPTO_ENABLE_WIPE;
@ -789,7 +830,7 @@ int cryptfs_enable(char *howarg, char *passwd)
get_orig_mount_parms(mount_point, fs_type, real_blkdev, &mnt_flags, fs_options); get_orig_mount_parms(mount_point, fs_type, real_blkdev, &mnt_flags, fs_options);
/* The init files are setup to stop the class main and late start when /* The init files are setup to stop the class main and late start when
* set to 4. They also unmount the fuse filesystem /mnt/sdcard on stingray. * vold sets trigger_shutdown_framework.
*/ */
property_set("vold.decrypt", "trigger_shutdown_framework"); property_set("vold.decrypt", "trigger_shutdown_framework");
SLOGD("Just asked init to shut down class main\n"); SLOGD("Just asked init to shut down class main\n");
@ -799,12 +840,56 @@ int cryptfs_enable(char *howarg, char *passwd)
} }
/* Now unmount the /data partition. */ /* Now unmount the /data partition. */
if (! (rc = wait_and_unmount("/data")) ) { if (wait_and_unmount(DATA_MNT_POINT)) {
/* OK, we've unmounted /data, time to setup an encrypted return -1;
* mapping, and either write a new filesystem or encrypt }
* in place.
*/
/* Do extra work for a better UX when doing the long inplace encryption */
if (how == CRYPTO_ENABLE_INPLACE) {
/* Now that /data is unmounted, we need to mount a tmpfs
* /data, set a property saying we're doing inplace encryption,
* and restart the framework.
*/
property_get("ro.crypto.tmpfs_options", tmpfs_options, "");
if (mount("tmpfs", DATA_MNT_POINT, "tmpfs", MS_NOATIME | MS_NOSUID | MS_NODEV,
tmpfs_options) < 0) {
return -1;
}
/* Tells the framework that inplace encryption is starting */
property_set("vold.encrypt_progress", "startup");
/* restart the framework. */
/* Create necessary paths on /data */
if (prep_data_fs()) {
return -1;
}
/* startup service classes main and late_start */
property_set("vold.decrypt", "trigger_restart_min_framework");
SLOGD("Just triggered restart_min_framework\n");
/* Wait till the framework is ready */
for (i=0; i<FRAMEWORK_BOOT_WAIT; i++) {
char progress_state[32];
sleep(1);
property_get("vold.encrypt_progress", progress_state, "");
if (! strcmp(progress_state, "ready")) {
break;
}
}
if (i == FRAMEWORK_BOOT_WAIT) {
/* The framework never rebooted, so abort */
return -1;
}
/* OK, the framework is restarted and displaying a progress bar,
* time to setup an encrypted mapping, and either write a new
* filesystem or encrypt in place, updating the progress bar
* as we work.
*/
}
/* Start the actual work of making an encrypted filesystem */
fd = open(real_blkdev, O_RDONLY); fd = open(real_blkdev, O_RDONLY);
if ( (nr_sec = get_blkdev_size(fd)) == 0) { if ( (nr_sec = get_blkdev_size(fd)) == 0) {
SLOGE("Cannot get size of block device %s\n", real_blkdev); SLOGE("Cannot get size of block device %s\n", real_blkdev);
@ -839,12 +924,58 @@ int cryptfs_enable(char *howarg, char *passwd)
return -1; return -1;
} }
if (! rc) { /* Undo the dm-crypt mapping whether we succeed or not */
delete_crypto_blk_dev(crypto_blkdev); delete_crypto_blk_dev(crypto_blkdev);
if (! rc) {
/* Success */
sleep(2); /* Give the UI a change to show 100% progress */
sync(); sync();
reboot(LINUX_REBOOT_CMD_RESTART); reboot(LINUX_REBOOT_CMD_RESTART);
} }
/* Only returns on error */
return rc;
}
int cryptfs_changepw(char *oldpw, char *newpw)
{
struct crypt_mnt_ftr crypt_ftr;
unsigned char encrypted_master_key[32], decrypted_master_key[32];
unsigned char new_key_sha1[20];
char real_blkdev[MAXPATHLEN];
/* This is only allowed after we've successfully decrypted the master key */
if (! key_sha1_saved) {
return -1;
}
property_get("ro.crypto.fs_real_blkdev", real_blkdev, "");
if (strlen(real_blkdev) == 0) {
return -1;
}
/* get key */
if (get_crypt_ftr_and_key(real_blkdev, &crypt_ftr, encrypted_master_key)) {
SLOGE("Error getting crypt footer and key\n");
return -1;
}
/* decrypt key with old passwd */
decrypt_master_key(oldpw, encrypted_master_key, decrypted_master_key);
/* compute sha1 of decrypted key */
SHA1(decrypted_master_key, KEY_LEN_BYTES, new_key_sha1);
/* If computed sha1 and saved sha1 match, encrypt key with new passwd */
if (! memcmp(saved_key_sha1, new_key_sha1, sizeof(saved_key_sha1))) {
/* they match, it's safe to re-encrypt the key */
encrypt_master_key(newpw, decrypted_master_key, encrypted_master_key);
/* save the key */
put_crypt_ftr_and_key(real_blkdev, &crypt_ftr, encrypted_master_key);
} else { } else {
SLOGE("SHA1 mismatch");
return -1; return -1;
} }

View file

@ -55,6 +55,7 @@ extern "C" {
int cryptfs_check_passwd(char *pw); int cryptfs_check_passwd(char *pw);
int cryptfs_restart(void); int cryptfs_restart(void);
int cryptfs_enable(char *flag, char *passwd); int cryptfs_enable(char *flag, char *passwd);
int cryptfs_changepw(char *oldpw, char *newpw);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif