/* * 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 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) { 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"); } bool Access::actionAllowed(const CallingContext& sctx, const char* tctx, const char* perm, const std::string& tname) { const char* tclass = "service_manager"; 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; 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