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
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <getopt.h>
20 #include <poll.h>
21 #include <string.h>
22 #include <sys/socket.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26
27 #include <memory>
28
29 #include <android-base/logging.h>
30 #include <cutils/sockets.h>
31 #include <libminijail.h>
32
33 #include <nvram/core/nvram_manager.h>
34 #include <nvram/messages/nvram_messages.h>
35
36 // This is defined in fake_nvram_storage.h
37 void InitStorage(int data_dir_fd);
38
39 namespace {
40
41 // Minijail parameters.
42 constexpr char kNvramUser[] = "nvram";
43 constexpr char kNvramGroup[] = "nvram";
44 constexpr char kNvramSeccompPolicyPath[] =
45 "/system/usr/share/policy/fake-nvram-seccomp.policy";
46
47 // Name of the control socket served by this daemon.
48 constexpr char kNvramControlSocketName[] = "nvram";
49
50 // The default data directory.
51 constexpr char kNvramDataDirectory[] = "/data/misc/fake-nvram/";
52
53 // Connection backlog on control socket.
54 constexpr int kControlSocketBacklog = 20;
55
56 // Maximum number of client sockets supported.
57 constexpr int kMaxClientSockets = 32;
58
59 // Size of the NVRAM message buffer for reading and writing serialized NVRAM
60 // command messages from and to the control socket.
61 constexpr int kNvramMessageBufferSize = 4096;
62
63 // Variables holding command-line flags.
64 const char* g_data_directory_path = kNvramDataDirectory;
65 const char* g_control_socket_name = kNvramControlSocketName;
66
67 // Parses the command line. Returns true if successful.
ParseCommandLine(int argc,char ** argv)68 bool ParseCommandLine(int argc, char** argv) {
69 while (true) {
70 static const struct option options[] = {
71 {"data_directory", required_argument, nullptr, 'd'},
72 {"control_socket", required_argument, nullptr, 's'},
73 };
74
75 int option_index = 0;
76 int c = getopt_long(argc, argv, "", options, &option_index);
77 if (c == -1) {
78 break;
79 }
80
81 switch (c) {
82 case 'd':
83 g_data_directory_path = optarg;
84 break;
85 case 's':
86 g_control_socket_name = optarg;
87 break;
88 default:
89 return false;
90 }
91 }
92
93 return true;
94 }
95
96 // Sets up a restricted environment using minijail and enters it.
InitMinijail()97 bool InitMinijail() {
98 std::unique_ptr<struct minijail, void (*)(struct minijail*)> minijail(
99 minijail_new(), &minijail_destroy);
100 if (minijail_change_user(minijail.get(), kNvramUser) ||
101 minijail_change_group(minijail.get(), kNvramGroup)) {
102 return false;
103 }
104 minijail_use_seccomp_filter(minijail.get());
105 minijail_no_new_privs(minijail.get());
106 minijail_parse_seccomp_filters(minijail.get(), kNvramSeccompPolicyPath);
107 minijail_enter(minijail.get());
108 return true;
109 }
110
111 // Reads a single command from |socket|, decodes the command, executes it on
112 // |nvram_manager|, encodes the response, and writes the reply back to |socket|.
113 // Returns true on success, false on errors (in which case the caller is
114 // expected the close the |socket|).
ProcessCommand(int socket,nvram::NvramManager * nvram_manager)115 bool ProcessCommand(int socket, nvram::NvramManager* nvram_manager) {
116 uint8_t command_buffer[kNvramMessageBufferSize];
117 ssize_t bytes_read =
118 TEMP_FAILURE_RETRY(read(socket, command_buffer, sizeof(command_buffer)));
119 if (bytes_read == 0) {
120 return false;
121 }
122
123 if (bytes_read < 0) {
124 PLOG(ERROR) << "Failed to read command from client socket";
125 return false;
126 }
127
128 nvram::Request request;
129 if (!nvram::Decode(command_buffer, bytes_read, &request)) {
130 LOG(WARNING) << "Failed to decode command request!";
131 return false;
132 }
133
134 nvram::Response response;
135 nvram_manager->Dispatch(request, &response);
136 size_t response_size = sizeof(command_buffer);
137 if (!nvram::Encode(response, command_buffer, &response_size)) {
138 LOG(WARNING) << "Failed to encode command response!";
139 return false;
140 }
141
142 if (TEMP_FAILURE_RETRY(write(socket, command_buffer, response_size)) < 0) {
143 PLOG(ERROR) << "Failed to write response to client socket";
144 return false;
145 }
146
147 return true;
148 }
149
150 // Listens for incoming connections or data, accepts connections and processes
151 // data as needed.
ProcessMessages(int control_socket_fd,nvram::NvramManager * nvram_manager)152 int ProcessMessages(int control_socket_fd, nvram::NvramManager* nvram_manager) {
153 struct pollfd poll_fds[kMaxClientSockets];
154 memset(poll_fds, 0, sizeof(poll_fds));
155 poll_fds[0].fd = control_socket_fd;
156 poll_fds[0].events = POLLIN;
157 poll_fds[0].revents = 0;
158 nfds_t poll_fds_count = 1;
159 while (TEMP_FAILURE_RETRY(poll(poll_fds, poll_fds_count, -1)) >= 0) {
160 if (poll_fds[0].revents & POLLIN) {
161 // Accept a new connection.
162 int client_socket = accept(control_socket_fd, NULL, 0);
163 if (client_socket < 0) {
164 PLOG(ERROR) << "Error accepting connection";
165 return errno;
166 }
167
168 // Add |client_socket| to |poll_fds|.
169 if (poll_fds_count < kMaxClientSockets) {
170 poll_fds[poll_fds_count].fd = client_socket;
171 poll_fds[poll_fds_count].events = POLLIN;
172 poll_fds[poll_fds_count].revents = 0;
173 ++poll_fds_count;
174 } else {
175 LOG(WARNING) << "Too many open client sockets, rejecting connection.";
176 // No need to handle EINTR specially here as bionic filters it out.
177 if (close(client_socket)) {
178 PLOG(ERROR) << "Failed to close connection socket after error";
179 }
180 }
181 }
182
183 // Walk the connection fds backwards. This way, we can remove fds by
184 // replacing the slot with the last array element, which we have processed
185 // already.
186 for (int i = poll_fds_count - 1; i > 0; --i) {
187 if (poll_fds[i].revents & POLLIN) {
188 if (!ProcessCommand(poll_fds[i].fd, nvram_manager)) {
189 // No need to handle EINTR specially here as bionic filters it out.
190 if (close(poll_fds[i].fd)) {
191 PLOG(ERROR) << "Failed to close connection socket after error";
192 }
193 --poll_fds_count;
194 poll_fds[i] = poll_fds[poll_fds_count];
195 }
196 }
197 poll_fds[i].revents = 0;
198 }
199 }
200
201 // poll error.
202 PLOG(ERROR) << "Failed to poll control socket";
203 return errno;
204 };
205
206 } // namespace
207
main(int argc,char ** argv)208 int main(int argc, char** argv) {
209 if (!ParseCommandLine(argc, argv)) {
210 return EINVAL;
211 }
212
213 int control_socket_fd = android_get_control_socket(g_control_socket_name);
214 if (control_socket_fd < 0) {
215 LOG(ERROR) << "Failed to get control socket.";
216 return EINVAL;
217 }
218
219 if (listen(control_socket_fd, kControlSocketBacklog)) {
220 PLOG(ERROR) << "Failed to listen on control socket";
221 return errno;
222 }
223
224 if (!InitMinijail()) {
225 LOG(ERROR) << "Failed to drop privileges.";
226 return -1;
227 }
228
229 int data_dir_fd =
230 TEMP_FAILURE_RETRY(open(g_data_directory_path, O_RDONLY | O_DIRECTORY));
231 if (data_dir_fd < 0) {
232 PLOG(ERROR) << "Failed to open data directory";
233 return errno;
234 }
235
236 InitStorage(data_dir_fd);
237
238 nvram::NvramManager nvram_manager;
239 return ProcessMessages(control_socket_fd, &nvram_manager);
240 }
241