浅谈android add_service PERMISSION DENIED问题

背景:

在Android开发中,经常会用到ServiceManager.addService(name, service);添加系统服务
也许你会很迷惑,在程序运行时,部分service name能成功添加,而部分service name会抛出如下异常:


Tag: ServiceManager
Message: add_service('xxx',xx) uid=xxxx - PERMISSION DENIED

这是Android SELinux 的policy配置引起的,在读了部分SELinux的源码之后就很容易理解这个异常了
接下来我会先给出解决办法,然后再进行分析。

解决办法:

针对不同得设备,可以在device或vendor目录下的BoardConfig.mk中使用BOARD_SEPOLICY_DIRS变量指定配置文件的目录;
新的service_type定义在service.te中,对应该service_type的service name定义在service_contexts中。

注: service_manager 不能add default_android_service类型的service

例如:
针对samsung tuna设备:device/samsung/tuna/BoardConfig.mk

BOARD_SEPOLICY_DIRS += device/samsung/tuna/sepolicy

device/samsung/tuna/sepolicy/service.te

type yyy,           service_manager_type

device/samsung/tuna/sepolicy/service_contexts

xxx             u:object_r:yyy:s0

这样再使用ServiceManager.addService(“xxx”, this);添加系统服务就不会抛异常了。

分析:

service_manager初始化以及ServiceManager.addService(“xxx”, this);方法调用流程:
service_manager.c

static bool check_mac_perms_from_lookup(pid_t spid, uid_t uid, const char *perm, const char *name)
{
    ...
    if (selabel_lookup(sehandle, &tctx, name, 0) != 0) {    //libselinux/src/label.c : selabel_lookup()
                                                            //service name 会在该函数中进行匹配
        ALOGE("SELinux: No match for %s in service_contexts.\n", name);
        return false;
    }
    ...
}

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

int do_add_service(struct binder_state *bs,
                   const uint16_t *s, size_t len,
                   uint32_t handle, uid_t uid, int allow_isolated,
                   pid_t spid)
{
    ...
    if (!svc_can_register(s, len, spid, uid)) { //若svc_can_registe()返回false,则抛出上述异常
        ALOGE("add_service('%s',%x) uid=%d - PERMISSION DENIED\n",
             str8(s, len), handle, uid);
        return -1;
    }
    ...
}

int svcmgr_handler(struct binder_state *bs,
                   struct binder_transaction_data *txn,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
    ...
    switch(txn->code) {
    case SVC_MGR_GET_SERVICE:
    case SVC_MGR_CHECK_SERVICE:
        ...
        handle = do_find_service(bs, s, len, txn->sender_euid, txn->sender_pid);
        ...
        return 0;
    case SVC_MGR_ADD_SERVICE:
        ...
        if (do_add_service(bs, s, len, handle, txn->sender_euid,
            allow_isolated, txn->sender_pid))   //ServiceManager.addService("xxx", this);最终调用do_add_service函数
            return -1;
        break;

    case SVC_MGR_LIST_SERVICES: {
        uint32_t n = bio_get_uint32(msg);

        if (!svc_can_list(txn->sender_pid, txn->sender_euid)) {
            ALOGE("list_service() uid=%d - PERMISSION DENIED\n",
                    txn->sender_euid);
            return -1;
        }
        si = svclist;
        while ((n-- > 0) && si)
            si = si->next;
        if (si) {
            bio_put_string16(reply, si->name);
            return 0;
        }
        return -1;
    }
    default:
        ...
    }

    bio_put_uint32(reply, 0);
    return 0;
}

int main(int argc, char **argv)
{
    struct binder_state *bs;

    bs = binder_open(128*1024); //binder.c : binder_open(size_t mapsize)
                                //bs->fd = open("/dev/binder", O_RDWR);
                                //bs->mapsize = mapsize;
                                //bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
    ...
    sehandle = selinux_android_service_context_handle();    //libselinux/src/android.c  :   selinux_android_service_context_handle(void)
    ...
    union selinux_callback cb;
    cb.func_audit = audit_callback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);
    ...

    binder_loop(bs, svcmgr_handler);    //binder.c : binder_loop(struct binder_state *bs, binder_handler func)
                                        //无限循环读取解析处理消息回调svcmgr_handler函数
    return 0;
}

