blob: 6db29612a36c98095d23a589ae6243c4672cbc4d [file] [log] [blame]
/*
* Copyright (C) 2021 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.
*/
#define TLOG_TAG "apploader-app-version"
#include <assert.h>
#include <inttypes.h>
#include <lib/app_manifest/app_manifest.h>
#include <lib/storage/storage.h>
#include <lib/system_state/system_state.h>
#include <stdarg.h>
#include <trusty_log.h>
#include <uapi/err.h>
#include <algorithm>
#include <array>
#include "apploader_package.h"
constexpr const char kStorageFilePrefix[] = "app_version.";
/*
* Size of storage file name:
* length of the prefix + UUID (32 bytes) + null terminator
*/
constexpr size_t kStorageFileNameSize =
(countof(kStorageFilePrefix) - 1) + 32 + 1;
using StorageFileName = std::array<char, kStorageFileNameSize>;
static StorageFileName get_storage_file_name(const uuid_t* app_uuid) {
StorageFileName result;
__UNUSED int written = snprintf(
result.data(), result.size(),
"%s%08" PRIx32 "%04" PRIx16 "%04" PRIx16 "%02" PRIx8 "%02" PRIx8
"%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8
"%02" PRIx8,
kStorageFilePrefix, app_uuid->time_low, app_uuid->time_mid,
app_uuid->time_hi_and_version, app_uuid->clock_seq_and_node[0],
app_uuid->clock_seq_and_node[1], app_uuid->clock_seq_and_node[2],
app_uuid->clock_seq_and_node[3], app_uuid->clock_seq_and_node[4],
app_uuid->clock_seq_and_node[5], app_uuid->clock_seq_and_node[6],
app_uuid->clock_seq_and_node[7]);
assert(static_cast<size_t>(written) == result.size() - 1);
return result;
}
/*
* Retrieves the version of an application from storage.
*/
static int get_app_storage_version(const uuid_t* app_uuid,
uint32_t* app_version) {
assert(app_version);
auto file_name = get_storage_file_name(app_uuid);
int rc;
storage_session_t session;
rc = storage_open_session(&session, STORAGE_CLIENT_TP_PORT);
if (rc < 0) {
TLOGE("Error opening storage session (%d)\n", rc);
goto err_open_session;
}
file_handle_t file;
rc = storage_open_file(session, &file, file_name.data(), 0, 0);
if (rc < 0) {
if (rc == ERR_NOT_FOUND) {
/* File does not exist, treat this case as version 0 */
*app_version = 0;
rc = 0;
} else {
TLOGE("Error opening storage file (%d)\n", rc);
}
goto err_open_file;
}
uint32_t file_version;
rc = storage_read(file, 0, &file_version, sizeof(file_version));
if (rc != sizeof(file_version)) {
TLOGE("Error reading file (%d)\n", rc);
goto err_read_file;
}
*app_version = file_version;
rc = NO_ERROR;
err_read_file:
storage_close_file(file);
err_open_file:
err_get_file:
storage_close_session(session);
err_open_session:
return rc;
}
static int update_app_version(uuid_t* app_uuid, uint32_t new_version) {
int rc;
auto file_name = get_storage_file_name(app_uuid);
storage_session_t session;
rc = storage_open_session(&session, STORAGE_CLIENT_TP_PORT);
if (rc < 0) {
TLOGE("Error opening storage session (%d)\n", rc);
goto err_open_session;
}
file_handle_t file;
rc = storage_open_file(session, &file, file_name.data(),
STORAGE_FILE_OPEN_CREATE, 0);
if (rc < 0) {
TLOGE("Error opening storage file (%d)\n", rc);
goto err_open_file;
}
rc = storage_write(file, 0, &new_version, sizeof(new_version),
STORAGE_OP_COMPLETE);
if (rc != sizeof(new_version)) {
TLOGE("Error writing to file (%d)\n", rc);
}
rc = NO_ERROR;
err_write_file:
storage_close_file(file);
err_open_file:
storage_close_session(session);
err_open_session:
return rc;
}
extern "C" bool apploader_check_app_version(
struct apploader_package_metadata* pkg_meta) {
int rc;
struct app_manifest_iterator iter;
rc = app_manifest_iterator_reset(
&iter, reinterpret_cast<const char*>(pkg_meta->manifest_start),
pkg_meta->manifest_size);
if (rc != NO_ERROR) {
TLOGE("Error parsing manifest (%d)\n", rc);
return false;
}
/* Apps without a version in the manifest get a default of 0 */
uuid_t app_uuid;
uint32_t manifest_version = 0;
struct app_manifest_config_entry entry;
while (app_manifest_iterator_next(&iter, &entry, &rc)) {
switch (entry.key) {
case APP_MANIFEST_CONFIG_KEY_UUID:
memcpy(&app_uuid, &entry.value.uuid, sizeof(uuid));
break;
case APP_MANIFEST_CONFIG_KEY_VERSION:
manifest_version = entry.value.version;
break;
case APP_MANIFEST_CONFIG_KEY_APP_NAME:
case APP_MANIFEST_CONFIG_KEY_MIN_STACK_SIZE:
case APP_MANIFEST_CONFIG_KEY_MIN_HEAP_SIZE:
case APP_MANIFEST_CONFIG_KEY_MAP_MEM:
case APP_MANIFEST_CONFIG_KEY_MGMT_FLAGS:
case APP_MANIFEST_CONFIG_KEY_START_PORT:
case APP_MANIFEST_CONFIG_KEY_PINNED_CPU:
case APP_MANIFEST_CONFIG_KEY_MIN_SHADOW_STACK_SIZE:
case APP_MANIFEST_CONFIG_KEY_APPLOADER_FLAGS:
/* We don't care about these here */
break;
}
}
if (rc != NO_ERROR) {
TLOGE("Error iterating over manifest entries (%d)\n", rc);
return false;
}
/* Check application version */
uint32_t storage_version;
rc = get_app_storage_version(&app_uuid, &storage_version);
if (rc < 0) {
TLOGE("Error retrieving application version from storage (%d)\n", rc);
return false;
}
if (manifest_version < storage_version) {
TLOGE("Application package version (%" PRIu32
") is lower than storage version (%" PRIu32 ")\n",
manifest_version, storage_version);
return false;
}
if (!system_state_app_loading_skip_version_update() &&
manifest_version > storage_version) {
rc = update_app_version(&app_uuid, manifest_version);
if (rc < 0) {
TLOGE("Error updating application version in storage (%d)\n", rc);
return false;
}
}
return true;
}