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