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 <string.h>
21#include <stdlib.h>
22#include <sys/capability.h>
23#include <sys/prctl.h>
24#include <sys/stat.h>
25#include <unistd.h>
26
27#include <private/android_filesystem_config.h>
28
29#include "ipc.h"
30#include "log.h"
31#include "rpmb.h"
32#include "storage.h"
33
34#define REQ_BUFFER_SIZE 4096
35static uint8_t req_buffer[REQ_BUFFER_SIZE + 1];
36
37static const char *ss_data_root;
38static const char *trusty_devname;
39static const char *rpmb_devname;
40static const char *ss_srv_name = STORAGE_DISK_PROXY_PORT;
41
42static const char *_sopts = "hp:d:r:";
43static const struct option _lopts[] =  {
44    {"help",       no_argument,       NULL, 'h'},
45    {"trusty_dev", required_argument, NULL, 'd'},
46    {"data_path",  required_argument, NULL, 'p'},
47    {"rpmb_dev",   required_argument, NULL, 'r'},
48    {0, 0, 0, 0}
49};
50
51static void show_usage_and_exit(int code)
52{
53    ALOGE("usage: storageproxyd -d <trusty_dev> -p <data_path> -r <rpmb_dev>\n");
54    exit(code);
55}
56
57static int drop_privs(void)
58{
59    struct __user_cap_header_struct capheader;
60    struct __user_cap_data_struct capdata[2];
61
62    if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
63        return -1;
64    }
65
66    /*
67     * ensure we're running as the system user
68     */
69    if (setgid(AID_SYSTEM) != 0) {
70        return -1;
71    }
72
73    if (setuid(AID_SYSTEM) != 0) {
74        return -1;
75    }
76
77    /*
78     * drop all capabilities except SYS_RAWIO
79     */
80    memset(&capheader, 0, sizeof(capheader));
81    memset(&capdata, 0, sizeof(capdata));
82    capheader.version = _LINUX_CAPABILITY_VERSION_3;
83    capheader.pid = 0;
84
85    capdata[CAP_TO_INDEX(CAP_SYS_RAWIO)].permitted = CAP_TO_MASK(CAP_SYS_RAWIO);
86    capdata[CAP_TO_INDEX(CAP_SYS_RAWIO)].effective = CAP_TO_MASK(CAP_SYS_RAWIO);
87
88    if (capset(&capheader, &capdata[0]) < 0) {
89        return -1;
90    }
91
92    /* no-execute for user, no access for group and other */
93    umask(S_IXUSR | S_IRWXG | S_IRWXO);
94
95    return 0;
96}
97
98static int handle_req(struct storage_msg *msg, const void *req, size_t req_len)
99{
100    int rc;
101
102    if ((msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) &&
103        (msg->cmd != STORAGE_RPMB_SEND)) {
104        /*
105         * handling post commit messages on non rpmb commands are not
106         * implemented as there is no use case for this yet.
107         */
108        ALOGE("cmd 0x%x: post commit option is not implemented\n", msg->cmd);
109        msg->result = STORAGE_ERR_UNIMPLEMENTED;
110        return ipc_respond(msg, NULL, 0);
111    }
112
113    if (msg->flags & STORAGE_MSG_FLAG_PRE_COMMIT) {
114        rc = storage_sync_checkpoint();
115        if (rc < 0) {
116            msg->result = STORAGE_ERR_GENERIC;
117            return ipc_respond(msg, NULL, 0);
118        }
119    }
120
121    switch (msg->cmd) {
122    case STORAGE_FILE_DELETE:
123        rc = storage_file_delete(msg, req, req_len);
124        break;
125
126    case STORAGE_FILE_OPEN:
127        rc = storage_file_open(msg, req, req_len);
128        break;
129
130    case STORAGE_FILE_CLOSE:
131        rc = storage_file_close(msg, req, req_len);
132        break;
133
134    case STORAGE_FILE_WRITE:
135        rc = storage_file_write(msg, req, req_len);
136        break;
137
138    case STORAGE_FILE_READ:
139        rc = storage_file_read(msg, req, req_len);
140        break;
141
142    case STORAGE_FILE_GET_SIZE:
143        rc = storage_file_get_size(msg, req, req_len);
144        break;
145
146    case STORAGE_FILE_SET_SIZE:
147        rc = storage_file_set_size(msg, req, req_len);
148        break;
149
150    case STORAGE_RPMB_SEND:
151        rc = rpmb_send(msg, req, req_len);
152        break;
153
154    default:
155        ALOGE("unhandled command 0x%x\n", msg->cmd);
156        msg->result = STORAGE_ERR_UNIMPLEMENTED;
157        rc = 1;
158    }
159
160    if (rc > 0) {
161        /* still need to send response */
162        rc = ipc_respond(msg, NULL, 0);
163    }
164    return rc;
165}
166
167static int proxy_loop(void)
168{
169    ssize_t rc;
170    struct storage_msg msg;
171
172    /* enter main message handling loop */
173    while (true) {
174
175        /* get incoming message */
176        rc = ipc_get_msg(&msg, req_buffer, REQ_BUFFER_SIZE);
177        if (rc < 0)
178            return rc;
179
180        /* handle request */
181        req_buffer[rc] = 0; /* force zero termination */
182        rc = handle_req(&msg, req_buffer, rc);
183        if (rc)
184            return rc;
185    }
186
187    return 0;
188}
189
190static void parse_args(int argc, char *argv[])
191{
192    int opt;
193    int oidx = 0;
194
195    while ((opt = getopt_long(argc, argv, _sopts, _lopts, &oidx)) != -1) {
196        switch (opt) {
197
198        case 'd':
199            trusty_devname = strdup(optarg);
200            break;
201
202        case 'p':
203            ss_data_root = strdup(optarg);
204            break;
205
206        case 'r':
207            rpmb_devname = strdup(optarg);
208            break;
209
210        default:
211            ALOGE("unrecognized option (%c):\n", opt);
212            show_usage_and_exit(EXIT_FAILURE);
213        }
214    }
215
216    if (ss_data_root == NULL ||
217        trusty_devname == NULL ||
218        rpmb_devname == NULL) {
219        ALOGE("missing required argument(s)\n");
220        show_usage_and_exit(EXIT_FAILURE);
221    }
222
223    ALOGI("starting storageproxyd\n");
224    ALOGI("storage data root: %s\n", ss_data_root);
225    ALOGI("trusty dev: %s\n", trusty_devname);
226    ALOGI("rpmb dev: %s\n", rpmb_devname);
227}
228
229int main(int argc, char *argv[])
230{
231    int rc;
232    uint retry_cnt;
233
234    /* drop privileges */
235    if (drop_privs() < 0)
236        return EXIT_FAILURE;
237
238    /* parse arguments */
239    parse_args(argc, argv);
240
241    /* initialize secure storage directory */
242    rc = storage_init(ss_data_root);
243    if (rc < 0)
244        return EXIT_FAILURE;
245
246    /* open rpmb device */
247    rc = rpmb_open(rpmb_devname);
248    if (rc < 0)
249        return EXIT_FAILURE;
250
251    /* connect to Trusty secure storage server */
252    rc = ipc_connect(trusty_devname, ss_srv_name);
253    if (rc < 0)
254        return EXIT_FAILURE;
255
256    /* enter main loop */
257    rc = proxy_loop();
258    ALOGE("exiting proxy loop with status (%d)\n", rc);
259
260    ipc_disconnect();
261    rpmb_close();
262
263    return (rc < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
264}
265