2

我正在制作基于 AOSP Nougat 最新源的定制 bsp。

Android 服务进程要求服务管理器查找或添加服务。服务管理器尝试通过调用 svc_can_register() 或 svc_can_find() 来检查 mac 权限,后者调用 check_mac_perms(),后者调用 getpidcon()。

让我们看看 svc_can_find()

static int svc_can_find(const uint16_t *name, size_t name_len, pid_t spid, uid_t uid)
{
    const char *perm = "find";
    return check_mac_perms_from_lookup(spid, uid, perm, str8(name, name_len)) ? 1 : 0;
}

check_mac_perms_from_lookup() 是这样的:

static bool check_mac_perms_from_lookup(pid_t spid, uid_t uid, const char *perm, const char *name)
{
    bool allowed;
    char *tctx = NULL;

    if (selinux_enabled <= 0) {
        return true;
    }

    if (!sehandle) {
        ALOGE("SELinux: Failed to find sehandle. Aborting service_manager.\n");
        abort();
    }

    if (selabel_lookup(sehandle, &tctx, name, 0) != 0) {
        ALOGE("SELinux: No match for %s in service_contexts.\n", name);
        return false;
    }

    allowed = check_mac_perms(spid, uid, tctx, perm, name);
    freecon(tctx);
    return allowed;
}

它调用 check_mac_perms()。check_mac_perms() 像这样:

static bool check_mac_perms(pid_t spid, uid_t uid, const char *tctx, const char *perm, const char *name)
{
    char *sctx = NULL;
    const char *class = "service_manager";
    bool allowed;
    struct audit_data ad;
    if (getpidcon(spid, &sctx) < 0) {
        ALOGE("SELinux: getpidcon(pid=%d) failed to retrieve pid context.\n", spid);
        return false;
    }
    ad.pid = spid;
    ad.uid = uid;
    ad.name = name;
    int result = selinux_check_access(sctx, tctx, class, perm, (void *) &ad);
    allowed = (result == 0);
    freecon(sctx);
    return allowed;
}

它调用 getpidcon()。getpidcon() 在 external/selinux/libselinux/src/procattr.c 中定义

getpidcon() 定义如下:

#define getpidattr_def(fn, attr) \
    int get##fn(pid_t pid, char **c)    \
    { \
        if (pid <= 0) { \
            errno = EINVAL; \
            return -1; \
        } else { \
            return getprocattrcon(c, pid, #attr); \
        } \
    }

...
...
    getpidattr_def(pidcon, current)

"getpidattr_def(pidcon, current)" 扩展为 getpidcon() 函数定义并调用 getprocatrcon()

getprocattrcon() 是这样的:

static int getprocattrcon(char ** context,
              pid_t pid, const char *attr)
{
    char *buf;
    size_t size;
    int fd;
    ssize_t ret;
    int errno_hold;

    fd = openattr(pid, attr, O_RDONLY);
    if (fd < 0)
        return -1;

    size = selinux_page_size;
    buf = malloc(size);
    if (!buf) {
        ret = -1;
        goto out;
    }
    memset(buf, 0, size);

    do {
        ret = read(fd, buf, size - 1);
    } while (ret < 0 && errno == EINTR);
    if (ret < 0)
        goto out2;

    if (ret == 0) {
        *context = NULL;
        goto out2;
    }

    *context = strdup(buf);
    if (!(*context)) {
        ret = -1;
        goto out2;
    }

    ret = 0;
      out2:
    free(buf);
      out:
    errno_hold = errno;
    close(fd);
    errno = errno_hold;
    return ret;
}

很简单吧?只需打开一些文件并读取内容并通过函数参数返回。

它在 openattr() 处失败。我已经通过在 openattr() 中插入一些日志函数来确认这一点。openattr() 也是一个简单的函数。

static int openattr(pid_t pid, const char *attr, int flags)
{
    int fd, rc;
    char *path;
    pid_t tid;

    if (pid > 0) {
        rc = asprintf(&path, "/proc/%d/attr/%s", pid, attr);
    } else if (pid == 0) {
        rc = asprintf(&path, "/proc/thread-self/attr/%s", attr);
        if (rc < 0)
            return -1;
        fd = open(path, flags | O_CLOEXEC);
        if (fd >= 0 || errno != ENOENT)
            goto out;
        free(path);
        tid = gettid();
        rc = asprintf(&path, "/proc/self/task/%d/attr/%s", tid, attr);
    } else {
        errno = EINVAL;
        return -1;
    }
    if (rc < 0)
        return -1;

    fd = open(path, flags | O_CLOEXEC);
out:
    free(path);
    return fd;
}

失败点是“fd = open(path, flags | O_CLOEXEC);”

即使文件存在,几乎总是打开失败。我不明白这一点,想知道是什么导致了问题。我通过插入一些日志打印代码、检查 android log(adb logcat) 并从 android shell(adb shell) 读取文件来确认失败,例如“cat /proc/412/attr/current”。'cat ...' 读取成功,但日志显示打开文件失败。奇怪的是,如果 'pid' 为 0,它就成功了。

如果打开失败,将无法启动服务,导致系统无法正常启动。如果我忽略失败并从 getpidcon() 返回成功,系统会正确启动,但这显然不是正确的做法。

我正在将 bsp 测试为 selinux 许可模式。

谁能有我这样的经历?如果有人,请分享经验和问题的解决方案。

谢谢你。李三永。

4

0 回答 0