blob: f0b6203578d73d30bbb9a44a27b3d1ef2e08f4be [file] [log] [blame]
Felipe Lemef0292972016-11-22 13:57:05 -08001/*
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
Yifan Hong8ded50c2017-07-26 10:47:53 -070021#include <grp.h>
22#include <pwd.h>
Felipe Lemef0292972016-11-22 13:57:05 -080023#include <stdint.h>
24#include <stdio.h>
25#include <string.h>
26#include <sys/capability.h>
27#include <sys/prctl.h>
28#include <sys/stat.h>
29#include <sys/types.h>
Jiyong Parkb22e65d2017-06-23 21:23:16 +090030#include <unistd.h>
Felipe Lemef0292972016-11-22 13:57:05 -080031
32#include <cstdint>
33#include <string>
34#include <vector>
35
36#include <android-base/file.h>
Jiyong Parkb22e65d2017-06-23 21:23:16 +090037#include <log/log.h>
Felipe Lemef0292972016-11-22 13:57:05 -080038
39uint64_t Nanotime() {
40 timespec ts;
41 clock_gettime(CLOCK_MONOTONIC, &ts);
42 return static_cast<uint64_t>(ts.tv_sec * NANOS_PER_SEC + ts.tv_nsec);
43}
44
45// Switches to non-root user and group.
46bool DropRootUser() {
Yifan Hong8ded50c2017-07-26 10:47:53 -070047 struct group* grp = getgrnam("shell");
48 gid_t shell_gid = grp != nullptr ? grp->gr_gid : 0;
49 struct passwd* pwd = getpwnam("shell");
50 uid_t shell_uid = pwd != nullptr ? pwd->pw_uid : 0;
51
52 if (!shell_gid || !shell_uid) {
53 MYLOGE("Unable to get AID_SHELL: %s\n", strerror(errno));
54 return false;
55 }
56
57 if (getgid() == shell_gid && getuid() == shell_uid) {
Felipe Lemef0292972016-11-22 13:57:05 -080058 MYLOGD("drop_root_user(): already running as Shell\n");
59 return true;
60 }
61 /* ensure we will keep capabilities when we drop root */
62 if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
63 MYLOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno));
64 return false;
65 }
66
Yifan Hong8ded50c2017-07-26 10:47:53 -070067 static const std::vector<std::string> group_names{
68 "log", "sdcard_r", "sdcard_rw", "mount", "inet", "net_bw_stats", "readproc", "bluetooth"};
69 std::vector<gid_t> groups(group_names.size(), 0);
70 for (size_t i = 0; i < group_names.size(); ++i) {
71 grp = getgrnam(group_names[i].c_str());
72 groups[i] = grp != nullptr ? grp->gr_gid : 0;
73 if (groups[i] == 0) {
74 MYLOGE("Unable to get required gid '%s': %s\n", group_names[i].c_str(),
75 strerror(errno));
76 return false;
77 }
78 }
79
80 if (setgroups(groups.size(), groups.data()) != 0) {
Felipe Lemef0292972016-11-22 13:57:05 -080081 MYLOGE("Unable to setgroups, aborting: %s\n", strerror(errno));
82 return false;
83 }
Yifan Hong8ded50c2017-07-26 10:47:53 -070084 if (setgid(shell_gid) != 0) {
Felipe Lemef0292972016-11-22 13:57:05 -080085 MYLOGE("Unable to setgid, aborting: %s\n", strerror(errno));
86 return false;
87 }
Yifan Hong8ded50c2017-07-26 10:47:53 -070088 if (setuid(shell_uid) != 0) {
Felipe Lemef0292972016-11-22 13:57:05 -080089 MYLOGE("Unable to setuid, aborting: %s\n", strerror(errno));
90 return false;
91 }
92
93 struct __user_cap_header_struct capheader;
94 struct __user_cap_data_struct capdata[2];
95 memset(&capheader, 0, sizeof(capheader));
96 memset(&capdata, 0, sizeof(capdata));
97 capheader.version = _LINUX_CAPABILITY_VERSION_3;
98 capheader.pid = 0;
99
Felipe Leme6ae5c4f2017-01-10 14:13:22 -0800100 capdata[CAP_TO_INDEX(CAP_SYSLOG)].permitted = CAP_TO_MASK(CAP_SYSLOG);
101 capdata[CAP_TO_INDEX(CAP_SYSLOG)].effective = CAP_TO_MASK(CAP_SYSLOG);
Felipe Lemef0292972016-11-22 13:57:05 -0800102 capdata[0].inheritable = 0;
103 capdata[1].inheritable = 0;
104
105 if (capset(&capheader, &capdata[0]) < 0) {
106 MYLOGE("capset failed: %s\n", strerror(errno));
107 return false;
108 }
109
110 return true;
111}
112
113int DumpFileFromFdToFd(const std::string& title, const std::string& path_string, int fd, int out_fd,
114 bool dry_run) {
115 const char* path = path_string.c_str();
116 if (!title.empty()) {
117 dprintf(out_fd, "------ %s (%s", title.c_str(), path);
118
119 struct stat st;
120 // Only show the modification time of non-device files.
121 size_t path_len = strlen(path);
122 if ((path_len < 6 || memcmp(path, "/proc/", 6)) &&
123 (path_len < 5 || memcmp(path, "/sys/", 5)) &&
124 (path_len < 3 || memcmp(path, "/d/", 3)) && !fstat(fd, &st)) {
125 char stamp[80];
126 time_t mtime = st.st_mtime;
127 strftime(stamp, sizeof(stamp), "%Y-%m-%d %H:%M:%S", localtime(&mtime));
128 dprintf(out_fd, ": %s", stamp);
129 }
130 dprintf(out_fd, ") ------\n");
131 fsync(out_fd);
132 }
133 if (dry_run) {
134 if (out_fd != STDOUT_FILENO) {
135 // There is no title, but we should still print a dry-run message
136 dprintf(out_fd, "%s: skipped on dry run\n", path);
137 } else if (!title.empty()) {
138 dprintf(out_fd, "\t(skipped on dry run)\n");
139 }
140 fsync(out_fd);
141 return 0;
142 }
143 bool newline = false;
144 fd_set read_set;
145 timeval tm;
146 while (true) {
147 FD_ZERO(&read_set);
148 FD_SET(fd, &read_set);
149 /* Timeout if no data is read for 30 seconds. */
150 tm.tv_sec = 30;
151 tm.tv_usec = 0;
152 uint64_t elapsed = Nanotime();
153 int ret = TEMP_FAILURE_RETRY(select(fd + 1, &read_set, nullptr, nullptr, &tm));
154 if (ret == -1) {
155 dprintf(out_fd, "*** %s: select failed: %s\n", path, strerror(errno));
156 newline = true;
157 break;
158 } else if (ret == 0) {
159 elapsed = Nanotime() - elapsed;
160 dprintf(out_fd, "*** %s: Timed out after %.3fs\n", path, (float)elapsed / NANOS_PER_SEC);
161 newline = true;
162 break;
163 } else {
164 char buffer[65536];
165 ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer)));
166 if (bytes_read > 0) {
167 android::base::WriteFully(out_fd, buffer, bytes_read);
168 newline = (buffer[bytes_read - 1] == '\n');
169 } else {
170 if (bytes_read == -1) {
171 dprintf(out_fd, "*** %s: Failed to read from fd: %s", path, strerror(errno));
172 newline = true;
173 }
174 break;
175 }
176 }
177 }
178 close(fd);
179
180 if (!newline) dprintf(out_fd, "\n");
181 if (!title.empty()) dprintf(out_fd, "\n");
182 return 0;
183}