why getpidcon() error on AOSP 7.1.2?

252 views Asked by At

I am working on making a customized bsp based on AOSP Nougat latest source.

Android service process ask service manager to find or add the service. And service manager try to check mac permissions by calling svc_can_register() or svc_can_find() which calls check_mac_perms() which calls getpidcon().

Let's see 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() is like this:

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;
}

It calls check_mac_perms(). check_mac_perms() like this:

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;
}

It calls getpidcon(). getpidcon() is defined in external/selinux/libselinux/src/procattr.c

getpidcon() is defined like this:

#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)" is expanded to getpidcon() function definition and it calls getprocatrcon()

getprocattrcon() is like this:

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;
}

Pretty simple huh? Just opening some files and reading the contents and return it by function argument.

It fails at openattr(). I've confirmed this by inserting some log function in openattr(). openattr() is also simple function.

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;
}

The fail point is "fd = open(path, flags | O_CLOEXEC);"

Even if the file exists, almost always opening fails. I don't understand ths and want to know what caused the problem. I've confirmed the failure by inserting some log printing codes, checking android log(adb logcat) and reading the file from android shell(adb shell), e.g. 'cat /proc/412/attr/current'. Reading by 'cat ...' succeeded but log shows the opening the file fails. The odd thing is if 'pid' is 0, it succeeds.

If opening fails, services can't be launched so the system don't boot properly. If I ignore the fails and return success from getpidcon() the system boots properly but this is not the right thing to do obviously.

I'm testing the bsp as selinux permissive mode.

Can anyone have a experience like me? If anyone, please share the experience and the solution of the problem.

Thank you. Sangyong Lee.

0

There are 0 answers