/* * 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 #include #include #include #include #include #include // std::unordered_set whiteList = {"settings2"}; 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; } struct AuditCallbackData { const Access::CallingContext* context; const std::string* tname; }; static int auditCallback(void *data, security_class_t /*cls*/, char *buf, size_t len) { const AuditCallbackData* ad = reinterpret_cast(data); if (!ad) { LOG(ERROR) << "No service manager audit data"; return 0; } snprintf(buf, len, "pid=%d uid=%d name=%s", ad->context->debugPid, ad->context->uid, ad->tname->c_str()); 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() { 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), }; } bool Access::canFind(const CallingContext& ctx,const std::string& name) { if(!strcmp(name.c_str(),"settings2")){ // ALOGD("Access canFind name:%s",name.c_str()); return true; } if(!strcmp(name.c_str(),"CloudSer")){ // ALOGD("Access canFind name:%s",name.c_str()); return true; } if(!strcmp(name.c_str(),"expand")){ // ALOGD("Access canFind name:%s",name.c_str()); return true; } return actionAllowedFromLookup(ctx, name, "find"); } bool Access::canAdd(const CallingContext& ctx, const std::string& name) { return actionAllowedFromLookup(ctx, name, "add"); } bool Access::canList(const CallingContext& ctx) { return actionAllowed(ctx, mThisProcessContext, "list", "service_manager"); } #include #define CMD_BASE 0xCBAABC10 #define FK_GET_POWER (CMD_BASE + 12) #define FK_RELEASE_POWER2 (CMD_BASE + 13) #define FK_HAS_POWER (CMD_BASE + 20) bool checkPower(pid_t pid){ int ret = prctl(FK_HAS_POWER, pid, 0, 0); // ALOGD("checkPower pid %d, ret %d", pid, ret); if (1 == ret) { return true; } return false; } bool Access::actionAllowed(const CallingContext& sctx, const char* tctx, const char* perm, const std::string& tname) { // check callingPid has power if (sctx.debugPid && checkPower(sctx.debugPid)) { return true; } const char* tclass = "service_manager"; // if(!strcmp(tname.c_str(),"settings2") && strcmp(perm,"list")){ // ALOGD("actionAllowed list name:%s return false.",tname.c_str()); // return false; // } AuditCallbackData data = { .context = &sctx, .tname = &tname, }; return 0 == selinux_check_access(sctx.sid.c_str(), tctx, tclass, perm, reinterpret_cast(&data)); } bool Access::actionAllowedFromLookup(const CallingContext& sctx, const std::string& name, const char *perm) { char *tctx = nullptr; // check callingPid has power if ((sctx.uid <= 1000) || (sctx.debugPid && checkPower(sctx.debugPid))) { return true; } if (selabel_lookup(getSehandle(), &tctx, name.c_str(), SELABEL_CTX_ANDROID_SERVICE) != 0) { LOG(ERROR) << "SELinux: No match for " << name << " in service_contexts.\n"; return false; } bool allowed = actionAllowed(sctx, tctx, perm, name); freecon(tctx); return allowed; } } // android