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