浅谈android add_service PERMISSION DENIED问题
背景:
在Android开发中,经常会用到ServiceManager.addService(name, service);添加系统服务
也许你会很迷惑,在程序运行时,部分service name能成功添加,而部分service name会抛出如下异常:
Tag: ServiceManagerMessage: 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)中的xxx前n个字符比较 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(安卓)Camera Hal 的初步实现1
- Java、Android中Math详解
- Android(安卓)ril原生代码(C/C++)和java代码部分分析
- 【Android】第5章(4) 单选和复选
- Android使用mob快速集成分享功能详解
- Android热修复之Tinker接入流程
- Android(安卓)Intent机制实例详解(1)
- Android属性动画、函数动画
- android 为桌面图标添加数字角标