devices.cpp revision ed506f7356346b74eabcf45e207f9afe54b63089
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 (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)
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
476DeviceHandler::DeviceHandler()
477    : DeviceHandler(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},
478                    std::vector<Subsystem>{}) {}
479