1 /*
2 * Copyright (C) 2018 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 "rpmb_dev.h"
18
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <getopt.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/socket.h>
26 #include <sys/stat.h>
27 #include <sys/un.h>
28 #include <unistd.h>
29
30 /*
31 * Receives data until one of the following is true:
32 * - The buffer is full (return will be len)
33 * - The connection closed (return > 0, < len)
34 * - An error occurred (return will be the negative error code from recv)
35 */
recv_until(int sock,void * dest,size_t len)36 ssize_t recv_until(int sock, void* dest, size_t len) {
37 size_t bytes_recvd = 0;
38 while (bytes_recvd < len) {
39 ssize_t ret = recv(sock, dest, len - bytes_recvd, 0);
40 if (ret < 0) {
41 return ret;
42 }
43 dest += ret;
44 bytes_recvd += ret;
45 if (ret == 0) {
46 break;
47 }
48 }
49 return bytes_recvd;
50 }
51
52 /*
53 * Handles an incoming connection to the rpmb daemon.
54 * Returns 0 if the client disconnects without violating the protocol.
55 * Returns a negative value if we terminated the connection abnormally.
56 *
57 * Arguments:
58 * conn_sock - an fd to send/recv on
59 * s - an initialized rpmb device
60 */
handle_conn(struct rpmb_dev_state * s,int conn_sock)61 int handle_conn(struct rpmb_dev_state* s, int conn_sock) {
62 int ret;
63
64 while (true) {
65 memset(s->res, 0, sizeof(s->res));
66 ret = recv_until(conn_sock, &s->res_count, sizeof(s->res_count));
67
68 /*
69 * Disconnected while not in the middle of anything.
70 */
71 if (ret <= 0) {
72 return 0;
73 }
74
75 if (s->res_count > MAX_PACKET_COUNT) {
76 fprintf(stderr, "rpmb_dev: Receive count too large: %d\n",
77 s->res_count);
78 return -1;
79 }
80 if (s->res_count <= 0) {
81 fprintf(stderr, "rpmb_dev: Receive count too small: %d\n",
82 s->res_count);
83 return -1;
84 }
85
86 ret = recv_until(conn_sock, &s->cmd_count, sizeof(s->cmd_count));
87 if (ret != sizeof(s->cmd_count)) {
88 fprintf(stderr, "rpmb_dev: Failed to read cmd_count");
89 return -1;
90 }
91
92 if (s->cmd_count == 0) {
93 fprintf(stderr, "rpmb_dev: Must contain at least one command\n");
94 return -1;
95 }
96
97 if (s->cmd_count > MAX_PACKET_COUNT) {
98 fprintf(stderr, "rpmb_dev: Command count is too large\n");
99 return -1;
100 }
101
102 size_t cmd_size = s->cmd_count * sizeof(s->cmd[0]);
103 ret = recv_until(conn_sock, s->cmd, cmd_size);
104 if (ret != (int)cmd_size) {
105 fprintf(stderr,
106 "rpmb_dev: Failed to read command: "
107 "cmd_size: %zu ret: %d, %s\n",
108 cmd_size, ret, strerror(errno));
109 return -1;
110 }
111
112 rpmb_dev_process_cmd(s);
113
114 size_t resp_size = sizeof(s->res[0]) * s->res_count;
115 ret = send(conn_sock, s->res, resp_size, 0);
116 if (ret != (int)resp_size) {
117 fprintf(stderr, "rpmb_dev: Failed to send response: %d, %s\n", ret,
118 strerror(errno));
119 return -1;
120 }
121 }
122 }
123
usage(const char * argv0)124 void usage(const char* argv0) {
125 fprintf(stderr, "Usage: %s [-d|--dev] <datafile> [--sock] <socket_path>\n",
126 argv0);
127 fprintf(stderr,
128 "or: %s [-d|--dev] <datafile> [--size <size>] [--key key]\n",
129 argv0);
130 }
131
main(int argc,char ** argv)132 int main(int argc, char** argv) {
133 struct rpmb_dev_state s = {0};
134 int ret;
135 int cmdres_sock;
136 struct sockaddr_un cmdres_sockaddr;
137 const char* data_file_name = NULL;
138 const char* socket_path = NULL;
139 int open_flags;
140 int init = false;
141
142 struct option long_options[] = {{"size", required_argument, 0, 0},
143 {"key", required_argument, 0, 0},
144 {"sock", required_argument, 0, 0},
145 {"dev", required_argument, 0, 'd'},
146 {"init", no_argument, &init, true},
147 {"verbose", no_argument, &verbose, true},
148 {0, 0, 0, 0}};
149
150 memset(&s.header, 0, sizeof(s.header));
151
152 while (1) {
153 int c;
154 int option_index = 0;
155 c = getopt_long(argc, argv, "d:", long_options, &option_index);
156 if (c == -1) {
157 break;
158 }
159
160 switch (c) {
161 /* long args */
162 case 0:
163 switch (option_index) {
164 /* size */
165 case 0:
166 s.header.max_block = atoi(optarg) - 1;
167 break;
168 /* key */
169 case 1:
170 for (size_t i = 0; i < sizeof(s.header.key.byte); i++) {
171 if (!optarg) {
172 break;
173 }
174 s.header.key.byte[i] = strtol(optarg, &optarg, 16);
175 s.header.key_programmed = 1;
176 }
177 break;
178 /* sock */
179 case 2:
180 socket_path = optarg;
181 break;
182 }
183 break;
184 /* dev */
185 case 'd':
186 data_file_name = optarg;
187 break;
188 default:
189 usage(argv[0]);
190 return EXIT_FAILURE;
191 }
192 }
193
194 /*
195 * We always need a data file, and at exactly one of --init or --sock
196 * must be specified.
197 */
198 if (!data_file_name || (!init == !socket_path)) {
199 usage(argv[0]);
200 return EXIT_FAILURE;
201 }
202
203 open_flags = O_RDWR;
204 if (init) {
205 open_flags |= O_CREAT | O_TRUNC;
206 }
207 s.data_fd = open(data_file_name, open_flags, S_IWUSR | S_IRUSR);
208 if (s.data_fd < 0) {
209 fprintf(stderr, "rpmb_dev: Failed to open rpmb data file, %s: %s\n",
210 data_file_name, strerror(errno));
211 return EXIT_FAILURE;
212 }
213
214 if (init) {
215 /* Create new rpmb data file */
216 if (s.header.max_block == 0) {
217 s.header.max_block = 512 - 1;
218 }
219 ret = write(s.data_fd, &s.header, sizeof(s.header));
220 if (ret != sizeof(s.header)) {
221 fprintf(stderr,
222 "rpmb_dev: Failed to write rpmb data file: %d, %s\n", ret,
223 strerror(errno));
224 return EXIT_FAILURE;
225 }
226 return EXIT_SUCCESS;
227 }
228
229 ret = read(s.data_fd, &s.header, sizeof(s.header));
230 if (ret != sizeof(s.header)) {
231 fprintf(stderr, "rpmb_dev: Failed to read rpmb data file: %d, %s\n",
232 ret, strerror(errno));
233 return EXIT_FAILURE;
234 }
235
236 cmdres_sock = socket(AF_UNIX, SOCK_STREAM, 0);
237 if (cmdres_sock < 0) {
238 fprintf(stderr,
239 "rpmb_dev: Failed to create command/response socket: %s\n",
240 strerror(errno));
241 return EXIT_FAILURE;
242 }
243
244 cmdres_sockaddr.sun_family = AF_UNIX;
245 strncpy(cmdres_sockaddr.sun_path, socket_path,
246 sizeof(cmdres_sockaddr.sun_path));
247
248 ret = bind(cmdres_sock, (struct sockaddr*)&cmdres_sockaddr,
249 sizeof(struct sockaddr_un));
250 if (ret < 0) {
251 fprintf(stderr,
252 "rpmb_dev: Failed to bind command/response socket: %s: %s\n",
253 socket_path, strerror(errno));
254 return EXIT_FAILURE;
255 }
256
257 ret = listen(cmdres_sock, 1);
258 if (ret < 0) {
259 fprintf(stderr,
260 "rpmb_dev: Failed to listen on command/response socket: %s\n",
261 strerror(errno));
262 return EXIT_FAILURE;
263 }
264
265 while (true) {
266 int conn_sock = accept(cmdres_sock, NULL, NULL);
267 if (conn_sock < 0) {
268 fprintf(stderr, "rpmb_dev: Could not accept connection: %s\n",
269 strerror(errno));
270 return EXIT_FAILURE;
271 }
272 ret = handle_conn(&s, conn_sock);
273 close(conn_sock);
274 if (ret) {
275 fprintf(stderr, "rpmb_dev: Connection terminated: %d", ret);
276 }
277 }
278 }
279