blob: 89688516a70fc38e43c3c33220c750d341d81b2a [file] [log] [blame]
// Copyright 2015 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "permission_broker/rule_engine.h"
#include <libudev.h>
#include <poll.h>
#include <sys/inotify.h>
#include "base/logging.h"
#include "permission_broker/rule.h"
namespace permission_broker {
RuleEngine::RuleEngine() : udev_(udev_new()) {}
RuleEngine::RuleEngine(const std::string& udev_run_path,
const base::TimeDelta& poll_interval)
: udev_(udev_new()),
poll_interval_(poll_interval),
udev_run_path_(udev_run_path) {
CHECK(udev_) << "Could not create udev context, is sysfs mounted?";
}
RuleEngine::~RuleEngine() {}
void RuleEngine::AddRule(Rule* rule) {
CHECK(rule) << "Cannot add NULL as a rule.";
rules_.push_back(std::unique_ptr<Rule>(rule));
}
Rule::Result RuleEngine::ProcessPath(const std::string& path) {
WaitForEmptyUdevQueue();
LOG(INFO) << "ProcessPath(" << path << ")";
Rule::Result result = Rule::IGNORE;
ScopedUdevDevicePtr device(FindUdevDevice(path));
if (device.get()) {
for (const std::unique_ptr<Rule>& rule : rules_) {
Rule::Result rule_result = rule->ProcessDevice(device.get());
LOG(INFO) << " " << rule->name() << ": "
<< Rule::ResultToString(rule_result);
if (rule_result == Rule::DENY) {
result = Rule::DENY;
break;
} else if (rule_result == Rule::ALLOW_WITH_DETACH) {
result = Rule::ALLOW_WITH_DETACH;
} else if (rule_result == Rule::ALLOW_WITH_LOCKDOWN) {
result = Rule::ALLOW_WITH_LOCKDOWN;
} else if (rule_result == Rule::ALLOW &&
result != Rule::ALLOW_WITH_DETACH &&
result != Rule::ALLOW_WITH_LOCKDOWN) {
result = Rule::ALLOW;
}
}
} else {
LOG(INFO) << "No udev entry found for " << path << ", denying access.";
result = Rule::DENY;
}
LOG(INFO) << "Verdict for " << path << ": " << Rule::ResultToString(result);
return result;
}
void RuleEngine::WaitForEmptyUdevQueue() {
struct udev_queue* queue = udev_queue_new(udev_.get());
if (udev_queue_get_queue_is_empty(queue)) {
udev_queue_unref(queue);
return;
}
struct pollfd udev_poll;
memset(&udev_poll, 0, sizeof(udev_poll));
udev_poll.fd = inotify_init();
udev_poll.events = POLLIN;
int watch =
inotify_add_watch(udev_poll.fd, udev_run_path_.c_str(), IN_MOVED_TO);
CHECK_NE(watch, -1) << "Could not add watch for udev run directory.";
while (!udev_queue_get_queue_is_empty(queue)) {
if (poll(&udev_poll, 1, poll_interval_.InMilliseconds()) > 0) {
char buffer[sizeof(struct inotify_event)];
const ssize_t result = read(udev_poll.fd, buffer, sizeof(buffer));
if (result < 0)
LOG(WARNING) << "Did not read complete udev event.";
}
}
udev_queue_unref(queue);
close(udev_poll.fd);
}
ScopedUdevDevicePtr RuleEngine::FindUdevDevice(const std::string& path) {
ScopedUdevEnumeratePtr enumerate(udev_enumerate_new(udev_.get()));
udev_enumerate_scan_devices(enumerate.get());
struct udev_list_entry* entry = nullptr;
udev_list_entry_foreach(entry,
udev_enumerate_get_list_entry(enumerate.get())) {
const char* syspath = udev_list_entry_get_name(entry);
ScopedUdevDevicePtr device(
udev_device_new_from_syspath(udev_.get(), syspath));
const char* devnode = udev_device_get_devnode(device.get());
if (devnode && !strcmp(devnode, path.c_str()))
return device;
}
return nullptr;
}
} // namespace permission_broker