| /* | |
| * Copyright (C) 2016 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 <errno.h> | |
| #include <getopt.h> | |
| #include <stdbool.h> | |
| #include <stdint.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <sys/capability.h> | |
| #include <sys/prctl.h> | |
| #include <sys/stat.h> | |
| #include <unistd.h> | |
| #include <cutils/android_filesystem_config.h> | |
| #include "ipc.h" | |
| #include "log.h" | |
| #include "rpmb.h" | |
| #include "storage.h" | |
| #define REQ_BUFFER_SIZE 4096 | |
| static uint8_t req_buffer[REQ_BUFFER_SIZE + 1]; | |
| static const char* ss_data_root; | |
| static const char* trusty_devname; | |
| static const char* rpmb_devname; | |
| static const char* ss_srv_name = STORAGE_DISK_PROXY_PORT; | |
| static enum dev_type dev_type = MMC_RPMB; | |
| static enum dev_type parse_dev_type(const char* dev_type_name) { | |
| if (!strcmp(dev_type_name, "mmc")) { | |
| return MMC_RPMB; | |
| } else if (!strcmp(dev_type_name, "virt")) { | |
| return VIRT_RPMB; | |
| } else { | |
| return UNKNOWN_RPMB; | |
| } | |
| } | |
| static const char* _sopts = "hp:d:r:t:"; | |
| static const struct option _lopts[] = {{"help", no_argument, NULL, 'h'}, | |
| {"trusty_dev", required_argument, NULL, 'd'}, | |
| {"data_path", required_argument, NULL, 'p'}, | |
| {"rpmb_dev", required_argument, NULL, 'r'}, | |
| {"dev_type", required_argument, NULL, 't'}, | |
| {0, 0, 0, 0}}; | |
| static void show_usage_and_exit(int code) { | |
| ALOGE("usage: storageproxyd -d <trusty_dev> -p <data_path> -r <rpmb_dev> -t <dev_type>\n"); | |
| ALOGE("Available dev types: mmc, virt\n"); | |
| exit(code); | |
| } | |
| static int drop_privs(void) { | |
| struct __user_cap_header_struct capheader; | |
| struct __user_cap_data_struct capdata[2]; | |
| if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) { | |
| return -1; | |
| } | |
| /* | |
| * ensure we're running as the system user | |
| */ | |
| if (setgid(AID_SYSTEM) != 0) { | |
| return -1; | |
| } | |
| if (setuid(AID_SYSTEM) != 0) { | |
| return -1; | |
| } | |
| /* | |
| * drop all capabilities except SYS_RAWIO | |
| */ | |
| memset(&capheader, 0, sizeof(capheader)); | |
| memset(&capdata, 0, sizeof(capdata)); | |
| capheader.version = _LINUX_CAPABILITY_VERSION_3; | |
| capheader.pid = 0; | |
| capdata[CAP_TO_INDEX(CAP_SYS_RAWIO)].permitted = CAP_TO_MASK(CAP_SYS_RAWIO); | |
| capdata[CAP_TO_INDEX(CAP_SYS_RAWIO)].effective = CAP_TO_MASK(CAP_SYS_RAWIO); | |
| if (capset(&capheader, &capdata[0]) < 0) { | |
| return -1; | |
| } | |
| /* no-execute for user, no access for group and other */ | |
| umask(S_IXUSR | S_IRWXG | S_IRWXO); | |
| return 0; | |
| } | |
| static int handle_req(struct storage_msg* msg, const void* req, size_t req_len) { | |
| int rc; | |
| if ((msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) && (msg->cmd != STORAGE_RPMB_SEND)) { | |
| /* | |
| * handling post commit messages on non rpmb commands are not | |
| * implemented as there is no use case for this yet. | |
| */ | |
| ALOGE("cmd 0x%x: post commit option is not implemented\n", msg->cmd); | |
| msg->result = STORAGE_ERR_UNIMPLEMENTED; | |
| return ipc_respond(msg, NULL, 0); | |
| } | |
| if (msg->flags & STORAGE_MSG_FLAG_PRE_COMMIT) { | |
| rc = storage_sync_checkpoint(); | |
| if (rc < 0) { | |
| msg->result = STORAGE_ERR_GENERIC; | |
| return ipc_respond(msg, NULL, 0); | |
| } | |
| } | |
| switch (msg->cmd) { | |
| case STORAGE_FILE_DELETE: | |
| rc = storage_file_delete(msg, req, req_len); | |
| break; | |
| case STORAGE_FILE_OPEN: | |
| rc = storage_file_open(msg, req, req_len); | |
| break; | |
| case STORAGE_FILE_CLOSE: | |
| rc = storage_file_close(msg, req, req_len); | |
| break; | |
| case STORAGE_FILE_WRITE: | |
| rc = storage_file_write(msg, req, req_len); | |
| break; | |
| case STORAGE_FILE_READ: | |
| rc = storage_file_read(msg, req, req_len); | |
| break; | |
| case STORAGE_FILE_GET_SIZE: | |
| rc = storage_file_get_size(msg, req, req_len); | |
| break; | |
| case STORAGE_FILE_SET_SIZE: | |
| rc = storage_file_set_size(msg, req, req_len); | |
| break; | |
| case STORAGE_RPMB_SEND: | |
| rc = rpmb_send(msg, req, req_len); | |
| break; | |
| default: | |
| ALOGE("unhandled command 0x%x\n", msg->cmd); | |
| msg->result = STORAGE_ERR_UNIMPLEMENTED; | |
| rc = 1; | |
| } | |
| if (rc > 0) { | |
| /* still need to send response */ | |
| rc = ipc_respond(msg, NULL, 0); | |
| } | |
| return rc; | |
| } | |
| static int proxy_loop(void) { | |
| ssize_t rc; | |
| struct storage_msg msg; | |
| /* enter main message handling loop */ | |
| while (true) { | |
| /* get incoming message */ | |
| rc = ipc_get_msg(&msg, req_buffer, REQ_BUFFER_SIZE); | |
| if (rc < 0) return rc; | |
| /* handle request */ | |
| req_buffer[rc] = 0; /* force zero termination */ | |
| rc = handle_req(&msg, req_buffer, rc); | |
| if (rc) return rc; | |
| } | |
| return 0; | |
| } | |
| static void parse_args(int argc, char* argv[]) { | |
| int opt; | |
| int oidx = 0; | |
| while ((opt = getopt_long(argc, argv, _sopts, _lopts, &oidx)) != -1) { | |
| switch (opt) { | |
| case 'd': | |
| trusty_devname = strdup(optarg); | |
| break; | |
| case 'p': | |
| ss_data_root = strdup(optarg); | |
| break; | |
| case 'r': | |
| rpmb_devname = strdup(optarg); | |
| break; | |
| case 't': | |
| dev_type = parse_dev_type(optarg); | |
| if (dev_type == UNKNOWN_RPMB) { | |
| ALOGE("Unrecognized dev type: %s\n", optarg); | |
| show_usage_and_exit(EXIT_FAILURE); | |
| } | |
| break; | |
| default: | |
| ALOGE("unrecognized option (%c):\n", opt); | |
| show_usage_and_exit(EXIT_FAILURE); | |
| } | |
| } | |
| if (ss_data_root == NULL || trusty_devname == NULL || rpmb_devname == NULL) { | |
| ALOGE("missing required argument(s)\n"); | |
| show_usage_and_exit(EXIT_FAILURE); | |
| } | |
| ALOGI("starting storageproxyd\n"); | |
| ALOGI("storage data root: %s\n", ss_data_root); | |
| ALOGI("trusty dev: %s\n", trusty_devname); | |
| ALOGI("rpmb dev: %s\n", rpmb_devname); | |
| } | |
| int main(int argc, char* argv[]) { | |
| int rc; | |
| /* drop privileges */ | |
| if (drop_privs() < 0) return EXIT_FAILURE; | |
| /* parse arguments */ | |
| parse_args(argc, argv); | |
| /* initialize secure storage directory */ | |
| rc = storage_init(ss_data_root); | |
| if (rc < 0) return EXIT_FAILURE; | |
| /* open rpmb device */ | |
| rc = rpmb_open(rpmb_devname, dev_type); | |
| if (rc < 0) return EXIT_FAILURE; | |
| /* connect to Trusty secure storage server */ | |
| rc = ipc_connect(trusty_devname, ss_srv_name); | |
| if (rc < 0) return EXIT_FAILURE; | |
| /* enter main loop */ | |
| rc = proxy_loop(); | |
| ALOGE("exiting proxy loop with status (%d)\n", rc); | |
| ipc_disconnect(); | |
| rpmb_close(); | |
| return (rc < 0) ? EXIT_FAILURE : EXIT_SUCCESS; | |
| } |