From 0523156775ca9a8242953f856e3daa7c1b82b0e5 Mon Sep 17 00:00:00 2001 From: Oscar Montemayor Date: Mon, 30 Nov 2009 08:40:57 -0800 Subject: [PATCH] Recovery changes for Encrypted File Systems. This change enables/disables the Encrypted file systems feature. It reads some properties form the data partition, wipes the partition out, and then rewrites the proper properties again into the data partition to signal that encrypted FS are enabled. --- Android.mk | 17 ++--- efs_migration.c | 183 ++++++++++++++++++++++++++++++++++++++++++++++++ efs_migration.h | 40 +++++++++++ recovery.c | 85 ++++++++++++++++++---- 4 files changed, 303 insertions(+), 22 deletions(-) create mode 100644 efs_migration.c create mode 100644 efs_migration.h diff --git a/Android.mk b/Android.mk index deec80a..913aa14 100644 --- a/Android.mk +++ b/Android.mk @@ -7,13 +7,14 @@ include $(CLEAR_VARS) commands_recovery_local_path := $(LOCAL_PATH) LOCAL_SRC_FILES := \ - recovery.c \ - bootloader.c \ - firmware.c \ - install.c \ - roots.c \ - ui.c \ - verifier.c + recovery.c \ + bootloader.c \ + firmware.c \ + install.c \ + roots.c \ + ui.c \ + verifier.c \ + efs_migration.c LOCAL_SRC_FILES += test_roots.c @@ -52,5 +53,5 @@ include $(commands_recovery_local_path)/updater/Android.mk commands_recovery_local_path := endif # TARGET_ARCH == arm -endif # !TARGET_SIMULATOR +endif # !TARGET_SIMULATOR diff --git a/efs_migration.c b/efs_migration.c new file mode 100644 index 0000000..aeadd18 --- /dev/null +++ b/efs_migration.c @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "efs_migration.h" +#include "cutils/misc.h" +#include "cutils/properties.h" +#include "common.h" +#include "mtdutils/mtdutils.h" +#include "mtdutils/mounts.h" +#include "roots.h" + +const char* efs_enabled_property = "persist.security.efs.enabled"; +const char* efs_transition_property = "persist.security.efs.trans"; +const char* efs_property_dir = "/data/property/"; + +void get_property_file_name(char *buffer, const char *property_name) { + sprintf(buffer, "%s%s", efs_property_dir, property_name); +} + +int get_text_file_contents(char *buffer, int buf_size, char *file_name) { + FILE *in_file; + char *read_data; + + in_file = fopen(file_name, "r"); + if (in_file == NULL) { + LOGE("Encrypted FS: error accessing properties."); + return EFS_ERROR; + } + + read_data = fgets(buffer, buf_size, in_file); + if (read_data == NULL) { + // Error or unexpected data + fclose(in_file); + LOGE("Encrypted FS: error accessing properties."); + return EFS_ERROR; + } + + fclose(in_file); + return EFS_OK; +} + +int set_text_file_contents(char *buffer, char *file_name) { + FILE *out_file; + int result; + + out_file = fopen(file_name, "w"); + if (out_file == NULL) { + LOGE("Encrypted FS: error setting up properties."); + return EFS_ERROR; + } + + result = fputs(buffer, out_file); + if (result != 0) { + // Error or unexpected data + fclose(out_file); + LOGE("Encrypted FS: error setting up properties."); + return EFS_ERROR; + } + + fflush(out_file); + fclose(out_file); + return EFS_OK; +} + + +int read_efs_boolean_property(const char *prop_name, int *value) { + char prop_file_name[PROPERTY_KEY_MAX + 32]; + char prop_value[PROPERTY_VALUE_MAX]; + int result; + + get_property_file_name(prop_file_name, prop_name); + result = get_text_file_contents(prop_value, PROPERTY_VALUE_MAX, prop_file_name); + + if (result < 0) { + return result; + } + + if (strncmp(prop_value, "1", 1) == 0) { + *value = 1; + } else if (strncmp(prop_value, "0", 1) == 0) { + *value = 0; + } else { + LOGE("Encrypted FS: error accessing properties."); + return EFS_ERROR; + } + + return EFS_OK; +} + +int write_efs_boolean_property(const char *prop_name, int value) { + char prop_file_name[PROPERTY_KEY_MAX + 32]; + char prop_value[PROPERTY_VALUE_MAX]; + int result; + + get_property_file_name(prop_file_name, prop_name); + + // Create the directory if needed + mkdir(efs_property_dir, 0755); + if (value == 1) { + result = set_text_file_contents("1", prop_file_name); + } else if (value == 0) { + result = set_text_file_contents("0", prop_file_name); + } else { + return EFS_ERROR; + } + if (result < 0) { + return result; + } + + return EFS_OK; +} + +int read_encrypted_fs_info(encrypted_fs_info *efs_data) { + int result; + int value; + result = ensure_root_path_mounted("DATA:"); + if (result != 0) { + LOGE("Encrypted FS: error mounting userdata partition."); + return EFS_ERROR; + } + + // STOPSHIP: Read the EFS key from a file (TBD later) + // Future code goes here... + + result = ensure_root_path_unmounted("DATA:"); + if (result != 0) { + LOGE("Encrypted FS: error unmounting data partition."); + return EFS_ERROR; + } + + return EFS_OK; +} + +int restore_encrypted_fs_info(encrypted_fs_info *efs_data) { + int result; + result = ensure_root_path_mounted("DATA:"); + if (result != 0) { + LOGE("Encrypted FS: error mounting userdata partition."); + return EFS_ERROR; + } + + // Set the EFS properties to their respective values + result = write_efs_boolean_property(efs_enabled_property, efs_data->encrypted_fs_mode); + if (result != 0) { + return result; + } + + // Signal "transition" of Encrypted File System settings + result = write_efs_boolean_property(efs_transition_property, 1); + if (result != 0) { + return result; + } + + result = ensure_root_path_unmounted("DATA:"); + if (result != 0) { + LOGE("Encrypted FS: error unmounting data partition."); + return EFS_ERROR; + } + + return EFS_OK; +} + diff --git a/efs_migration.h b/efs_migration.h new file mode 100644 index 0000000..7857f94 --- /dev/null +++ b/efs_migration.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#ifndef __EFS_MIGRATION_H__ +#define __EFS_MIGRATION_H__ + +#define MODE_ENCRYPTEDFS_DISABLED 0 +#define MODE_ENCRYPTEDFS_ENABLED 1 + +#define EFS_OK 0 +#define EFS_ERROR (-1) + +struct encrypted_fs_info { + int encrypted_fs_mode; + char *encrypted_fs_key; +}; + +typedef struct encrypted_fs_info encrypted_fs_info; + +int read_encrypted_fs_info(encrypted_fs_info *efs_data); + +int restore_encrypted_fs_info(encrypted_fs_info *efs_data); + +#endif /* __EFS_MIGRATION_H__ */ + diff --git a/recovery.c b/recovery.c index 4385307..58c84ef 100644 --- a/recovery.c +++ b/recovery.c @@ -37,12 +37,15 @@ #include "minzip/DirUtil.h" #include "roots.h" #include "recovery_ui.h" +#include "efs_migration.h" static const struct option OPTIONS[] = { { "send_intent", required_argument, NULL, 's' }, { "update_package", required_argument, NULL, 'u' }, { "wipe_data", no_argument, NULL, 'w' }, { "wipe_cache", no_argument, NULL, 'c' }, + // TODO{oam}: implement improved command line passing key, egnot to review. + { "set_encrypted_filesystem", required_argument, NULL, 'e' }, { NULL, 0, NULL, 0 }, }; @@ -63,6 +66,7 @@ static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log"; * --update_package=root:path - verify install an OTA package file * --wipe_data - erase user data (and cache), then reboot * --wipe_cache - wipe cache (but not user data), then reboot + * --set_encrypted_filesystem=on|off - enables / diasables encrypted fs * * After completing, we remove /cache/recovery/command and reboot. * Arguments may also be supplied in the bootloader control block (BCB). @@ -107,6 +111,26 @@ static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log"; * 8g. finish_recovery() erases BCB * -- after this, rebooting will (try to) restart the main system -- * 9. main() calls reboot() to boot main system + * + * ENCRYPTED FILE SYSTEMS ENABLE/DISABLE + * 1. user selects "enable encrypted file systems" + * 2. main system writes "--set_encrypted_filesystem=on|off" to + * /cache/recovery/command + * 3. main system reboots into recovery + * 4. get_args() writes BCB with "boot-recovery" and + * "--set_encrypted_filesystems=on|off" + * -- after this, rebooting will restart the transition -- + * 5. read_encrypted_fs_info() retrieves encrypted file systems settings from /data + * Settings include: property to specify the Encrypted FS istatus and + * FS encryption key if enabled (not yet implemented) + * 6. erase_root() reformats /data + * 7. erase_root() reformats /cache + * 8. restore_encrypted_fs_info() writes required encrypted file systems settings to /data + * Settings include: property to specify the Encrypted FS status and + * FS encryption key if enabled (not yet implemented) + * 9. finish_recovery() erases BCB + * -- after this, rebooting will restart the main system -- + * 10. main() calls reboot() to boot main system */ static const int MAX_ARG_LENGTH = 4096; @@ -209,8 +233,7 @@ get_args(int *argc, char ***argv) { } static void -set_sdcard_update_bootloader_message() -{ +set_sdcard_update_bootloader_message() { struct bootloader_message boot; memset(&boot, 0, sizeof(boot)); strlcpy(boot.command, "boot-recovery", sizeof(boot.command)); @@ -223,8 +246,7 @@ set_sdcard_update_bootloader_message() // record any intent we were asked to communicate back to the system. // this function is idempotent: call it as many times as you like. static void -finish_recovery(const char *send_intent) -{ +finish_recovery(const char *send_intent) { // By this point, we're ready to return to the main system... if (send_intent != NULL) { FILE *fp = fopen_root_path(INTENT_FILE, "w"); @@ -255,7 +277,7 @@ finish_recovery(const char *send_intent) check_and_fclose(log, LOG_FILE); } - // Reset the bootloader message to revert to a normal main system boot. + // Reset to mormal system boot so recovery won't cycle indefinitely. struct bootloader_message boot; memset(&boot, 0, sizeof(boot)); set_bootloader_message(&boot); @@ -272,8 +294,7 @@ finish_recovery(const char *send_intent) } static int -erase_root(const char *root) -{ +erase_root(const char *root) { ui_set_background(BACKGROUND_ICON_INSTALLING); ui_show_indeterminate_progress(); ui_print("Formatting %s...\n", root); @@ -384,8 +405,7 @@ wipe_data(int confirm) { } static void -prompt_and_wait() -{ +prompt_and_wait() { char** headers = prepend_title(MENU_HEADERS); for (;;) { @@ -438,14 +458,12 @@ prompt_and_wait() } static void -print_property(const char *key, const char *name, void *cookie) -{ +print_property(const char *key, const char *name, void *cookie) { fprintf(stderr, "%s=%s\n", key, name); } int -main(int argc, char **argv) -{ +main(int argc, char **argv) { time_t start = time(NULL); // If these fail, there's not really anywhere to complain... @@ -459,7 +477,10 @@ main(int argc, char **argv) int previous_runs = 0; const char *send_intent = NULL; const char *update_package = NULL; + const char *efs_mode = NULL; int wipe_data = 0, wipe_cache = 0; + int toggle_efs = 0; + encrypted_fs_info efs_data; int arg; while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) { @@ -469,6 +490,7 @@ main(int argc, char **argv) case 'u': update_package = optarg; break; case 'w': wipe_data = wipe_cache = 1; break; case 'c': wipe_cache = 1; break; + case 'e': efs_mode = optarg; toggle_efs = 1; break; case '?': LOGE("Invalid command argument\n"); continue; @@ -486,7 +508,42 @@ main(int argc, char **argv) int status = INSTALL_SUCCESS; - if (update_package != NULL) { + if (toggle_efs) { + if (strcmp(efs_mode,"on") == 0) { + efs_data.encrypted_fs_mode = MODE_ENCRYPTEDFS_ENABLED; + ui_print("Enabling Encrypted FS.\n"); + } else if (strcmp(efs_mode,"off") == 0) { + efs_data.encrypted_fs_mode = MODE_ENCRYPTEDFS_DISABLED; + ui_print("Disabling Encrypted FS.\n"); + } else { + ui_print("Error: invalid Encrypted FS setting.\n"); + status = INSTALL_ERROR; + } + + // Recovery strategy: if the data partition is damaged, disable encrypted file systems. + // This preventsthe device recycling endlessly in recovery mode. + // TODO{oam}: implement improved recovery strategy later. egnor to review. + if (read_encrypted_fs_info(&efs_data)) { + ui_print("Encrypted FS change aborted, resetting to disabled state.\n"); + efs_data.encrypted_fs_mode = MODE_ENCRYPTEDFS_DISABLED; + } + + if (status != INSTALL_ERROR) { + if (erase_root("DATA:")) { + ui_print("Data wipe failed.\n"); + status = INSTALL_ERROR; + } else if (erase_root("CACHE:")) { + ui_print("Cache wipe failed.\n"); + status = INSTALL_ERROR; + } else if (restore_encrypted_fs_info(&efs_data)) { + ui_print("Encrypted FS change aborted.\n"); + status = INSTALL_ERROR; + } else { + ui_print("Successfully updated Encrypted FS.\n"); + status = INSTALL_SUCCESS; + } + } + } else if (update_package != NULL) { status = install_package(update_package); if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n"); } else if (wipe_data) {