u-boot-2016/tools/sysupgrade.c
Ram Chandra Jangir 46352bfda4 sysupgrade: Consider minimum metadata size as 0x1000
When we use root certificate count as four, then metadata
is generated with size as 0x4000, but for single root
certificate, metadata size will be less than 0x4000, hence
consider minimum metadata size as 0x1000

Change-Id: Ib713e5fd4bcfe493cf482efda54d20ca1604939b
Signed-off-by: Ram Chandra Jangir <quic_rjangir@quicinc.com>
2023-04-12 02:42:36 -07:00

1662 lines
40 KiB
C

/*
* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <sysupgrade.h>
#define SBL_VERSION_FILE "sbl_version"
#define TZ_VERSION_FILE "tz_version"
#define HLOS_VERSION_FILE "hlos_version"
#define APPSBL_VERSION_FILE "appsbl_version"
#define RPM_VERSION_FILE "rpm_version"
#define DEVCFG_VERSION_FILE "devcfg_version"
#define APDP_VERSION_FILE "apdp_version"
#define VERSION_FILE_BASENAME "/sys/devices/system/qfprom/qfprom0/"
#define AUTHENTICATE_FILE "/sys/devices/system/qfprom/qfprom0/authenticate"
#define SEC_AUTHENTICATE_FILE "/sys/sec_upgrade/sec_auth"
#define TEMP_KERNEL_PATH "/tmp/tmp_kernel.bin"
#define MAX_SBL_VERSION 11
#define MAX_HLOS_VERSION 32
#define MAX_TZ_VERSION 14
#define MAX_APPSBL_VERSION 14
#define MAX_RPM_VERSION 8
#define MAX_DEVCFG_VERSION 11
#define MAX_APDP_VERSION 8
#define HASH_P_FLAG 0x02200000
#define TMP_FILE_DIR "/tmp/"
#define CERT_SIZE 2048
#define PRESENT 1
#define MBN_HDR_SIZE 40
#define SBL_HDR_SIZE 80
#define SIG_SIZE 256
#define NOT_PRESENT 0
#define SIG_CERT_2_SIZE 4352
#define SIG_CERT_3_SIZE 6400
#define SBL_NAND_PREAMBLE 10240
#define SBL_HDR_RESERVED 12
#define UBI_EC_HDR_MAGIC 0x55424923
#define UBI_VID_HDR_MAGIC 0x55424921
#define NO_OF_PROGRAM_HDRS 3
struct image_section sections[] = {
{
.section_type = UBOOT_TYPE,
.type = "u-boot",
.max_version = MAX_APPSBL_VERSION,
.file = TMP_FILE_DIR,
.version_file = APPSBL_VERSION_FILE,
.is_present = NOT_PRESENT,
.img_code = "0x9"
},
{
.section_type = HLOS_TYPE,
.type = "hlos",
.max_version = MAX_HLOS_VERSION,
.tmp_file = TMP_FILE_DIR,
.pre_op = parse_elf_image_phdr,
.file = TMP_FILE_DIR,
.version_file = HLOS_VERSION_FILE,
.is_present = NOT_PRESENT,
.img_code = "0x17"
},
{
.section_type = HLOS_TYPE,
.type = "rootfs",
.max_version = MAX_HLOS_VERSION,
.tmp_file = TMP_FILE_DIR,
.pre_op = compute_sha384,
.file = TMP_FILE_DIR,
.version_file = HLOS_VERSION_FILE,
.is_present = NOT_PRESENT,
.img_code = "0x17"
},
{
.section_type = HLOS_TYPE,
.type = "ubi",
.tmp_file = TMP_FILE_DIR,
.pre_op = extract_kernel_binary,
.max_version = MAX_HLOS_VERSION,
.file = TEMP_KERNEL_PATH,
.version_file = HLOS_VERSION_FILE,
.is_present = NOT_PRESENT,
.img_code = "0x17"
},
{
.section_type = TZ_TYPE,
.type = "tz",
.max_version = MAX_TZ_VERSION,
.file = TMP_FILE_DIR,
.version_file = TZ_VERSION_FILE,
.is_present = NOT_PRESENT,
.img_code = "0x7"
},
{
.section_type = SBL_TYPE,
.type = "sbl1",
.max_version = MAX_SBL_VERSION,
.file = TMP_FILE_DIR,
.version_file = SBL_VERSION_FILE,
.is_present = NOT_PRESENT,
.img_code = "0x0"
},
{
.section_type = SBL_TYPE,
.type = "sbl2",
.max_version = MAX_SBL_VERSION,
.file = TMP_FILE_DIR,
.version_file = SBL_VERSION_FILE,
.is_present = NOT_PRESENT,
.img_code = "0x5"
},
{
.section_type = SBL_TYPE,
.type = "sbl3",
.max_version = MAX_SBL_VERSION,
.file = TMP_FILE_DIR,
.version_file = SBL_VERSION_FILE,
.is_present = NOT_PRESENT,
.img_code = "0x6"
},
{
.section_type = RPM_TYPE,
.type = "rpm",
.max_version = MAX_RPM_VERSION,
.file = TMP_FILE_DIR,
.version_file = RPM_VERSION_FILE,
.is_present = NOT_PRESENT,
.img_code = "0xA"
},
{
.section_type = DEVCFG_TYPE,
.type = "devcfg",
.max_version = MAX_DEVCFG_VERSION,
.file = TMP_FILE_DIR,
.version_file = DEVCFG_VERSION_FILE,
.is_present = NOT_PRESENT,
.img_code = "0x5"
},
{
.section_type = APDP_TYPE,
.type = "apdp",
.max_version = MAX_APDP_VERSION,
.file = TMP_FILE_DIR,
.version_file = APDP_VERSION_FILE,
.is_present = NOT_PRESENT,
.img_code = "0x200"
},
};
#define NO_OF_SECTIONS ARRAY_SIZE(sections)
int src_size;
int check_mbn_elf(struct image_section **sec)
{
int fd = open((*sec)->file, O_RDONLY);
struct stat sb;
uint8_t *fp;
Elf32_Ehdr *elf;
if (fd < 0) {
perror((*sec)->file);
return 0;
}
memset(&sb, 0, sizeof(struct stat));
if (fstat(fd, &sb) == -1) {
perror("fstat");
close(fd);
return 0;
}
fp = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (fp == MAP_FAILED) {
perror("mmap");
close(fd);
return 0;
}
elf = (Elf32_Ehdr *)fp;
if (!strncmp((char *)&(elf->e_ident[1]), "ELF", 3)) {
/* EI_CLASS Check for 32/64-bit */
if( ((int)(elf->e_ident[4])) == 2) {
(*sec)->get_sw_id = get_sw_id_from_component_bin_elf64;
(*sec)->split_components = split_code_signature_cert_from_component_bin_elf64;
} else {
(*sec)->get_sw_id = get_sw_id_from_component_bin_elf;
(*sec)->split_components = split_code_signature_cert_from_component_bin_elf;
}
} else if (!strncmp((char *)&(((Elf32_Ehdr *)(fp + SBL_NAND_PREAMBLE))->e_ident[1]), "ELF", 3)) {
if( ((int)(elf->e_ident[4])) == 2) {
(*sec)->get_sw_id = get_sw_id_from_component_bin_elf64;
(*sec)->split_components = split_code_signature_cert_from_component_bin_elf64;
} else {
(*sec)->get_sw_id = get_sw_id_from_component_bin_elf;
(*sec)->split_components = split_code_signature_cert_from_component_bin_elf;
}
} else {
(*sec)->get_sw_id = get_sw_id_from_component_bin;
(*sec)->split_components = split_code_signature_cert_from_component_bin;
}
return 1;
}
int get_sections(void)
{
DIR *dir = opendir(TMP_FILE_DIR);
struct dirent *file;
int i,data_size;
struct image_section *sec;
if (dir == NULL) {
printf("Error accessing the image directory\n");
return 0;
}
data_size = find_mtd_part_size();
while ((file = readdir(dir)) != NULL) {
for (i = 0, sec = &sections[0]; i < NO_OF_SECTIONS; i++, sec++) {
/* Skip loading of ubi section if board is not from nand boot */
if (data_size == -1 && !strncmp(sec->type, "ubi", strlen("ubi")))
continue;
if (!strncmp(file->d_name, sec->type, strlen(sec->type))) {
strlcat(sec->file, file->d_name, sizeof(sec->file));
if (sec->pre_op) {
strlcat(sec->tmp_file, file->d_name,
sizeof(sec->tmp_file));
if (!sec->pre_op(sec)) {
printf("Error extracting kernel from ubi\n");
return 0;
}
}
if (!check_mbn_elf(&sec)) {
closedir(dir);
return 0;
}
if (!sec->get_sw_id(sec)) {
closedir(dir);
return 0;
}
get_local_image_version(sec);
sec->is_present = PRESENT;
break;
}
}
}
closedir(dir);
return 1;
}
int load_sections(void)
{
DIR *dir;
int i,data_size;
struct dirent *file;
struct image_section *sec;
dir = opendir(TMP_FILE_DIR);
if (dir == NULL) {
printf("Error accessing the %s image directory\n", TMP_FILE_DIR);
return 0;
}
data_size = find_mtd_part_size();
while ((file = readdir(dir)) != NULL) {
for (i = 0, sec = &sections[0]; i < NO_OF_SECTIONS; i++, sec++) {
/* Skip loading of ubi section if board is not from nand boot */
if (data_size == -1 && !strncmp(sec->type, "ubi", strlen("ubi")))
continue;
if (!strncmp(file->d_name, sec->type, strlen(sec->type))) {
strlcat(sec->file, file->d_name, sizeof(sec->file));
if (sec->pre_op) {
strlcat(sec->tmp_file, file->d_name,
sizeof(sec->tmp_file));
if (!sec->pre_op(sec)) {
printf("Error extracting %s from ubi\n",
sec->tmp_file);
closedir(dir);
return 0;
}
}
sec->is_present = PRESENT;
break;
}
}
}
closedir(dir);
return 1;
}
/**
* is_authentication_check_enabled() - checks whether installed image is
* secure(1) or not(0)
*
*/
int is_authentication_check_enabled(void)
{
int fd = open(AUTHENTICATE_FILE, O_RDONLY);
char authenticate_string[4];
int len;
if (fd == -1) {
perror(AUTHENTICATE_FILE);
return 0;
}
len = read(fd, authenticate_string, 1);
close(fd);
if (len > 0 && authenticate_string[0] == '0') {
return 0;
}
return 1;
}
int is_tz_authentication_enabled(void)
{
struct stat sb;
if (stat(SEC_AUTHENTICATE_FILE, &sb) == -1) {
perror("stat");
return 0;
}
return 1;
}
/**
* get_local_image_version() check the version file & if it exists, read the
* value & save it into global variable local_version
*
*/
int get_local_image_version(struct image_section *section)
{
int len, fd;
char local_version_string[16], version_file[64];
struct stat st;
snprintf(version_file, sizeof(version_file), "%s%s", VERSION_FILE_BASENAME, section->version_file);
fd = open(version_file, O_RDONLY);
if (fd == -1) {
perror(version_file);
return 0;
}
memset(&st, 0, sizeof(struct stat));
fstat(fd, &st);
len = st.st_size < sizeof(local_version_string) - 1 ? st.st_size :
sizeof(local_version_string) - 1;
if (read(fd, local_version_string, len) == -1) {
close(fd);
return 0;
}
local_version_string[len] = '\0';
close(fd);
section->local_version = atoi(local_version_string);
printf("Local image version:%s\n", local_version_string);
return 1;
}
/**
* set_local_image_version() update the version of the image by writing the version
* to the version file
*
*/
int set_local_image_version(struct image_section *section)
{
int fd;
char version_string[16], version_file[64];
int len;
snprintf(version_file, sizeof(version_file), "%s%s", TMP_FILE_DIR, section->version_file);
fd = open(version_file, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
if (fd == -1) {
perror(version_file);
return 0;
}
len = snprintf(version_string, 8, "%d", section->img_version);
if (len < 0) {
printf("Error in formatting the version string");
return 0;
}
printf("Version to be updated:%s\n", version_string);
if (write(fd, version_string, len) == -1) {
printf("Error writing version to %s\n", version_file);
close(fd);
return 0;
}
close(fd);
return 1;
}
/**
* is_version_check_enabled() checks whether version check is
* enabled(non-zero value) or not
*
*/
int is_version_check_enabled()
{
if (get_local_image_version(&sections[0]) != -1) {
printf("Returning 1 from is_version_check_enabled because local_version_string is non-ZERO\n");
return 1;
}
return 0;
}
char *find_value(char *buffer, char *search, int size)
{
char *value = malloc(size * sizeof(char));
int i, j;
if (value == NULL) {
return NULL;
}
for (i = 0; i < CERT_SIZE; i++) {
for (j = 0; search[j] && (buffer[i + j] == search[j]); j++);
if (search[j] == '\0') {
strlcpy(value, &buffer[i - size], size);
value[size - 1] = '\0';
return value;
}
}
free(value);
return NULL;
}
/**
* check_nand_preamble() compares first 12 bytes of section with
* pre defined PREAMBLE value and returns 0 if both value matches
*/
int check_nand_preamble(uint8_t *mfp)
{
char magic[12] = { 0xd1, 0xdc, 0x4b, 0x84,
0x34, 0x10, 0xd7, 0x73,
0x5a, 0x43, 0x0b, 0x7d };
return memcmp(magic, mfp, sizeof(magic));
}
/**
* get_sw_id_from_component_bin() parses the MBN header & checks image size v/s
* code size. If both differ, it means signature & certificates are
* appended at end.
* Extract the attestation certificate & read the Subject & retreive the SW_ID.
*
* @bin_file: struct image_section *
*/
int get_sw_id_from_component_bin(struct image_section *section)
{
Mbn_Hdr *mbn_hdr;
int fd = open(section->file, O_RDONLY);
struct stat sb;
uint8_t *fp;
int cert_offset;
char *sw_version;
int sig_cert_size;
if (fd == -1) {
perror(section->file);
return 0;
}
memset(&sb, 0, sizeof(struct stat));
if (fstat(fd, &sb) == -1) {
perror("fstat");
close(fd);
return 0;
}
fp = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (fp == MAP_FAILED) {
perror("mmap");
close(fd);
return 0;
}
mbn_hdr = (Mbn_Hdr *)fp;
if (strstr(section->file, sections[4].type)) {
uint32_t preamble = !check_nand_preamble(fp) ? SBL_NAND_PREAMBLE : 0;
Sbl_Hdr *sbl_hdr = (Sbl_Hdr *)(fp + preamble);
sig_cert_size = sbl_hdr->image_size - sbl_hdr->code_size;
cert_offset = preamble + sbl_hdr->cert_ptr - sbl_hdr->image_dest_ptr +
SBL_HDR_SIZE;
} else {
sig_cert_size = mbn_hdr->image_size - mbn_hdr->code_size;
cert_offset = mbn_hdr->cert_ptr - mbn_hdr->image_dest_ptr + 40;
}
if (sig_cert_size != SIG_CERT_2_SIZE && sig_cert_size != SIG_CERT_3_SIZE) {
printf("WARNING: signature certificate size is different\n");
// ipq807x has certificate size as dynamic, hence ignore this check
}
printf("Image with version information\n");
sw_version = find_value((char *)(fp + cert_offset), "SW_ID", 17);
if (sw_version != NULL) {
sw_version[8] = '\0';
sscanf(sw_version, "%x", &section->img_version);
printf("SW ID:%d\n", section->img_version);
free(sw_version);
}
close(fd);
return 1;
}
int process_elf(char *bin_file, uint8_t **fp, Elf32_Ehdr **elf, Elf32_Phdr **phdr, Mbn_Hdr **mbn_hdr)
{
int fd = open(bin_file, O_RDONLY);
struct stat sb;
int version = 0;
int i = 0;
if (fd < 0) {
perror(bin_file);
return 0;
}
memset(&sb, 0, sizeof(struct stat));
if (fstat(fd, &sb) == -1) {
perror("fstat");
close(fd);
return 0;
}
*fp = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (*fp == MAP_FAILED) {
perror("mmap");
close(fd);
return 0;
}
*elf = (Elf32_Ehdr *)*fp;
while (strncmp((char *)&((*elf)->e_ident[1]), "ELF", 3)) {
*fp = (uint8_t *)((char *)(*fp) + SBL_NAND_PREAMBLE);
*elf = (Elf32_Ehdr *)*fp;
}
*phdr = (Elf32_Phdr *)(*fp + (*elf)->e_phoff);
for (i = 0; i < (*elf)->e_phnum; i++, (*phdr)++) {
if ((*phdr)->p_flags == HASH_P_FLAG) {
*mbn_hdr = (Mbn_Hdr *)(*fp + (*phdr)->p_offset);
if ((*mbn_hdr)->image_size != (*mbn_hdr)->code_size) {
version = 1;
break;
} else {
printf("Error: Image without version information\n");
close(fd);
return 0;
}
}
}
if (version != 1) {
printf("Error: Image without version information\n");
return 0;
}
close(fd);
return 1;
}
int process_elf64(char *bin_file, uint8_t **fp, Elf64_Ehdr **elf, Elf64_Phdr **phdr, Mbn_Hdr **mbn_hdr)
{
struct stat sb;
int i, fd, version = 0;
fd = open(bin_file, O_RDONLY);
if (fd < 0) {
perror(bin_file);
return 0;
}
memset(&sb, 0, sizeof(struct stat));
if (fstat(fd, &sb) == -1) {
perror("fstat");
close(fd);
return 0;
}
*fp = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (*fp == MAP_FAILED) {
perror("mmap");
close(fd);
return 0;
}
*elf = (Elf64_Ehdr *)*fp;
while (strncmp((char *)&((*elf)->e_ident[1]), "ELF", 3)) {
*fp = (uint8_t *)((char *)(*fp) + SBL_NAND_PREAMBLE);
*elf = (Elf64_Ehdr *)*fp;
}
*phdr = (Elf64_Phdr *)(*fp + (*elf)->e_phoff);
for (i = 0; i < (*elf)->e_phnum; i++, (*phdr)++) {
if ((*phdr)->p_flags == HASH_P_FLAG) {
*mbn_hdr = (Mbn_Hdr *)(*fp + (*phdr)->p_offset);
if ((*mbn_hdr)->image_size != (*mbn_hdr)->code_size) {
version = 1;
break;
} else {
printf("Error: Image without version information\n");
close(fd);
return 0;
}
}
}
if (version != 1) {
printf("Error: Image without version information\n");
return 0;
}
close(fd);
return 1;
}
/**
* get_sw_id_from_component_bin_elf() parses the ELF header to get the MBN header
* of the hash table segment. Parses the MBN header of hash table segment & checks
* total size v/s actual component size. If both differ, it means signature &
* certificates are appended at end.
* Extract the attestation certificate & read the Subject & retreive the SW_ID.
*
* @bin_file: struct image_section *
*/
int get_sw_id_from_component_bin_elf(struct image_section *section)
{
Elf32_Ehdr *elf;
Elf32_Phdr *phdr;
Mbn_Hdr *mbn_hdr;
uint8_t *fp;
int cert_offset;
char *sw_version;
if (!process_elf(section->file, &fp, &elf, &phdr, &mbn_hdr)) {
return 0;
}
cert_offset = mbn_hdr->cert_ptr - mbn_hdr->image_dest_ptr + 40;
printf("Image with version information\n");
sw_version = find_value((char *)(fp + phdr->p_offset + cert_offset), "SW_ID", 17);
if (sw_version) {
sw_version[8] = '\0';
sscanf(sw_version, "%x", &section->img_version);
printf("SW ID:%d\n", section->img_version);
free(sw_version);
}
return 1;
}
/**
* get_sw_id_from_component_bin_elf64() parses the ELF64 header to get the MBN header
* of the hash table segment. Parses the MBN header of hash table segment & checks
* total size v/s actual component size. If both differ, it means signature &
* certificates are appended at end.
* Extract the attestation certificate & read the Subject & retreive the SW_ID.
*
32_Phdr *phdr;* @bin_file: struct image_section *
*/
int get_sw_id_from_component_bin_elf64(struct image_section *section)
{
Elf64_Ehdr *elf;
Elf64_Phdr *phdr;
Mbn_Hdr *mbn_hdr;
uint8_t *fp;
int cert_offset;
char *sw_version;
if (!process_elf64(section->file, &fp, &elf, &phdr, &mbn_hdr)) {
return 0;
}
cert_offset = mbn_hdr->code_size + mbn_hdr->sig_sz + 40;
printf("Image with version information64\n");
sw_version = find_value((char *)(fp + phdr->p_offset + cert_offset), "SW_ID", 17);
if (sw_version) {
sw_version[8] = '\0';
sscanf(sw_version, "%x", &section->img_version);
printf("SW ID:%d\n", section->img_version);
free(sw_version);
}
return 1;
}
int find_mtd_part_size(void)
{
char *mtdname = "kernel";
char prefix[] = "/dev/mtd";
char dev[PATH_MAX];
int i = -1, fd;
int vol_size;
int flag = 0;
char mtd_part[256];
FILE *fp = fopen("/proc/mtd", "r");
mtd_info_t mtd_dev_info;
if (fp == NULL) {
printf("Error finding mtd part\n");
return -1;
}
while (fgets(dev, sizeof(dev), fp)) {
if (strstr(dev, mtdname)) {
flag = 1;
break;
}
i++;
}
fclose(fp);
if (flag != 1) {
printf("%s block not found\n", mtdname);
return -1;
}
snprintf(mtd_part, sizeof(mtd_part), "%s%d", prefix, i);
fd = open(mtd_part, O_RDWR);
if (fd == -1) {
return -1;
}
if (ioctl(fd, MEMGETINFO, &mtd_dev_info) == -1) {
printf("Error getting block size\n");
close(fd);
return -1;
}
vol_size = mtd_dev_info.erasesize;
close(fd);
return vol_size;
}
/**
* Helper function to dynamically get volume id
*/
int get_kernel_volume_id(void)
{
int i, number_of_ubi_volumes;
char ubi_vol_count[] = "/sys/class/ubi/ubi0/volumes_count";
char prefix[] = "/sys/class/ubi/ubi0_";
char suffix[] = "/name";
char ubi_vol[256];
char ubi_vol_name[256];
char current_ubi_volumes_count[256];
FILE *fp;
fp = fopen(ubi_vol_count, "r");
fgets(current_ubi_volumes_count, sizeof(current_ubi_volumes_count), fp);
number_of_ubi_volumes = atoi(current_ubi_volumes_count);
printf("number of ubi volumes = %d\n", number_of_ubi_volumes);
for (i = 0; i < number_of_ubi_volumes; i++)
{
snprintf(ubi_vol, sizeof(ubi_vol), "%s%d%s", prefix, i, suffix);
fp = fopen(ubi_vol, "r");
fgets(ubi_vol_name, sizeof(ubi_vol_name), fp);
if (strstr(ubi_vol_name, "kernel")) {
printf("kernel ubi volume id = %d\n", i);
return i;
}
}
return -1;
}
/**
* In case of NAND image, Kernel image is ubinized & version information is
* part of Kernel image. Hence need to un-ubinize the image.
* To get the kernel image, Find the volume with kernel's volume id. Kernel image
* is fragmented and hence we need to assemble it to get complete image.
* In UBI image, first look for UBI#, which is the magic number used to identify
* each eraseble block. Parse the UBI header, which starts with UBI# & get
* the VID(volume ID) header offset as well as Data offset.
* Traverse to VID header offset & check the volume ID. If it is matching with
* kernel Volume id extracted based on ubi sysfs, Kernel image is stored in
* this volume. Use Data offset to extract the Kernel image.
*
* @bin_file: struct image_section *
*/
int extract_kernel_binary(struct image_section *section)
{
struct ubi_ec_hdr *ubi_ec;
struct ubi_vid_hdr *ubi_vol;
uint8_t *fp;
int fd, ofd, magic, data_size, vid_hdr_offset, data_offset;
int kernel_vol_id, curr_vol_id;
struct stat sb;
fd = open(section->tmp_file, O_RDONLY);
if (fd < 0) {
perror(section->tmp_file);
return 0;
}
memset(&sb, 0, sizeof(struct stat));
if (fstat(fd, &sb) == -1) {
perror("fstat");
close(fd);
return 0;
}
fp = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (fp == MAP_FAILED) {
perror("mmap");
close(fd);
return 0;
}
ofd = open(section->file, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
if (ofd == -1) {
perror(section->file);
close(fd);
return 0;
}
data_size = find_mtd_part_size();
if (data_size == -1) {
printf("Error finding data size\n");
return 0;
}
kernel_vol_id = get_kernel_volume_id();
if (kernel_vol_id == -1) {
printf("Wrong ubi volume id for kernel\n");
return 0;
}
ubi_ec = (struct ubi_ec_hdr *)fp;
magic = be32_to_cpu(ubi_ec->magic);
while (magic == UBI_EC_HDR_MAGIC) {
vid_hdr_offset = be32_to_cpu(ubi_ec->vid_hdr_offset);
data_offset = be32_to_cpu(ubi_ec->data_offset);
ubi_vol = (struct ubi_vid_hdr *)((uint8_t *)ubi_ec + vid_hdr_offset);
magic = be32_to_cpu(ubi_vol->magic);
if (magic != UBI_VID_HDR_MAGIC) {
printf("Wrong ubi format\n");
close(ofd);
close(fd);
return 0;
}
curr_vol_id = be32_to_cpu(ubi_vol->vol_id);
if (curr_vol_id == kernel_vol_id) {
if (write(ofd, (void *)((uint8_t *)ubi_ec + data_offset), data_size) == -1) {
printf("Write error\n");
close(fd);
close(ofd);
return 0;
}
}
ubi_ec = (struct ubi_ec_hdr *)((uint8_t *)ubi_ec + data_offset + data_size);
magic = be32_to_cpu(ubi_ec->magic);
}
close(ofd);
close(fd);
printf("Kernel extracted from ubi image\n");
return 1;
}
/**
* The digest functions output the message digest of a supplied file and
* write sha384 to /tmp/sha384_keyXXXXXX file
*/
int compute_sha384(struct image_section *section)
{
char sha384_hash[] = "/tmp/sha384_keyXXXXXX";
char command[300];
int retval;
snprintf(command, sizeof(command),
"openssl dgst -sha384 -binary -out %s %s", sha384_hash, section->tmp_file);
retval = system(command);
if (retval != 0) {
printf("Error generating sha384 hash\n");
return 0;
}
printf("sha384_hash file is created: %s \n",sha384_hash);
return 1;
}
/**
* is_image_version_higher() iterates through each component and check
* versions against locally installed version.
* If newer component version is lower than locally insatlled image,
* abort the FW upgrade process.
*
* @img: char *
*/
int is_image_version_higher(void)
{
int i;
for (i = 0; i < NO_OF_SECTIONS; i++) {
if (!sections[i].is_present) {
continue;
}
if (sections[i].local_version > sections[i].img_version) {
printf("Version of image %s (%d) is lower than minimal supported version(%d)\n",
sections[i].file,
sections[i].img_version,
sections[i].local_version);
return 0;
}
if (sections[i].img_version > sections[i].max_version) {
printf("Version of image %s (%d) is higher than maximum supported version(%d)\n",
sections[i].file,
sections[i].img_version,
sections[i].max_version);
}
}
return 1;
}
/**
* Update the version information file based on currently SW_ID being installed.
*
*/
int update_version(void)
{
int i;
for (i = 0; i < NO_OF_SECTIONS; i++) {
if (!sections[i].is_present) {
continue;
}
if (set_local_image_version(&sections[i]) != 1) {
printf("Error updating version of %s\n", sections[i].file);
return 0;
}
}
return 1;
}
int check_image_version(void)
{
if (is_version_check_enabled() == 0) {
printf("Version check is not enabled, upgrade to continue !!!\n");
return 1;
}
if (is_image_version_higher() == 0) {
printf("New image versions are lower than existing image, upgrade to STOP !!!\n");
return 0;
}
if (update_version() != 1) {
printf("Error while updating verison information\n");
return 0;
}
printf("Update completed!\n");
return 1;
}
/**
* split_code_signature_cert_from_component_bin splits the component
* binary by splitting into code(including MBN header), signature file &
* attenstation certificate.
*
* @bin_file: char *
* @src: char *
* @sig: char *
* @cert: char *
*/
int split_code_signature_cert_from_component_bin(struct image_section *section,
char **src, char **sig, char **cert)
{
Mbn_Hdr *mbn_hdr;
Sbl_Hdr *sbl_hdr;
int fd = open(section->file, O_RDONLY);
uint8_t *fp;
int sig_offset = 0;
int src_offset = 0;
int cert_offset = 0;
struct stat sb;
int sig_cert_size;
if (fd == -1) {
perror(section->file);
return 0;
}
memset(&sb, 0, sizeof(struct stat));
if (fstat(fd, &sb) == -1) {
perror("fstat");
close(fd);
return 0;
}
fp = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (fp == MAP_FAILED) {
perror("mmap");
close(fd);
return 0;
}
mbn_hdr = (Mbn_Hdr *)fp;
if (strstr(section->file, sections[4].type)) {
uint32_t preamble = !check_nand_preamble(fp) ? SBL_NAND_PREAMBLE : 0;
sbl_hdr = (Sbl_Hdr *)(fp + preamble);
src_offset = preamble;
sig_offset = preamble + sbl_hdr->sig_ptr - sbl_hdr->image_dest_ptr +
SBL_HDR_SIZE;
cert_offset = preamble + sbl_hdr->cert_ptr - sbl_hdr->image_dest_ptr +
SBL_HDR_SIZE;
sig_cert_size = sbl_hdr->image_size - sbl_hdr->code_size;
src_size = sbl_hdr->sig_ptr - sbl_hdr->image_dest_ptr + SBL_HDR_SIZE;
} else {
sig_cert_size = mbn_hdr->image_size - mbn_hdr->code_size;
src_size = mbn_hdr->sig_ptr - mbn_hdr->image_dest_ptr + MBN_HDR_SIZE;
sig_offset += mbn_hdr->sig_ptr - mbn_hdr->image_dest_ptr + MBN_HDR_SIZE;
cert_offset += mbn_hdr->cert_ptr - mbn_hdr->image_dest_ptr + MBN_HDR_SIZE;
}
if (sig_cert_size != SIG_CERT_2_SIZE && sig_cert_size != SIG_CERT_3_SIZE) {
printf("WARNING: signature certificate size is different\n");
}
*src = malloc(src_size + 1);
if (*src == NULL) {
close(fd);
return 0;
}
memcpy(*src, fp + src_offset, src_size);
(*src)[src_size] = '\0';
*sig = malloc((SIG_SIZE + 1) * sizeof(char));
if (*sig == NULL) {
free(*src);
return 0;
}
memcpy(*sig, fp + sig_offset, SIG_SIZE);
(*sig)[SIG_SIZE] = '\0';
*cert = malloc((CERT_SIZE + 1) * sizeof(char));
if (*cert == NULL) {
free(*src);
free(*sig);
return 0;
}
memcpy(*cert, fp + cert_offset, CERT_SIZE);
(*cert)[CERT_SIZE] = '\0';
close(fd);
return 1;
}
/**
* split_code_signature_cert_from_component_bin_elf splits the component
* binary by splitting into code(including ELF header), signature file &
* attenstation certificate.
*
* @bin_file: char *
* @src: char *
* @sig: char *
* @cert: char *
*/
int split_code_signature_cert_from_component_bin_elf(struct image_section *section,
char **src, char **sig, char **cert)
{
Elf32_Ehdr *elf;
Elf32_Phdr *phdr;
Mbn_Hdr *mbn_hdr;
uint8_t *fp;
int sig_offset;
int cert_offset;
int len;
if (!process_elf(section->file, &fp, &elf, &phdr, &mbn_hdr)) {
return 0;
}
sig_offset = mbn_hdr->sig_ptr - mbn_hdr->image_dest_ptr + MBN_HDR_SIZE;
len = sig_offset;
*src = malloc((len + 1) * sizeof(char));
if (*src == NULL) {
return 0;
}
memcpy(*src, fp + phdr->p_offset, len);
src_size = len;
(*src)[len] = '\0';
*sig = malloc((SIG_SIZE + 1) * sizeof(char));
if (*sig == NULL) {
free(*src);
return 0;
}
memcpy(*sig, fp + phdr->p_offset + sig_offset, SIG_SIZE);
(*sig)[SIG_SIZE] = '\0';
cert_offset = mbn_hdr->cert_ptr - mbn_hdr->image_dest_ptr + MBN_HDR_SIZE;
*cert = malloc((CERT_SIZE + 1) * sizeof(char));
if (*cert == NULL) {
free(*src);
free(*sig);
return 0;
}
memcpy(*cert, fp + phdr->p_offset + cert_offset, CERT_SIZE);
(*cert)[CERT_SIZE] = '\0';
return 1;
}
/**
* split_code_signature_cert_from_component_bin_elf64 splits the component
* binary by splitting into code(including ELF header), signature file &
* attenstation certificate.
*
* @bin_file: char *
* @src: char *
* @sig: char *
* @cert: char *
*/
int split_code_signature_cert_from_component_bin_elf64(struct image_section *section,
char **src, char **sig, char **cert)
{
Elf64_Ehdr *elf;
Elf64_Phdr *phdr;
Mbn_Hdr *mbn_hdr;
uint8_t *fp;
int len, sig_offset, cert_offset;
if (!process_elf64(section->file, &fp, &elf, &phdr, &mbn_hdr)) {
return 0;
}
sig_offset = mbn_hdr->code_size + MBN_HDR_SIZE;
len = sig_offset;
*src = malloc((len + 1) * sizeof(char));
if (*src == NULL) {
return 0;
}
memcpy(*src, fp + phdr->p_offset, len);
src_size = len;
(*src)[len] = '\0';
*sig = malloc((SIG_SIZE + 1) * sizeof(char));
if (*sig == NULL) {
free(*src);
return 0;
}
memcpy(*sig, fp + phdr->p_offset + sig_offset, SIG_SIZE);
(*sig)[SIG_SIZE] = '\0';
cert_offset = mbn_hdr->code_size + mbn_hdr->sig_sz + MBN_HDR_SIZE;
*cert = malloc((CERT_SIZE + 1) * sizeof(char));
if (*cert == NULL) {
free(*src);
free(*sig);
return 0;
}
memcpy(*cert, fp + phdr->p_offset + cert_offset, CERT_SIZE);
(*cert)[CERT_SIZE] = '\0';
return 1;
}
/**
* parse_elf_image_phdr() parses the ELF32 program header to get the
* ELF header of rootfs metadata. It parses the metadata and writes
* to a new /tmp/metadata.bin file.
*/
int parse_elf_image_phdr(struct image_section *section)
{
Elf32_Ehdr *ehdr; /* Elf header structure pointer */
Elf32_Phdr *phdr; /* Program header structure pointer */
struct stat sb;
uint8_t *fp;
int i;
int fd = open(section->file, O_RDONLY);
if (fd < 0) {
perror(section->file);
return 0;
}
memset(&sb, 0, sizeof(struct stat));
if (fstat(fd, &sb) == -1) {
perror("fstat");
close(fd);
return 0;
}
fp = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (fp == MAP_FAILED) {
perror("mmap");
close(fd);
return 0;
}
ehdr = (Elf32_Ehdr *)fp;
phdr = (Elf32_Phdr *)(((char*)fp) + ehdr->e_phoff);
printf(" ELF headers are initialized\n");
if (ehdr->e_type != ET_EXEC) {
printf("Not a valid elf image\n");
close(fd);
return 0;
}
/* Load each program header */
for (i = 0; i < NO_OF_PROGRAM_HDRS; ++i) {
if(phdr->p_type == PT_LOAD) {
printf(" PT_LOAD Segment Found\n");
printf("Parsing img_info load addr 0x%x offset 0x%x size 0x%x type 0x%x\n",
phdr->p_paddr, phdr->p_offset, phdr->p_filesz, phdr->p_type);
int size = sb.st_size - (phdr->p_offset + phdr->p_filesz );
if (size < 0x1000) {
printf("rootfs metada is not available\n");
return 1;
}
create_file("/tmp/metadata.bin", (char *)(fp + phdr->p_offset + phdr->p_filesz), size);
printf("rootfs meta data file: %s created with size:%x\n","/tmp/metadata.bin", size);
close(fd);
return 1;
}
++phdr;
}
close(fd);
return 1;
}
/**
* being used to calculate the image hash
*
*/
#define SW_MASK 0x3636363636363636ull
void generate_swid_ipad(char *sw_id, unsigned long long *swid_xor_ipad)
{
unsigned long long int val;
val = strtoull(sw_id, NULL, 16);
*swid_xor_ipad = val ^ SW_MASK;
printf("%llx\n", *swid_xor_ipad);
}
/**
* being used to calculate the image hash
*
*/
#define HW_ID_MASK 0x5c5c5c5cull
#define OEM_ID_MASK 0x00005c5cull
#define OEM_MODEL_ID_MASK 0x00005c5cull
void generate_hwid_opad(char *hw_id, char *oem_id, char *oem_model_id,
unsigned long long *hwid_xor_opad)
{
unsigned long long val;
val = strtoull(hw_id, NULL, 16);
*hwid_xor_opad = (((val >> 32) ^ HW_ID_MASK) << 32);
val = strtoul(oem_id, NULL, 16);
*hwid_xor_opad |= ((val ^ OEM_ID_MASK) << 16);
val = strtoul(oem_model_id, NULL, 16);
*hwid_xor_opad |= (val ^ OEM_MODEL_ID_MASK) & 0xffff;
printf("%llx\n", *hwid_xor_opad);
}
int create_file(char *name, char *buffer, int size)
{
int fd;
fd = open(name, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
if (fd == -1) {
perror(name);
return 0;
}
if (write(fd, buffer, size) == -1) {
close(fd);
return 0;
}
close(fd);
return 1;
}
char *create_xor_ipad_opad(char *f_xor, unsigned long long *xor_buffer)
{
int fd;
char *file;
unsigned long long sw_id, sw_id_be;
file = mktemp(f_xor);
fd = open(file, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
if (fd == -1) {
perror(file);
return NULL;
}
sw_id = *xor_buffer;
sw_id_be = htobe64(sw_id);
write(fd, &sw_id_be, sizeof(sw_id_be));
close(fd);
return file;
}
char *read_file(char *file_name, size_t *file_size)
{
int fd;
struct stat st;
char *buffer;
fd = open(file_name, O_RDONLY);
if (fd == -1) {
perror(file_name);
return NULL;
}
memset(&st, 0, sizeof(struct stat));
fstat(fd, &st);
buffer = malloc(st.st_size * sizeof(buffer));
if (buffer == NULL) {
close(fd);
return NULL;
}
if (read(fd, buffer, st.st_size) == -1) {
close(fd);
return NULL;
}
*file_size = (size_t) st.st_size;
close(fd);
return buffer;
}
int generate_hash(char *cert, char *sw_file, char *hw_file)
{
unsigned long long swid_xor_ipad, hwid_xor_opad;
char *tmp;
char *sw_id_str = find_value(cert, "SW_ID", 17);
char *hw_id_str = find_value(cert, "HW_ID", 17);
char *oem_id_str = find_value(cert, "OEM_ID", 5);
char *oem_model_id_str = find_value(cert, "MODEL_ID", 5);
char f_sw_xor[] = "/tmp/swid_xor_XXXXXX";
char f_hw_xor[] = "/tmp/hwid_xor_XXXXXX";
if (sw_id_str == NULL || hw_id_str == NULL || oem_id_str == NULL || oem_model_id_str == NULL) {
if (sw_id_str != NULL) {
free(sw_id_str);
}
if (hw_id_str != NULL) {
free(hw_id_str);
}
if (oem_id_str != NULL) {
free(oem_id_str);
}
if (oem_model_id_str != NULL) {
free(oem_model_id_str);
}
return 0;
}
printf("sw_id=%s\thw_id=%s\t", sw_id_str, hw_id_str);
printf("oem_id=%s\toem_model_id=%s\n", oem_id_str, oem_model_id_str);
generate_swid_ipad(sw_id_str, &swid_xor_ipad);
tmp = create_xor_ipad_opad(f_sw_xor, &swid_xor_ipad);
if (tmp == NULL) {
free(sw_id_str);
free(hw_id_str);
free(oem_id_str);
free(oem_model_id_str);
return 0;
}
strlcpy(sw_file, tmp, 32);
generate_hwid_opad(hw_id_str, oem_id_str, oem_model_id_str, &hwid_xor_opad);
tmp = create_xor_ipad_opad(f_hw_xor, &hwid_xor_opad);
if (tmp == NULL) {
free(sw_id_str);
free(hw_id_str);
free(oem_id_str);
free(oem_model_id_str);
return 0;
}
strlcpy(hw_file, tmp, 32);
free(sw_id_str);
free(hw_id_str);
free(oem_id_str);
free(oem_model_id_str);
return 1;
}
void remove_file(char *sw_file, char *hw_file, char *code_file, char *pub_file)
{
remove(sw_file);
remove(hw_file);
remove(code_file);
remove(pub_file);
remove("src");
remove("sig");
remove("cert");
}
/**
* is_component_authenticated() usage the code, signature & public key retrieved
* for each component.
*
* @src: char *
* @sig: char *
* @cert: char *
*/
int is_component_authenticated(char *src, char *sig, char *cert)
{
char command[256];
char *computed_hash;
char *reference_hash;
char pub_key[] = "/tmp/pub_keyXXXXXX", *pub_file;
char code_hash[] = "/tmp/code_hash_XXXXXX", *code_file;
char tmp_hash[] = "/tmp/tmp_hash_XXXXXX", *tmp_file;
char f_computed_hash[] = "/tmp/computed_hash_XXXXXX", *computed_file;
char f_reference_hash[] = "/tmp/reference_hash_XXXXXX", *reference_file;
char sw_file[32],hw_file[32];
int retval;
size_t comp_hash_size, ref_hash_size;
if (!create_file("src", src, src_size) || !create_file("sig", sig, SIG_SIZE) ||
!create_file("cert", cert, CERT_SIZE)) {
return 0;
}
pub_file = mktemp(pub_key);
snprintf(command, sizeof(command),
"openssl x509 -in cert -pubkey -inform DER -noout > %s", pub_file);
retval = system(command);
if (retval != 0) {
remove("src");
remove("sig");
remove("cert");
printf("Error generating public key\n");
return 0;
}
retval = generate_hash(cert, sw_file, hw_file);
if (retval == 0) {
return 0;
}
code_file = mktemp(code_hash);
snprintf(command, sizeof(command),
"openssl dgst -sha256 -binary -out %s src", code_file);
retval = system(command);
if (retval != 0) {
remove_file(sw_file, hw_file, code_file, pub_file);
printf("Error in openssl digest\n");
return 0;
}
tmp_file = mktemp(tmp_hash);
snprintf(command, sizeof(command),
"cat %s %s | openssl dgst -sha256 -binary -out %s",
sw_file, code_file, tmp_file);
retval = system(command);
if (retval != 0) {
remove_file(sw_file, hw_file, code_file, pub_file);
remove(tmp_file);
printf("Error generating temp has\n");
return 0;
}
computed_file = mktemp(f_computed_hash);
snprintf(command, sizeof(command),
"cat %s %s | openssl dgst -sha256 -binary -out %s",
hw_file, tmp_file, computed_file);
retval = system(command);
if (retval != 0) {
remove_file(sw_file, hw_file, code_file, pub_file);
remove(tmp_file);
remove(computed_file);
printf("Error generating hash\n");
return 0;
}
reference_file = mktemp(f_reference_hash);
snprintf(command, sizeof(command),
"openssl rsautl -in sig -pubin -inkey %s -verify > %s",
pub_file, reference_file);
retval = system(command);
if (retval != 0) {
remove_file(sw_file, hw_file, code_file, pub_file);
remove(tmp_file);
remove(computed_file);
remove(reference_file);
printf("Error generating reference hash\n");
return 0;
}
computed_hash = read_file(computed_file, &comp_hash_size);
reference_hash = read_file(reference_file, &ref_hash_size);
if (computed_hash == NULL || reference_hash == NULL) {
remove_file(sw_file, hw_file, code_file, pub_file);
remove(tmp_file);
remove(computed_file);
remove(reference_file);
free(computed_hash?computed_hash:reference_hash);
return 0;
}
remove_file(sw_file, hw_file, code_file, pub_file);
remove(tmp_file);
remove(computed_file);
remove(reference_file);
if (memcmp(computed_hash, reference_hash, ref_hash_size) ||
(comp_hash_size != ref_hash_size)) {
free(computed_hash);
free(reference_hash);
printf("Error: Hash or file_size not equal\n");
return 0;
}
free(computed_hash);
free(reference_hash);
return 1;
}
/**
* is_image_authenticated() iterates through each component and check
* whether individual component is authenticated. If not, abort the FW
* upgrade process.
*
* @img: char *
*/
int is_image_authenticated(void)
{
int i;
char *src, *sig, *cert;
for (i = 0; i < NO_OF_SECTIONS; i++) {
if (!sections[i].is_present) {
continue;
}
if (!sections[i].split_components(&sections[i], &src, &sig, &cert)) {
printf("Error while splitting code/signature/Certificate from %s\n",
sections[i].file);
return 0;
}
if (!is_component_authenticated(src, sig, cert)) {
printf("Error while authenticating %s\n", sections[i].file);
return 0;
}
}
return 1;
}
int sec_image_auth(void)
{
int fd, i, len;
char *buf = NULL;
fd = open(SEC_AUTHENTICATE_FILE, O_RDWR);
if (-1 == fd) {
perror(SEC_AUTHENTICATE_FILE);
return 1;
}
buf = (char*)malloc(SIG_SIZE);
if (buf == NULL) {
perror("Memory allocation failed\n");
close(fd);
return 1;
}
for (i = 0; i < NO_OF_SECTIONS; i++) {
if (!sections[i].is_present) {
continue;
}
len = snprintf(buf, SIG_SIZE, "%s %s", sections[i].img_code, sections[i].file);
if (!strncmp(sections[i].type, "rootfs", strlen("rootfs"))) {
struct stat sb;
if (stat("/tmp/metadata.bin", &sb) == -1)
continue;
len = snprintf(buf, SIG_SIZE, "%s %s %s", sections[i].img_code,
"/tmp/metadata.bin", "/tmp/sha384_keyXXXXXX");
}
if (len < 0 || len > SIG_SIZE) {
perror("Array out of Index\n");
free(buf);
close(fd);
return 1;
}
if (write(fd, buf, len) != len) {
perror("write");
printf("%s Image authentication failed\n", buf);
free(buf);
close(fd);
return 1;
}
}
close(fd);
free(buf);
return 0;
}
int do_board_upgrade_check(char *img)
{
if (is_tz_authentication_enabled()) {
printf("TZ authentication enabled ...\n");
if (!load_sections()) {
printf("Error: Failed to load sections from image: %s\n", img);
return 1;
}
return sec_image_auth();
} else if (is_authentication_check_enabled()) {
if (!get_sections()) {
printf("Error: %s is not a signed image\n", img);
return 1;
}
if (!is_image_authenticated()) {
printf("Error: \"%s\" couldn't be authenticated. Abort...\n", img);
return 1;
}
if (!check_image_version()) {
printf("Error: \"%s\" couldn't be upgraded. Abort...\n", img);
return 1;
}
}
return 0;
}