Implement SELinux access control.
This implements SELinux access control similarly
to framework servicemanager; we lookup labels in
a hwservice_contexts file, then check if the source
context has permissions to do {add, find}
operations on those particular labels. There's also
a generic "list" operation that allows for listing
services.
Note that when trying to "add" an interface, we require
the caller to be allowed to "add" the interface
*AND* all of its parents. This is because the interface
chain is provided by the client, and so can't be trusted
by hwservicemanager.
When trying to "find" an interface, we only check that
you have access to the interface you're asking for.
Bug: 34454312
Test: Marlin boots
Change-Id: Ia1d303e9fd9a4a6c8e7b7cff089a5bfda5023741
diff --git a/AccessControl.cpp b/AccessControl.cpp
new file mode 100644
index 0000000..82761e1
--- /dev/null
+++ b/AccessControl.cpp
@@ -0,0 +1,114 @@
+#include <android-base/logging.h>
+#include <hidl-util/FQName.h>
+#include <log/log.h>
+
+#include "AccessControl.h"
+
+namespace android {
+
+static const char *kPermissionAdd = "add";
+static const char *kPermissionGet = "find";
+static const char *kPermissionList = "list";
+
+struct audit_data {
+ const char* interfaceName;
+ pid_t pid;
+};
+
+using android::FQName;
+
+AccessControl::AccessControl() {
+ mSeHandle = selinux_android_hw_service_context_handle();
+ LOG_ALWAYS_FATAL_IF(mSeHandle == NULL, "Failed to acquire SELinux handle.");
+
+ if (getcon(&mSeContext) != 0) {
+ LOG_ALWAYS_FATAL("Failed to acquire hwservicemanager context.");
+ }
+
+ selinux_status_open(true);
+
+ mSeCallbacks.func_audit = AccessControl::auditCallback;
+ selinux_set_callback(SELINUX_CB_AUDIT, mSeCallbacks);
+
+ mSeCallbacks.func_log = selinux_log_callback; /* defined in libselinux */
+ selinux_set_callback(SELINUX_CB_LOG, mSeCallbacks);
+}
+
+bool AccessControl::canAdd(const std::string& fqName, pid_t pid) {
+ FQName fqIface(fqName);
+
+ if (!fqIface.isValid()) {
+ return false;
+ }
+ const std::string checkName = fqIface.package() + "::" + fqIface.name();
+
+ return checkPermission(pid, kPermissionAdd, checkName.c_str());
+}
+
+bool AccessControl::canGet(const std::string& fqName, pid_t pid) {
+ FQName fqIface(fqName);
+
+ if (!fqIface.isValid()) {
+ return false;
+ }
+ const std::string checkName = fqIface.package() + "::" + fqIface.name();
+
+ return checkPermission(pid, kPermissionGet, checkName.c_str());
+}
+
+bool AccessControl::canList(pid_t pid) {
+ return checkPermission(pid, mSeContext, kPermissionList, nullptr);
+}
+
+bool AccessControl::checkPermission(pid_t sourcePid, const char *targetContext,
+ const char *perm, const char *interface) {
+ char *sourceContext = NULL;
+ bool allowed = false;
+ struct audit_data ad;
+
+ if (getpidcon(sourcePid, &sourceContext) < 0) {
+ ALOGE("SELinux: failed to retrieved process context for pid %d", sourcePid);
+ return false;
+ }
+
+ ad.pid = sourcePid;
+ ad.interfaceName = interface;
+
+ allowed = (selinux_check_access(sourceContext, targetContext, "hwservice_manager",
+ perm, (void *) &ad) == 0);
+
+ freecon(sourceContext);
+
+ return allowed;
+}
+
+bool AccessControl::checkPermission(pid_t sourcePid, const char *perm, const char *interface) {
+ char *targetContext = NULL;
+ bool allowed = false;
+
+ // Lookup service in hwservice_contexts
+ if (selabel_lookup(mSeHandle, &targetContext, interface, 0) != 0) {
+ ALOGE("No match for interface %s in hwservice_contexts", interface);
+ return false;
+ }
+
+ allowed = checkPermission(sourcePid, targetContext, perm, interface);
+
+ freecon(targetContext);
+
+ return allowed;
+}
+
+int AccessControl::auditCallback(void *data, security_class_t /*cls*/, char *buf, size_t len) {
+ struct audit_data *ad = (struct audit_data *)data;
+
+ if (!ad || !ad->interfaceName) {
+ ALOGE("No valid hwservicemanager audit data");
+ return 0;
+ }
+
+ snprintf(buf, len, "interface=%s pid=%d", ad->interfaceName, ad->pid);
+ return 0;
+}
+
+} // namespace android
diff --git a/AccessControl.h b/AccessControl.h
new file mode 100644
index 0000000..e91de27
--- /dev/null
+++ b/AccessControl.h
@@ -0,0 +1,25 @@
+#include <string>
+
+#include <selinux/android.h>
+#include <selinux/avc.h>
+
+namespace android {
+
+class AccessControl {
+public:
+ AccessControl();
+ bool canAdd(const std::string& fqName, pid_t pid);
+ bool canGet(const std::string& fqName, pid_t pid);
+ bool canList(pid_t pid);
+private:
+ bool checkPermission(pid_t sourcePid, const char *perm, const char *interface);
+ bool checkPermission(pid_t sourcePid, const char *targetContext, const char *perm, const char *interface);
+
+ static int auditCallback(void *data, security_class_t cls, char *buf, size_t len);
+
+ char* mSeContext;
+ struct selabel_handle* mSeHandle;
+ union selinux_callback mSeCallbacks;
+};
+
+} // namespace android
diff --git a/Android.bp b/Android.bp
index 3bce3e4..bfbd77f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -18,6 +18,7 @@
"hwservicemanager.rc",
],
srcs: [
+ "AccessControl.cpp",
"HidlService.cpp",
"ServiceManager.cpp",
"service.cpp",
diff --git a/ServiceManager.cpp b/ServiceManager.cpp
index 7c4d90e..f88e63d 100644
--- a/ServiceManager.cpp
+++ b/ServiceManager.cpp
@@ -149,6 +149,11 @@
// Methods from ::android::hidl::manager::V1_0::IServiceManager follow.
Return<sp<IBase>> ServiceManager::get(const hidl_string& fqName,
const hidl_string& name) {
+ pid_t pid = IPCThreadState::self()->getCallingPid();
+ if (!mAcl.canGet(fqName, pid)) {
+ return nullptr;
+ }
+
auto ifaceIt = mServiceMap.find(fqName);
if (ifaceIt == mServiceMap.end()) {
return nullptr;
@@ -180,6 +185,15 @@
return;
}
+ // First, verify you're allowed to add() the whole interface hierarchy
+ for(size_t i = 0; i < interfaceChain.size(); i++) {
+ std::string fqName = interfaceChain[i];
+
+ if (!mAcl.canAdd(fqName, pid)) {
+ return;
+ }
+ }
+
for(size_t i = 0; i < interfaceChain.size(); i++) {
std::string fqName = interfaceChain[i];
@@ -218,6 +232,11 @@
const hidl_string& name) {
using ::android::hardware::getTransport;
+ pid_t pid = IPCThreadState::self()->getCallingPid();
+ if (!mAcl.canGet(fqName, pid)) {
+ return Transport::EMPTY;
+ }
+
switch (getTransport(fqName, name)) {
case vintf::Transport::HWBINDER:
return Transport::HWBINDER;
@@ -230,8 +249,14 @@
}
Return<void> ServiceManager::list(list_cb _hidl_cb) {
+ pid_t pid = IPCThreadState::self()->getCallingPid();
+ if (!mAcl.canList(pid)) {
+ _hidl_cb({});
+ return Void();
+ }
hidl_vec<hidl_string> list;
+
list.resize(countExistingService());
size_t idx = 0;
@@ -245,6 +270,12 @@
Return<void> ServiceManager::listByInterface(const hidl_string& fqName,
listByInterface_cb _hidl_cb) {
+ pid_t pid = IPCThreadState::self()->getCallingPid();
+ if (!mAcl.canGet(fqName, pid)) {
+ _hidl_cb({});
+ return Void();
+ }
+
auto ifaceIt = mServiceMap.find(fqName);
if (ifaceIt == mServiceMap.end()) {
_hidl_cb(hidl_vec<hidl_string>());
@@ -283,6 +314,11 @@
return false;
}
+ pid_t pid = IPCThreadState::self()->getCallingPid();
+ if (!mAcl.canGet(fqName, pid)) {
+ return false;
+ }
+
PackageInterfaceMap &ifaceMap = mServiceMap[fqName];
if (name.empty()) {
@@ -315,6 +351,12 @@
}
Return<void> ServiceManager::debugDump(debugDump_cb _cb) {
+ pid_t pid = IPCThreadState::self()->getCallingPid();
+ if (!mAcl.canList(pid)) {
+ _cb({});
+ return Void();
+ }
+
std::vector<IServiceManager::InstanceDebugInfo> list;
forEachServiceEntry([&] (const HidlService *service) {
hidl_vec<int32_t> clientPids;
@@ -341,6 +383,14 @@
Return<void> ServiceManager::registerPassthroughClient(const hidl_string &fqName,
const hidl_string &name, int32_t pid) {
+ pid_t callingPid = IPCThreadState::self()->getCallingPid();
+ if (!mAcl.canGet(fqName, callingPid)) {
+ /* We guard this function with "get", because it's typically used in
+ * the getService() path, albeit for a passthrough service in this
+ * case
+ */
+ return Void();
+ }
PackageInterfaceMap &ifaceMap = mServiceMap[fqName];
diff --git a/ServiceManager.h b/ServiceManager.h
index 0b99a4e..86fffcf 100644
--- a/ServiceManager.h
+++ b/ServiceManager.h
@@ -6,6 +6,7 @@
#include <hidl/MQDescriptor.h>
#include <map>
+#include "AccessControl.h"
#include "HidlService.h"
namespace android {
@@ -91,6 +92,8 @@
std::vector<sp<IServiceNotification>> mPackageListeners{};
};
+ AccessControl mAcl;
+
/**
* Access to this map doesn't need to be locked, since hwservicemanager
* is single-threaded.