/*
* Copyright (C) 2017 The Android Open Source Project
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stdio.h>
#include <string.h>
#include <sysexits.h>
#include <android-base/properties.h>
#include <libavb_user/libavb_user.h>
namespace {
static bool g_opt_force = false;
/* Prints program usage to |where|. */
void usage(FILE* where, int /* argc */, char* argv[]) {
fprintf(where,
"%s - command-line tool for AVB.\n"
"\n"
"Usage:\n"
" %s [--force] COMMAND\n"
"\n"
"Commands:\n"
" %s get-verity - Prints whether verity is enabled in "
"current slot.\n"
" %s disable-verity - Disable verity in current slot.\n"
" %s enable-verity - Enable verity in current slot.\n"
" %s get-verification - Prints whether verification is enabled "
"in current slot.\n"
" %s disable-verification - Disable verification in current slot.\n"
" %s enable-verification - Enable verification in current slot.\n",
argv[0],
argv[0],
argv[0],
argv[0],
argv[0],
argv[0],
argv[0],
argv[0]);
}
/* Returns true if device is in LOCKED mode and --force wasn't
* passed. In this case also prints diagnostic message to stderr as a
* side-effect.
*/
bool is_locked_and_not_forced() {
std::string device_state;
device_state = android::base::GetProperty("ro.boot.vbmeta.device_state", "");
if (device_state == "locked" && !g_opt_force) {
fprintf(stderr,
"Manipulating vbmeta on a LOCKED device will likely cause the\n"
"device to fail booting with little chance of recovery.\n"
"\n"
"If you really want to do this, use the --force option.\n"
"\n"
"ONLY DO THIS IF YOU KNOW WHAT YOU ARE DOING.\n"
"\n");
return false;
}
return true;
}
/* Function to enable and disable verification. The |ops| parameter
* should be an |AvbOps| from libavb_user.
*/
int do_set_verification(AvbOps* ops,
const std::string& ab_suffix,
bool enable_verification) {
bool verification_enabled;
if (!avb_user_verification_get(
ops, ab_suffix.c_str(), &verification_enabled)) {
fprintf(stderr, "Error getting whether verification is enabled.\n");
return EX_SOFTWARE;
}
if ((verification_enabled && enable_verification) ||
(!verification_enabled && !enable_verification)) {
fprintf(stdout,
"verification is already %s",
verification_enabled ? "enabled" : "disabled");
if (ab_suffix != "") {
fprintf(stdout, " on slot with suffix %s", ab_suffix.c_str());
}
fprintf(stdout, ".\n");
return EX_OK;
}
if (!is_locked_and_not_forced()) {
return EX_NOPERM;
}
if (!avb_user_verification_set(ops, ab_suffix.c_str(), enable_verification)) {
fprintf(stderr, "Error setting verification.\n");
return EX_SOFTWARE;
}
fprintf(stdout,
"Successfully %s verification",
enable_verification ? "enabled" : "disabled");
if (ab_suffix != "") {
fprintf(stdout, " on slot with suffix %s", ab_suffix.c_str());
}
fprintf(stdout, ". Reboot the device for changes to take effect.\n");
return EX_OK;
}
/* Function to query if verification. The |ops| parameter should be an
* |AvbOps| from libavb_user.
*/
int do_get_verification(AvbOps* ops, const std::string& ab_suffix) {
bool verification_enabled;
if (!avb_user_verification_get(
ops, ab_suffix.c_str(), &verification_enabled)) {
fprintf(stderr, "Error getting whether verification is enabled.\n");
return EX_SOFTWARE;
}
fprintf(stdout,
"verification is %s",
verification_enabled ? "enabled" : "disabled");
if (ab_suffix != "") {
fprintf(stdout, " on slot with suffix %s", ab_suffix.c_str());
}
fprintf(stdout, ".\n");
return EX_OK;
}
/* Function to enable and disable dm-verity. The |ops| parameter
* should be an |AvbOps| from libavb_user.
*/
int do_set_verity(AvbOps* ops,
const std::string& ab_suffix,
bool enable_verity) {
bool verity_enabled;
if (!avb_user_verity_get(ops, ab_suffix.c_str(), &verity_enabled)) {
fprintf(stderr, "Error getting whether verity is enabled.\n");
return EX_SOFTWARE;
}
if ((verity_enabled && enable_verity) ||
(!verity_enabled && !enable_verity)) {
fprintf(stdout,
"verity is already %s",
verity_enabled ? "enabled" : "disabled");
if (ab_suffix != "") {
fprintf(stdout, " on slot with suffix %s", ab_suffix.c_str());
}
fprintf(stdout, ".\n");
return EX_OK;
}
if (!is_locked_and_not_forced()) {
return EX_NOPERM;
}
if (!avb_user_verity_set(ops, ab_suffix.c_str(), enable_verity)) {
fprintf(stderr, "Error setting verity.\n");
return EX_SOFTWARE;
}
fprintf(
stdout, "Successfully %s verity", enable_verity ? "enabled" : "disabled");
if (ab_suffix != "") {
fprintf(stdout, " on slot with suffix %s", ab_suffix.c_str());
}
fprintf(stdout, ". Reboot the device for changes to take effect.\n");
return EX_OK;
}
/* Function to query if dm-verity is enabled. The |ops| parameter
* should be an |AvbOps| from libavb_user.
*/
int do_get_verity(AvbOps* ops, const std::string& ab_suffix) {
bool verity_enabled;
if (!avb_user_verity_get(ops, ab_suffix.c_str(), &verity_enabled)) {
fprintf(stderr, "Error getting whether verity is enabled.\n");
return EX_SOFTWARE;
}
fprintf(stdout, "verity is %s", verity_enabled ? "enabled" : "disabled");
if (ab_suffix != "") {
fprintf(stdout, " on slot with suffix %s", ab_suffix.c_str());
}
fprintf(stdout, ".\n");
return EX_OK;
}
/* Helper function to get A/B suffix, if any. If the device isn't
* using A/B the empty string is returned. Otherwise either "_a",
* "_b", ... is returned.
*/
std::string get_ab_suffix() {
return android::base::GetProperty("ro.boot.slot_suffix", "");
}
} // namespace
enum class Command {
kNone,
kDisableVerity,
kEnableVerity,
kGetVerity,
kDisableVerification,
kEnableVerification,
kGetVerification,
};
int main(int argc, char* argv[]) {
int ret;
AvbOps* ops = nullptr;
std::string ab_suffix = get_ab_suffix();
Command cmd = Command::kNone;
if (argc < 2) {
usage(stderr, argc, argv);
ret = EX_USAGE;
goto out;
}
ops = avb_ops_user_new();
if (ops == nullptr) {
fprintf(stderr, "Error getting AVB ops.\n");
ret = EX_SOFTWARE;
goto out;
}
for (int n = 1; n < argc; n++) {
if (strcmp(argv[n], "--force") == 0) {
g_opt_force = true;
} else if (strcmp(argv[n], "disable-verity") == 0) {
cmd = Command::kDisableVerity;
} else if (strcmp(argv[n], "enable-verity") == 0) {
cmd = Command::kEnableVerity;
} else if (strcmp(argv[n], "get-verity") == 0) {
cmd = Command::kGetVerity;
} else if (strcmp(argv[n], "disable-verification") == 0) {
cmd = Command::kDisableVerification;
} else if (strcmp(argv[n], "enable-verification") == 0) {
cmd = Command::kEnableVerification;
} else if (strcmp(argv[n], "get-verification") == 0) {
cmd = Command::kGetVerification;
}
}
switch (cmd) {
case Command::kNone:
usage(stderr, argc, argv);
ret = EX_USAGE;
break;
case Command::kDisableVerity:
ret = do_set_verity(ops, ab_suffix, false);
break;
case Command::kEnableVerity:
ret = do_set_verity(ops, ab_suffix, true);
break;
case Command::kGetVerity:
ret = do_get_verity(ops, ab_suffix);
break;
case Command::kDisableVerification:
ret = do_set_verification(ops, ab_suffix, false);
break;
case Command::kEnableVerification:
ret = do_set_verification(ops, ab_suffix, true);
break;
case Command::kGetVerification:
ret = do_get_verification(ops, ab_suffix);
break;
}
out:
if (ops != nullptr) {
avb_ops_user_free(ops);
}
return ret;
}