blob: 9583b174cd165da06d034a845f9ba347fddf44cd [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
5#include "pciguard/pciguard_utils.h"
6
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
22// Sysfs driver allowlist file (contains drivers that are allowlisted for
23// external PCI devices)).
24constexpr char kAllowlistPath[] = "/sys/bus/pci/drivers_allowlist";
25
26// Sysfs PCI lockdown file. When set to 1, this prevents any driver to bind to
27// external PCI devices (including allowlisted drivers).
28constexpr char kExtPCILockdownPath[] =
29 "/sys/bus/pci/drivers_allowlist_lockdown";
30
31// Sysfs PCI rescan file. It rescans the PCI bus to discover any new devices.
32constexpr char kPCIRescanPath[] = "/sys/bus/pci/rescan";
33
34// Actual driver allowlist.
35const char* kAllowlist[] = {
36 // TODO(b/163121310): Finalize allowlist
37 "pcieport", // PCI Core services - AER, Hotplug etc.
38 "xhci_hcd", // XHCI host controller driver.
39 "nvme", // PCI Express NVME host controller driver.
Chiranjeevi Rapolu487112d2021-01-06 12:13:29 -080040 "ahci", // AHCI driver.
41 "igb", // Intel Giga Bit Ethernet driver on TBT devices.
Rajat Jain8e567e22020-11-03 14:59:28 -080042};
43
44int SetAuthorizedAttribute(base::FilePath devpath, bool enable) {
45 if (!PathExists(devpath)) {
46 PLOG(ERROR) << "Path doesn't exist : " << devpath;
47 return EXIT_FAILURE;
48 }
49
50 base::FilePath symlink;
51 // Check it is a thunderbolt path
52 if (!base::ReadSymbolicLink(devpath.Append("subsystem"), &symlink) ||
53 !base::EndsWith(symlink.value(), "/bus/thunderbolt",
54 base::CompareCase::SENSITIVE)) {
55 LOG(ERROR) << "Not a thunderbolt devpath: " << devpath;
56 return EXIT_FAILURE;
57 }
58
59 base::FilePath authorized_path = devpath.Append("authorized");
60 std::string authorized;
61
62 // Proceed only if authorized file exists
63 if (!base::ReadFileToString(authorized_path, &authorized))
64 return EXIT_SUCCESS;
65
66 // Nevermind if no need to change the state.
67 if (!authorized.empty() &&
68 ((enable && authorized[0] != '0') || (!enable && authorized[0] == '0')))
69 return EXIT_SUCCESS;
70
71 auto val = "0";
72 if (enable) {
73 LOG(INFO) << "Authorizing:" << devpath;
74 val = "1";
75 } else {
76 LOG(INFO) << "Deauthorizing:" << devpath;
77 }
78
79 if (base::WriteFile(authorized_path, val, 1) != 1) {
80 PLOG(ERROR) << "Couldn't write " << val << " to " << authorized_path;
81 return EXIT_FAILURE;
82 }
83
84 return EXIT_SUCCESS;
85}
86
87int DeauthorizeThunderboltDev(base::FilePath devpath) {
88 return SetAuthorizedAttribute(devpath, false);
89}
90
91} // namespace
92
93int OnInit(void) {
94 if (!base::PathIsWritable(base::FilePath(kAllowlistPath)) ||
95 !base::PathIsWritable(base::FilePath(kExtPCILockdownPath))) {
96 PLOG(ERROR) << "Kernel is missing needed support for external PCI security";
97 return EX_OSFILE;
98 }
99
100 if (base::WriteFile(base::FilePath(kExtPCILockdownPath), "1", 1) != 1) {
101 PLOG(ERROR) << "Couldn't write 1 to " << kExtPCILockdownPath;
102 return EX_IOERR;
103 }
104
105 const base::FilePath allowlist_file(kAllowlistPath);
106 for (auto drvr_name : kAllowlist) {
107 if (base::WriteFile(allowlist_file, drvr_name, sizeof(drvr_name)) ==
108 sizeof(drvr_name))
109 LOG(INFO) << "Allowed " << drvr_name;
110 else
111 PLOG(ERROR) << "Couldn't allow " << drvr_name;
112 }
113 return EX_OK;
114}
115
116int AuthorizeThunderboltDev(base::FilePath devpath) {
117 return SetAuthorizedAttribute(devpath, true);
118}
119
120int AuthorizeAllDevices(void) {
121 LOG(INFO) << "Authorizing all external PCI devices";
122
123 // Allow drivers to bind to PCI devices. This also binds any PCI devices
124 // that may have been hotplugged "into" external peripherals, while the
125 // screen was locked.
126 if (base::WriteFile(base::FilePath(kExtPCILockdownPath), "0", 1) != 1) {
127 PLOG(ERROR) << "Couldn't write 0 to " << kExtPCILockdownPath;
128 return EXIT_FAILURE;
129 }
130
131 int ret = EXIT_SUCCESS;
132
133 // Add any PCI devices that we removed when the user had logged off.
134 if (base::WriteFile(base::FilePath(kPCIRescanPath), "1", 1) != 1) {
135 PLOG(ERROR) << "Couldn't write 1 to " << kPCIRescanPath;
136 ret = EXIT_FAILURE;
137 }
138
Rajat Jain306e4c52021-01-22 13:05:25 -0800139 // Create an ordered set of thunderbolt devices.
140 std::set<base::FilePath> thunderbolt_devs;
Rajat Jain8e567e22020-11-03 14:59:28 -0800141 base::FileEnumerator iter(base::FilePath("/sys/bus/thunderbolt/devices"),
142 false, base::FileEnumerator::DIRECTORIES);
Rajat Jain306e4c52021-01-22 13:05:25 -0800143 for (auto devpath = iter.Next(); !devpath.empty(); devpath = iter.Next())
144 thunderbolt_devs.insert(devpath);
145
146 // Authorize the thunderbolt devices in BFS order (Due to kernel device
147 // naming scheme, the ordered set always results in BFS order). This is
148 // required because if a parent is deauthorized, the children are
149 // automatically deauthorized, but vice versa is not true.
150 for (auto dev : thunderbolt_devs) {
151 if (AuthorizeThunderboltDev(dev))
Rajat Jain8e567e22020-11-03 14:59:28 -0800152 ret = EXIT_FAILURE;
153 }
154
155 return ret;
156}
157
158int DenyNewDevices(void) {
159 LOG(INFO) << "Will deny all new external PCI devices";
160
161 // Deny drivers to bind to any *new* external PCI devices.
162 if (base::WriteFile(base::FilePath(kExtPCILockdownPath), "1", 1) != 1) {
163 PLOG(ERROR) << "Couldn't write 1 to " << kExtPCILockdownPath;
164 return EXIT_FAILURE;
165 }
166 return EXIT_SUCCESS;
167}
168
169int DeauthorizeAllDevices(void) {
170 int ret = EXIT_SUCCESS;
171 if (DenyNewDevices())
172 return EXIT_FAILURE;
173
174 LOG(INFO) << "Deauthorizing all external PCI devices";
175
176 // Remove all untrusted (external) PCI devices.
177 base::FileEnumerator iter(base::FilePath("/sys/bus/pci/devices"), false,
178 base::FileEnumerator::DIRECTORIES);
179 for (auto devpath = iter.Next(); !devpath.empty(); devpath = iter.Next()) {
180 std::string untrusted;
181
182 // It is possible this device may already been have removed (as an effect
183 // of its parent being removed).
184 if (!PathExists(devpath))
185 continue;
186
187 // Proceed only if there is an "untrusted" file.
188 if (!base::ReadFileToString(devpath.Append("untrusted"), &untrusted) ||
189 untrusted.empty()) {
190 PLOG(ERROR) << "Couldn't read " << devpath << "/untrusted";
191 ret = EXIT_FAILURE;
192 continue;
193 }
194
195 // Nevermind the trusted devices.
196 if (untrusted[0] == '0')
197 continue;
198
199 // Remove untrusted device.
200 if (base::WriteFile(devpath.Append("remove"), "1", 1) != 1) {
201 PLOG(ERROR) << "Couldn't remove untrusted device " << devpath;
202 ret = EXIT_FAILURE;
203 }
204 }
205
206 // Deauthorize all thunderbolt devices.
207 base::FileEnumerator tbt_iter(base::FilePath("/sys/bus/thunderbolt/devices"),
208 false, base::FileEnumerator::DIRECTORIES);
209 for (auto devpath = tbt_iter.Next(); !devpath.empty();
210 devpath = tbt_iter.Next()) {
211 if (DeauthorizeThunderboltDev(devpath))
212 ret = EXIT_FAILURE;
213 }
214 return ret;
215}
216
217} // namespace pciguard