1 /*
2 * Copyright (C) 2016 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 #include <errno.h>
17 #include <getopt.h>
18 #include <stdbool.h>
19 #include <stdint.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <sys/capability.h>
23 #include <sys/prctl.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26
27 #include <android/binder_process.h>
28 #include <cutils/android_filesystem_config.h>
29
30 #include "checkpoint_handling.h"
31 #include "ipc.h"
32 #include "log.h"
33 #include "rpmb.h"
34 #include "storage.h"
35 #include "watchdog.h"
36
37 #define REQ_BUFFER_SIZE 4096
38 static uint8_t req_buffer[REQ_BUFFER_SIZE + 1];
39
40 static const char* ss_data_root;
41 static const char* trusty_devname;
42 static const char* rpmb_devname;
43 static const char* ss_srv_name = STORAGE_DISK_PROXY_PORT;
44
45 static enum dev_type dev_type = MMC_RPMB;
46
parse_dev_type(const char * dev_type_name)47 static enum dev_type parse_dev_type(const char* dev_type_name) {
48 if (!strcmp(dev_type_name, "mmc")) {
49 return MMC_RPMB;
50 } else if (!strcmp(dev_type_name, "virt")) {
51 return VIRT_RPMB;
52 } else if (!strcmp(dev_type_name, "sock")) {
53 return SOCK_RPMB;
54 } else if (!strcmp(dev_type_name, "ufs")) {
55 return UFS_RPMB;
56 } else {
57 return UNKNOWN_RPMB;
58 }
59 }
60
61 static const char* _sopts = "hp:d:r:t:";
62 static const struct option _lopts[] = {{"help", no_argument, NULL, 'h'},
63 {"trusty_dev", required_argument, NULL, 'd'},
64 {"data_path", required_argument, NULL, 'p'},
65 {"rpmb_dev", required_argument, NULL, 'r'},
66 {"dev_type", required_argument, NULL, 't'},
67 {0, 0, 0, 0}};
68
show_usage_and_exit(int code)69 static void show_usage_and_exit(int code) {
70 ALOGE("usage: storageproxyd -d <trusty_dev> -p <data_path> -r <rpmb_dev> -t <dev_type>\n");
71 ALOGE("Available dev types: mmc, virt\n");
72 exit(code);
73 }
74
handle_req(struct storage_msg * msg,const void * req,size_t req_len)75 static int handle_req(struct storage_msg* msg, const void* req, size_t req_len) {
76 int rc;
77
78 struct watcher* watcher = watch_start("request", msg);
79
80 if ((msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) && msg->cmd != STORAGE_RPMB_SEND &&
81 msg->cmd != STORAGE_FILE_WRITE) {
82 /*
83 * handling post commit messages on commands other than rpmb and write
84 * operations are not implemented as there is no use case for this yet.
85 */
86 ALOGE("cmd 0x%x: post commit option is not implemented\n", msg->cmd);
87 msg->result = STORAGE_ERR_UNIMPLEMENTED;
88 goto err_response;
89 }
90
91 if (msg->flags & STORAGE_MSG_FLAG_PRE_COMMIT) {
92 rc = storage_sync_checkpoint(watcher);
93 if (rc < 0) {
94 msg->result = STORAGE_ERR_SYNC_FAILURE;
95 goto err_response;
96 }
97 }
98
99 if (msg->flags & STORAGE_MSG_FLAG_PRE_COMMIT_CHECKPOINT) {
100 bool is_checkpoint_active = false;
101
102 rc = is_data_checkpoint_active(&is_checkpoint_active);
103 if (rc != 0) {
104 ALOGE("is_data_checkpoint_active failed in an unexpected way. Aborting.\n");
105 msg->result = STORAGE_ERR_GENERIC;
106 goto err_response;
107 } else if (is_checkpoint_active) {
108 ALOGE("Checkpoint in progress, dropping write ...\n");
109 msg->result = STORAGE_ERR_GENERIC;
110 goto err_response;
111 }
112 }
113
114 switch (msg->cmd) {
115 case STORAGE_FILE_DELETE:
116 rc = storage_file_delete(msg, req, req_len, watcher);
117 break;
118
119 case STORAGE_FILE_OPEN:
120 rc = storage_file_open(msg, req, req_len, watcher);
121 break;
122
123 case STORAGE_FILE_CLOSE:
124 rc = storage_file_close(msg, req, req_len, watcher);
125 break;
126
127 case STORAGE_FILE_WRITE:
128 rc = storage_file_write(msg, req, req_len, watcher);
129 break;
130
131 case STORAGE_FILE_READ:
132 rc = storage_file_read(msg, req, req_len, watcher);
133 break;
134
135 case STORAGE_FILE_GET_SIZE:
136 rc = storage_file_get_size(msg, req, req_len, watcher);
137 break;
138
139 case STORAGE_FILE_SET_SIZE:
140 rc = storage_file_set_size(msg, req, req_len, watcher);
141 break;
142
143 case STORAGE_FILE_GET_MAX_SIZE:
144 rc = storage_file_get_max_size(msg, req, req_len, watcher);
145 break;
146
147 case STORAGE_RPMB_SEND:
148 rc = rpmb_send(msg, req, req_len, watcher);
149 break;
150
151 default:
152 ALOGE("unhandled command 0x%x\n", msg->cmd);
153 msg->result = STORAGE_ERR_UNIMPLEMENTED;
154 goto err_response;
155 }
156
157 /* response was sent in handler */
158 goto finish;
159
160 err_response:
161 rc = ipc_respond(msg, NULL, 0);
162
163 finish:
164 watch_finish(watcher);
165 return rc;
166 }
167
proxy_loop(void)168 static int proxy_loop(void) {
169 ssize_t rc;
170 struct storage_msg msg;
171
172 /* enter main message handling loop */
173 while (true) {
174 /* get incoming message */
175 rc = ipc_get_msg(&msg, req_buffer, REQ_BUFFER_SIZE);
176 if (rc < 0) return rc;
177
178 /* handle request */
179 req_buffer[rc] = 0; /* force zero termination */
180 rc = handle_req(&msg, req_buffer, rc);
181 if (rc) return rc;
182 }
183
184 return 0;
185 }
186
parse_args(int argc,char * argv[])187 static void parse_args(int argc, char* argv[]) {
188 int opt;
189 int oidx = 0;
190
191 while ((opt = getopt_long(argc, argv, _sopts, _lopts, &oidx)) != -1) {
192 switch (opt) {
193 case 'd':
194 trusty_devname = strdup(optarg);
195 break;
196
197 case 'p':
198 ss_data_root = strdup(optarg);
199 break;
200
201 case 'r':
202 rpmb_devname = strdup(optarg);
203 break;
204
205 case 't':
206 dev_type = parse_dev_type(optarg);
207 if (dev_type == UNKNOWN_RPMB) {
208 ALOGE("Unrecognized dev type: %s\n", optarg);
209 show_usage_and_exit(EXIT_FAILURE);
210 }
211 break;
212
213 default:
214 ALOGE("unrecognized option (%c):\n", opt);
215 show_usage_and_exit(EXIT_FAILURE);
216 }
217 }
218
219 if (ss_data_root == NULL || trusty_devname == NULL || rpmb_devname == NULL) {
220 ALOGE("missing required argument(s)\n");
221 show_usage_and_exit(EXIT_FAILURE);
222 }
223
224 ALOGI("starting storageproxyd\n");
225 ALOGI("storage data root: %s\n", ss_data_root);
226 ALOGI("trusty dev: %s\n", trusty_devname);
227 ALOGI("rpmb dev: %s\n", rpmb_devname);
228 }
229
main(int argc,char * argv[])230 int main(int argc, char* argv[]) {
231 int rc;
232
233 /*
234 * No access for group and other. We need execute access for user to create
235 * an accessible directory.
236 */
237 umask(S_IRWXG | S_IRWXO);
238
239 /* parse arguments */
240 parse_args(argc, argv);
241
242 /*
243 * Start binder threadpool. At least one extra binder thread is needed to
244 * connect to the wakelock service without relying on polling. If we poll on
245 * the main thread we end up pausing for at least 1s even if the service
246 * starts faster. We set the max thread count to 0 because startThreadPool
247 * "Starts one thread, PLUS those requested in setThreadPoolMaxThreadCount,
248 * PLUS those manually requested in joinThreadPool." We only need a single
249 * binder thread to receive notifications on.
250 */
251 ABinderProcess_setThreadPoolMaxThreadCount(0);
252 ABinderProcess_startThreadPool();
253
254 /* initialize secure storage directory */
255 rc = storage_init(ss_data_root);
256 if (rc < 0) return EXIT_FAILURE;
257
258 /* open rpmb device */
259 rc = rpmb_open(rpmb_devname, dev_type);
260 if (rc < 0) return EXIT_FAILURE;
261
262 /* connect to Trusty secure storage server */
263 rc = ipc_connect(trusty_devname, ss_srv_name);
264 if (rc < 0) return EXIT_FAILURE;
265
266 /* enter main loop */
267 rc = proxy_loop();
268 ALOGE("exiting proxy loop with status (%d)\n", rc);
269
270 ipc_disconnect();
271 rpmb_close();
272
273 return (rc < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
274 }
275