| // 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 |