在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)中的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);
}