| Felipe Leme | f029297 | 2016-11-22 13:57:05 -0800 | [diff] [blame] | 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 | #define LOG_TAG "dumpstate" | 
 | 18 |  | 
 | 19 | #include "DumpstateInternal.h" | 
 | 20 |  | 
| Dan Albert | 3c9c33a | 2017-10-11 12:42:46 -0700 | [diff] [blame] | 21 | #include <errno.h> | 
| Yifan Hong | dc3cb64 | 2017-07-26 10:47:53 -0700 | [diff] [blame] | 22 | #include <grp.h> | 
| Elliott Hughes | ec616bc | 2018-03-16 15:07:20 -0700 | [diff] [blame] | 23 | #include <poll.h> | 
| Yifan Hong | dc3cb64 | 2017-07-26 10:47:53 -0700 | [diff] [blame] | 24 | #include <pwd.h> | 
| Felipe Leme | f029297 | 2016-11-22 13:57:05 -0800 | [diff] [blame] | 25 | #include <stdint.h> | 
 | 26 | #include <stdio.h> | 
 | 27 | #include <string.h> | 
 | 28 | #include <sys/capability.h> | 
 | 29 | #include <sys/prctl.h> | 
 | 30 | #include <sys/stat.h> | 
 | 31 | #include <sys/types.h> | 
| Jiyong Park | 522ae9a | 2017-06-23 21:23:16 +0900 | [diff] [blame] | 32 | #include <unistd.h> | 
| Felipe Leme | f029297 | 2016-11-22 13:57:05 -0800 | [diff] [blame] | 33 |  | 
 | 34 | #include <cstdint> | 
 | 35 | #include <string> | 
 | 36 | #include <vector> | 
 | 37 |  | 
 | 38 | #include <android-base/file.h> | 
| Elliott Hughes | ec616bc | 2018-03-16 15:07:20 -0700 | [diff] [blame] | 39 | #include <android-base/macros.h> | 
| Jiyong Park | 522ae9a | 2017-06-23 21:23:16 +0900 | [diff] [blame] | 40 | #include <log/log.h> | 
| Felipe Leme | f029297 | 2016-11-22 13:57:05 -0800 | [diff] [blame] | 41 |  | 
 | 42 | uint64_t Nanotime() { | 
 | 43 |     timespec ts; | 
 | 44 |     clock_gettime(CLOCK_MONOTONIC, &ts); | 
 | 45 |     return static_cast<uint64_t>(ts.tv_sec * NANOS_PER_SEC + ts.tv_nsec); | 
 | 46 | } | 
 | 47 |  | 
 | 48 | // Switches to non-root user and group. | 
 | 49 | bool DropRootUser() { | 
| Yifan Hong | dc3cb64 | 2017-07-26 10:47:53 -0700 | [diff] [blame] | 50 |     struct group* grp = getgrnam("shell"); | 
 | 51 |     gid_t shell_gid = grp != nullptr ? grp->gr_gid : 0; | 
 | 52 |     struct passwd* pwd = getpwnam("shell"); | 
 | 53 |     uid_t shell_uid = pwd != nullptr ? pwd->pw_uid : 0; | 
 | 54 |  | 
 | 55 |     if (!shell_gid || !shell_uid) { | 
 | 56 |         MYLOGE("Unable to get AID_SHELL: %s\n", strerror(errno)); | 
 | 57 |         return false; | 
 | 58 |     } | 
 | 59 |  | 
 | 60 |     if (getgid() == shell_gid && getuid() == shell_uid) { | 
| Felipe Leme | f029297 | 2016-11-22 13:57:05 -0800 | [diff] [blame] | 61 |         MYLOGD("drop_root_user(): already running as Shell\n"); | 
 | 62 |         return true; | 
 | 63 |     } | 
 | 64 |     /* ensure we will keep capabilities when we drop root */ | 
 | 65 |     if (prctl(PR_SET_KEEPCAPS, 1) < 0) { | 
 | 66 |         MYLOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno)); | 
 | 67 |         return false; | 
 | 68 |     } | 
 | 69 |  | 
| Yifan Hong | dc3cb64 | 2017-07-26 10:47:53 -0700 | [diff] [blame] | 70 |     static const std::vector<std::string> group_names{ | 
| Sahana Rao | f35ed43 | 2019-07-12 10:47:52 +0100 | [diff] [blame] | 71 |         "log", "sdcard_r", "sdcard_rw", "mount", "inet", "net_bw_stats", | 
 | 72 |             "readproc", "bluetooth", "wakelock"}; | 
