blob: 6b6b1c0a0aac774cccbe3e32aa8a0b63bc809d55 [file] [log] [blame]
Rajat Jain8e567e22020-11-03 14:59:28 -08001// Copyright 2020 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Rajat Jain5bf732b2021-01-22 14:57:59 -08005#include "pciguard/sysfs_utils.h"
Rajat Jain8e567e22020-11-03 14:59:28 -08006
7#include <base/command_line.h>
8#include <base/files/file_enumerator.h>
9#include <base/files/file_util.h>
10#include <base/logging.h>
11#include <base/strings/string_util.h>
12#include <brillo/syslog_logging.h>
13
Rajat Jain306e4c52021-01-22 13:05:25 -080014#include <set>
Rajat Jain8e567e22020-11-03 14:59:28 -080015#include <string>
16#include <sysexits.h>
17
18namespace pciguard {
19
20namespace {
21
Rajat Jain8e567e22020-11-03 14:59:28 -080022// Actual driver allowlist.
23const char* kAllowlist[] = {
24 // TODO(b/163121310): Finalize allowlist
25 "pcieport", // PCI Core services - AER, Hotplug etc.
26 "xhci_hcd", // XHCI host controller driver.
27 "nvme", // PCI Express NVME host controller driver.
Aashay Shringarpure2187a882021-01-26 23:32:22 +000028 "ahci", // AHCI driver
Rajat Jain97129042021-03-22 22:56:44 -070029 "igb", // Intel Gigabit Ethernet driver
Rajat Jain8e567e22020-11-03 14:59:28 -080030};
31
Rajat Jain5bf732b2021-01-22 14:57:59 -080032} // namespace
33
34SysfsUtils::SysfsUtils() : SysfsUtils(FilePath("/")) {}
35
36SysfsUtils::SysfsUtils(FilePath root)
37 : allowlist_path_(root.Append("sys/bus/pci/drivers_allowlist")),
38 pci_lockdown_path_(root.Append("sys/bus/pci/drivers_allowlist_lockdown")),
39 pci_rescan_path_(root.Append("sys/bus/pci/rescan")),
40 tbt_devices_path_(root.Append("sys/bus/thunderbolt/devices")),
41 pci_devices_path_(root.Append("sys/bus/pci/devices")) {}
42
43int SysfsUtils::SetAuthorizedAttribute(base::FilePath devpath, bool enable) {
Rajat Jain8e567e22020-11-03 14:59:28 -080044 if (!PathExists(devpath)) {
45 PLOG(ERROR) << "Path doesn't exist : " << devpath;
46 return EXIT_FAILURE;
47 }
48
49 base::FilePath symlink;
50 // Check it is a thunderbolt path
51 if (!base::ReadSymbolicLink(devpath.Append("subsystem"), &symlink) ||
52 !base::EndsWith(symlink.value(), "/bus/thunderbolt",
53 base::CompareCase::SENSITIVE)) {
54 LOG(ERROR) << "Not a thunderbolt devpath: " << devpath;
55 return EXIT_FAILURE;
56 }
57
58 base::FilePath authorized_path = devpath.Append("authorized");
59 std::string authorized;
60
61 // Proceed only if authorized file exists
62 if (!base::ReadFileToString(authorized_path, &authorized))
63 return EXIT_SUCCESS;
64
65 // Nevermind if no need to change the state.
66 if (!authorized.empty() &&
67 ((enable && authorized[0] != '0') || (!enable && authorized[0] == '0')))
68 return EXIT_SUCCESS;
69
70 auto val = "0";
71 if (enable) {
72 LOG(INFO) << "Authorizing:" << devpath;
73 val = "1";
74 } else {
75 LOG(INFO) << "Deauthorizing:" << devpath;
76 }
77
78 if (base::WriteFile(authorized_path, val, 1) != 1) {
79 PLOG(ERROR) << "Couldn't write " << val << " to " << authorized_path;
80 return EXIT_FAILURE;
81 }
82
83 return EXIT_SUCCESS;
84}
85
Rajat Jain5bf732b2021-01-22 14:57:59 -080086int SysfsUtils::DeauthorizeThunderboltDev(base::FilePath devpath) {
Rajat Jain8e567e22020-11-03 14:59:28 -080087 return SetAuthorizedAttribute(devpath, false);
88}
89
Rajat Jain5bf732b2021-01-22 14:57:59 -080090int SysfsUtils::OnInit(void) {
91 if (!base::PathIsWritable(allowlist_path_) ||
92 !base::PathIsWritable(pci_lockdown_path_)) {
Rajat Jain8e567e22020-11-03 14:59:28 -080093 PLOG(ERROR) << "Kernel is missing needed support for external PCI security";
94 return EX_OSFILE;
95 }
96
Rajat Jain5bf732b2021-01-22 14:57:59 -080097 if (base::WriteFile(pci_lockdown_path_, "1", 1) != 1) {
98 PLOG(ERROR) << "Couldn't write 1 to " << pci_lockdown_path_;
Rajat Jain8e567e22020-11-03 14:59:28 -080099 return EX_IOERR;
100 }
101
Rajat Jain8e567e22020-11-03 14:59:28 -0800102 for (auto drvr_name : kAllowlist) {
Rajat Jain5bf732b2021-01-22 14:57:59 -0800103 if (base::WriteFile(allowlist_path_, drvr_name, sizeof(drvr_name)) ==
Rajat Jain8e567e22020-11-03 14:59:28 -0800104 sizeof(drvr_name))
105 LOG(INFO) << "Allowed " << drvr_name;
106 else
107 PLOG(ERROR) << "Couldn't allow " << drvr_name;
108 }
109 return EX_OK;
110}
111
Rajat Jain5bf732b2021-01-22 14:57:59 -0800112int SysfsUtils::AuthorizeThunderboltDev(base::FilePath devpath) {
Rajat Jain8e567e22020-11-03 14:59:28 -0800113 return SetAuthorizedAttribute(devpath, true);
114}
115
Rajat Jain5bf732b2021-01-22 14:57:59 -0800116int SysfsUtils::AuthorizeAllDevices(void) {
Rajat Jain8e567e22020-11-03 14:59:28 -0800117 LOG(INFO) << "Authorizing all external PCI devices";
118
119 // Allow drivers to bind to PCI devices. This also binds any PCI devices
120 // that may have been hotplugged "into" external peripherals, while the
121 // screen was locked.
Rajat Jain5bf732b2021-01-22 14:57:59 -0800122 if (base::WriteFile(pci_lockdown_path_, "0", 1) != 1) {
123 PLOG(ERROR) << "Couldn't write 0 to " << pci_lockdown_path_;
Rajat Jain8e567e22020-11-03 14:59:28 -0800124 return EXIT_FAILURE;
125 }
126
127 int ret = EXIT_SUCCESS;
128
129 // Add any PCI devices that we removed when the user had logged off.
Rajat Jain5bf732b2021-01-22 14:57:59 -0800130 if (base::WriteFile(pci_rescan_path_, "1", 1) != 1) {
131 PLOG(ERROR) << "Couldn't write 1 to " << pci_rescan_path_;
Rajat Jain8e567e22020-11-03 14:59:28 -0800132 ret = EXIT_FAILURE;
133 }
134
Rajat Jain38e32312021-02-10 14:25:02 -0800135 // Create an BFS ordered set of thunderbolt devices.
136 auto cmp = [](const base::FilePath& dev1, const base::FilePath& dev2) {
137 base::FilePath symlink1, symlink2;
138 (void)base::ReadSymbolicLink(dev1, &symlink1);
139 (void)base::ReadSymbolicLink(dev2, &symlink2);
140 return symlink1 < symlink2;
141 };
142 std::set<base::FilePath, decltype(cmp)> thunderbolt_devs(cmp);
Rajat Jain5bf732b2021-01-22 14:57:59 -0800143 base::FileEnumerator iter(tbt_devices_path_, false,
144 base::FileEnumerator::DIRECTORIES);
Rajat Jain306e4c52021-01-22 13:05:25 -0800145 for (auto devpath = iter.Next(); !devpath.empty(); devpath = iter.Next())
146 thunderbolt_devs.insert(devpath);
147
Rajat Jain38e32312021-02-10 14:25:02 -0800148 // Authorize the thunderbolt devices in BFS order (sorting using the
149 // symlinks to which the devices point, gives us BFS). This is
Rajat Jain306e4c52021-01-22 13:05:25 -0800150 // required because if a parent is deauthorized, the children are
151 // automatically deauthorized, but vice versa is not true.
152 for (auto dev : thunderbolt_devs) {
153 if (AuthorizeThunderboltDev(dev))
Rajat Jain8e567e22020-11-03 14:59:28 -0800154 ret = EXIT_FAILURE;
155 }
156
157 return ret;
158}
159
Rajat Jain5bf732b2021-01-22 14:57:59 -0800160int SysfsUtils::DenyNewDevices(void) {
Rajat Jain8e567e22020-11-03 14:59:28 -0800161 LOG(INFO) << "Will deny all new external PCI devices";
162
163 // Deny drivers to bind to any *new* external PCI devices.
Rajat Jain5bf732b2021-01-22 14:57:59 -0800164 if (base::WriteFile(pci_lockdown_path_, "1", 1) != 1) {
165 PLOG(ERROR) << "Couldn't write 1 to " << pci_lockdown_path_;
Rajat Jain8e567e22020-11-03 14:59:28 -0800166 return EXIT_FAILURE;
167 }
168 return EXIT_SUCCESS;
169}
170
Rajat Jain5bf732b2021-01-22 14:57:59 -0800171int SysfsUtils::DeauthorizeAllDevices(void) {
Rajat Jain8e567e22020-11-03 14:59:28 -0800172 int ret = EXIT_SUCCESS;
173 if (DenyNewDevices())
174 return EXIT_FAILURE;
175
176 LOG(INFO) << "Deauthorizing all external PCI devices";
177
178 // Remove all untrusted (external) PCI devices.
Rajat Jain5bf732b2021-01-22 14:57:59 -0800179 base::FileEnumerator iter(pci_devices_path_, false,
Rajat Jain8e567e22020-11-03 14:59:28 -0800180 base::FileEnumerator::DIRECTORIES);
181 for (auto devpath = iter.Next(); !devpath.empty(); devpath = iter.Next()) {
182 std::string untrusted;
183
184 // It is possible this device may already been have removed (as an effect
185 // of its parent being removed).
186 if (!PathExists(devpath))
187 continue;
188
189 // Proceed only if there is an "untrusted" file.
190 if (!base::ReadFileToString(devpath.Append("untrusted"), &untrusted) ||
191 untrusted.empty()) {
192 PLOG(ERROR) << "Couldn't read " << devpath << "/untrusted";
193 ret = EXIT_FAILURE;
194 continue;
195 }
196
197 // Nevermind the trusted devices.
198 if (untrusted[0] == '0')
199 continue;
200
201 // Remove untrusted device.
202 if (base::WriteFile(devpath.Append("remove"), "1", 1) != 1) {
203 PLOG(ERROR) << "Couldn't remove untrusted device " << devpath;
204 ret = EXIT_FAILURE;
205 }
206 }
207
208 // Deauthorize all thunderbolt devices.
Rajat Jain5bf732b2021-01-22 14:57:59 -0800209 base::FileEnumerator tbt_iter(tbt_devices_path_, false,
210 base::FileEnumerator::DIRECTORIES);
Rajat Jain8e567e22020-11-03 14:59:28 -0800211 for (auto devpath = tbt_iter.Next(); !devpath.empty();
212 devpath = tbt_iter.Next()) {
213 if (DeauthorizeThunderboltDev(devpath))
214 ret = EXIT_FAILURE;
215 }
216 return ret;
217}
218
219} // namespace pciguard