diff options
45 files changed, 666 insertions, 568 deletions
diff --git a/Android.mk b/Android.mk index deec80ae4..5f7406466 100644 --- a/Android.mk +++ b/Android.mk @@ -7,13 +7,13 @@ 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 \ + install.c \ + roots.c \ + ui.c \ + verifier.c \ + efs_migration.c LOCAL_SRC_FILES += test_roots.c @@ -21,7 +21,7 @@ LOCAL_MODULE := recovery LOCAL_FORCE_STATIC_EXECUTABLE := true -RECOVERY_API_VERSION := 2 +RECOVERY_API_VERSION := 3 LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION) # This binary is in the recovery ramdisk, which is otherwise a copy of root. @@ -43,6 +43,22 @@ LOCAL_STATIC_LIBRARIES += libstdc++ libc include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := verifier_test.c verifier.c + +LOCAL_MODULE := verifier_test + +LOCAL_FORCE_STATIC_EXECUTABLE := true + +LOCAL_MODULE_TAGS := tests + +LOCAL_STATIC_LIBRARIES := libmincrypt libcutils libstdc++ libc + +include $(BUILD_EXECUTABLE) + + include $(commands_recovery_local_path)/minui/Android.mk include $(commands_recovery_local_path)/minzip/Android.mk include $(commands_recovery_local_path)/mtdutils/Android.mk @@ -52,5 +68,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/bootloader.c b/bootloader.c index bc79bee76..38b5651bf 100644 --- a/bootloader.c +++ b/bootloader.c @@ -119,153 +119,3 @@ int set_bootloader_message(const struct bootloader_message *in) { LOGI("Set boot command \"%s\"\n", in->command[0] != 255 ? in->command : ""); return 0; } - -/* Update Image - * - * - will be stored in the "cache" partition - * - bad blocks will be ignored, like boot.img and recovery.img - * - the first block will be the image header (described below) - * - the size is in BYTES, inclusive of the header - * - offsets are in BYTES from the start of the update header - * - two raw bitmaps will be included, the "busy" and "fail" bitmaps - * - for dream, the bitmaps will be 320x480x16bpp RGB565 - */ - -#define UPDATE_MAGIC "MSM-RADIO-UPDATE" -#define UPDATE_MAGIC_SIZE 16 -#define UPDATE_VERSION 0x00010000 - -struct update_header { - unsigned char MAGIC[UPDATE_MAGIC_SIZE]; - - unsigned version; - unsigned size; - - unsigned image_offset; - unsigned image_length; - - unsigned bitmap_width; - unsigned bitmap_height; - unsigned bitmap_bpp; - - unsigned busy_bitmap_offset; - unsigned busy_bitmap_length; - - unsigned fail_bitmap_offset; - unsigned fail_bitmap_length; -}; - -int write_update_for_bootloader( - const char *update, int update_length, - int bitmap_width, int bitmap_height, int bitmap_bpp, - const char *busy_bitmap, const char *fail_bitmap) { - if (ensure_root_path_unmounted(CACHE_NAME)) { - LOGE("Can't unmount %s\n", CACHE_NAME); - return -1; - } - - const MtdPartition *part = get_root_mtd_partition(CACHE_NAME); - if (part == NULL) { - LOGE("Can't find %s\n", CACHE_NAME); - return -1; - } - - MtdWriteContext *write = mtd_write_partition(part); - if (write == NULL) { - LOGE("Can't open %s\n(%s)\n", CACHE_NAME, strerror(errno)); - return -1; - } - - /* Write an invalid (zero) header first, to disable any previous - * update and any other structured contents (like a filesystem), - * and as a placeholder for the amount of space required. - */ - - struct update_header header; - memset(&header, 0, sizeof(header)); - const ssize_t header_size = sizeof(header); - if (mtd_write_data(write, (char*) &header, header_size) != header_size) { - LOGE("Can't write header to %s\n(%s)\n", CACHE_NAME, strerror(errno)); - mtd_write_close(write); - return -1; - } - - /* Write each section individually block-aligned, so we can write - * each block independently without complicated buffering. - */ - - memcpy(&header.MAGIC, UPDATE_MAGIC, UPDATE_MAGIC_SIZE); - header.version = UPDATE_VERSION; - header.size = header_size; - - off_t image_start_pos = mtd_erase_blocks(write, 0); - header.image_length = update_length; - if ((int) header.image_offset == -1 || - mtd_write_data(write, update, update_length) != update_length) { - LOGE("Can't write update to %s\n(%s)\n", CACHE_NAME, strerror(errno)); - mtd_write_close(write); - return -1; - } - off_t busy_start_pos = mtd_erase_blocks(write, 0); - header.image_offset = mtd_find_write_start(write, image_start_pos); - - header.bitmap_width = bitmap_width; - header.bitmap_height = bitmap_height; - header.bitmap_bpp = bitmap_bpp; - - int bitmap_length = (bitmap_bpp + 7) / 8 * bitmap_width * bitmap_height; - - header.busy_bitmap_length = busy_bitmap != NULL ? bitmap_length : 0; - if ((int) header.busy_bitmap_offset == -1 || - mtd_write_data(write, busy_bitmap, bitmap_length) != bitmap_length) { - LOGE("Can't write bitmap to %s\n(%s)\n", CACHE_NAME, strerror(errno)); - mtd_write_close(write); - return -1; - } - off_t fail_start_pos = mtd_erase_blocks(write, 0); - header.busy_bitmap_offset = mtd_find_write_start(write, busy_start_pos); - - header.fail_bitmap_length = fail_bitmap != NULL ? bitmap_length : 0; - if ((int) header.fail_bitmap_offset == -1 || - mtd_write_data(write, fail_bitmap, bitmap_length) != bitmap_length) { - LOGE("Can't write bitmap to %s\n(%s)\n", CACHE_NAME, strerror(errno)); - mtd_write_close(write); - return -1; - } - mtd_erase_blocks(write, 0); - header.fail_bitmap_offset = mtd_find_write_start(write, fail_start_pos); - - /* Write the header last, after all the blocks it refers to, so that - * when the magic number is installed everything is valid. - */ - - if (mtd_write_close(write)) { - LOGE("Can't finish writing %s\n(%s)\n", CACHE_NAME, strerror(errno)); - return -1; - } - - write = mtd_write_partition(part); - if (write == NULL) { - LOGE("Can't reopen %s\n(%s)\n", CACHE_NAME, strerror(errno)); - return -1; - } - - if (mtd_write_data(write, (char*) &header, header_size) != header_size) { - LOGE("Can't rewrite header to %s\n(%s)\n", CACHE_NAME, strerror(errno)); - mtd_write_close(write); - return -1; - } - - if (mtd_erase_blocks(write, 0) != image_start_pos) { - LOGE("Misalignment rewriting %s\n(%s)\n", CACHE_NAME, strerror(errno)); - mtd_write_close(write); - return -1; - } - - if (mtd_write_close(write)) { - LOGE("Can't finish header of %s\n(%s)\n", CACHE_NAME, strerror(errno)); - return -1; - } - - return 0; -} diff --git a/bootloader.h b/bootloader.h index 3d4302f1e..2e749aa12 100644 --- a/bootloader.h +++ b/bootloader.h @@ -47,13 +47,4 @@ struct bootloader_message { int get_bootloader_message(struct bootloader_message *out); int set_bootloader_message(const struct bootloader_message *in); -/* Write an update to the cache partition for update-radio or update-hboot. - * Note, this destroys any filesystem on the cache partition! - * The expected bitmap format is 240x320, 16bpp (2Bpp), RGB 5:6:5. - */ -int write_update_for_bootloader( - const char *update, int update_len, - int bitmap_width, int bitmap_height, int bitmap_bpp, - const char *busy_bitmap, const char *error_bitmap); - #endif @@ -49,17 +49,10 @@ enum { BACKGROUND_ICON_NONE, BACKGROUND_ICON_INSTALLING, BACKGROUND_ICON_ERROR, - BACKGROUND_ICON_FIRMWARE_INSTALLING, - BACKGROUND_ICON_FIRMWARE_ERROR, NUM_BACKGROUND_ICONS }; void ui_set_background(int icon); -// Get a malloc'd copy of the screen image showing (only) the specified icon. -// Also returns the width, height, and bits per pixel of the returned image. -// TODO: Use some sort of "struct Bitmap" here instead of all these variables? -char *ui_copy_image(int icon, int *width, int *height, int *bpp); - // Show a progress bar and define the scope of the next operation: // portion - fraction of the progress bar the next operation will use // seconds - expected time interval (progress bar moves at this minimum rate) diff --git a/default_recovery_ui.c b/default_recovery_ui.c index d4e620403..409d67934 100644 --- a/default_recovery_ui.c +++ b/default_recovery_ui.c @@ -29,6 +29,10 @@ char* MENU_ITEMS[] = { "reboot system now", "wipe cache partition", NULL }; +int device_recovery_start() { + return 0; +} + int device_toggle_display(volatile char* key_pressed, int key_code) { return key_code == KEY_HOME; } diff --git a/efs_migration.c b/efs_migration.c new file mode 100644 index 000000000..aeadd1865 --- /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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#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 000000000..7857f94fe --- /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 <stdio.h> + +#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/firmware.c b/firmware.c deleted file mode 100644 index e2e4fe630..000000000 --- a/firmware.c +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2008 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 "bootloader.h" -#include "common.h" -#include "firmware.h" -#include "roots.h" - -#include <errno.h> -#include <string.h> -#include <sys/reboot.h> - -static const char *update_type = NULL; -static const char *update_data = NULL; -static int update_length = 0; - -int remember_firmware_update(const char *type, const char *data, int length) { - if (update_type != NULL || update_data != NULL) { - LOGE("Multiple firmware images\n"); - return -1; - } - - update_type = type; - update_data = data; - update_length = length; - return 0; -} - -// Return true if there is a firmware update pending. -int firmware_update_pending() { - return update_data != NULL && update_length > 0; -} - -/* Bootloader / Recovery Flow - * - * On every boot, the bootloader will read the bootloader_message - * from flash and check the command field. The bootloader should - * deal with the command field not having a 0 terminator correctly - * (so as to not crash if the block is invalid or corrupt). - * - * The bootloader will have to publish the partition that contains - * the bootloader_message to the linux kernel so it can update it. - * - * if command == "boot-recovery" -> boot recovery.img - * else if command == "update-radio" -> update radio image (below) - * else if command == "update-hboot" -> update hboot image (below) - * else -> boot boot.img (normal boot) - * - * Radio/Hboot Update Flow - * 1. the bootloader will attempt to load and validate the header - * 2. if the header is invalid, status="invalid-update", goto #8 - * 3. display the busy image on-screen - * 4. if the update image is invalid, status="invalid-radio-image", goto #8 - * 5. attempt to update the firmware (depending on the command) - * 6. if successful, status="okay", goto #8 - * 7. if failed, and the old image can still boot, status="failed-update" - * 8. write the bootloader_message, leaving the recovery field - * unchanged, updating status, and setting command to - * "boot-recovery" - * 9. reboot - * - * The bootloader will not modify or erase the cache partition. - * It is recovery's responsibility to clean up the mess afterwards. - */ - -int maybe_install_firmware_update(const char *send_intent) { - if (update_data == NULL || update_length == 0) return 0; - - /* We destroy the cache partition to pass the update image to the - * bootloader, so all we can really do afterwards is wipe cache and reboot. - * Set up this instruction now, in case we're interrupted while writing. - */ - - struct bootloader_message boot; - memset(&boot, 0, sizeof(boot)); - strlcpy(boot.command, "boot-recovery", sizeof(boot.command)); - strlcpy(boot.recovery, "recovery\n--wipe_cache\n", sizeof(boot.command)); - if (send_intent != NULL) { - strlcat(boot.recovery, "--send_intent=", sizeof(boot.recovery)); - strlcat(boot.recovery, send_intent, sizeof(boot.recovery)); - strlcat(boot.recovery, "\n", sizeof(boot.recovery)); - } - if (set_bootloader_message(&boot)) return -1; - - int width = 0, height = 0, bpp = 0; - char *busy_image = ui_copy_image( - BACKGROUND_ICON_FIRMWARE_INSTALLING, &width, &height, &bpp); - char *fail_image = ui_copy_image( - BACKGROUND_ICON_FIRMWARE_ERROR, &width, &height, &bpp); - - ui_print("Writing %s image...\n", update_type); - if (write_update_for_bootloader( - update_data, update_length, - width, height, bpp, busy_image, fail_image)) { - LOGE("Can't write %s image\n(%s)\n", update_type, strerror(errno)); - format_root_device("CACHE:"); // Attempt to clean cache up, at least. - return -1; - } - - free(busy_image); - free(fail_image); - - /* The update image is fully written, so now we can instruct the bootloader - * to install it. (After doing so, it will come back here, and we will - * wipe the cache and reboot into the system.) - */ - snprintf(boot.command, sizeof(boot.command), "update-%s", update_type); - if (set_bootloader_message(&boot)) { - format_root_device("CACHE:"); - return -1; - } - - reboot(RB_AUTOBOOT); - - // Can't reboot? WTF? - LOGE("Can't reboot\n"); - return -1; -} diff --git a/firmware.h b/firmware.h deleted file mode 100644 index aeb8f97aa..000000000 --- a/firmware.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2008 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. - */ - -#ifndef _RECOVERY_FIRMWARE_H -#define _RECOVERY_FIRMWARE_H - -/* Save a radio or bootloader update image for later installation. - * The type should be one of "hboot" or "radio". - * Takes ownership of type and data. Returns nonzero on error. - */ -int remember_firmware_update(const char *type, const char *data, int length); - -/* Returns true if a firmware update has been saved. */ -int firmware_update_pending(); - -/* If an update was saved, reboot into the bootloader now to install it. - * Returns 0 if no radio image was defined, nonzero on error, - * doesn't return at all on success... - */ -int maybe_install_firmware_update(const char *send_intent); - -#endif @@ -32,71 +32,10 @@ #include "mtdutils/mtdutils.h" #include "roots.h" #include "verifier.h" -#include "firmware.h" #define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary" #define PUBLIC_KEYS_FILE "/res/keys" -// The update binary ask us to install a firmware file on reboot. Set -// that up. Takes ownership of type and filename. -static int -handle_firmware_update(char* type, char* filename, ZipArchive* zip) { - unsigned int data_size; - const ZipEntry* entry = NULL; - - if (strncmp(filename, "PACKAGE:", 8) == 0) { - entry = mzFindZipEntry(zip, filename+8); - if (entry == NULL) { - LOGE("Failed to find \"%s\" in package", filename+8); - return INSTALL_ERROR; - } - data_size = entry->uncompLen; - } else { - struct stat st_data; - if (stat(filename, &st_data) < 0) { - LOGE("Error stat'ing %s: %s\n", filename, strerror(errno)); - return INSTALL_ERROR; - } - data_size = st_data.st_size; - } - - LOGI("type is %s; size is %d; file is %s\n", - type, data_size, filename); - - char* data = malloc(data_size); - if (data == NULL) { - LOGI("Can't allocate %d bytes for firmware data\n", data_size); - return INSTALL_ERROR; - } - - if (entry) { - if (mzReadZipEntry(zip, entry, data, data_size) == false) { - LOGE("Failed to read \"%s\" from package", filename+8); - return INSTALL_ERROR; - } - } else { - FILE* f = fopen(filename, "rb"); - if (f == NULL) { - LOGE("Failed to open %s: %s\n", filename, strerror(errno)); - return INSTALL_ERROR; - } - if (fread(data, 1, data_size, f) != data_size) { - LOGE("Failed to read firmware data: %s\n", strerror(errno)); - return INSTALL_ERROR; - } - fclose(f); - } - - if (remember_firmware_update(type, data, data_size)) { - LOGE("Can't store %s image\n", type); - free(data); - return INSTALL_ERROR; - } - free(filename); - - return INSTALL_SUCCESS; -} - // If the package contains an update binary, extract it and run it. static int try_update_binary(const char *path, ZipArchive *zip) { @@ -145,9 +84,12 @@ try_update_binary(const char *path, ZipArchive *zip) { // // firmware <"hboot"|"radio"> <filename> // arrange to install the contents of <filename> in the - // given partition on reboot. (API v2: <filename> may - // start with "PACKAGE:" to indicate taking a file from - // the OTA package.) + // given partition on reboot. + // + // (API v2: <filename> may start with "PACKAGE:" to + // indicate taking a file from the OTA package.) + // + // (API v3: this command no longer exists.) // // ui_print <string> // display <string> on the screen. @@ -172,9 +114,6 @@ try_update_binary(const char *path, ZipArchive *zip) { } close(pipefd[1]); - char* firmware_type = NULL; - char* firmware_filename = NULL; - char buffer[1024]; FILE* from_child = fdopen(pipefd[0], "r"); while (fgets(buffer, sizeof(buffer), from_child) != NULL) { @@ -194,18 +133,6 @@ try_update_binary(const char *path, ZipArchive *zip) { char* fraction_s = strtok(NULL, " \n"); float fraction = strtof(fraction_s, NULL); ui_set_progress(fraction); - } else if (strcmp(command, "firmware") == 0) { - char* type = strtok(NULL, " \n"); - char* filename = strtok(NULL, " \n"); - - if (type != NULL && filename != NULL) { - if (firmware_type != NULL) { - LOGE("ignoring attempt to do multiple firmware updates"); - } else { - firmware_type = strdup(type); - firmware_filename = strdup(filename); - } - } } else if (strcmp(command, "ui_print") == 0) { char* str = strtok(NULL, "\n"); if (str) { @@ -226,11 +153,7 @@ try_update_binary(const char *path, ZipArchive *zip) { return INSTALL_ERROR; } - if (firmware_type != NULL) { - return handle_firmware_update(firmware_type, firmware_filename, zip); - } else { - return INSTALL_SUCCESS; - } + return INSTALL_SUCCESS; } static int @@ -275,7 +198,7 @@ load_keys(const char* filename, int* numKeys) { ++*numKeys; out = realloc(out, *numKeys * sizeof(RSAPublicKey)); RSAPublicKey* key = out + (*numKeys - 1); - if (fscanf(f, " { %i , %i , { %i", + if (fscanf(f, " { %i , 0x%x , { %u", &(key->len), &(key->n0inv), &(key->n[0])) != 3) { goto exit; } @@ -284,11 +207,11 @@ load_keys(const char* filename, int* numKeys) { goto exit; } for (i = 1; i < key->len; ++i) { - if (fscanf(f, " , %i", &(key->n[i])) != 1) goto exit; + if (fscanf(f, " , %u", &(key->n[i])) != 1) goto exit; } - if (fscanf(f, " } , { %i", &(key->rr[0])) != 1) goto exit; + if (fscanf(f, " } , { %u", &(key->rr[0])) != 1) goto exit; for (i = 1; i < key->len; ++i) { - if (fscanf(f, " , %i", &(key->rr[i])) != 1) goto exit; + if (fscanf(f, " , %u", &(key->rr[i])) != 1) goto exit; } fscanf(f, " } } "); diff --git a/minui/resources.c b/minui/resources.c index 7ecfeefce..3d2c727fb 100644 --- a/minui/resources.c +++ b/minui/resources.c @@ -97,9 +97,10 @@ int res_create_surface(const char* name, gr_surface* pSurface) { int color_type = info_ptr->color_type; int bit_depth = info_ptr->bit_depth; int channels = info_ptr->channels; - if (bit_depth != 8 || (channels != 3 && channels != 4) || - (color_type != PNG_COLOR_TYPE_RGB && - color_type != PNG_COLOR_TYPE_RGBA)) { + if (!(bit_depth == 8 && + ((channels == 3 && color_type == PNG_COLOR_TYPE_RGB) || + (channels == 4 && color_type == PNG_COLOR_TYPE_RGBA) || + (channels == 1 && color_type == PNG_COLOR_TYPE_PALETTE)))) { return -7; goto exit; } @@ -118,6 +119,10 @@ int res_create_surface(const char* name, gr_surface* pSurface) { surface->format = (channels == 3) ? GGL_PIXEL_FORMAT_RGBX_8888 : GGL_PIXEL_FORMAT_RGBA_8888; + if (color_type == PNG_COLOR_TYPE_PALETTE) { + png_set_palette_to_rgb(png_ptr); + } + int y; if (channels == 3) { for (y = 0; y < height; ++y) { diff --git a/minzip/Zip.c b/minzip/Zip.c index 8cdb89874..46d2f829e 100644 --- a/minzip/Zip.c +++ b/minzip/Zip.c @@ -810,6 +810,43 @@ bool mzExtractZipEntryToFile(const ZipArchive *pArchive, return true; } +typedef struct { + unsigned char* buffer; + long len; +} BufferExtractCookie; + +static bool bufferProcessFunction(const unsigned char *data, int dataLen, + void *cookie) { + BufferExtractCookie *bec = (BufferExtractCookie*)cookie; + + memmove(bec->buffer, data, dataLen); + bec->buffer += dataLen; + bec->len -= dataLen; + + return true; +} + +/* + * Uncompress "pEntry" in "pArchive" to buffer, which must be large + * enough to hold mzGetZipEntryUncomplen(pEntry) bytes. + */ +bool mzExtractZipEntryToBuffer(const ZipArchive *pArchive, + const ZipEntry *pEntry, unsigned char *buffer) +{ + BufferExtractCookie bec; + bec.buffer = buffer; + bec.len = mzGetZipEntryUncompLen(pEntry); + + bool ret = mzProcessZipEntryContents(pArchive, pEntry, + bufferProcessFunction, (void*)&bec); + if (!ret || bec.len != 0) { + LOGE("Can't extract entry to memory buffer.\n"); + return false; + } + return true; +} + + /* Helper state to make path translation easier and less malloc-happy. */ typedef struct { diff --git a/minzip/Zip.h b/minzip/Zip.h index 1c1df2fae..9f99fba5b 100644 --- a/minzip/Zip.h +++ b/minzip/Zip.h @@ -169,6 +169,13 @@ bool mzExtractZipEntryToFile(const ZipArchive *pArchive, const ZipEntry *pEntry, int fd); /* + * Inflate and write an entry to a memory buffer, which must be long + * enough to hold mzGetZipEntryUncomplen(pEntry) bytes. + */ +bool mzExtractZipEntryToBuffer(const ZipArchive *pArchive, + const ZipEntry *pEntry, unsigned char* buffer); + +/* * Inflate all entries under zipDir to the directory specified by * targetDir, which must exist and be a writable directory. * diff --git a/recovery.c b/recovery.c index 438530710..4c437e599 100644 --- a/recovery.c +++ b/recovery.c @@ -31,18 +31,20 @@ #include "bootloader.h" #include "common.h" #include "cutils/properties.h" -#include "firmware.h" #include "install.h" #include "minui/minui.h" #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, egnor to review. + { "set_encrypted_filesystem", required_argument, NULL, 'e' }, { NULL, 0, NULL, 0 }, }; @@ -63,6 +65,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 +110,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 +232,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 +245,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 +276,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 +293,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 +404,7 @@ wipe_data(int confirm) { } static void -prompt_and_wait() -{ +prompt_and_wait() { char** headers = prepend_title(MENU_HEADERS); for (;;) { @@ -425,12 +444,7 @@ prompt_and_wait() } else if (!ui_text_visible()) { return; // reboot if logs aren't visible } else { - if (firmware_update_pending()) { - ui_print("\nReboot via menu to complete\n" - "installation.\n"); - } else { - ui_print("\nInstall from sdcard complete.\n"); - } + ui_print("\nInstall from sdcard complete.\n"); } break; } @@ -438,14 +452,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 +471,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,12 +484,15 @@ 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; } } + device_recovery_start(); + fprintf(stderr, "Command:"); for (arg = 0; arg < argc; arg++) { fprintf(stderr, " \"%s\"", argv[arg]); @@ -486,7 +504,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) { @@ -504,9 +557,6 @@ main(int argc, char **argv) if (status != INSTALL_SUCCESS) ui_set_background(BACKGROUND_ICON_ERROR); if (status != INSTALL_SUCCESS || ui_text_visible()) prompt_and_wait(); - // If there is a radio image pending, reboot now to install it. - maybe_install_firmware_update(send_intent); - // Otherwise, get ready to boot the main system... finish_recovery(send_intent); ui_print("Rebooting...\n"); diff --git a/recovery_ui.h b/recovery_ui.h index 8818ef303..e451bdfa2 100644 --- a/recovery_ui.h +++ b/recovery_ui.h @@ -17,6 +17,9 @@ #ifndef _RECOVERY_UI_H #define _RECOVERY_UI_H +// Called when recovery starts up. Returns 0. +extern int device_recovery_start(); + // Called in the input thread when a new key (key_code) is pressed. // *key_pressed is an array of KEY_MAX+1 bytes indicating which other // keys are already pressed. Return true if the text display should diff --git a/res/images/icon_firmware_error.png b/res/images/icon_firmware_error.png Binary files differdeleted file mode 100644 index 0c32c9ede..000000000 --- a/res/images/icon_firmware_error.png +++ /dev/null diff --git a/res/images/icon_firmware_install.png b/res/images/icon_firmware_install.png Binary files differdeleted file mode 100755 index 2da9e5ffe..000000000 --- a/res/images/icon_firmware_install.png +++ /dev/null diff --git a/res/images/indeterminate1.png b/res/images/indeterminate1.png Binary files differindex 264bf27e5..90cb9fba9 100644 --- a/res/images/indeterminate1.png +++ b/res/images/indeterminate1.png diff --git a/res/images/indeterminate2.png b/res/images/indeterminate2.png Binary files differindex c30c049ab..f7fb28989 100644 --- a/res/images/indeterminate2.png +++ b/res/images/indeterminate2.png diff --git a/res/images/indeterminate3.png b/res/images/indeterminate3.png Binary files differindex 891a00095..ba10dfa53 100644 --- a/res/images/indeterminate3.png +++ b/res/images/indeterminate3.png diff --git a/res/images/indeterminate4.png b/res/images/indeterminate4.png Binary files differindex 7a6415149..ad5d9a542 100644 --- a/res/images/indeterminate4.png +++ b/res/images/indeterminate4.png diff --git a/res/images/indeterminate5.png b/res/images/indeterminate5.png Binary files differindex cd6ab20a7..8c19c8d57 100644 --- a/res/images/indeterminate5.png +++ b/res/images/indeterminate5.png diff --git a/res/images/indeterminate6.png b/res/images/indeterminate6.png Binary files differindex ddd9e7384..c0c66386a 100644 --- a/res/images/indeterminate6.png +++ b/res/images/indeterminate6.png diff --git a/res/images/progress_bar_empty.png b/res/images/progress_bar_empty.png Binary files differdeleted file mode 100644 index 9013f04ac..000000000 --- a/res/images/progress_bar_empty.png +++ /dev/null diff --git a/res/images/progress_bar_empty_left_round.png b/res/images/progress_bar_empty_left_round.png Binary files differdeleted file mode 100644 index dae7d5d13..000000000 --- a/res/images/progress_bar_empty_left_round.png +++ /dev/null diff --git a/res/images/progress_bar_empty_right_round.png b/res/images/progress_bar_empty_right_round.png Binary files differdeleted file mode 100644 index 542708823..000000000 --- a/res/images/progress_bar_empty_right_round.png +++ /dev/null diff --git a/res/images/progress_bar_fill.png b/res/images/progress_bar_fill.png Binary files differdeleted file mode 100644 index 37c04b4f4..000000000 --- a/res/images/progress_bar_fill.png +++ /dev/null diff --git a/res/images/progress_bar_left_round.png b/res/images/progress_bar_left_round.png Binary files differdeleted file mode 100644 index e72af47d4..000000000 --- a/res/images/progress_bar_left_round.png +++ /dev/null diff --git a/res/images/progress_bar_right_round.png b/res/images/progress_bar_right_round.png Binary files differdeleted file mode 100644 index d04c980b9..000000000 --- a/res/images/progress_bar_right_round.png +++ /dev/null diff --git a/res/images/progress_empty.png b/res/images/progress_empty.png Binary files differnew file mode 100644 index 000000000..4cb4998dd --- /dev/null +++ b/res/images/progress_empty.png diff --git a/res/images/progress_fill.png b/res/images/progress_fill.png Binary files differnew file mode 100644 index 000000000..eb71754db --- /dev/null +++ b/res/images/progress_fill.png diff --git a/testdata/alter-footer.zip b/testdata/alter-footer.zip Binary files differnew file mode 100644 index 000000000..f497ec000 --- /dev/null +++ b/testdata/alter-footer.zip diff --git a/testdata/alter-metadata.zip b/testdata/alter-metadata.zip Binary files differnew file mode 100644 index 000000000..1c71fbc49 --- /dev/null +++ b/testdata/alter-metadata.zip diff --git a/testdata/fake-eocd.zip b/testdata/fake-eocd.zip Binary files differnew file mode 100644 index 000000000..15dc0a946 --- /dev/null +++ b/testdata/fake-eocd.zip diff --git a/testdata/jarsigned.zip b/testdata/jarsigned.zip Binary files differnew file mode 100644 index 000000000..8b1ef8bdd --- /dev/null +++ b/testdata/jarsigned.zip diff --git a/testdata/otasigned.zip b/testdata/otasigned.zip Binary files differnew file mode 100644 index 000000000..a6bc53e41 --- /dev/null +++ b/testdata/otasigned.zip diff --git a/testdata/random.zip b/testdata/random.zip Binary files differnew file mode 100644 index 000000000..18c0b3b9f --- /dev/null +++ b/testdata/random.zip diff --git a/testdata/unsigned.zip b/testdata/unsigned.zip Binary files differnew file mode 100644 index 000000000..24e3eadac --- /dev/null +++ b/testdata/unsigned.zip @@ -38,33 +38,23 @@ #define PROGRESSBAR_INDETERMINATE_STATES 6 #define PROGRESSBAR_INDETERMINATE_FPS 15 -enum { LEFT_SIDE, CENTER_TILE, RIGHT_SIDE, NUM_SIDES }; - static pthread_mutex_t gUpdateMutex = PTHREAD_MUTEX_INITIALIZER; static gr_surface gBackgroundIcon[NUM_BACKGROUND_ICONS]; static gr_surface gProgressBarIndeterminate[PROGRESSBAR_INDETERMINATE_STATES]; -static gr_surface gProgressBarEmpty[NUM_SIDES]; -static gr_surface gProgressBarFill[NUM_SIDES]; +static gr_surface gProgressBarEmpty; +static gr_surface gProgressBarFill; static const struct { gr_surface* surface; const char *name; } BITMAPS[] = { { &gBackgroundIcon[BACKGROUND_ICON_INSTALLING], "icon_installing" }, { &gBackgroundIcon[BACKGROUND_ICON_ERROR], "icon_error" }, - { &gBackgroundIcon[BACKGROUND_ICON_FIRMWARE_INSTALLING], - "icon_firmware_install" }, - { &gBackgroundIcon[BACKGROUND_ICON_FIRMWARE_ERROR], - "icon_firmware_error" }, { &gProgressBarIndeterminate[0], "indeterminate1" }, { &gProgressBarIndeterminate[1], "indeterminate2" }, { &gProgressBarIndeterminate[2], "indeterminate3" }, { &gProgressBarIndeterminate[3], "indeterminate4" }, { &gProgressBarIndeterminate[4], "indeterminate5" }, { &gProgressBarIndeterminate[5], "indeterminate6" }, - { &gProgressBarEmpty[LEFT_SIDE], "progress_bar_empty_left_round" }, - { &gProgressBarEmpty[CENTER_TILE], "progress_bar_empty" }, - { &gProgressBarEmpty[RIGHT_SIDE], "progress_bar_empty_right_round" }, - { &gProgressBarFill[LEFT_SIDE], "progress_bar_left_round" }, - { &gProgressBarFill[CENTER_TILE], "progress_bar_fill" }, - { &gProgressBarFill[RIGHT_SIDE], "progress_bar_right_round" }, + { &gProgressBarEmpty, "progress_empty" }, + { &gProgressBarFill, "progress_fill" }, { NULL, NULL }, }; @@ -123,8 +113,8 @@ static void draw_progress_locked() if (gProgressBarType == PROGRESSBAR_TYPE_NONE) return; int iconHeight = gr_get_height(gBackgroundIcon[BACKGROUND_ICON_INSTALLING]); - int width = gr_get_width(gProgressBarIndeterminate[0]); - int height = gr_get_height(gProgressBarIndeterminate[0]); + int width = gr_get_width(gProgressBarEmpty); + int height = gr_get_height(gProgressBarEmpty); int dx = (gr_fb_width() - width)/2; int dy = (3*gr_fb_height() + iconHeight - 2*height)/4; @@ -137,18 +127,12 @@ static void draw_progress_locked() float progress = gProgressScopeStart + gProgress * gProgressScopeSize; int pos = (int) (progress * width); - gr_surface s = (pos ? gProgressBarFill : gProgressBarEmpty)[LEFT_SIDE]; - gr_blit(s, 0, 0, gr_get_width(s), gr_get_height(s), dx, dy); - - int x = gr_get_width(s); - while (x + (int) gr_get_width(gProgressBarEmpty[RIGHT_SIDE]) < width) { - s = (pos > x ? gProgressBarFill : gProgressBarEmpty)[CENTER_TILE]; - gr_blit(s, 0, 0, gr_get_width(s), gr_get_height(s), dx + x, dy); - x += gr_get_width(s); + if (pos > 0) { + gr_blit(gProgressBarFill, 0, 0, pos, height, dx, dy); + } + if (pos < width-1) { + gr_blit(gProgressBarEmpty, pos, 0, width-pos, height, dx+pos, dy); } - - s = (pos > x ? gProgressBarFill : gProgressBarEmpty)[RIGHT_SIDE]; - gr_blit(s, 0, 0, gr_get_width(s), gr_get_height(s), dx + x, dy); } if (gProgressBarType == PROGRESSBAR_TYPE_INDETERMINATE) { @@ -353,23 +337,6 @@ void ui_init(void) pthread_create(&t, NULL, input_thread, NULL); } -char *ui_copy_image(int icon, int *width, int *height, int *bpp) { - pthread_mutex_lock(&gUpdateMutex); - draw_background_locked(gBackgroundIcon[icon]); - *width = gr_fb_width(); - *height = gr_fb_height(); - *bpp = sizeof(gr_pixel) * 8; - int size = *width * *height * sizeof(gr_pixel); - char *ret = malloc(size); - if (ret == NULL) { - LOGE("Can't allocate %d bytes for image\n", size); - } else { - memcpy(ret, gr_fb_data(), size); - } - pthread_mutex_unlock(&gUpdateMutex); - return ret; -} - void ui_set_background(int icon) { pthread_mutex_lock(&gUpdateMutex); diff --git a/updater/install.c b/updater/install.c index aa80d7576..852b393ea 100644 --- a/updater/install.c +++ b/updater/install.c @@ -315,37 +315,82 @@ char* PackageExtractDirFn(const char* name, State* state, // package_extract_file(package_path, destination_path) +// or +// package_extract_file(package_path) +// to return the entire contents of the file as the result of this +// function (the char* returned points to a long giving the length +// followed by that many bytes of data). char* PackageExtractFileFn(const char* name, State* state, int argc, Expr* argv[]) { - if (argc != 2) { - return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); + if (argc != 1 && argc != 2) { + return ErrorAbort(state, "%s() expects 1 or 2 args, got %d", + name, argc); } - char* zip_path; - char* dest_path; - if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL; - bool success = false; + if (argc == 2) { + // The two-argument version extracts to a file. + + char* zip_path; + char* dest_path; + if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL; + + ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; + const ZipEntry* entry = mzFindZipEntry(za, zip_path); + if (entry == NULL) { + fprintf(stderr, "%s: no %s in package\n", name, zip_path); + goto done2; + } - ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; - const ZipEntry* entry = mzFindZipEntry(za, zip_path); - if (entry == NULL) { - fprintf(stderr, "%s: no %s in package\n", name, zip_path); - goto done; - } + FILE* f = fopen(dest_path, "wb"); + if (f == NULL) { + fprintf(stderr, "%s: can't open %s for write: %s\n", + name, dest_path, strerror(errno)); + goto done2; + } + success = mzExtractZipEntryToFile(za, entry, fileno(f)); + fclose(f); - FILE* f = fopen(dest_path, "wb"); - if (f == NULL) { - fprintf(stderr, "%s: can't open %s for write: %s\n", - name, dest_path, strerror(errno)); - goto done; - } - success = mzExtractZipEntryToFile(za, entry, fileno(f)); - fclose(f); + done2: + free(zip_path); + free(dest_path); + return strdup(success ? "t" : ""); + } else { + // The one-argument version returns the contents of the file + // as the result. - done: - free(zip_path); - free(dest_path); - return strdup(success ? "t" : ""); + char* zip_path; + char* buffer = NULL; + + if (ReadArgs(state, argv, 1, &zip_path) < 0) return NULL; + + ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; + const ZipEntry* entry = mzFindZipEntry(za, zip_path); + if (entry == NULL) { + fprintf(stderr, "%s: no %s in package\n", name, zip_path); + goto done1; + } + + long size = mzGetZipEntryUncompLen(entry); + buffer = malloc(size + sizeof(long)); + if (buffer == NULL) { + fprintf(stderr, "%s: failed to allocate %ld bytes for %s\n", + name, size+sizeof(long), zip_path); + goto done1; + } + + *(long *)buffer = size; + success = mzExtractZipEntryToBuffer( + za, entry, (unsigned char *)(buffer + sizeof(long))); + + done1: + free(zip_path); + if (!success) { + free(buffer); + buffer = malloc(sizeof(long)); + *(long *)buffer = -1L; + } + return buffer; + } } @@ -658,44 +703,6 @@ done: return result; } -// write_firmware_image(file, partition) -// -// partition is "radio" or "hboot" -// file is not used until after updater exits -// -// TODO: this should live in some HTC-specific library -char* WriteFirmwareImageFn(const char* name, State* state, - int argc, Expr* argv[]) { - char* result = NULL; - - char* partition; - char* filename; - if (ReadArgs(state, argv, 2, &filename, &partition) < 0) { - return NULL; - } - - if (strlen(partition) == 0) { - ErrorAbort(state, "partition argument to %s can't be empty", name); - goto done; - } - if (strlen(filename) == 0) { - ErrorAbort(state, "file argument to %s can't be empty", name); - goto done; - } - - FILE* cmd = ((UpdaterInfo*)(state->cookie))->cmd_pipe; - fprintf(cmd, "firmware %s %s\n", partition, filename); - - printf("will write %s firmware from %s\n", partition, filename); - result = partition; - -done: - if (result != partition) free(partition); - free(filename); - return result; -} - - extern int applypatch(int argc, char** argv); // apply_patch(srcfile, tgtfile, tgtsha1, tgtsize, sha1:patch, ...) @@ -843,7 +850,6 @@ void RegisterInstallFunctions() { RegisterFunction("getprop", GetPropFn); RegisterFunction("file_getprop", FileGetPropFn); RegisterFunction("write_raw_image", WriteRawImageFn); - RegisterFunction("write_firmware_image", WriteFirmwareImageFn); RegisterFunction("apply_patch", ApplyPatchFn); RegisterFunction("apply_patch_check", ApplyPatchFn); diff --git a/updater/updater.c b/updater/updater.c index 1aa277c7f..2d16deeba 100644 --- a/updater/updater.c +++ b/updater/updater.c @@ -39,9 +39,11 @@ int main(int argc, char** argv) { } char* version = argv[1]; - if ((version[0] != '1' && version[0] != '2') || version[1] != '\0') { - // We support version "1" or "2". - fprintf(stderr, "wrong updater binary API; expected 1 or 2, got %s\n", + if ((version[0] != '1' && version[0] != '2' && version[0] != '3') || + version[1] != '\0') { + // We support version 1, 2, or 3. + fprintf(stderr, "wrong updater binary API; expected 1, 2, or 3; " + "got %s\n", argv[1]); return 2; } @@ -100,6 +102,7 @@ int main(int argc, char** argv) { UpdaterInfo updater_info; updater_info.cmd_pipe = cmd_pipe; updater_info.package_zip = &za; + updater_info.version = atoi(version); State state; state.cookie = &updater_info; diff --git a/updater/updater.h b/updater/updater.h index 22fbfd285..bd60dc1fd 100644 --- a/updater/updater.h +++ b/updater/updater.h @@ -23,6 +23,7 @@ typedef struct { FILE* cmd_pipe; ZipArchive* package_zip; + int version; } UpdaterInfo; #endif diff --git a/verifier.c b/verifier.c index 164fb4a01..9d39fd139 100644 --- a/verifier.c +++ b/verifier.c @@ -42,7 +42,7 @@ int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKey // An archive with a whole-file signature will end in six bytes: // - // $ff $ff (2-byte comment size) (2-byte signature start) + // (2-byte signature start) $ff $ff (2-byte comment size) // // (As far as the ZIP format is concerned, these are part of the // archive comment.) We start by reading this footer, this tells @@ -169,7 +169,7 @@ int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKey const uint8_t* sha1 = SHA_final(&ctx); for (i = 0; i < numKeys; ++i) { - // The 6 bytes is the "$ff $ff (signature_start) (comment_size)" that + // The 6 bytes is the "(signature_start) $ff $ff (comment_size)" that // the signing tool appends after the signature itself. if (RSA_verify(pKeys+i, eocd + eocd_size - 6 - RSANUMBYTES, RSANUMBYTES, sha1)) { diff --git a/verifier_test.c b/verifier_test.c new file mode 100644 index 000000000..5b6c1f451 --- /dev/null +++ b/verifier_test.c @@ -0,0 +1,91 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <stdarg.h> + +#include "verifier.h" + +// This is build/target/product/security/testkey.x509.pem after being +// dumped out by dumpkey.jar. +RSAPublicKey test_key = + { 64, 0xc926ad21, + { 1795090719, 2141396315, 950055447, -1713398866, + -26044131, 1920809988, 546586521, -795969498, + 1776797858, -554906482, 1805317999, 1429410244, + 129622599, 1422441418, 1783893377, 1222374759, + -1731647369, 323993566, 28517732, 609753416, + 1826472888, 215237850, -33324596, -245884705, + -1066504894, 774857746, 154822455, -1797768399, + -1536767878, -1275951968, -1500189652, 87251430, + -1760039318, 120774784, 571297800, -599067824, + -1815042109, -483341846, -893134306, -1900097649, + -1027721089, 950095497, 555058928, 414729973, + 1136544882, -1250377212, 465547824, -236820568, + -1563171242, 1689838846, -404210357, 1048029507, + 895090649, 247140249, 178744550, -747082073, + -1129788053, 109881576, -350362881, 1044303212, + -522594267, -1309816990, -557446364, -695002876}, + { -857949815, -510492167, -1494742324, -1208744608, + 251333580, 2131931323, 512774938, 325948880, + -1637480859, 2102694287, -474399070, 792812816, + 1026422502, 2053275343, -1494078096, -1181380486, + 165549746, -21447327, -229719404, 1902789247, + 772932719, -353118870, -642223187, 216871947, + -1130566647, 1942378755, -298201445, 1055777370, + 964047799, 629391717, -2062222979, -384408304, + 191868569, -1536083459, -612150544, -1297252564, + -1592438046, -724266841, -518093464, -370899750, + -739277751, -1536141862, 1323144535, 61311905, + 1997411085, 376844204, 213777604, -217643712, + 9135381, 1625809335, -1490225159, -1342673351, + 1117190829, -57654514, 1825108855, -1281819325, + 1111251351, -1726129724, 1684324211, -1773988491, + 367251975, 810756730, -1941182952, 1175080310 } + }; + +void ui_print(const char* fmt, ...) { + char buf[256]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, 256, fmt, ap); + va_end(ap); + + fputs(buf, stderr); +} + +void ui_set_progress(float fraction) { +} + +int main(int argc, char **argv) { + if (argc != 2) { + fprintf(stderr, "Usage: %s <package>\n", argv[0]); + return 2; + } + + int result = verify_file(argv[1], &test_key, 1); + if (result == VERIFY_SUCCESS) { + printf("SUCCESS\n"); + return 0; + } else if (result == VERIFY_FAILURE) { + printf("FAILURE\n"); + return 1; + } else { + printf("bad return value\n"); + return 3; + } +} diff --git a/verifier_test.sh b/verifier_test.sh new file mode 100755 index 000000000..6350e80d3 --- /dev/null +++ b/verifier_test.sh @@ -0,0 +1,94 @@ +#!/bin/bash +# +# A test suite for applypatch. Run in a client where you have done +# envsetup, choosecombo, etc. +# +# DO NOT RUN THIS ON A DEVICE YOU CARE ABOUT. It will mess up your +# system partition. +# +# +# TODO: find some way to get this run regularly along with the rest of +# the tests. + +EMULATOR_PORT=5580 +DATA_DIR=$ANDROID_BUILD_TOP/bootable/recovery/testdata + +WORK_DIR=/data/local/tmp + +# set to 0 to use a device instead +USE_EMULATOR=0 + +# ------------------------ + +if [ "$USE_EMULATOR" == 1 ]; then + emulator -wipe-data -noaudio -no-window -port $EMULATOR_PORT & + pid_emulator=$! + ADB="adb -s emulator-$EMULATOR_PORT " +else + ADB="adb -d " +fi + +echo "waiting to connect to device" +$ADB wait-for-device + +# run a command on the device; exit with the exit status of the device +# command. +run_command() { + $ADB shell "$@" \; echo \$? | awk '{if (b) {print a}; a=$0; b=1} END {exit a}' +} + +testname() { + echo + echo "::: testing $1 :::" + testname="$1" +} + +fail() { + echo + echo FAIL: $testname + echo + [ "$open_pid" == "" ] || kill $open_pid + [ "$pid_emulator" == "" ] || kill $pid_emulator + exit 1 +} + + +cleanup() { + # not necessary if we're about to kill the emulator, but nice for + # running on real devices or already-running emulators. + run_command rm $WORK_DIR/verifier_test + run_command rm $WORK_DIR/package.zip + + [ "$pid_emulator" == "" ] || kill $pid_emulator +} + +$ADB push $ANDROID_PRODUCT_OUT/system/bin/verifier_test \ + $WORK_DIR/verifier_test + +expect_succeed() { + testname "$1 (should succeed)" + $ADB push $DATA_DIR/$1 $WORK_DIR/package.zip + run_command $WORK_DIR/verifier_test $WORK_DIR/package.zip || fail +} + +expect_fail() { + testname "$1 (should fail)" + $ADB push $DATA_DIR/$1 $WORK_DIR/package.zip + run_command $WORK_DIR/verifier_test $WORK_DIR/package.zip && fail +} + +expect_fail unsigned.zip +expect_fail jarsigned.zip +expect_succeed otasigned.zip +expect_fail random.zip +expect_fail fake-eocd.zip +expect_fail alter-metadata.zip +expect_fail alter-footer.zip + +# --------------- cleanup ---------------------- + +cleanup + +echo +echo PASS +echo |