Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 90113773 authored by Steven Moreland's avatar Steven Moreland Committed by Android (Google) Code Review
Browse files

Merge "servicemanager: use libbinder"

parents b50f95d3 3e092daa
Loading
Loading
Loading
Loading
+140 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "Access.h"

#include <android-base/logging.h>
#include <binder/IPCThreadState.h>
#include <log/log_safetynet.h>
#include <selinux/android.h>
#include <selinux/avc.h>

namespace android {

#ifdef VENDORSERVICEMANAGER
constexpr bool kIsVendor = true;
#else
constexpr bool kIsVendor = false;
#endif

static std::string getPidcon(pid_t pid) {
    android_errorWriteLog(0x534e4554, "121035042");

    char* lookup = nullptr;
    if (getpidcon(pid, &lookup) < 0) {
        LOG(ERROR) << "SELinux: getpidcon(pid=" << pid << ") failed to retrieve pid context";
        return "";
    }
    std::string result = lookup;
    freecon(lookup);
    return result;
}

static struct selabel_handle* getSehandle() {
    static struct selabel_handle* gSehandle = nullptr;

    if (gSehandle != nullptr && selinux_status_updated()) {
        selabel_close(gSehandle);
        gSehandle = nullptr;
    }

    if (gSehandle == nullptr) {
        gSehandle = kIsVendor
            ? selinux_android_vendor_service_context_handle()
            : selinux_android_service_context_handle();
    }

    CHECK(gSehandle != nullptr);
    return gSehandle;
}

static int auditCallback(void *data, security_class_t /*cls*/, char *buf, size_t len) {
    const Access::CallingContext* ad = reinterpret_cast<Access::CallingContext*>(data);

    if (!ad) {
        LOG(ERROR) << "No service manager audit data";
        return 0;
    }

    snprintf(buf, len, "service=%s pid=%d uid=%d", ad->name.c_str(), ad->debugPid, ad->uid);
    return 0;
}

Access::Access() {
    union selinux_callback cb;

    cb.func_audit = auditCallback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);

    cb.func_log = kIsVendor ? selinux_vendor_log_callback : selinux_log_callback;
    selinux_set_callback(SELINUX_CB_LOG, cb);

    CHECK(selinux_status_open(true /*fallback*/) >= 0);

    CHECK(getcon(&mThisProcessContext) == 0);
}

Access::~Access() {
    freecon(mThisProcessContext);
}

Access::CallingContext Access::getCallingContext(const std::string& name) {
    IPCThreadState* ipc = IPCThreadState::self();

    const char* callingSid = ipc->getCallingSid();
    pid_t callingPid = ipc->getCallingPid();

    return CallingContext {
        .debugPid = callingPid,
        .uid = ipc->getCallingUid(),
        .sid = callingSid ? std::string(callingSid) : getPidcon(callingPid),
        .name = name,
    };
}

bool Access::canFind(const CallingContext& ctx) {
    return actionAllowedFromLookup(ctx, "find");
}

bool Access::canAdd(const CallingContext& ctx) {
    return actionAllowedFromLookup(ctx, "add");
}

bool Access::canList(const CallingContext& ctx) {
    CHECK(ctx.name == "");

    return actionAllowed(ctx, mThisProcessContext, "list");
}

bool Access::actionAllowed(const CallingContext& sctx, const char* tctx, const char* perm) {
    const char* tclass = "service_manager";

    return 0 == selinux_check_access(sctx.sid.c_str(), tctx, tclass, perm, reinterpret_cast<void*>(const_cast<CallingContext*>((&sctx))));
}

bool Access::actionAllowedFromLookup(const CallingContext& sctx, const char *perm) {
    char *tctx = nullptr;
    if (selabel_lookup(getSehandle(), &tctx, sctx.name.c_str(), 0) != 0) {
        LOG(ERROR) << "SELinux: No match for " << sctx.name << " in service_contexts.\n";
        return false;
    }

    bool allowed = actionAllowed(sctx, tctx, perm);
    freecon(tctx);
    return allowed;
}

}  // android
+59 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include <string>
#include <sys/types.h>

namespace android {

// singleton
class Access {
public:
    Access();
    virtual ~Access();

    Access(const Access&) = delete;
    Access& operator=(const Access&) = delete;
    Access(Access&&) = delete;
    Access& operator=(Access&&) = delete;

    struct CallingContext {
        pid_t debugPid;
        uid_t uid;
        std::string sid;

        // name of the service
        //
        // empty if call is unrelated to service (e.g. list)
        std::string name;
    };

    virtual CallingContext getCallingContext(const std::string& name);

    virtual bool canFind(const CallingContext& ctx);
    virtual bool canAdd(const CallingContext& ctx);
    virtual bool canList(const CallingContext& ctx);

private:
    bool actionAllowed(const CallingContext& sctx, const char* tctx, const char* perm);
    bool actionAllowedFromLookup(const CallingContext& sctx, const char *perm);

    char* mThisProcessContext = nullptr;
};

};
+27 −27
Original line number Diff line number Diff line
cc_defaults {
    name: "servicemanager_flags",
    name: "servicemanager_defaults",

    cflags: [
        "-Wall",
        "-Wextra",
        "-Werror",
    ],
    product_variables: {
        binder32bit: {
            cflags: ["-DBINDER_IPC_32BIT=1"],
        },
    },

    shared_libs: ["liblog"],
}

cc_binary {
    name: "bctest",
    defaults: ["servicemanager_flags"],
    srcs: [
        "bctest.c",
        "binder.c",
        "Access.cpp",
        "ServiceManager.cpp",
    ],

    shared_libs: [
        "libbase",
        "libbinder", // also contains servicemanager_interface
        "libcutils",
        "liblog",
        "libutils",
        "libselinux",
    ],
}

