diff --git a/meta-digi-dey/recipes-core/recovery/recovery-utils/recovery-utils/include/recovery.h b/meta-digi-dey/recipes-core/recovery/recovery-utils/recovery-utils/include/recovery.h index 329193542..0e29522b3 100644 --- a/meta-digi-dey/recipes-core/recovery/recovery-utils/recovery-utils/include/recovery.h +++ b/meta-digi-dey/recipes-core/recovery/recovery-utils/recovery-utils/include/recovery.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Digi International Inc. + * Copyright (c) 2017-2021, Digi International Inc. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -48,13 +48,25 @@ int reboot_recovery(unsigned int reboot_timeout); int wipe_update_partition(void); /* - * Configure recovery commands to set a file system encryption key. + * Configure recovery commands to set a partition encryption key. * * Params: * 'key' (input) Encryption key to use, NULL or empty to generate a random one * * Return: 0 on sucess, -1 on failure */ -int set_fs_encryption_key(char *key); +int set_encryption_key(char *key); + +/* + * Configure recovery commands to encrypt/un-encrypt provided partitions. + * + * Params: + * 'to_encrypt' (input) Comma-separated list of partitions to be encrypted + * 'to_unencrypt' (input) Comma-separated list of partition names to be un-encrypted + * 'force' (input) Skips warning and confirmation prompt if non-zero + * + * Return: 0 on sucess, -1 on failure and 1 on success with no recovery command set + */ +int encrypt_partitions(char *to_encrypt, char *to_unencrypt, unsigned char force); #endif /* RECOVERY_H */ diff --git a/meta-digi-dey/recipes-core/recovery/recovery-utils/recovery-utils/lib/recovery.c b/meta-digi-dey/recipes-core/recovery/recovery-utils/recovery-utils/lib/recovery.c index ace2cee66..28ddc1551 100644 --- a/meta-digi-dey/recipes-core/recovery/recovery-utils/recovery-utils/lib/recovery.c +++ b/meta-digi-dey/recipes-core/recovery/recovery-utils/recovery-utils/lib/recovery.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Digi International Inc. + * Copyright (c) 2017-2021, Digi International Inc. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -42,6 +42,34 @@ */ #define BASE64_SIZE_BYTES(x) (4 * ((x + (3 - 1)) / 3)) +/* 20 partitions + 1 NULL array terminator */ +#define MAX_PARTITIONS (20 + 1) +#define MAX_LIST_LEN 2048 + +/* Shell command used to obtain the eMMC partition list */ +#define EMMC_FDISK_CMD "fdisk -l /dev/mmcblk0 | grep '^ *' | rev | cut -d ' ' -f1 | rev" +#define MAX_PART_NAME_LENGTH 64 + +#define PARTS_BLACKLIST is_device_nand() ? nand_parts_blacklist : emmc_parts_blacklist +#define PARSE_PARTITION_INFO(x, y, z) \ + is_device_nand() ? parse_nand_partition_info(x, y, z) : parse_emmc_partition_info(x, y, z) + +static char *nand_parts_blacklist[] = { + "bootloader", + "environment", + "linux", + "recovery", + "safe", + NULL +}; + +static char *emmc_parts_blacklist[] = { + "linux", + "recovery", + "safe", + NULL +}; + /* * Function: append_recovery_command * Description: append configuration to the 'recovery_command' variable @@ -105,6 +133,417 @@ static int is_device_closed(void) return 0; } +/* + * Function: is_device_nand + * Description: check if the device's storage is NAND/MTD + */ +static int is_device_nand(void) +{ + const char *path_mtd = "/proc/mtd"; + + if (access(path_mtd, F_OK) != -1) + return 1; + + return 0; +} + +/* + * Function: print_open_device_warning + * Description: print a warning if device isn't closed + */ +static void print_open_device_warning(void) +{ + if (!is_device_closed()) { + printf("\n" + " *****************************************************************\n" + " * Warning: Use filesystem encryption only on CLOSED devices. *\n" + " * Filesystem encryption on open devices is not secure. *\n" + " *****************************************************************\n"); + } +} + +/* + * Function: remove_entry + * Description: remove a specific entry from a NULL-terminated array + */ +static void remove_entry(char **array, unsigned char index) +{ + free(array[index]); + array[index] = array[index+1]; + + while (array[index++]) + array[index] = array[index+1]; +} + +/* + * Function: subtract_array + * Description: remove the entries of a NULL-terminated array from another NULL-terminated array + */ +static void subtract_array(char **subtract, char **from) +{ + unsigned char i, j; + i = 0; + + while (from[i]) { + j = 0; + while(subtract[j]) { + if (!strcmp(subtract[j], from[i])) { + remove_entry(from, i); + break; + } + j++; + } + + if (subtract[j]) + continue; + i++; + } +} + +/* + * Function: add_array + * Description: add the entries of a NULL-terminated array to the end of another NULL-terminated array + */ +static int add_array(char **add, char **to, unsigned char limit) +{ + unsigned char i = 0, j = 0; + + /* Get to the end of 'to' */ + while (to[i]) + i++; + + /* Add all 'add' entries to the end of 'to' */ + while (add[j] && i < (limit - 1)) { + to[i] = strdup(add[j++]); + if (!to[i++]) + return -1; + } + + to[i] = NULL; + + if (i == (limit - 1) && add[j]) + return -1; + + return 0; +} + +/* + * Function: modify_array + * Description: add and remove NULL-terminated array entries from another NULL-terminated array + */ +static int modify_array(char **target, char **to_add, char **to_remove, unsigned char limit) +{ + /* Remove the entries in 'to_add' that are already in 'target' */ + subtract_array(target, to_add); + + /* Remove the 'to_remove' entries from 'target' */ + subtract_array(to_remove, target); + + /* Add 'to_add' to 'target' while respecting the limit */ + return add_array(to_add, target, limit); +} + +/* + * Function: entry_exists + * Description: check if a string is an entry in a NULL-terminated array + */ +static int entry_exists(char *entry, char **array) +{ + unsigned char i = 0; + + while (array[i]) { + if (!strcmp(entry, array[i++])) + return 1; + } + + return 0; +} + +/* + * Function: is_subset + * Description: check if a NULL-terminated array is a subset of another NULL-terminated array + */ +static int is_subset(char **subset, char **set) +{ + unsigned char i = 0; + + /* Return 1 if and only if all subset entries are in set */ + while (subset[i]) { + if (!entry_exists(subset[i++], set)) + return 0; + } + + return 1; +} + +/* + * Function: list_to_array + * Description: convert a list into a NULL-terminated string array, while checking against a blacklist and a superset + */ +static int list_to_array(char *list, char **array, char **blacklist, + char **superset, const char *delim, unsigned char limit) +{ + char *tmp = NULL; + char *entry = NULL; + int ret = -1; + unsigned char i = 0; + unsigned char j; + + if (!list) + return 0; + + tmp = strdup(list); + if (!tmp) + return -1; + + /* Tokenize the list and iterate through it to build the array */ + entry = strtok(tmp, delim); + while (entry && i < (limit - 1)) { + /* If entry is in the blacklist, print a warning and discard it */ + if (blacklist && entry_exists(entry, blacklist)) { + printf("Warning: encryption of partition '%s' is forbidden, skipping\n", + entry); + entry = strtok(NULL, delim); + continue; + } + + /* If entry isn't a part of the superset, exit with an error */ + if (superset && !entry_exists(entry, superset)) { + fprintf(stderr, "Error: partition '%s' doesn't exist\n", entry); + goto err; + } + + array[i] = strdup(entry); + if (!array[i++]) + goto err; + + entry = strtok(NULL, delim); + } + + /* Return an error if the number of entries surpasses the limit */ + if (entry && i == (limit - 1)) + goto err; + + array[i] = NULL; + ret = 0; + + /* Remove duplicate entries */ + i = 0; + while (array[i]) { + j = i+1; + while (array[j]) { + if (!strcmp(array[j], array[i])) { + remove_entry(array, j); + continue; + } + j++; + } + i++; + } +err: + array[i] = NULL; + free(tmp); + return ret; +} + +/* + * Function: array_to_list + * Description: convert a non-empty NULL-terminated string array to a list + */ +static char *array_to_list(char **array, const char delim, unsigned int limit) +{ + char *list; + char *tmp; + unsigned char i = 0; + unsigned int len; + + /* Obtain the total length of the list to allocate the exact string size */ + len = 0; + while (array[i]) + len += strlen(array[i++]) + 1; + + /* Make sure that we don't surpass the character limit */ + if (len > limit) + return NULL; + + list = calloc(1, len); + if (!list) + return NULL; + + /* Iterate through the array to build the list */ + i = 0; + tmp = list; + while (array[i]) { + len = strlen(array[i]); + memcpy(tmp, array[i], len); + tmp += len; + if (array[++i]) { + *tmp = delim; + tmp++; + } + } + *tmp = '\0'; + + return list; +} + +/* + * Function: free_array + * Description: free all entries in a NULL-terminated string array + */ +static inline void free_array(char **array) +{ + unsigned char i = 0; + + while (array[i]) + free(array[i++]); +} + +/* + * Function: parse_nand_partition_info + * Description: puts the NAND partition and encrypted partition lists into NULL-terminated arrays + */ +static int parse_nand_partition_info(char **parts, char **encrypted, unsigned char limit) +{ + const char *var; + char *tmp; + char *entry; + char *start; + char *end; + int ret; + unsigned char i = 0, j = 0; + + /* Parse mtdparts for all partition info */ + ret = uboot_getenv("mtdparts", &var); + if (ret) { + fprintf(stderr, "Error: getenv 'mtdparts'\n"); + return ret; + } + + tmp = strdup(var); + if (!tmp) + return -1; + + ret = -1; + + /* Discard first part of mtdparts to get the partition list */ + entry = strtok(tmp, ":"); + + /* + * Parse the partition names from each mtdparts entry. + * Expected entry format is: + * + * ()[enc] + */ + entry = strtok(NULL, ","); + while (entry && i < (limit - 1)) { + start = strchr(entry, '('); + if (!start) + goto err; + start++; + + end = strchr(entry, ')'); + if (!end) + goto err; + + parts[i] = calloc(1, (end - start) + 1); + if (!parts[i]) + goto err; + + strncpy(parts[i], start, end - start); + parts[i++][end - start] = '\0'; + + /* If the encryption flag is found, add the part to the encrypted list */ + if (strstr(entry, ")enc")) { + encrypted[j] = strdup(parts[i-1]); + if (!encrypted[j++]) + goto err; + } + + entry = strtok(NULL, ","); + } + + /* Return an error if the number of entries surpasses the limit */ + if (entry && i == (limit - 1)) + goto err; + + ret = 0; +err: + if (tmp) + free(tmp); + + /* NULL-terminate the arrays */ + parts[i] = NULL; + encrypted[j] = NULL; + + return ret ? -1 : 0; +} + +/* + * Function: parse_emmc_partition_info + * Description: puts the eMMC partition and encrypted partition lists into NULL-terminated arrays + */ +static int parse_emmc_partition_info(char **parts, char **encrypted, unsigned char limit) +{ + FILE *fp = NULL; + const char *var; + char *tmp; + char *end; + int ret; + unsigned char i = 0; + + /* + * For now, obtain eMMC partition list via the + * "fdisk -l" command, just like we do in the recovery + * script. + */ + fp = popen(EMMC_FDISK_CMD, "r"); + if (!fp) + return -1; + + ret = -1; + tmp = calloc(1, MAX_PART_NAME_LENGTH); + if (!tmp) + goto err; + + /* Entries have a newline character at the end, make sure to remove it */ + while (i < (limit - 1) && fgets(tmp, MAX_PART_NAME_LENGTH, fp) != NULL) { + end = strchr(tmp, '\n'); + parts[i] = calloc(1, (end - tmp) + 1); + if (!parts[i]) + goto err; + strncpy(parts[i], tmp, (end - tmp)); + parts[i++][end - tmp] = '\0'; + } + + /* Return an error if the number of partitions surpasses the limit */ + if (i == (limit - 1) && fgets(tmp, MAX_PART_NAME_LENGTH, fp) != NULL) + goto err; + + /* Obtain encrypted partition list from the environment */ + ret = uboot_getenv("encrypted_parts_list", &var); + if (ret) { + fprintf(stderr, "Error: getenv 'encrypted_parts_list'\n"); + goto err; + } + + /* + * If non-existing partitions have been added by hand to + * encrypted_parts_list, the process will fail at this point. + */ + ret = list_to_array(var, encrypted, emmc_parts_blacklist, parts, " ", limit); +err: + if (fp) + pclose(fp); + if (tmp) + free(tmp); + + /* NULL-terminate the parts array */ + parts[i] = NULL; + + return ret ? -1 : 0; +} + /* * Function: secure_memzero * Description: secure memzero that is not optimized out by the compiler @@ -198,10 +637,10 @@ int wipe_update_partition(void) } /* - * Function: set_fs_encryption_key - * Description: configure recovery commands to set a file system encryption key + * Function: set_encryption_key + * Description: configure recovery commands to set a partition encryption key */ -int set_fs_encryption_key(char *key) +int set_encryption_key(char *key) { char *key_cmd = NULL; int generate_random_key = 0; @@ -215,13 +654,7 @@ int set_fs_encryption_key(char *key) goto err; } - if (!is_device_closed()) { - printf("\n" - " *****************************************************************\n" - " * Warning: Use filesystem encryption only on CLOSED devices. *\n" - " * Filesystem encryption on open devices is not secure. *\n" - " *****************************************************************\n"); - } + print_open_device_warning(); key_cmd = calloc(1, @@ -245,3 +678,169 @@ err: return ret ? -1 : 0; } + +/* + * Function: encrypt_partitions + * Description: configure recovery commands to encrypt/un-encrypt provided partitions + */ +int encrypt_partitions(char *to_encrypt, char *to_unencrypt, unsigned char force) +{ + char *parts[MAX_PARTITIONS]; + char *encrypted[MAX_PARTITIONS]; + char *new_encrypted[MAX_PARTITIONS]; + char *enc_diff[MAX_PARTITIONS]; + char *unenc_diff[MAX_PARTITIONS]; + char *true_unenc_diff[MAX_PARTITIONS]; + char *new_list = NULL; + char *cmd = NULL; + + char confirmation; + unsigned char i = 0; + + int ret; + + /* If both lists are empty, we have nothing to do */ + if (!to_encrypt && !to_unencrypt) + return 1; + + /* Initialize arrays */ + parts[0] = NULL; + encrypted[0] = NULL; + enc_diff[0] = NULL; + unenc_diff[0] = NULL; + true_unenc_diff[0] = NULL; + new_encrypted[0] = NULL; + + /* Get current partition info */ + ret = PARSE_PARTITION_INFO(parts, encrypted, MAX_PARTITIONS); + if (ret) { + fprintf(stderr, "Error: parse_partition_info\n"); + goto err; + } + + /* Transform the lists into arrays for easier processing */ + ret = list_to_array(to_encrypt, enc_diff, PARTS_BLACKLIST, parts, ",", MAX_PARTITIONS); + if (ret) { + fprintf(stderr, "Error: list_to_array 'to_encrypt'\n"); + goto err; + } + ret = list_to_array(to_unencrypt, unenc_diff, NULL, parts, ",", MAX_PARTITIONS); + if (ret) { + fprintf(stderr, "Error: list_to_array 'to_unencrypt'\n"); + goto err; + } + + /* If both diffs are empty, return immediately. */ + if (!enc_diff[0] && !unenc_diff[0]) { + ret = 1; + goto err; + } + + /* + * Create a copy of the encrypted parts array. We know the copy won't + * surpass the limit, but check the return code in case of a strdup() + * failure. + */ + ret = add_array(encrypted, new_encrypted, MAX_PARTITIONS); + if (ret) { + fprintf(stderr, "Error: add_array 'new_encrypted'\n"); + goto err; + } + + /* Build the new array of encrypted parts using the diffs */ + ret = modify_array(new_encrypted, enc_diff, unenc_diff, MAX_PARTITIONS); + if (ret) { + fprintf(stderr, "Error: modify_array\n"); + goto err; + } + + /* + * If the new encrypted parts array is equal to the old one, there's + * no need to set the recovery command, so return immediately. + */ + if (is_subset(encrypted, new_encrypted) && is_subset(new_encrypted, encrypted)) { + ret = 1; + goto err; + } + + print_open_device_warning(); + + /* + * At this point, we know that we have at least one partition to + * (un)encrypt, so ask for confirmation before continuing. + */ + if (!force) { + /* + * Even though the unenc_diff list is sanitized at this + * point, it might contain partitions that are already + * unencrypted. Calculate the true diff between the current + * list and the new one. + */ + ret = add_array(encrypted, true_unenc_diff, MAX_PARTITIONS); + if (ret) { + fprintf(stderr, "Error: add_array 'true_unenc_diff'\n"); + goto err; + } + subtract_array(new_encrypted, true_unenc_diff); + printf("\n" + " *****************************************************************\n" + " * Warning: Partition (un)encryption is a destructive operation. *\n" + " * The affected partitions' contents will be erased in *\n" + " * the process. *\n" + " *****************************************************************\n" + " Affected partitions:\n"); + + while (enc_diff[i]) + printf(" %s\n", enc_diff[i++]); + + i = 0; + while (true_unenc_diff[i]) + printf(" %s\n", true_unenc_diff[i++]); + + printf("\n Continue? (y/n): "); + confirmation = getchar(); + if (confirmation != 'y' && confirmation != 'Y') { + printf("\nSkipping (un)encryption of partitions\n"); + ret = 1; + goto err; + } + } + + ret = -1; + + /* Create comma-separated list from the encrypted parts array */ + if (new_encrypted[0]) { + new_list = array_to_list(new_encrypted, ',', MAX_LIST_LEN); + if (!new_list) { + fprintf(stderr, "Error: array_to_list\n"); + goto err; + } + } + + cmd = calloc(1, + strlen("encrypt_partitions=") + + (new_list ? strlen(new_list) : 0) + 1); + if (!cmd) { + fprintf(stderr, "Error: calloc 'cmd'\n"); + goto err_cmd; + } + + sprintf(cmd, "encrypt_partitions=%s", new_list ? new_list : ""); + + ret = append_recovery_command(cmd); + + free(cmd); +err_cmd: + if (new_list) + free(new_list); +err: + free_array(enc_diff); + free_array(unenc_diff); + free_array(true_unenc_diff); + free_array(new_encrypted); + free_array(encrypted); + free_array(parts); + + return ret < 0 ? -1 : ret; +} + diff --git a/meta-digi-dey/recipes-core/recovery/recovery-utils/recovery-utils/recovery-reboot/Makefile b/meta-digi-dey/recipes-core/recovery/recovery-utils/recovery-utils/recovery-reboot/Makefile index f65b4ba84..b8b406071 100644 --- a/meta-digi-dey/recipes-core/recovery/recovery-utils/recovery-utils/recovery-reboot/Makefile +++ b/meta-digi-dey/recipes-core/recovery/recovery-utils/recovery-utils/recovery-reboot/Makefile @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, Digi International Inc. +# Copyright (c) 2017-2021, Digi International Inc. # # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this @@ -41,6 +41,7 @@ install: $(PROGRAM) install -d $(DESTDIR)/usr/bin install -m 0755 $(PROGRAM) $(DESTDIR)/usr/bin/ ln -sf $(PROGRAM) $(DESTDIR)/usr/bin/update-firmware + ln -sf $(PROGRAM) $(DESTDIR)/usr/bin/encrypt-partitions .PHONY: clean clean: diff --git a/meta-digi-dey/recipes-core/recovery/recovery-utils/recovery-utils/recovery-reboot/recovery-reboot.c b/meta-digi-dey/recipes-core/recovery/recovery-utils/recovery-utils/recovery-reboot/recovery-reboot.c index f14b80368..2ba2f80b6 100644 --- a/meta-digi-dey/recipes-core/recovery/recovery-utils/recovery-utils/recovery-reboot/recovery-reboot.c +++ b/meta-digi-dey/recipes-core/recovery/recovery-utils/recovery-utils/recovery-reboot/recovery-reboot.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Digi International Inc. + * Copyright (c) 2017-2021, Digi International Inc. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -26,12 +26,13 @@ #include -#define VERSION "0.2" GIT_REVISION +#define VERSION "0.3" GIT_REVISION #define REBOOT_TIMEOUT 10 #define CMD_RECOVERY "recovery-reboot" #define CMD_UPDATEFW "update-firmware" +#define CMD_ENCRYPT "encrypt-partitions" #define RECOVERY_USAGE \ "Reboot into recovery mode setting recovery commands.\n" \ @@ -40,13 +41,15 @@ "Version: %s\n" \ "\n" \ "Usage: %s [options] []\n\n" \ - " -u --update-firmware Perform firmware update\n" \ - " -k[] --encryption-key[=] Set as file system encryption key.\n" \ - " Empty to generate a random key.\n" \ - " Use this option along with update firmware option (-u).\n" \ - " -w --wipe-update-partition Wipe 'update' partition\n" \ - " -T --reboot-timeout= Reboot after N seconds (default %d)\n" \ - " --help Print help and exit\n" \ + " -u --update-firmware Perform firmware update\n" \ + " -e --encrypt= Encrypt the list of provided partitions.\n" \ + " -d --unencrypt= Un-encrypt the list of provided partitions.\n" \ + " -k [] --encryption-key[=] Set as file system encryption key.\n" \ + " Empty to generate a random key.\n" \ + " -w --wipe-update-partition Wipe 'update' partition\n" \ + " -T --reboot-timeout= Reboot after N seconds (default %d)\n" \ + " -f --force Force (un)encryption operations.\n" \ + " --help Print help and exit\n" \ "\n" \ " Absolute path to the firmware update package\n" \ "\n" @@ -58,16 +61,33 @@ "Version: %s\n" \ "\n" \ "Usage: %s [options] \n\n" \ - " -k[] --encryption-key[=] Set as file system encryption key.\n" \ + " -k [] --encryption-key[=] Set as file system encryption key.\n" \ " Empty to generate a random key.\n" \ - " -T --reboot-timeout= Reboot after N seconds (default %d)\n" \ + " -T --reboot-timeout= Reboot after N seconds (default %d)\n" \ " --help Print help and exit\n" \ "\n" \ " Absolute path to the firmware update package\n" \ "\n" -/* Check if application was called as update-firmware */ +#define ENCRYPT_USAGE \ + "Encrypt/unencrypt partitions using the recovery reboot.\n" \ + "Copyright(c) Digi International Inc.\n" \ + "\n" \ + "Version: %s\n" \ + "\n" \ + "Usage: %s [-e ] [-d ] [options]\n\n" \ + " -e --encrypt= Encrypt the list of provided partitions.\n" \ + " -d --unencrypt= Un-encrypt the list of provided partitions.\n" \ + " -k [] --encryption-key[=] Set as file system encryption key.\n" \ + " Empty to generate a random key.\n" \ + " -T --reboot-timeout= Reboot after N seconds (default %d)\n" \ + " -f --force Force (un)encryption operations.\n" \ + " --help Print help and exit\n" \ + "\n" + +/* Check if application was called as update-firmware or encrypt-partitions */ #define IS_UPDATEFW(cmd) (!strcmp(cmd, CMD_UPDATEFW)) +#define IS_ENCRYPT(cmd) (!strcmp(cmd, CMD_ENCRYPT)) /* Actual command name */ static char *cmd_name; @@ -75,9 +95,12 @@ static char *cmd_name; /* Command line options */ static char *swu_package; static char *key = NULL; +static char *to_encrypt = NULL; +static char *to_unencrypt = NULL; static int wipe_update; static int update_fw; static int set_key; +static int force; static int reboot_timeout = REBOOT_TIMEOUT; /* @@ -89,6 +112,9 @@ static void usage_and_exit(int exitval) if (IS_UPDATEFW(cmd_name)) fprintf(stdout, UPDATEFW_USAGE, VERSION, CMD_UPDATEFW, REBOOT_TIMEOUT); + else if (IS_ENCRYPT(cmd_name)) + fprintf(stdout, ENCRYPT_USAGE, VERSION, CMD_ENCRYPT, + REBOOT_TIMEOUT); else fprintf(stdout, RECOVERY_USAGE, VERSION, CMD_RECOVERY, REBOOT_TIMEOUT); @@ -103,12 +129,15 @@ static void usage_and_exit(int exitval) static void parse_options(int argc, char *argv[]) { static int opt_index, opt; - static const char *short_options = "uk::wT:"; + static const char *short_options = "uk::wT:e:d:f"; static const struct option long_options[] = { {"update-firmware", no_argument, NULL, 'u'}, {"encryption-key", optional_argument, NULL, 'k'}, {"wipe-update-partition", no_argument, NULL, 'w'}, {"reboot-timeout", required_argument, NULL, 'T'}, + {"encrypt", required_argument, NULL, 'e'}, + {"unencrypt", required_argument, NULL, 'd'}, + {"force", no_argument, NULL, 'f'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0} }; @@ -143,6 +172,15 @@ static void parse_options(int argc, char *argv[]) exit(EXIT_FAILURE); } break; + case 'e': + to_encrypt = optarg; + break; + case 'd': + to_unencrypt = optarg; + break; + case 'f': + force = 1; + break; case 'h': usage_and_exit(EXIT_SUCCESS); break; @@ -152,10 +190,15 @@ static void parse_options(int argc, char *argv[]) } } - /* If command is 'update-firmware' reset the options */ + /* If command is 'update-firmware' or 'encrypt-partitions' reset the options */ if (IS_UPDATEFW(cmd_name)) { update_fw = 1; wipe_update = 0; + to_encrypt = NULL; + to_unencrypt = NULL; + } else if (IS_ENCRYPT(cmd_name)) { + update_fw = 0; + wipe_update = 0; } if (update_fw) { @@ -165,16 +208,13 @@ static void parse_options(int argc, char *argv[]) printf("Error: missing SWU package argument\n"); exit(EXIT_FAILURE); } - } else if (set_key) { - /* Always need to update firmware when setting a new key */ - printf("Error: encryption key can only be set while performing a firmware update\n"); - exit(EXIT_FAILURE); } } int main(int argc, char *argv[]) { int ret = 0; + unsigned char need_reboot = 0; cmd_name = basename(argv[0]); if (!cmd_name) { @@ -187,11 +227,12 @@ int main(int argc, char *argv[]) if (set_key) { /* Configure recovery commands to set a fs encryption key */ - ret = set_fs_encryption_key(key); + ret = set_encryption_key(key); if (ret) { - printf("Error: set_fs_encryption_key\n"); + printf("Error: set_encryption_key\n"); goto out; } + need_reboot++; } if (swu_package) { @@ -201,6 +242,7 @@ int main(int argc, char *argv[]) printf("Error: update_firmware\n"); goto out; } + need_reboot++; } if (wipe_update) { @@ -210,15 +252,38 @@ int main(int argc, char *argv[]) printf("Error: wipe_update_partition\n"); goto out; } + need_reboot++; } - /* Reboot to recovery */ - ret = reboot_recovery(reboot_timeout); - if (ret) { - printf("Error: reboot_recovery\n"); - goto out; + if (to_encrypt || to_unencrypt) { + /* Configure recovery commands to encrypt/unencrypt partitions */ + ret = encrypt_partitions(to_encrypt, to_unencrypt, force); + if (ret < 0) { + printf("Error: encrypt_partitions\n"); + goto out; + } else if (ret == 0) { + /* + * Only reboot if strictly necessary, since the function + * might succeed without setting a recovery command + * (for example, if you try to encrypt partitions that + * are already encrypted). + */ + need_reboot++; + } else { + ret = 0; + } } + if (need_reboot > 0) { + /* Reboot to recovery */ + ret = reboot_recovery(reboot_timeout); + if (ret) { + printf("Error: reboot_recovery\n"); + goto out; + } + } + + printf("\nNo recovery commands configured.\n"); out: return ret; }