/* * 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; }