blob: 6b8f90fa46b271ebe6293c56fabe597b5537d117 [file] [log] [blame]
/*
* Copyright (C) 2020 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 <getopt.h>
#include <string>
#include <vector>
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
namespace {
const char* sopts = "hb:d:p:s:M:m:i:c:";
const struct option lopts[] = {
{"help", no_argument, nullptr, 'h'},
{"brand", required_argument, nullptr, 'b'},
{"device", required_argument, nullptr, 'd'},
{"product", required_argument, nullptr, 'p'},
{"serial", required_argument, nullptr, 's'},
{"manufacturer", required_argument, nullptr, 'M'},
{"model", required_argument, nullptr, 'm'},
{"imei", required_argument, nullptr, 'i'},
{"meid", required_argument, nullptr, 'c'},
{"imei2", required_argument, nullptr, '2'},
{0, 0, 0, 0},
};
std::string TELEPHONY_CMD_GET_IMEI = "cmd phone get-imei ";
// Run a shell command and collect the output of it. If any error, set an empty string as the
// output.
std::string exec_command(const std::string& command) {
char buffer[128];
std::string result = "";
FILE* pipe = popen(command.c_str(), "r");
if (!pipe) {
fprintf(stderr, "popen('%s') failed\n", command.c_str());
return result;
}
while (!feof(pipe)) {
if (fgets(buffer, 128, pipe) != NULL) {
result += buffer;
}
}
pclose(pipe);
return result;
}
// Get IMEI using Telephony service shell command. If any error while executing the command
// then empty string will be returned as output.
std::string get_imei(int slot) {
std::string cmd = TELEPHONY_CMD_GET_IMEI + std::to_string(slot);
std::string output = exec_command(cmd);
if (output.empty()) {
fprintf(stderr, "Retrieve IMEI command ('%s') failed\n", cmd.c_str());
return "";
}
std::vector<std::string> out =
::android::base::Tokenize(::android::base::Trim(output), "Device IMEI:");
if (out.size() != 1) {
fprintf(stderr, "Error parsing command ('%s') output '%s'\n", cmd.c_str(), output.c_str());
return "";
}
std::string imei = ::android::base::Trim(out[0]);
if (imei.compare("null") == 0) {
fprintf(stderr, "IMEI value from command ('%s') is null, skipping", cmd.c_str());
return "";
}
return imei;
}
std::string buf2string(const keymaster::Buffer& buf) {
return std::string(reinterpret_cast<const char*>(buf.peek_read()), buf.available_read());
}
void print_usage(const char* prog, const keymaster::SetAttestationIdsKM3Request& req) {
fprintf(stderr,
"Usage: %s [options]\n"
"\n"
"options:\n"
" -h, --help prints this message and exit\n"
" -b, --brand <val> set brand (default '%s')\n"
" -d, --device <val> set device (default '%s')\n"
" -p, --product <val> set product (default '%s')\n"
" -s, --serial <val> set serial (default '%s')\n"
" -M, --manufacturer <val> set manufacturer (default '%s')\n"
" -m, --model <val> set model (default '%s')\n"
" -i, --imei <val> set IMEI (default '%s')\n"
" -c, --meid <val> set MEID (default '%s')\n"
" -2, --imei2 <val> set second IMEI (default '%s')\n"
"\n",
prog, buf2string(req.base.brand).c_str(), buf2string(req.base.device).c_str(),
buf2string(req.base.product).c_str(), buf2string(req.base.serial).c_str(),
buf2string(req.base.manufacturer).c_str(), buf2string(req.base.model).c_str(),
buf2string(req.base.imei).c_str(), buf2string(req.base.meid).c_str(),
buf2string(req.second_imei).c_str());
}
void set_to(keymaster::Buffer* buf, const std::string& value) {
if (!value.empty()) {
buf->Reinitialize(value.data(), value.size());
}
}
void set_from_prop(keymaster::Buffer* buf, const std::string& prop) {
std::string prop_value = ::android::base::GetProperty(prop, /* default_value = */ "");
set_to(buf, prop_value);
}
void populate_base_ids(keymaster::SetAttestationIdsRequest* req) {
set_from_prop(&req->brand, "ro.product.brand");
set_from_prop(&req->device, "ro.product.device");
set_from_prop(&req->product, "ro.product.name");
set_from_prop(&req->serial, "ro.serialno");
set_from_prop(&req->manufacturer, "ro.product.manufacturer");
set_from_prop(&req->model, "ro.product.model");
std::string imei = get_imei(0);
set_to(&req->imei, imei);
}
void populate_ids(keymaster::SetAttestationIdsKM3Request* req) {
populate_base_ids(&req->base);
// - "What about IMEI?"
// - "You've already had it."
// - "We've had one, yes. What about second IMEI?"
// - "I don't think he knows about second IMEI, Pip."
std::string imei2 = get_imei(1);
set_to(&req->second_imei, imei2);
}
} // namespace
int main(int argc, char** argv) {
// By default, set attestation IDs to the values in userspace properties.
keymaster::SetAttestationIdsKM3Request req(/* ver = */ 4);
populate_ids(&req);
while (true) {
int oidx = 0;
int c = getopt_long(argc, argv, sopts, lopts, &oidx);
if (c == -1) {
break; /* done */
}
switch (c) {
case 'b':
req.base.brand.Reinitialize(optarg, strlen(optarg));
break;
case 'd':
req.base.device.Reinitialize(optarg, strlen(optarg));
break;
case 'p':
req.base.product.Reinitialize(optarg, strlen(optarg));
break;
case 's':
req.base.serial.Reinitialize(optarg, strlen(optarg));
break;
case 'M':
req.base.manufacturer.Reinitialize(optarg, strlen(optarg));
break;
case 'm':
req.base.model.Reinitialize(optarg, strlen(optarg));
break;
case 'i':
req.base.imei.Reinitialize(optarg, strlen(optarg));
break;
case 'c':
req.base.meid.Reinitialize(optarg, strlen(optarg));
break;
case '2':
req.second_imei.Reinitialize(optarg, strlen(optarg));
break;
case 'h':
print_usage(argv[0], req);
exit(EXIT_SUCCESS);
default:
print_usage(argv[0], req);
exit(EXIT_FAILURE);
}
}
if (optind != argc) {
print_usage(argv[0], req);
exit(EXIT_FAILURE);
}
int ret = trusty_keymaster_connect();
if (ret) {
fprintf(stderr, "trusty_keymaster_connect failed: %d\n", ret);
return EXIT_FAILURE;
}
printf("Setting:\n"
" brand: %s\n"
" device: %s\n"
" product: %s\n"
" serial: %s\n"
" manufacturer: %s\n"
" model: %s\n"
" IMEI: %s\n"
" MEID: %s\n"
" SECOND_IMEI: %s\n\n",
buf2string(req.base.brand).c_str(), buf2string(req.base.device).c_str(),
buf2string(req.base.product).c_str(), buf2string(req.base.serial).c_str(),
buf2string(req.base.manufacturer).c_str(), buf2string(req.base.model).c_str(),
buf2string(req.base.imei).c_str(), buf2string(req.base.meid).c_str(),
buf2string(req.second_imei).c_str());
fflush(stdout);
keymaster::EmptyKeymasterResponse rsp(/* ver = */ 4);
const char* msg;
if (req.second_imei.available_read() == 0) {
// No SECOND_IMEI set, use base command.
ret = trusty_keymaster_send(KM_SET_ATTESTATION_IDS, req.base, &rsp);
msg = "SET_ATTESTATION_IDS";
} else {
// SECOND_IMEI is set, use updated command.
ret = trusty_keymaster_send(KM_SET_ATTESTATION_IDS_KM3, req, &rsp);
msg = "SET_ATTESTATION_IDS_KM3";
}
trusty_keymaster_disconnect();
if (ret) {
fprintf(stderr, "%s failed: %d\n", msg, ret);
return EXIT_FAILURE;
} else {
printf("done\n");
return EXIT_SUCCESS;
}
}