浅谈android add_service PERMISSION DENIED问题

Posted ximsfei

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈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 add_service PERMISSION DENIED问题的主要内容,如果未能解决你的问题,请参考以下文章

Android Widget 小部件(四---完结) 使用ListViewGridViewStackViewViewFlipper展示Widget

flutter Android CPU包构建命令 --split-per-abi

Android开发实战(二十一):浅谈android:clipChildren属性

Android项目实战(十三):浅谈EventBus

浅谈Android编码规范及命名规范

浅谈Android 01