blob: 22d2740ac79de81e3025106dd7c552c933f5a96a [file] [log] [blame] [edit]
// Copyright 2019 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/logging.h>
#include <base/test/bind.h>
#include <gtest/gtest.h>
#include "usb_bouncer/util.h"
#include "usb_bouncer/util_internal.h"
using brillo::SafeFD;
namespace usb_bouncer {
namespace {
constexpr char kSubdirName[] = "usb1";
constexpr char kNonUsbName[] = "domain0";
constexpr char kSysFSAuthorized[] = "authorized";
constexpr char kSysFSAuthorizedDefault[] = "authorized_default";
constexpr char kSysFSEnabled[] = "1";
constexpr char kSysFSDisabled[] = "0";
bool CreateDisabledFlag(const base::FilePath& flag) {
if (!base::WriteFile(flag, kSysFSDisabled, sizeof(kSysFSDisabled) - 1)) {
LOG(ERROR) << "WriteFile('" << flag.value() << "', ...) failed.";
return false;
}
return true;
}
bool CreateDeviceNode(const base::FilePath& dir) {
if (!base::CreateDirectory(dir)) {
LOG(ERROR) << "CreateDirectory('" << dir.value() << "') failed.";
return false;
}
return CreateDisabledFlag(dir.Append(kSysFSAuthorized)) &&
CreateDisabledFlag(dir.Append(kSysFSAuthorizedDefault));
}
bool CheckEnabledFlag(const base::FilePath& flag) {
std::string flag_content;
if (!base::ReadFileToString(flag, &flag_content)) {
LOG(ERROR) << "ReadFileToString('" << flag.value() << "', ...) failed.";
return false;
}
return flag_content == kSysFSEnabled;
}
bool CheckDeviceNodeAuthorized(const base::FilePath& dir) {
return CheckEnabledFlag(dir.Append(kSysFSAuthorized)) &&
CheckEnabledFlag(dir.Append(kSysFSAuthorizedDefault));
}
} // namespace
constexpr size_t kTestMaxAttempts = 123;
constexpr uint32_t kTestDelayMicroseconds = 789;
// Do not actually sleep for tests.
int mock_usleep(useconds_t delay) {
EXPECT_EQ(delay, kTestDelayMicroseconds);
return 0;
}
TEST(UtilTest, WriteWithTimeout_Success) {
SafeFD fd;
SafeFD::Error err;
std::tie(fd, err) = SafeFD::Root();
ASSERT_EQ(err, SafeFD::Error::kNoError);
base::TimeDelta delay = base::Microseconds(kTestDelayMicroseconds);
std::string value = "test value";
static size_t final_length = value.size();
static bool ftruncate_called;
ftruncate_called = false;
EXPECT_TRUE(WriteWithTimeout(
&fd, value, kTestMaxAttempts, delay,
/* write_func */
[](int fd, const void* data, size_t len) -> ssize_t {
EXPECT_EQ(len, final_length);
return len;
},
&mock_usleep,
/* ftruncate_func */
[](int fd, off_t len) -> int {
EXPECT_EQ(len, final_length);
ftruncate_called = true;
return 0;
}));
EXPECT_TRUE(ftruncate_called);
}
TEST(UtilTest, WriteWithTimeout_IncrementalSuccess) {
SafeFD fd;
SafeFD::Error err;
std::tie(fd, err) = SafeFD::Root();
ASSERT_EQ(err, SafeFD::Error::kNoError);
base::TimeDelta delay = base::Microseconds(kTestDelayMicroseconds);
std::string value = "test value";
static size_t final_length = value.size();
static size_t expected_length;
expected_length = value.size();
static bool ftruncate_called;
ftruncate_called = false;
EXPECT_TRUE(WriteWithTimeout(
&fd, value, kTestMaxAttempts, delay,
/* write_func */
[](int fd, const void* data, size_t len) -> ssize_t {
EXPECT_EQ(expected_length, len);
--expected_length;
return 1;
},
&mock_usleep,
/* ftruncate_func */
[](int fd, off_t len) -> int {
EXPECT_EQ(len, final_length);
ftruncate_called = true;
return 0;
}));
EXPECT_TRUE(ftruncate_called);
}
TEST(UtilTest, WriteWithTimeout_WriteError) {
SafeFD fd;
SafeFD::Error err;
std::tie(fd, err) = SafeFD::Root();
ASSERT_EQ(err, SafeFD::Error::kNoError);
base::TimeDelta delay = base::Microseconds(kTestDelayMicroseconds);
std::string value = "test value";
static bool ftruncate_called;
ftruncate_called = false;
EXPECT_FALSE(WriteWithTimeout(
&fd, value, kTestMaxAttempts, delay,
/* write_func */
[](int fd, const void* data, size_t len) -> ssize_t { return -1; },
&mock_usleep,
/* ftruncate_func */
[](int fd, off_t len) -> int {
ftruncate_called = true;
return 0;
}));
EXPECT_FALSE(ftruncate_called);
}
TEST(UtilTest, WriteWithTimeout_TruncateError) {
SafeFD fd;
SafeFD::Error err;
std::tie(fd, err) = SafeFD::Root();
ASSERT_EQ(err, SafeFD::Error::kNoError);
base::TimeDelta delay = base::Microseconds(kTestDelayMicroseconds);
std::string value = "test value";
static size_t final_length = value.size();
static bool ftruncate_called;
ftruncate_called = false;
EXPECT_FALSE(WriteWithTimeout(
&fd, value, kTestMaxAttempts, delay,
/* write_func */
[](int fd, const void* data, size_t len) -> ssize_t {
EXPECT_EQ(len, final_length);
return len;
},
&mock_usleep,
/* ftruncate_func */
[](int fd, off_t len) -> int {
EXPECT_EQ(len, final_length);
ftruncate_called = true;
return -1;
}));
EXPECT_TRUE(ftruncate_called);
}
TEST(UtilTest, WriteWithTimeout_Timeout) {
SafeFD fd;
SafeFD::Error err;
std::tie(fd, err) = SafeFD::Root();
ASSERT_EQ(err, SafeFD::Error::kNoError);
base::TimeDelta delay = base::Microseconds(kTestDelayMicroseconds);
std::string value = "test value";
static bool ftruncate_called;
ftruncate_called = false;
static size_t tries;
tries = 0;
ASSERT_FALSE(WriteWithTimeout(
&fd, value, kTestMaxAttempts, delay,
/* write_func */
[](int fd, const void* data, size_t len) -> ssize_t {
errno = EAGAIN;
return -1;
},
/* ftruncate_func */
[](useconds_t delay) -> int {
++tries;
return 0;
},
/* ftruncate_func */
[](int fd, off_t len) -> int {
ftruncate_called = true;
return 0;
}));
EXPECT_EQ(tries, kTestMaxAttempts);
EXPECT_FALSE(ftruncate_called);
}
TEST(UtilTest, IncludeRuleAtLockscreen) {
EXPECT_FALSE(IncludeRuleAtLockscreen(""));
const std::string blocked_device =
"allow id 0781:5588 serial \"12345678BF05\" name \"USB Extreme Pro\" "
"hash \"9hMkYEMPjuNegGmzLIKwUp2MPctSL0tCWk7ruWGuOzc=\" with-interface "
"08:06:50 with-connect-type \"unknown\"";
EXPECT_FALSE(IncludeRuleAtLockscreen(blocked_device))
<< "Failed to filter: " << blocked_device;
const std::string allowed_device =
"allow id 0bda:8153 serial \"000001000000\" name \"USB 10/100/1000 LAN\" "
"hash \"dljXy8thtljhoJo+O+hfhSlp1J89rz0Z4404iqKzakI=\" with-interface "
"{ ff:ff:00 02:06:00 0a:00:00 0a:00:00 }";
EXPECT_TRUE(IncludeRuleAtLockscreen(allowed_device))
<< "Failed to allow:" << allowed_device;
}
TEST(UtilTest, AuthorizeAll_Success) {
base::ScopedTempDir temp_dir_;
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()) << strerror(errno);
ASSERT_TRUE(AuthorizeAll(temp_dir_.GetPath().value()));
const base::FilePath subdir = temp_dir_.GetPath().Append(kSubdirName);
ASSERT_TRUE(CreateDeviceNode(subdir))
<< "CreateDeviceNode('" << subdir.value() << "') failed.";
const base::FilePath deepdir =
temp_dir_.GetPath().Append(kSubdirName).Append(kSubdirName);
ASSERT_TRUE(CreateDeviceNode(deepdir))
<< "CreateDeviceNode('" << deepdir.value() << "') failed.";
const base::FilePath non_usb_dir = temp_dir_.GetPath().Append(kNonUsbName);
ASSERT_TRUE(CreateDeviceNode(non_usb_dir))
<< "CreateDeviceNode('" << non_usb_dir.value() << "') failed.";
const base::FilePath non_usb_deepdir =
temp_dir_.GetPath().Append(kNonUsbName).Append(kNonUsbName);
ASSERT_TRUE(CreateDeviceNode(non_usb_deepdir))
<< "CreateDeviceNode('" << non_usb_deepdir.value() << "') failed.";
EXPECT_TRUE(AuthorizeAll(temp_dir_.GetPath().value()));
EXPECT_TRUE(CheckDeviceNodeAuthorized(subdir));
EXPECT_TRUE(CheckDeviceNodeAuthorized(deepdir));
EXPECT_FALSE(CheckDeviceNodeAuthorized(non_usb_dir));
EXPECT_FALSE(CheckDeviceNodeAuthorized(non_usb_deepdir));
}
TEST(UtilTest, AuthorizeAll_Failure) {
base::ScopedTempDir temp_dir_;
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()) << strerror(errno);
const base::FilePath subdir = temp_dir_.GetPath().Append(kSubdirName);
ASSERT_TRUE(CreateDeviceNode(subdir))
<< "CreateDeviceNode('" << subdir.value() << "') failed.";
const base::FilePath deepdir =
temp_dir_.GetPath().Append(kSubdirName).Append(kSubdirName);
ASSERT_TRUE(CreateDeviceNode(deepdir))
<< "CreateDeviceNode('" << deepdir.value() << "') failed.";
chmod(subdir.Append(kSysFSAuthorizedDefault).value().c_str(), 0000);
EXPECT_FALSE(AuthorizeAll(temp_dir_.GetPath().value()));
EXPECT_FALSE(CheckDeviceNodeAuthorized(subdir));
EXPECT_TRUE(CheckDeviceNodeAuthorized(deepdir));
}
TEST(UtilTest, GetRuleFromString_Success) {
EXPECT_TRUE(GetRuleFromString("allow id 0000:0000"));
}
TEST(UtilTest, GetRuleFromString_Failure) {
EXPECT_FALSE(GetRuleFromString("aaff44"));
}
TEST(UtilTest, GetClassFromRule_EmptyInterfaces) {
auto rule = GetRuleFromString("allow id 0000:0000");
ASSERT_TRUE(rule);
ASSERT_TRUE(rule.attributeWithInterface().empty());
EXPECT_EQ(GetClassFromRule(rule), UMADeviceClass::kOther);
}
TEST(UtilTest, GetClassFromRule_SingleInterface) {
auto rule = GetRuleFromString("allow with-interface 03:00:01");
ASSERT_TRUE(rule);
ASSERT_EQ(rule.attributeWithInterface().count(), 1);
EXPECT_EQ(GetClassFromRule(rule), UMADeviceClass::kHID);
}
TEST(UtilTest, GetClassFromRule_MatchingInterfaces) {
auto rule = GetRuleFromString("allow with-interface { 03:00:00 03:00:01 }");
ASSERT_TRUE(rule);
ASSERT_EQ(rule.attributeWithInterface().count(), 2);
EXPECT_EQ(GetClassFromRule(rule), UMADeviceClass::kHID);
}
TEST(UtilTest, GetClassFromRule_DifferentInterfaces) {
auto rule = GetRuleFromString("allow with-interface { 03:00:00 09:00:00 }");
ASSERT_TRUE(rule);
ASSERT_EQ(rule.attributeWithInterface().count(), 2);
EXPECT_EQ(GetClassFromRule(rule), UMADeviceClass::kOther);
}
TEST(UtilTest, GetClassFromRule_AV) {
auto rule =
GetRuleFromString("allow with-interface { 01:00:00 0E:00:00 10:00:00 }");
ASSERT_TRUE(rule);
ASSERT_EQ(rule.attributeWithInterface().count(), 3);
EXPECT_EQ(GetClassFromRule(rule), UMADeviceClass::kAV);
}
} // namespace usb_bouncer