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