| Yifan Hong | dc3cb64 | 2017-07-26 10:47:53 -0700 | [diff] [blame] | 73 |     std::vector<gid_t> groups(group_names.size(), 0); | 
 | 74 |     for (size_t i = 0; i < group_names.size(); ++i) { | 
 | 75 |         grp = getgrnam(group_names[i].c_str()); | 
 | 76 |         groups[i] = grp != nullptr ? grp->gr_gid : 0; | 
 | 77 |         if (groups[i] == 0) { | 
 | 78 |             MYLOGE("Unable to get required gid '%s': %s\n", group_names[i].c_str(), | 
 | 79 |                    strerror(errno)); | 
 | 80 |             return false; | 
 | 81 |         } | 
 | 82 |     } | 
 | 83 |  | 
 | 84 |     if (setgroups(groups.size(), groups.data()) != 0) { | 
| Felipe Leme | f029297 | 2016-11-22 13:57:05 -0800 | [diff] [blame] | 85 |         MYLOGE("Unable to setgroups, aborting: %s\n", strerror(errno)); | 
 | 86 |         return false; | 
 | 87 |     } | 
| Yifan Hong | dc3cb64 | 2017-07-26 10:47:53 -0700 | [diff] [blame] | 88 |     if (setgid(shell_gid) != 0) { | 
| Felipe Leme | f029297 | 2016-11-22 13:57:05 -0800 | [diff] [blame] | 89 |         MYLOGE("Unable to setgid, aborting: %s\n", strerror(errno)); | 
 | 90 |         return false; | 
 | 91 |     } | 
| Yifan Hong | dc3cb64 | 2017-07-26 10:47:53 -0700 | [diff] [blame] | 92 |     if (setuid(shell_uid) != 0) { | 
| Felipe Leme | f029297 | 2016-11-22 13:57:05 -0800 | [diff] [blame] | 93 |         MYLOGE("Unable to setuid, aborting: %s\n", strerror(errno)); | 
 | 94 |         return false; | 
 | 95 |     } | 
 | 96 |  | 
 | 97 |     struct __user_cap_header_struct capheader; | 
 | 98 |     struct __user_cap_data_struct capdata[2]; | 
 | 99 |     memset(&capheader, 0, sizeof(capheader)); | 
 | 100 |     memset(&capdata, 0, sizeof(capdata)); | 
 | 101 |     capheader.version = _LINUX_CAPABILITY_VERSION_3; | 
 | 102 |     capheader.pid = 0; | 
 | 103 |  | 
| Luis Hector Chavez | d7feeaa | 2018-03-14 12:47:25 -0700 | [diff] [blame] | 104 |     if (capget(&capheader, &capdata[0]) != 0) { | 
 | 105 |         MYLOGE("capget failed: %s\n", strerror(errno)); | 
 | 106 |         return false; | 
 | 107 |     } | 
| Felipe Leme | f029297 | 2016-11-22 13:57:05 -0800 | [diff] [blame] | 108 |  | 
| Luis Hector Chavez | d7feeaa | 2018-03-14 12:47:25 -0700 | [diff] [blame] | 109 |     const uint32_t cap_syslog_mask = CAP_TO_MASK(CAP_SYSLOG); | 
 | 110 |     const uint32_t cap_syslog_index = CAP_TO_INDEX(CAP_SYSLOG); | 
 | 111 |     bool has_cap_syslog = (capdata[cap_syslog_index].effective & cap_syslog_mask) != 0; | 
 | 112 |  | 
 | 113 |     memset(&capdata, 0, sizeof(capdata)); | 
 | 114 |     if (has_cap_syslog) { | 
 | 115 |         // Only attempt to keep CAP_SYSLOG if it was present to begin with. | 
 | 116 |         capdata[cap_syslog_index].permitted |= cap_syslog_mask; | 
 | 117 |         capdata[cap_syslog_index].effective |= cap_syslog_mask; | 
 | 118 |     } | 
 | 119 |  | 
| Sahana Rao | f35ed43 | 2019-07-12 10:47:52 +0100 | [diff] [blame] | 120 |     const uint32_t cap_block_suspend_mask = CAP_TO_MASK(CAP_BLOCK_SUSPEND); | 
 | 121 |     const uint32_t cap_block_suspend_index = CAP_TO_INDEX(CAP_BLOCK_SUSPEND); | 
 | 122 |     capdata[cap_block_suspend_index].permitted |= cap_block_suspend_mask; | 
 | 123 |     capdata[cap_block_suspend_index].effective |= cap_block_suspend_mask; | 
 | 124 |  | 
