1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <getopt.h>
18
19 #include <string>
20 #include <vector>
21
22 #include <android-base/properties.h>
23 #include <android-base/strings.h>
24 #include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
25
26 namespace {
27
28 const char* sopts = "hb:d:p:s:M:m:i:c:";
29 const struct option lopts[] = {
30 {"help", no_argument, nullptr, 'h'},
31 {"brand", required_argument, nullptr, 'b'},
32 {"device", required_argument, nullptr, 'd'},
33 {"product", required_argument, nullptr, 'p'},
34 {"serial", required_argument, nullptr, 's'},
35 {"manufacturer", required_argument, nullptr, 'M'},
36 {"model", required_argument, nullptr, 'm'},
37 {"imei", required_argument, nullptr, 'i'},
38 {"meid", required_argument, nullptr, 'c'},
39 {"imei2", required_argument, nullptr, '2'},
40 {0, 0, 0, 0},
41 };
42
43 std::string TELEPHONY_CMD_GET_IMEI = "cmd phone get-imei ";
44
45 // Run a shell command and collect the output of it. If any error, set an empty string as the
46 // output.
exec_command(const std::string & command)47 std::string exec_command(const std::string& command) {
48 char buffer[128];
49 std::string result = "";
50
51 FILE* pipe = popen(command.c_str(), "r");
52 if (!pipe) {
53 fprintf(stderr, "popen('%s') failed\n", command.c_str());
54 return result;
55 }
56
57 while (!feof(pipe)) {
58 if (fgets(buffer, 128, pipe) != NULL) {
59 result += buffer;
60 }
61 }
62
63 pclose(pipe);
64 return result;
65 }
66
67 // Get IMEI using Telephony service shell command. If any error while executing the command
68 // then empty string will be returned as output.
get_imei(int slot)69 std::string get_imei(int slot) {
70 std::string cmd = TELEPHONY_CMD_GET_IMEI + std::to_string(slot);
71 std::string output = exec_command(cmd);
72
73 if (output.empty()) {
74 fprintf(stderr, "Retrieve IMEI command ('%s') failed\n", cmd.c_str());
75 return "";
76 }
77
78 std::vector<std::string> out =
79 ::android::base::Tokenize(::android::base::Trim(output), "Device IMEI:");
80
81 if (out.size() != 1) {
82 fprintf(stderr, "Error parsing command ('%s') output '%s'\n", cmd.c_str(), output.c_str());
83 return "";
84 }
85
86 std::string imei = ::android::base::Trim(out[0]);
87 if (imei.compare("null") == 0) {
88 fprintf(stderr, "IMEI value from command ('%s') is null, skipping", cmd.c_str());
89 return "";
90 }
91 return imei;
92 }
93
buf2string(const keymaster::Buffer & buf)94 std::string buf2string(const keymaster::Buffer& buf) {
95 return std::string(reinterpret_cast<const char*>(buf.peek_read()), buf.available_read());
96 }
97
print_usage(const char * prog,const keymaster::SetAttestationIdsKM3Request & req)98 void print_usage(const char* prog, const keymaster::SetAttestationIdsKM3Request& req) {
99 fprintf(stderr,
100 "Usage: %s [options]\n"
101 "\n"
102 "options:\n"
103 " -h, --help prints this message and exit\n"
104 " -b, --brand <val> set brand (default '%s')\n"
105 " -d, --device <val> set device (default '%s')\n"
106 " -p, --product <val> set product (default '%s')\n"
107 " -s, --serial <val> set serial (default '%s')\n"
108 " -M, --manufacturer <val> set manufacturer (default '%s')\n"
109 " -m, --model <val> set model (default '%s')\n"
110 " -i, --imei <val> set IMEI (default '%s')\n"
111 " -c, --meid <val> set MEID (default '%s')\n"
112 " -2, --imei2 <val> set second IMEI (default '%s')\n"
113 "\n",
114 prog, buf2string(req.base.brand).c_str(), buf2string(req.base.device).c_str(),
115 buf2string(req.base.product).c_str(), buf2string(req.base.serial).c_str(),
116 buf2string(req.base.manufacturer).c_str(), buf2string(req.base.model).c_str(),
117 buf2string(req.base.imei).c_str(), buf2string(req.base.meid).c_str(),
118 buf2string(req.second_imei).c_str());
119 }
120
set_to(keymaster::Buffer * buf,const std::string & value)121 void set_to(keymaster::Buffer* buf, const std::string& value) {
122 if (!value.empty()) {
123 buf->Reinitialize(value.data(), value.size());
124 }
125 }
126
set_from_prop(keymaster::Buffer * buf,const std::string & prop)127 void set_from_prop(keymaster::Buffer* buf, const std::string& prop) {
128 std::string prop_value = ::android::base::GetProperty(prop, /* default_value = */ "");
129 set_to(buf, prop_value);
130 }
131
populate_base_ids(keymaster::SetAttestationIdsRequest * req)132 void populate_base_ids(keymaster::SetAttestationIdsRequest* req) {
133 set_from_prop(&req->brand, "ro.product.brand");
134 set_from_prop(&req->device, "ro.product.device");
135 set_from_prop(&req->product, "ro.product.name");
136 set_from_prop(&req->serial, "ro.serialno");
137 set_from_prop(&req->manufacturer, "ro.product.manufacturer");
138 set_from_prop(&req->model, "ro.product.model");
139 std::string imei = get_imei(0);
140 set_to(&req->imei, imei);
141 }
142
populate_ids(keymaster::SetAttestationIdsKM3Request * req)143 void populate_ids(keymaster::SetAttestationIdsKM3Request* req) {
144 populate_base_ids(&req->base);
145
146 // - "What about IMEI?"
147 // - "You've already had it."
148 // - "We've had one, yes. What about second IMEI?"
149 // - "I don't think he knows about second IMEI, Pip."
150 std::string imei2 = get_imei(1);
151 set_to(&req->second_imei, imei2);
152 }
153
154 } // namespace
155
main(int argc,char ** argv)156 int main(int argc, char** argv) {
157 // By default, set attestation IDs to the values in userspace properties.
158 keymaster::SetAttestationIdsKM3Request req(/* ver = */ 4);
159 populate_ids(&req);
160
161 while (true) {
162 int oidx = 0;
163 int c = getopt_long(argc, argv, sopts, lopts, &oidx);
164 if (c == -1) {
165 break; /* done */
166 }
167
168 switch (c) {
169 case 'b':
170 req.base.brand.Reinitialize(optarg, strlen(optarg));
171 break;
172 case 'd':
173 req.base.device.Reinitialize(optarg, strlen(optarg));
174 break;
175 case 'p':
176 req.base.product.Reinitialize(optarg, strlen(optarg));
177 break;
178 case 's':
179 req.base.serial.Reinitialize(optarg, strlen(optarg));
180 break;
181 case 'M':
182 req.base.manufacturer.Reinitialize(optarg, strlen(optarg));
183 break;
184 case 'm':
185 req.base.model.Reinitialize(optarg, strlen(optarg));
186 break;
187 case 'i':
188 req.base.imei.Reinitialize(optarg, strlen(optarg));
189 break;
190 case 'c':
191 req.base.meid.Reinitialize(optarg, strlen(optarg));
192 break;
193 case '2':
194 req.second_imei.Reinitialize(optarg, strlen(optarg));
195 break;
196 case 'h':
197 print_usage(argv[0], req);
198 exit(EXIT_SUCCESS);
199 default:
200 print_usage(argv[0], req);
201 exit(EXIT_FAILURE);
202 }
203 }
204 if (optind != argc) {
205 print_usage(argv[0], req);
206 exit(EXIT_FAILURE);
207 }
208
209 int ret = trusty_keymaster_connect();
210 if (ret) {
211 fprintf(stderr, "trusty_keymaster_connect failed: %d\n", ret);
212 return EXIT_FAILURE;
213 }
214
215 printf("Setting:\n"
216 " brand: %s\n"
217 " device: %s\n"
218 " product: %s\n"
219 " serial: %s\n"
220 " manufacturer: %s\n"
221 " model: %s\n"
222 " IMEI: %s\n"
223 " MEID: %s\n"
224 " SECOND_IMEI: %s\n\n",
225 buf2string(req.base.brand).c_str(), buf2string(req.base.device).c_str(),
226 buf2string(req.base.product).c_str(), buf2string(req.base.serial).c_str(),
227 buf2string(req.base.manufacturer).c_str(), buf2string(req.base.model).c_str(),
228 buf2string(req.base.imei).c_str(), buf2string(req.base.meid).c_str(),
229 buf2string(req.second_imei).c_str());
230 fflush(stdout);
231
232 keymaster::EmptyKeymasterResponse rsp(/* ver = */ 4);
233 const char* msg;
234 if (req.second_imei.available_read() == 0) {
235 // No SECOND_IMEI set, use base command.
236 ret = trusty_keymaster_send(KM_SET_ATTESTATION_IDS, req.base, &rsp);
237 msg = "SET_ATTESTATION_IDS";
238 } else {
239 // SECOND_IMEI is set, use updated command.
240 ret = trusty_keymaster_send(KM_SET_ATTESTATION_IDS_KM3, req, &rsp);
241 msg = "SET_ATTESTATION_IDS_KM3";
242 }
243 trusty_keymaster_disconnect();
244
245 if (ret) {
246 fprintf(stderr, "%s failed: %d\n", msg, ret);
247 return EXIT_FAILURE;
248 } else {
249 printf("done\n");
250 printf("\nNOTE: device reboot may be required before changes take effect.\n");
251 return EXIT_SUCCESS;
252 }
253 }
254