libselinux/src/label_internal.h

struct selabel_handle {
    /* arguments that were passed to selabel_open */
    unsigned int backend;
    int validating;

    /* labeling operations */
    struct selabel_lookup_rec *(*func_lookup) (struct selabel_handle *h,
                           const char *key, int type);
    void (*func_close) (struct selabel_handle *h);
    void (*func_stats) (struct selabel_handle *h);
    bool (*func_partial_match) (struct selabel_handle *h, const char *key);
    struct selabel_lookup_rec *(*func_lookup_best_match) (struct selabel_handle *h,
                             const char *key,
                             const char **aliases,
                             int type);

    /* supports backend-specific state information */
    void *data;

    /* substitution support */
    struct selabel_sub *subs;
};

libselinux/src/android.c

static const struct selinux_opt seopts_service[] = {
    { SELABEL_OPT_PATH, "/service_contexts" },  //service_contexts文件中配置service name
    { SELABEL_OPT_PATH, "/data/security/current/service_contexts" },
    { 0, NULL }
};

struct selabel_handle* selinux_android_service_context_handle(void)
{
    struct selabel_handle* sehandle;

    set_policy_index();
    sehandle = selabel_open(SELABEL_CTX_ANDROID_PROP/* 4 */,
            &seopts_service[policy_index/* 0 /service_contexts */], 1);
    ...
    return sehandle;
}

libselinux/src/label.c

static selabel_initfunc initfuncs[] = {
    &selabel_file_init,
    &selabel_media_init,
    &selabel_x_init,
    &selabel_db_init,
    &selabel_property_init, //最终在该函数中设置selabel_handle结构体内容
};

struct selabel_handle *selabel_open(unsigned int backend,
                    const struct selinux_opt *opts,
                    unsigned nopts)
{
    ...
    if ((*initfuncs[backend/* 4 selabel_property_init*/])(rec, opts, nopts)) {
        free(rec);
        rec = NULL;
    }
    ...
}

static struct selabel_lookup_rec *
selabel_lookup_common(struct selabel_handle *rec,
              const char *key, int type)
{
    struct selabel_lookup_rec *lr;
    lr = rec->func_lookup(rec, key, type); //libselinux/src/label_android_property.c : lookup函数
    if (!lr)
        return NULL;

    return lr;
}

int selabel_lookup(struct selabel_handle *rec, char **con,
           const char *key, int type)
{
    ...
    lr = selabel_lookup_common(rec, key, type);
    ...
}

libselinux/src/label_android_property.c

static struct selabel_lookup_rec *lookup(struct selabel_handle *rec, 
                     const char *key, 
                     int __attribute__((unused)) type)
{
    struct saved_data *data = (struct saved_data *)rec->data;
    spec_t *spec_arr = data->spec_arr;
    unsigned int i;
    struct selabel_lookup_rec *ret = NULL;

    if (!data->nspec) {
        errno = ENOENT;
        goto finish;
    }

    for (i = 0; i < data->nspec; i++) {
        if (strncmp(spec_arr[i].property_key, key, 
            strlen(spec_arr[i].property_key)) == 0) {   //取service_contexts中的service name,
                                                        //并且根据该service name的字符串长度n
                                                        //与addService("xxx", this)中的xxxn个字符比较
            break;
        }
        if (strncmp(spec_arr[i].property_key, "*", 1) == 0)
            break;
    }

    if (i >= data->nspec) {
        /* No matching specification. */
        errno = ENOENT;
        goto finish;
    }

    ret = &spec_arr[i].lr;

finish:
    return ret;
}

int selabel_property_init(struct selabel_handle *rec,
              const struct selinux_opt *opts,
              unsigned nopts)
{
    struct saved_data *data;

    data = (struct saved_data *)malloc(sizeof(*data));
    if (!data)
        return -1;
    memset(data, 0, sizeof(*data));

    rec->data = data;
    rec->func_close = &closef;
    rec->func_stats = &stats;
    rec->func_lookup = &lookup; //初始化selabel_handle : func_lookup函数指针,service name匹配函数

    return init(rec, opts, nopts);
}

你可能感兴趣的:(Android,零碎的问题)