| Luis Hector Chavez | d7feeaa | 2018-03-14 12:47:25 -0700 | [diff] [blame] | 125 |     if (capset(&capheader, &capdata[0]) != 0) { | 
 | 126 |         MYLOGE("capset({%#x, %#x}) failed: %s\n", capdata[0].effective, | 
 | 127 |                capdata[1].effective, strerror(errno)); | 
| Felipe Leme | f029297 | 2016-11-22 13:57:05 -0800 | [diff] [blame] | 128 |         return false; | 
 | 129 |     } | 
 | 130 |  | 
 | 131 |     return true; | 
 | 132 | } | 
 | 133 |  | 
 | 134 | int DumpFileFromFdToFd(const std::string& title, const std::string& path_string, int fd, int out_fd, | 
 | 135 |                        bool dry_run) { | 
 | 136 |     const char* path = path_string.c_str(); | 
 | 137 |     if (!title.empty()) { | 
 | 138 |         dprintf(out_fd, "------ %s (%s", title.c_str(), path); | 
 | 139 |  | 
 | 140 |         struct stat st; | 
 | 141 |         // Only show the modification time of non-device files. | 
 | 142 |         size_t path_len = strlen(path); | 
 | 143 |         if ((path_len < 6 || memcmp(path, "/proc/", 6)) && | 
 | 144 |             (path_len < 5 || memcmp(path, "/sys/", 5)) && | 
 | 145 |             (path_len < 3 || memcmp(path, "/d/", 3)) && !fstat(fd, &st)) { | 
 | 146 |             char stamp[80]; | 
 | 147 |             time_t mtime = st.st_mtime; | 
 | 148 |             strftime(stamp, sizeof(stamp), "%Y-%m-%d %H:%M:%S", localtime(&mtime)); | 
 | 149 |             dprintf(out_fd, ": %s", stamp); | 
 | 150 |         } | 
 | 151 |         dprintf(out_fd, ") ------\n"); | 
 | 152 |         fsync(out_fd); | 
 | 153 |     } | 
 | 154 |     if (dry_run) { | 
 | 155 |         if (out_fd != STDOUT_FILENO) { | 
 | 156 |             // There is no title, but we should still print a dry-run message | 
 | 157 |             dprintf(out_fd, "%s: skipped on dry run\n", path); | 
 | 158 |         } else if (!title.empty()) { | 
 | 159 |             dprintf(out_fd, "\t(skipped on dry run)\n"); | 
 | 160 |         } | 
 | 161 |         fsync(out_fd); | 
 | 162 |         return 0; | 
 | 163 |     } | 
 | 164 |     bool newline = false; | 
| Felipe Leme | f029297 | 2016-11-22 13:57:05 -0800 | [diff] [blame] | 165 |     while (true) { | 
| Elliott Hughes | ec616bc | 2018-03-16 15:07:20 -0700 | [diff] [blame] | 166 |         uint64_t start_time = Nanotime(); | 
 | 167 |         pollfd fds[] = { { .fd = fd, .events = POLLIN } }; | 
 | 168 |         int ret = TEMP_FAILURE_RETRY(poll(fds, arraysize(fds), 30 * 1000)); | 
| Felipe Leme | f029297 | 2016-11-22 13:57:05 -0800 | [diff] [blame] | 169 |         if (ret == -1) { | 
| Elliott Hughes | ec616bc | 2018-03-16 15:07:20 -0700 | [diff] [blame] | 170 |             dprintf(out_fd, "*** %s: poll failed: %s\n", path, strerror(errno)); | 
| Felipe Leme | f029297 | 2016-11-22 13:57:05 -0800 | [diff] [blame] | 171 |             newline = true; | 
 | 172 |             break; | 
 | 173 |         } else if (ret == 0) { | 
| Elliott Hughes | ec616bc | 2018-03-16 15:07:20 -0700 | [diff] [blame] | 174 |             uint64_t elapsed = Nanotime() - start_time; | 
| Felipe Leme | f029297 | 2016-11-22 13:57:05 -0800 | [diff] [blame] | 175 |             dprintf(out_fd, "*** %s: Timed out after %.3fs\n", path, (float)elapsed / NANOS_PER_SEC); | 
 | 176 |             newline = true; | 
 | 177 |             break; | 
 | 178 |         } else { | 
 | 179 |             char buffer[65536]; | 
 | 180 |             ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer))); | 
 | 181 |             if (bytes_read > 0) { | 
 | 182 |                 android::base::WriteFully(out_fd, buffer, bytes_read); | 
 | 183 |                 newline = (buffer[bytes_read - 1] == '\n'); | 
 | 184 |             } else { | 
 | 185 |                 if (bytes_read == -1) { | 
 | 186 |                     dprintf(out_fd, "*** %s: Failed to read from fd: %s", path, strerror(errno)); | 
 | 187 |                     newline = true; | 
 | 188 |                 } | 
 | 189 |                 break; | 
 | 190 |             } | 
 | 191 |         } | 
 | 192 |     } | 
| Felipe Leme | f029297 | 2016-11-22 13:57:05 -0800 | [diff] [blame] | 193 |  | 
 | 194 |     if (!newline) dprintf(out_fd, "\n"); | 
 | 195 |     if (!title.empty()) dprintf(out_fd, "\n"); | 
 | 196 |     return 0; | 
 | 197 | } |