devices.cpp revision c583305ed7b459604ce619a6c5d44c4a377fcdfe
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "devices.h" 18 19#include <errno.h> 20#include <fnmatch.h> 21#include <sys/sysmacros.h> 22#include <unistd.h> 23 24#include <memory> 25 26#include <android-base/logging.h> 27#include <android-base/stringprintf.h> 28#include <android-base/strings.h> 29#include <private/android_filesystem_config.h> 30#include <selinux/android.h> 31#include <selinux/selinux.h> 32 33#include "ueventd.h" 34#include "util.h" 35 36#ifdef _INIT_INIT_H 37#error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals" 38#endif 39 40/* Given a path that may start with a PCI device, populate the supplied buffer 41 * with the PCI domain/bus number and the peripheral ID and return 0. 42 * If it doesn't start with a PCI device, or there is some error, return -1 */ 43static bool FindPciDevicePrefix(const std::string& path, std::string* result) { 44 result->clear(); 45 46 if (!android::base::StartsWith(path, "/devices/pci")) return false; 47 48 /* Beginning of the prefix is the initial "pci" after "/devices/" */ 49 std::string::size_type start = 9; 50 51 /* End of the prefix is two path '/' later, capturing the domain/bus number 52 * and the peripheral ID. Example: pci0000:00/0000:00:1f.2 */ 53 auto end = path.find('/', start); 54 if (end == std::string::npos) return false; 55 56 end = path.find('/', end + 1); 57 if (end == std::string::npos) return false; 58 59 auto length = end - start; 60 if (length <= 4) { 61 // The minimum string that will get to this check is 'pci/', which is malformed, 62 // so return false 63 return false; 64 } 65 66 *result = path.substr(start, length); 67 return true; 68} 69 70/* Given a path that may start with a virtual block device, populate 71 * the supplied buffer with the virtual block device ID and return 0. 72 * If it doesn't start with a virtual block device, or there is some 73 * error, return -1 */ 74static bool FindVbdDevicePrefix(const std::string& path, std::string* result) { 75 result->clear(); 76 77 if (!android::base::StartsWith(path, "/devices/vbd-")) return false; 78 79 /* Beginning of the prefix is the initial "vbd-" after "/devices/" */ 80 std::string::size_type start = 13; 81 82 /* End of the prefix is one path '/' later, capturing the 83 virtual block device ID. Example: 768 */ 84 auto end = path.find('/', start); 85 if (end == std::string::npos) return false; 86 87 auto length = end - start; 88 if (length == 0) return false; 89 90 *result = path.substr(start, length); 91 return true; 92} 93 94Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid) 95 : name_(name), perm_(perm), uid_(uid), gid_(gid), prefix_(false), wildcard_(false) { 96 // Set 'prefix_' or 'wildcard_' based on the below cases: 97 // 98 // 1) No '*' in 'name' -> Neither are set and Match() checks a given path for strict 99 // equality with 'name' 100 // 101 // 2) '*' only appears as the last character in 'name' -> 'prefix'_ is set to true and 102 // Match() checks if 'name' is a prefix of a given path. 103 // 104 // 3) '*' appears elsewhere -> 'wildcard_' is set to true and Match() uses fnmatch() 105 // with FNM_PATHNAME to compare 'name' to a given path. 106 107 auto wildcard_position = name_.find('*'); 108 if (wildcard_position != std::string::npos) { 109 if (wildcard_position == name_.length() - 1) { 110 prefix_ = true; 111 name_.pop_back(); 112 } else { 113 wildcard_ = true; 114 } 115 } 116} 117 118bool Permissions::Match(const std::string& path) const { 119 if (prefix_) return android::base::StartsWith(path, name_.c_str()); 120 if (wildcard_) return fnmatch(name_.c_str(), path.c_str(), FNM_PATHNAME) == 0; 121 return path == name_; 122} 123 124bool SysfsPermissions::MatchWithSubsystem(const std::string& path, 125 const std::string& subsystem) const { 126 std::string path_basename = android::base::Basename(path); 127 if (name().find(subsystem) != std::string::npos) { 128 if (Match("/sys/class/" + subsystem + "/" + path_basename)) return true; 129 if (Match("/sys/bus/" + subsystem + "/devices/" + path_basename)) return true; 130 } 131 return Match(path); 132} 133 134void SysfsPermissions::SetPermissions(const std::string& path) const { 135 std::string attribute_file = path + "/" + attribute_; 136 LOG(VERBOSE) << "fixup " << attribute_file << " " << uid() << " " << gid() << " " << std::oct 137 << perm(); 138 139 if (access(attribute_file.c_str(), F_OK) == 0) { 140 if (chown(attribute_file.c_str(), uid(), gid()) != 0) { 141 PLOG(ERROR) << "chown(" << attribute_file << ", " << uid() << ", " << gid() 142 << ") failed"; 143 } 144 if (chmod(attribute_file.c_str(), perm()) != 0) { 145 PLOG(ERROR) << "chmod(" << attribute_file << ", " << perm() << ") failed"; 146 } 147 } 148} 149 150// Given a path that may start with a platform device, find the length of the 151// platform device prefix. If it doesn't start with a platform device, return false 152bool PlatformDeviceList::Find(const std::string& path, std::string* out_path) const { 153 out_path->clear(); 154 // platform_devices is searched backwards, since parents are added before their children, 155 // and we want to match as deep of a child as we can. 156 for (auto it = platform_devices_.crbegin(); it != platform_devices_.crend(); ++it) { 157 auto platform_device_path_length = it->length(); 158 if (platform_device_path_length < path.length() && 159 path[platform_device_path_length] == '/' && 160 android::base::StartsWith(path, it->c_str())) { 161 *out_path = *it; 162 return true; 163 } 164 } 165 return false; 166} 167 168void DeviceHandler::FixupSysPermissions(const std::string& upath, 169 const std::string& subsystem) const { 170 // upaths omit the "/sys" that paths in this list 171 // contain, so we prepend it... 172 std::string path = "/sys" + upath; 173 174 for (const auto& s : sysfs_permissions_) { 175 if (s.MatchWithSubsystem(path, subsystem)) s.SetPermissions(path); 176 } 177 178 if (!skip_restorecon_ && access(path.c_str(), F_OK) == 0) { 179 LOG(VERBOSE) << "restorecon_recursive: " << path; 180 if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) { 181 PLOG(ERROR) << "selinux_android_restorecon(" << path << ") failed"; 182 } 183 } 184} 185 186std::tuple<mode_t, uid_t, gid_t> DeviceHandler::GetDevicePermissions( 187 const std::string& path, const std::vector<std::string>& links) const { 188 // Search the perms list in reverse so that ueventd.$hardware can override ueventd.rc. 189 for (auto it = dev_permissions_.crbegin(); it != dev_permissions_.crend(); ++it) { 190 if (it->Match(path) || std::any_of(links.cbegin(), links.cend(), 191 [it](const auto& link) { return it->Match(link); })) { 192 return {it->perm(), it->uid(), it->gid()}; 193 } 194 } 195 /* Default if nothing found. */ 196 return {0600, 0, 0}; 197} 198 199void DeviceHandler::MakeDevice(const std::string& path, int block, int major, int minor, 200 const std::vector<std::string>& links) const { 201 auto[mode, uid, gid] = GetDevicePermissions(path, links); 202 mode |= (block ? S_IFBLK : S_IFCHR); 203 204 char* secontext = nullptr; 205 if (sehandle_) { 206 std::vector<const char*> c_links; 207 for (const auto& link : links) { 208 c_links.emplace_back(link.c_str()); 209 } 210 c_links.emplace_back(nullptr); 211 if (selabel_lookup_best_match(sehandle_, &secontext, path.c_str(), &c_links[0], mode)) { 212 PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label"; 213 return; 214 } 215 setfscreatecon(secontext); 216 } 217 218 dev_t dev = makedev(major, minor); 219 /* Temporarily change egid to avoid race condition setting the gid of the 220 * device node. Unforunately changing the euid would prevent creation of 221 * some device nodes, so the uid has to be set with chown() and is still 222 * racy. Fixing the gid race at least fixed the issue with system_server 223 * opening dynamic input devices under the AID_INPUT gid. */ 224 if (setegid(gid)) { 225 PLOG(ERROR) << "setegid(" << gid << ") for " << path << " device failed"; 226 goto out; 227 } 228 /* If the node already exists update its SELinux label to handle cases when 229 * it was created with the wrong context during coldboot procedure. */ 230 if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && secontext) { 231 char* fcon = nullptr; 232 int rc = lgetfilecon(path.c_str(), &fcon); 233 if (rc < 0) { 234 PLOG(ERROR) << "Cannot get SELinux label on '" << path << "' device"; 235 goto out; 236 } 237 238 bool different = strcmp(fcon, secontext) != 0; 239 freecon(fcon); 240 241 if (different && lsetfilecon(path.c_str(), secontext)) { 242 PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path 243 << "' device"; 244 } 245 } 246 247out: 248 chown(path.c_str(), uid, -1); 249 if (setegid(AID_ROOT)) { 250 PLOG(FATAL) << "setegid(AID_ROOT) failed"; 251 } 252 253 if (secontext) { 254 freecon(secontext); 255 setfscreatecon(nullptr); 256 } 257} 258 259std::vector<std::string> DeviceHandler::GetCharacterDeviceSymlinks(const Uevent& uevent) const { 260 std::string parent_device; 261 if (!platform_devices_.Find(uevent.path, &parent_device)) return {}; 262 263 // skip path to the parent driver 264 std::string path = uevent.path.substr(parent_device.length()); 265 266 if (!android::base::StartsWith(path, "/usb")) return {}; 267 268 // skip root hub name and device. use device interface 269 // skip 3 slashes, including the first / by starting the search at the 1st character, not 0th. 270 // then extract what comes between the 3rd and 4th slash 271 // e.g. "/usb/usb_device/name/tty2-1:1.0" -> "name" 272 273 std::string::size_type start = 0; 274 start = path.find('/', start + 1); 275 if (start == std::string::npos) return {}; 276 277 start = path.find('/', start + 1); 278 if (start == std::string::npos) return {}; 279 280 auto end = path.find('/', start + 1); 281 if (end == std::string::npos) return {}; 282 283 start++; // Skip the first '/' 284 285 auto length = end - start; 286 if (length == 0) return {}; 287 288 auto name_string = path.substr(start, length); 289 290 std::vector<std::string> links; 291 links.emplace_back("/dev/usb/" + uevent.subsystem + name_string); 292 293 mkdir("/dev/usb", 0755); 294 295 return links; 296} 297 298// replaces any unacceptable characters with '_', the 299// length of the resulting string is equal to the input string 300void SanitizePartitionName(std::string* string) { 301 const char* accept = 302 "abcdefghijklmnopqrstuvwxyz" 303 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 304 "0123456789" 305 "_-."; 306 307 if (!string) return; 308 309 std::string::size_type pos = 0; 310 while ((pos = string->find_first_not_of(accept, pos)) != std::string::npos) { 311 (*string)[pos] = '_'; 312 } 313} 314 315std::vector<std::string> DeviceHandler::GetBlockDeviceSymlinks(const Uevent& uevent) const { 316 std::string device; 317 std::string type; 318 319 if (platform_devices_.Find(uevent.path, &device)) { 320 // Skip /devices/platform or /devices/ if present 321 static const std::string devices_platform_prefix = "/devices/platform/"; 322 static const std::string devices_prefix = "/devices/"; 323 324 if (android::base::StartsWith(device, devices_platform_prefix.c_str())) { 325 device = device.substr(devices_platform_prefix.length()); 326 } else if (android::base::StartsWith(device, devices_prefix.c_str())) { 327 device = device.substr(devices_prefix.length()); 328 } 329 330 type = "platform"; 331 } else if (FindPciDevicePrefix(uevent.path, &device)) { 332 type = "pci"; 333 } else if (FindVbdDevicePrefix(uevent.path, &device)) { 334 type = "vbd"; 335 } else { 336 return {}; 337 } 338 339 std::vector<std::string> links; 340 341 LOG(VERBOSE) << "found " << type << " device " << device; 342 343 auto link_path = "/dev/block/" + type + "/" + device; 344 345 if (!uevent.partition_name.empty()) { 346 std::string partition_name_sanitized(uevent.partition_name); 347 SanitizePartitionName(&partition_name_sanitized); 348 if (partition_name_sanitized != uevent.partition_name) { 349 LOG(VERBOSE) << "Linking partition '" << uevent.partition_name << "' as '" 350 << partition_name_sanitized << "'"; 351 } 352 links.emplace_back(link_path + "/by-name/" + partition_name_sanitized); 353 } 354 355 if (uevent.partition_num >= 0) { 356 links.emplace_back(link_path + "/by-num/p" + std::to_string(uevent.partition_num)); 357 } 358 359 auto last_slash = uevent.path.rfind('/'); 360 links.emplace_back(link_path + "/" + uevent.path.substr(last_slash + 1)); 361 362 return links; 363} 364 365void DeviceHandler::HandleDevice(const std::string& action, const std::string& devpath, int block, 366 int major, int minor, const std::vector<std::string>& links) const { 367 if (action == "add") { 368 MakeDevice(devpath, block, major, minor, links); 369 for (const auto& link : links) { 370 if (mkdir_recursive(android::base::Dirname(link), 0755, sehandle_)) { 371 PLOG(ERROR) << "Failed to create directory " << android::base::Dirname(link); 372 } 373 374 if (symlink(devpath.c_str(), link.c_str()) && errno != EEXIST) { 375 PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link; 376 } 377 } 378 } 379 380 if (action == "remove") { 381 for (const auto& link : links) { 382 std::string link_path; 383 if (android::base::Readlink(link, &link_path) && link_path == devpath) { 384 unlink(link.c_str()); 385 } 386 } 387 unlink(devpath.c_str()); 388 } 389} 390 391void DeviceHandler::HandlePlatformDeviceEvent(const Uevent& uevent) { 392 if (uevent.action == "add") { 393 platform_devices_.Add(uevent.path); 394 } else if (uevent.action == "remove") { 395 platform_devices_.Remove(uevent.path); 396 } 397} 398 399void DeviceHandler::HandleBlockDeviceEvent(const Uevent& uevent) const { 400 // if it's not a /dev device, nothing to do 401 if (uevent.major < 0 || uevent.minor < 0) return; 402 403 const char* base = "/dev/block/"; 404 make_dir(base, 0755, sehandle_); 405 406 std::string name = android::base::Basename(uevent.path); 407 std::string devpath = base + name; 408 409 std::vector<std::string> links; 410 if (android::base::StartsWith(uevent.path, "/devices")) { 411 links = GetBlockDeviceSymlinks(uevent); 412 } 413 414 HandleDevice(uevent.action, devpath, 1, uevent.major, uevent.minor, links); 415} 416 417void DeviceHandler::HandleGenericDeviceEvent(const Uevent& uevent) const { 418 // if it's not a /dev device, nothing to do 419 if (uevent.major < 0 || uevent.minor < 0) return; 420 421 std::string devpath; 422 423 if (android::base::StartsWith(uevent.subsystem, "usb")) { 424 if (uevent.subsystem == "usb") { 425 if (!uevent.device_name.empty()) { 426 devpath = "/dev/" + uevent.device_name; 427 } else { 428 // This imitates the file system that would be created 429 // if we were using devfs instead. 430 // Minors are broken up into groups of 128, starting at "001" 431 int bus_id = uevent.minor / 128 + 1; 432 int device_id = uevent.minor % 128 + 1; 433 devpath = android::base::StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id); 434 } 435 } else { 436 // ignore other USB events 437 return; 438 } 439 } else if (const auto subsystem = 440 std::find(subsystems_.cbegin(), subsystems_.cend(), uevent.subsystem); 441 subsystem != subsystems_.cend()) { 442 devpath = subsystem->ParseDevPath(uevent); 443 } else { 444 devpath = "/dev/" + android::base::Basename(uevent.path); 445 } 446 447 mkdir_recursive(android::base::Dirname(devpath), 0755, sehandle_); 448 449 auto links = GetCharacterDeviceSymlinks(uevent); 450 451 HandleDevice(uevent.action, devpath, 0, uevent.major, uevent.minor, links); 452} 453 454void DeviceHandler::HandleDeviceEvent(const Uevent& uevent) { 455 if (uevent.action == "add" || uevent.action == "change" || uevent.action == "online") { 456 FixupSysPermissions(uevent.path, uevent.subsystem); 457 } 458 459 if (uevent.subsystem == "block") { 460 HandleBlockDeviceEvent(uevent); 461 } else if (uevent.subsystem == "platform") { 462 HandlePlatformDeviceEvent(uevent); 463 } else { 464 HandleGenericDeviceEvent(uevent); 465 } 466} 467 468DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions, 469 std::vector<SysfsPermissions> sysfs_permissions, 470 std::vector<Subsystem> subsystems, bool skip_restorecon) 471 : dev_permissions_(std::move(dev_permissions)), 472 sysfs_permissions_(std::move(sysfs_permissions)), 473 subsystems_(std::move(subsystems)), 474 sehandle_(selinux_android_file_context_handle()), 475 skip_restorecon_(skip_restorecon) {} 476 477DeviceHandler::DeviceHandler() 478 : DeviceHandler(std::vector<Permissions>{}, std::vector<SysfsPermissions>{}, 479 std::vector<Subsystem>{}, false) {} 480