cc_binary {
    name: "servicemanager",
    defaults: ["servicemanager_flags"],
    srcs: [
        "service_manager.c",
        "binder.c",
    ],
    shared_libs: ["libcutils", "libselinux"],
    defaults: ["servicemanager_defaults"],
    init_rc: ["servicemanager.rc"],
    srcs: ["main.cpp"],
}

cc_binary {
    name: "vndservicemanager",
    defaults: ["servicemanager_flags"],
    defaults: ["servicemanager_defaults"],
    init_rc: ["vndservicemanager.rc"],
    vendor: true,
    srcs: [
        "service_manager.c",
        "binder.c",
    ],
    cflags: [
        "-DVENDORSERVICEMANAGER=1",
    ],
    shared_libs: ["libcutils", "libselinux"],
    init_rc: ["vndservicemanager.rc"],
    srcs: ["main.cpp"],
}

cc_test {
    name: "servicemanager_test",
    test_suites: ["device-tests"],
    defaults: ["servicemanager_defaults"],
    srcs: [
        "test_sm.cpp",
    ],
    static_libs: ["libgmock"],
}
+144 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ServiceManager.h"

#include <android-base/logging.h>
#include <cutils/android_filesystem_config.h>
#include <cutils/multiuser.h>

using ::android::binder::Status;

namespace android {

ServiceManager::ServiceManager(std::unique_ptr<Access>&& access) : mAccess(std::move(access)) {}

Status ServiceManager::getService(const std::string& name, sp<IBinder>* outBinder) {
    // Servicemanager is single-threaded and cannot block. This method exists for legacy reasons.
    return checkService(name, outBinder);
}

Status ServiceManager::checkService(const std::string& name, sp<IBinder>* outBinder) {
    auto ctx = mAccess->getCallingContext(name);

    auto it = mNameToService.find(name);
    if (it == mNameToService.end()) {
        *outBinder = nullptr;
        return Status::ok();
    }

    const Service& service = it->second;

    if (!service.allowIsolated) {
        uid_t appid = multiuser_get_app_id(ctx.uid);
        bool isIsolated = appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END;

        if (isIsolated) {
            *outBinder = nullptr;
            return Status::ok();
        }
    }

    // TODO(b/136023468): move this check to be first
    if (!mAccess->canFind(ctx)) {
        // returns ok and null for legacy reasons
        *outBinder = nullptr;
        return Status::ok();
    }

    *outBinder = service.binder;
    return Status::ok();
}

Status ServiceManager::addService(const std::string& name, const sp<IBinder>& binder, bool allowIsolated, int32_t dumpPriority) {
    auto ctx = mAccess->getCallingContext(name);

    // apps cannot add services
    if (multiuser_get_app_id(ctx.uid) >= AID_APP) {
        return Status::fromExceptionCode(Status::EX_SECURITY);
    }

    if (!mAccess->canAdd(ctx)) {
        return Status::fromExceptionCode(Status::EX_SECURITY);
    }

    if (binder == nullptr) {
        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
    }

    // match legacy rules
    if (name.size() == 0 || name.size() > 127) {
        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
    }

    if (OK != binder->linkToDeath(this)) {
        LOG(ERROR) << "Could not linkToDeath when adding " << name;
        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
    }

    auto it = mNameToService.find(name);
    if (it != mNameToService.end()) {
        if (OK != it->second.binder->unlinkToDeath(this)) {
            LOG(WARNING) << "Could not unlinkToDeath when adding " << name;
        }
    }

    mNameToService[name] = Service {
        .binder = binder,
        .allowIsolated = allowIsolated,
        .dumpPriority = dumpPriority,
    };

    return Status::ok();
}

Status ServiceManager::listServices(int32_t dumpPriority, std::vector<std::string>* outList) {
    if (!mAccess->canList(mAccess->getCallingContext(""))) {
        return Status::fromExceptionCode(Status::EX_SECURITY);
    }

    size_t toReserve = 0;
    for (auto const& [name, service] : mNameToService) {
        (void) name;

        if (service.dumpPriority & dumpPriority) ++toReserve;
    }

    CHECK(outList->empty());

    outList->reserve(toReserve);
    for (auto const& [name, service] : mNameToService) {
        (void) service;

        if (service.dumpPriority & dumpPriority) {
            outList->push_back(name);
        }
    }

    return Status::ok();
}

void ServiceManager::binderDied(const wp<IBinder>& who) {
    for (auto it = mNameToService.begin(); it != mNameToService.end();) {
        if (who == it->second.binder) {
            it = mNameToService.erase(it);
        } else {
            ++it;
        }
    }
}

}  // namespace android
+47 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include <android/os/BnServiceManager.h>

#include "Access.h"

namespace android {

class ServiceManager : public os::BnServiceManager, public IBinder::DeathRecipient {
public:
    ServiceManager(std::unique_ptr<Access>&& access);

    binder::Status getService(const std::string& name, sp<IBinder>* outBinder) override;
    binder::Status checkService(const std::string& name, sp<IBinder>* outBinder) override;
    binder::Status addService(const std::string& name, const sp<IBinder>& binder, bool allowIsolated, int32_t dumpPriority) override;
    binder::Status listServices(int32_t dumpPriority, std::vector<std::string>* outList) override;

    void binderDied(const wp<IBinder>& who) override;

private:
    struct Service {
        sp<IBinder> binder;
        bool allowIsolated;
        int32_t dumpPriority;
    };

    std::map<std::string, Service> mNameToService;
    std::unique_ptr<Access> mAccess;
};

}  // namespace android
Loading