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. 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. 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|). 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. 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 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