Home | History | Annotate | Download | only in init
      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 "selinux.h"
     34 #include "ueventd.h"
     35 #include "util.h"
     36 
     37 #ifdef _INIT_INIT_H
     38 #error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
     39 #endif
     40 
     41 using android::base::Basename;
     42 using android::base::Dirname;
     43 using android::base::Readlink;
     44 using android::base::Realpath;
     45 using android::base::StartsWith;
     46 using android::base::StringPrintf;
     47 
     48 namespace android {
     49 namespace init {
     50 
     51 /* Given a path that may start with a PCI device, populate the supplied buffer
     52  * with the PCI domain/bus number and the peripheral ID and return 0.
     53  * If it doesn't start with a PCI device, or there is some error, return -1 */
     54 static bool FindPciDevicePrefix(const std::string& path, std::string* result) {
     55     result->clear();
     56 
     57     if (!StartsWith(path, "/devices/pci")) return false;
     58 
     59     /* Beginning of the prefix is the initial "pci" after "/devices/" */
     60     std::string::size_type start = 9;
     61 
     62     /* End of the prefix is two path '/' later, capturing the domain/bus number
     63      * and the peripheral ID. Example: pci0000:00/0000:00:1f.2 */
     64     auto end = path.find('/', start);
     65     if (end == std::string::npos) return false;
     66 
     67     end = path.find('/', end + 1);
     68     if (end == std::string::npos) return false;
     69 
     70     auto length = end - start;
     71     if (length <= 4) {
     72         // The minimum string that will get to this check is 'pci/', which is malformed,
     73         // so return false
     74         return false;
     75     }
     76 
     77     *result = path.substr(start, length);
     78     return true;
     79 }
     80 
     81 /* Given a path that may start with a virtual block device, populate
     82  * the supplied buffer with the virtual block device ID and return 0.
     83  * If it doesn't start with a virtual block device, or there is some
     84  * error, return -1 */
     85 static bool FindVbdDevicePrefix(const std::string& path, std::string* result) {
     86     result->clear();
     87 
     88     if (!StartsWith(path, "/devices/vbd-")) return false;
     89 
     90     /* Beginning of the prefix is the initial "vbd-" after "/devices/" */
     91     std::string::size_type start = 13;
     92 
     93     /* End of the prefix is one path '/' later, capturing the
     94        virtual block device ID. Example: 768 */
     95     auto end = path.find('/', start);
     96     if (end == std::string::npos) return false;
     97 
     98     auto length = end - start;
     99     if (length == 0) return false;
    100 
    101     *result = path.substr(start, length);
    102     return true;
    103 }
    104 
    105 Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid)
    106     : name_(name), perm_(perm), uid_(uid), gid_(gid), prefix_(false), wildcard_(false) {
    107     // Set 'prefix_' or 'wildcard_' based on the below cases:
    108     //
    109     // 1) No '*' in 'name' -> Neither are set and Match() checks a given path for strict
    110     //    equality with 'name'
    111     //
    112     // 2) '*' only appears as the last character in 'name' -> 'prefix'_ is set to true and
    113     //    Match() checks if 'name' is a prefix of a given path.
    114     //
    115     // 3) '*' appears elsewhere -> 'wildcard_' is set to true and Match() uses fnmatch()
    116     //    with FNM_PATHNAME to compare 'name' to a given path.
    117 
    118     auto wildcard_position = name_.find('*');
    119     if (wildcard_position != std::string::npos) {
    120         if (wildcard_position == name_.length() - 1) {
    121             prefix_ = true;
    122             name_.pop_back();
    123         } else {
    124             wildcard_ = true;
    125         }
    126     }
    127 }
    128 
    129 bool Permissions::Match(const std::string& path) const {
    130     if (prefix_) return StartsWith(path, name_);
    131     if (wildcard_) return fnmatch(name_.c_str(), path.c_str(), FNM_PATHNAME) == 0;
    132     return path == name_;
    133 }
    134 
    135 bool SysfsPermissions::MatchWithSubsystem(const std::string& path,
    136                                           const std::string& subsystem) const {
    137     std::string path_basename = Basename(path);
    138     if (name().find(subsystem) != std::string::npos) {
    139         if (Match("/sys/class/" + subsystem + "/" + path_basename)) return true;
    140         if (Match("/sys/bus/" + subsystem + "/devices/" + path_basename)) return true;
    141     }
    142     return Match(path);
    143 }
    144 
    145 void SysfsPermissions::SetPermissions(const std::string& path) const {
    146     std::string attribute_file = path + "/" + attribute_;
    147     LOG(VERBOSE) << "fixup " << attribute_file << " " << uid() << " " << gid() << " " << std::oct
    148                  << perm();
    149 
    150     if (access(attribute_file.c_str(), F_OK) == 0) {
    151         if (chown(attribute_file.c_str(), uid(), gid()) != 0) {
    152             PLOG(ERROR) << "chown(" << attribute_file << ", " << uid() << ", " << gid()
    153                         << ") failed";
    154         }
    155         if (chmod(attribute_file.c_str(), perm()) != 0) {
    156             PLOG(ERROR) << "chmod(" << attribute_file << ", " << perm() << ") failed";
    157         }
    158     }
    159 }
    160 
    161 // Given a path that may start with a platform device, find the parent platform device by finding a
    162 // parent directory with a 'subsystem' symlink that points to the platform bus.
    163 // If it doesn't start with a platform device, return false
    164 bool DeviceHandler::FindPlatformDevice(std::string path, std::string* platform_device_path) const {
    165     platform_device_path->clear();
    166 
    167     // Uevents don't contain the mount point, so we need to add it here.
    168     path.insert(0, sysfs_mount_point_);
    169 
    170     std::string directory = Dirname(path);
    171 
    172     while (directory != "/" && directory != ".") {
    173         std::string subsystem_link_path;
    174         if (Realpath(directory + "/subsystem", &subsystem_link_path) &&
    175             subsystem_link_path == sysfs_mount_point_ + "/bus/platform") {
    176             // We need to remove the mount point that we added above before returning.
    177             directory.erase(0, sysfs_mount_point_.size());
    178             *platform_device_path = directory;
    179             return true;
    180         }
    181 
    182         auto last_slash = path.rfind('/');
    183         if (last_slash == std::string::npos) return false;
    184 
    185         path.erase(last_slash);
    186         directory = Dirname(path);
    187     }
    188 
    189     return false;
    190 }
    191 
    192 void DeviceHandler::FixupSysPermissions(const std::string& upath,
    193                                         const std::string& subsystem) const {
    194     // upaths omit the "/sys" that paths in this list
    195     // contain, so we prepend it...
    196     std::string path = "/sys" + upath;
    197 
    198     for (const auto& s : sysfs_permissions_) {
    199         if (s.MatchWithSubsystem(path, subsystem)) s.SetPermissions(path);
    200     }
    201 
    202     if (!skip_restorecon_ && access(path.c_str(), F_OK) == 0) {
    203         LOG(VERBOSE) << "restorecon_recursive: " << path;
    204         if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
    205             PLOG(ERROR) << "selinux_android_restorecon(" << path << ") failed";
    206         }
    207     }
    208 }
    209 
    210 std::tuple<mode_t, uid_t, gid_t> DeviceHandler::GetDevicePermissions(
    211     const std::string& path, const std::vector<std::string>& links) const {
    212     // Search the perms list in reverse so that ueventd.$hardware can override ueventd.rc.
    213     for (auto it = dev_permissions_.crbegin(); it != dev_permissions_.crend(); ++it) {
    214         if (it->Match(path) || std::any_of(links.cbegin(), links.cend(),
    215                                            [it](const auto& link) { return it->Match(link); })) {
    216             return {it->perm(), it->uid(), it->gid()};
    217         }
    218     }
    219     /* Default if nothing found. */
    220     return {0600, 0, 0};
    221 }
    222 
    223 void DeviceHandler::MakeDevice(const std::string& path, bool block, int major, int minor,
    224                                const std::vector<std::string>& links) const {
    225     auto[mode, uid, gid] = GetDevicePermissions(path, links);
    226     mode |= (block ? S_IFBLK : S_IFCHR);
    227 
    228     std::string secontext;
    229     if (!SelabelLookupFileContextBestMatch(path, links, mode, &secontext)) {
    230         PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
    231         return;
    232     }
    233     if (!secontext.empty()) {
    234         setfscreatecon(secontext.c_str());
    235     }
    236 
    237     dev_t dev = makedev(major, minor);
    238     /* Temporarily change egid to avoid race condition setting the gid of the
    239      * device node. Unforunately changing the euid would prevent creation of
    240      * some device nodes, so the uid has to be set with chown() and is still
    241      * racy. Fixing the gid race at least fixed the issue with system_server
    242      * opening dynamic input devices under the AID_INPUT gid. */
    243     if (setegid(gid)) {
    244         PLOG(ERROR) << "setegid(" << gid << ") for " << path << " device failed";
    245         goto out;
    246     }
    247     /* If the node already exists update its SELinux label to handle cases when
    248      * it was created with the wrong context during coldboot procedure. */
    249     if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && !secontext.empty()) {
    250         char* fcon = nullptr;
    251         int rc = lgetfilecon(path.c_str(), &fcon);
    252         if (rc < 0) {
    253             PLOG(ERROR) << "Cannot get SELinux label on '" << path << "' device";
    254             goto out;
    255         }
    256 
    257         bool different = fcon != secontext;
    258         freecon(fcon);
    259 
    260         if (different && lsetfilecon(path.c_str(), secontext.c_str())) {
    261             PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path
    262                         << "' device";
    263         }
    264     }
    265 
    266 out:
    267     chown(path.c_str(), uid, -1);
    268     if (setegid(AID_ROOT)) {
    269         PLOG(FATAL) << "setegid(AID_ROOT) failed";
    270     }
    271 
    272     if (!secontext.empty()) {
    273         setfscreatecon(nullptr);
    274     }
    275 }
    276 
    277 // replaces any unacceptable characters with '_', the
    278 // length of the resulting string is equal to the input string
    279 void SanitizePartitionName(std::string* string) {
    280     const char* accept =
    281         "abcdefghijklmnopqrstuvwxyz"
    282         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    283         "0123456789"
    284         "_-.";
    285 
    286     if (!string) return;
    287 
    288     std::string::size_type pos = 0;
    289     while ((pos = string->find_first_not_of(accept, pos)) != std::string::npos) {
    290         (*string)[pos] = '_';
    291     }
    292 }
    293 
    294 std::vector<std::string> DeviceHandler::GetBlockDeviceSymlinks(const Uevent& uevent) const {
    295     std::string device;
    296     std::string type;
    297 
    298     if (FindPlatformDevice(uevent.path, &device)) {
    299         // Skip /devices/platform or /devices/ if present
    300         static const std::string devices_platform_prefix = "/devices/platform/";
    301         static const std::string devices_prefix = "/devices/";
    302 
    303         if (StartsWith(device, devices_platform_prefix)) {
    304             device = device.substr(devices_platform_prefix.length());
    305         } else if (StartsWith(device, devices_prefix)) {
    306             device = device.substr(devices_prefix.length());
    307         }
    308 
    309         type = "platform";
    310     } else if (FindPciDevicePrefix(uevent.path, &device)) {
    311         type = "pci";
    312     } else if (FindVbdDevicePrefix(uevent.path, &device)) {
    313         type = "vbd";
    314     } else {
    315         return {};
    316     }
    317 
    318     std::vector<std::string> links;
    319 
    320     LOG(VERBOSE) << "found " << type << " device " << device;
    321 
    322     auto link_path = "/dev/block/" + type + "/" + device;
    323 
    324     if (!uevent.partition_name.empty()) {
    325         std::string partition_name_sanitized(uevent.partition_name);
    326         SanitizePartitionName(&partition_name_sanitized);
    327         if (partition_name_sanitized != uevent.partition_name) {
    328             LOG(VERBOSE) << "Linking partition '" << uevent.partition_name << "' as '"
    329                          << partition_name_sanitized << "'";
    330         }
    331         links.emplace_back(link_path + "/by-name/" + partition_name_sanitized);
    332         // Adds symlink: /dev/block/by-name/<partition_name>.
    333         if (boot_devices_.find(device) != boot_devices_.end()) {
    334             links.emplace_back("/dev/block/by-name/" + partition_name_sanitized);
    335         }
    336     }
    337 
    338     auto last_slash = uevent.path.rfind('/');
    339     links.emplace_back(link_path + "/" + uevent.path.substr(last_slash + 1));
    340 
    341     return links;
    342 }
    343 
    344 void DeviceHandler::HandleDevice(const std::string& action, const std::string& devpath, bool block,
    345                                  int major, int minor, const std::vector<std::string>& links) const {
    346     if (action == "add") {
    347         MakeDevice(devpath, block, major, minor, links);
    348         for (const auto& link : links) {
    349             if (!mkdir_recursive(Dirname(link), 0755)) {
    350                 PLOG(ERROR) << "Failed to create directory " << Dirname(link);
    351             }
    352 
    353             if (symlink(devpath.c_str(), link.c_str())) {
    354                 if (errno != EEXIST) {
    355                     PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link;
    356                 } else if (std::string link_path;
    357                            Readlink(link, &link_path) && link_path != devpath) {
    358                     PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link
    359                                 << ", which already links to: " << link_path;
    360                 }
    361             }
    362         }
    363     }
    364 
    365     if (action == "remove") {
    366         for (const auto& link : links) {
    367             std::string link_path;
    368             if (Readlink(link, &link_path) && link_path == devpath) {
    369                 unlink(link.c_str());
    370             }
    371         }
    372         unlink(devpath.c_str());
    373     }
    374 }
    375 
    376 void DeviceHandler::HandleDeviceEvent(const Uevent& uevent) {
    377     if (uevent.action == "add" || uevent.action == "change" || uevent.action == "online") {
    378         FixupSysPermissions(uevent.path, uevent.subsystem);
    379     }
    380 
    381     // if it's not a /dev device, nothing to do
    382     if (uevent.major < 0 || uevent.minor < 0) return;
    383 
    384     std::string devpath;
    385     std::vector<std::string> links;
    386     bool block = false;
    387 
    388     if (uevent.subsystem == "block") {
    389         block = true;
    390         devpath = "/dev/block/" + Basename(uevent.path);
    391 
    392         if (StartsWith(uevent.path, "/devices")) {
    393             links = GetBlockDeviceSymlinks(uevent);
    394         }
    395     } else if (const auto subsystem =
    396                    std::find(subsystems_.cbegin(), subsystems_.cend(), uevent.subsystem);
    397                subsystem != subsystems_.cend()) {
    398         devpath = subsystem->ParseDevPath(uevent);
    399     } else if (uevent.subsystem == "usb") {
    400         if (!uevent.device_name.empty()) {
    401             devpath = "/dev/" + uevent.device_name;
    402         } else {
    403             // This imitates the file system that would be created
    404             // if we were using devfs instead.
    405             // Minors are broken up into groups of 128, starting at "001"
    406             int bus_id = uevent.minor / 128 + 1;
    407             int device_id = uevent.minor % 128 + 1;
    408             devpath = StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id);
    409         }
    410     } else if (StartsWith(uevent.subsystem, "usb")) {
    411         // ignore other USB events
    412         return;
    413     } else {
    414         devpath = "/dev/" + Basename(uevent.path);
    415     }
    416 
    417     mkdir_recursive(Dirname(devpath), 0755);
    418 
    419     HandleDevice(uevent.action, devpath, block, uevent.major, uevent.minor, links);
    420 }
    421 
    422 DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
    423                              std::vector<SysfsPermissions> sysfs_permissions,
    424                              std::vector<Subsystem> subsystems, std::set<std::string> boot_devices,
    425                              bool skip_restorecon)
    426     : dev_permissions_(std::move(dev_permissions)),
    427       sysfs_permissions_(std::move(sysfs_permissions)),
    428       subsystems_(std::move(subsystems)),
    429       boot_devices_(std::move(boot_devices)),
    430       skip_restorecon_(skip_restorecon),
    431       sysfs_mount_point_("/sys") {}
    432 
    433 DeviceHandler::DeviceHandler()
    434     : DeviceHandler(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},
    435                     std::vector<Subsystem>{}, std::set<std::string>{}, false) {}
    436 
    437 }  // namespace init
    438 }  // namespace android
    439