1/* 2 * Copyright (C) 2015 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 "cmd" 18 19#include <utils/Log.h> 20#include <binder/Parcel.h> 21#include <binder/ProcessState.h> 22#include <binder/IResultReceiver.h> 23#include <binder/IServiceManager.h> 24#include <binder/IShellCallback.h> 25#include <binder/TextOutput.h> 26#include <utils/Condition.h> 27#include <utils/Mutex.h> 28#include <utils/Vector.h> 29 30#include <getopt.h> 31#include <stdlib.h> 32#include <stdio.h> 33#include <string.h> 34#include <unistd.h> 35#include <fcntl.h> 36#include <sys/time.h> 37#include <errno.h> 38#include <memory> 39 40#include "selinux/selinux.h" 41#include "selinux/android.h" 42 43#define DEBUG 0 44 45using namespace android; 46 47static int sort_func(const String16* lhs, const String16* rhs) 48{ 49 return lhs->compare(*rhs); 50} 51 52struct SecurityContext_Delete { 53 void operator()(security_context_t p) const { 54 freecon(p); 55 } 56}; 57typedef std::unique_ptr<char[], SecurityContext_Delete> Unique_SecurityContext; 58 59class MyShellCallback : public BnShellCallback 60{ 61public: 62 bool mActive = true; 63 64 virtual int openFile(const String16& path, const String16& seLinuxContext, 65 const String16& mode) { 66 String8 path8(path); 67 char cwd[256]; 68 getcwd(cwd, 256); 69 String8 fullPath(cwd); 70 fullPath.appendPath(path8); 71 if (!mActive) { 72 aerr << "Open attempt after active for: " << fullPath << endl; 73 return -EPERM; 74 } 75#if DEBUG 76 ALOGD("openFile: %s, full=%s", path8.string(), fullPath.string()); 77#endif 78 int flags = 0; 79 bool checkRead = false; 80 bool checkWrite = false; 81 if (mode == String16("w")) { 82 flags = O_WRONLY|O_CREAT|O_TRUNC; 83 checkWrite = true; 84 } else if (mode == String16("w+")) { 85 flags = O_RDWR|O_CREAT|O_TRUNC; 86 checkRead = checkWrite = true; 87 } else if (mode == String16("r")) { 88 flags = O_RDONLY; 89 checkRead = true; 90 } else if (mode == String16("r+")) { 91 flags = O_RDWR; 92 checkRead = checkWrite = true; 93 } else { 94 aerr << "Invalid mode requested: " << mode.string() << endl; 95 return -EINVAL; 96 } 97 int fd = open(fullPath.string(), flags, S_IRWXU|S_IRWXG); 98#if DEBUG 99 ALOGD("openFile: fd=%d", fd); 100#endif 101 if (fd < 0) { 102 return fd; 103 } 104 if (is_selinux_enabled() && seLinuxContext.size() > 0) { 105 String8 seLinuxContext8(seLinuxContext); 106 security_context_t tmp = NULL; 107 getfilecon(fullPath.string(), &tmp); 108 Unique_SecurityContext context(tmp); 109 if (checkWrite) { 110 int accessGranted = selinux_check_access(seLinuxContext8.string(), context.get(), 111 "file", "write", NULL); 112 if (accessGranted != 0) { 113#if DEBUG 114 ALOGD("openFile: failed selinux write check!"); 115#endif 116 close(fd); 117 aerr << "System server has no access to write file context " << context.get() 118 << " (from path " << fullPath.string() << ", context " 119 << seLinuxContext8.string() << ")" << endl; 120 return -EPERM; 121 } 122 } 123 if (checkRead) { 124 int accessGranted = selinux_check_access(seLinuxContext8.string(), context.get(), 125 "file", "read", NULL); 126 if (accessGranted != 0) { 127#if DEBUG 128 ALOGD("openFile: failed selinux read check!"); 129#endif 130 close(fd); 131 aerr << "System server has no access to read file context " << context.get() 132 << " (from path " << fullPath.string() << ", context " 133 << seLinuxContext8.string() << ")" << endl; 134 return -EPERM; 135 } 136 } 137 } 138 return fd; 139 } 140}; 141 142class MyResultReceiver : public BnResultReceiver 143{ 144public: 145 Mutex mMutex; 146 Condition mCondition; 147 bool mHaveResult = false; 148 int32_t mResult = 0; 149 150 virtual void send(int32_t resultCode) { 151 AutoMutex _l(mMutex); 152 mResult = resultCode; 153 mHaveResult = true; 154 mCondition.signal(); 155 } 156 157 int32_t waitForResult() { 158 AutoMutex _l(mMutex); 159 while (!mHaveResult) { 160 mCondition.wait(mMutex); 161 } 162 return mResult; 163 } 164}; 165 166int main(int argc, char* const argv[]) 167{ 168 signal(SIGPIPE, SIG_IGN); 169 sp<ProcessState> proc = ProcessState::self(); 170 // setThreadPoolMaxThreadCount(0) actually tells the kernel it's 171 // not allowed to spawn any additional threads, but we still spawn 172 // a binder thread from userspace when we call startThreadPool(). 173 // This is safe because we only have 2 callbacks, neither of which 174 // block. 175 // See b/36066697 for rationale 176 proc->setThreadPoolMaxThreadCount(0); 177 proc->startThreadPool(); 178 179#if DEBUG 180 ALOGD("cmd: starting"); 181#endif 182 sp<IServiceManager> sm = defaultServiceManager(); 183 fflush(stdout); 184 if (sm == NULL) { 185 ALOGW("Unable to get default service manager!"); 186 aerr << "cmd: Unable to get default service manager!" << endl; 187 return 20; 188 } 189 190 if (argc == 1) { 191 aerr << "cmd: No service specified; use -l to list all services" << endl; 192 return 20; 193 } 194 195 if ((argc == 2) && (strcmp(argv[1], "-l") == 0)) { 196 Vector<String16> services = sm->listServices(); 197 services.sort(sort_func); 198 aout << "Currently running services:" << endl; 199 200 for (size_t i=0; i<services.size(); i++) { 201 sp<IBinder> service = sm->checkService(services[i]); 202 if (service != NULL) { 203 aout << " " << services[i] << endl; 204 } 205 } 206 return 0; 207 } 208 209 Vector<String16> args; 210 for (int i=2; i<argc; i++) { 211 args.add(String16(argv[i])); 212 } 213 String16 cmd = String16(argv[1]); 214 sp<IBinder> service = sm->checkService(cmd); 215 if (service == NULL) { 216 ALOGW("Can't find service %s", argv[1]); 217 aerr << "cmd: Can't find service: " << argv[1] << endl; 218 return 20; 219 } 220 221 sp<MyShellCallback> cb = new MyShellCallback(); 222 sp<MyResultReceiver> result = new MyResultReceiver(); 223 224#if DEBUG 225 ALOGD("cmd: Invoking %s in=%d, out=%d, err=%d", argv[1], STDIN_FILENO, STDOUT_FILENO, 226 STDERR_FILENO); 227#endif 228 229 // TODO: block until a result is returned to MyResultReceiver. 230 status_t err = IBinder::shellCommand(service, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, args, 231 cb, result); 232 if (err < 0) { 233 const char* errstr; 234 switch (err) { 235 case BAD_TYPE: errstr = "Bad type"; break; 236 case FAILED_TRANSACTION: errstr = "Failed transaction"; break; 237 case FDS_NOT_ALLOWED: errstr = "File descriptors not allowed"; break; 238 case UNEXPECTED_NULL: errstr = "Unexpected null"; break; 239 default: errstr = strerror(-err); break; 240 } 241 ALOGW("Failure calling service %s: %s (%d)", argv[1], errstr, -err); 242 aout << "cmd: Failure calling service " << argv[1] << ": " << errstr << " (" 243 << (-err) << ")" << endl; 244 return err; 245 } 246 247 cb->mActive = false; 248 status_t res = result->waitForResult(); 249#if DEBUG 250 ALOGD("result=%d", (int)res); 251#endif 252 return res; 253} 254