安卓SystemProperties.set()方法权限问题
本文首发于:安卓SystemProperties.set()方法权限问题
对于系统中的每个进程来说:
读取属性值对任何进程都是没有限制的,直接由本进程从共享区域中读取。
修改属性值则必须通过Init进程完成,同时Init进程还需要检查发起请求的进程是否具有相应的权限。
安卓5.0之前
安卓4.4 system/core/init/property_service.c源代码:
..........
/* White list of permissions for setting property services. */
struct {
const char *prefix;
unsigned int uid;
unsigned int gid;
} property_perms[] = {
{
"net.rmnet0.", AID_RADIO, 0 },
{
"net.gprs.", AID_RADIO, 0 },
{
"net.ppp", AID_RADIO, 0 },
{
"net.qmi", AID_RADIO, 0 },
{
"net.lte", AID_RADIO, 0 },
{
"net.cdma", AID_RADIO, 0 },
{
"ril.", AID_RADIO, 0 },
{
"gsm.", AID_RADIO, 0 },
{
"persist.radio", AID_RADIO, 0 },
{
"net.dns", AID_RADIO, 0 },
{
"sys.usb.config", AID_RADIO, 0 },
{
"net.", AID_SYSTEM, 0 },
{
"dev.", AID_SYSTEM, 0 },
{
"runtime.", AID_SYSTEM, 0 },
{
"hw.", AID_SYSTEM, 0 },
{
"sys.", AID_SYSTEM, 0 },
{
"sys.powerctl", AID_SHELL, 0 },
{
"service.", AID_SYSTEM, 0 },
{
"wlan.", AID_SYSTEM, 0 },
{
"bluetooth.", AID_BLUETOOTH, 0 },
{
"dhcp.", AID_SYSTEM, 0 },
{
"dhcp.", AID_DHCP, 0 },
{
"debug.", AID_SYSTEM, 0 },
{
"debug.", AID_SHELL, 0 },
{
"log.", AID_SHELL, 0 },
{
"service.adb.root", AID_SHELL, 0 },
{
"service.adb.tcp.port", AID_SHELL, 0 },
{
"persist.sys.", AID_SYSTEM, 0 },
{
"persist.service.", AID_SYSTEM, 0 },
{
"persist.security.", AID_SYSTEM, 0 },
{
"persist.service.bdroid.", AID_BLUETOOTH, 0 },
{
"selinux." , AID_SYSTEM, 0 },
{
NULL, 0, 0 }
};
.........
static int check_mac_perms(const char *name, char *sctx)
{
if (is_selinux_enabled() <= 0)
return 1;
char *tctx = NULL;
const char *class = "property_service";
const char *perm = "set";
int result = 0;
if (!sctx)
goto err;
if (!sehandle_prop)
goto err;
if (selabel_lookup(sehandle_prop, &tctx, name, 1) != 0)
goto err;
if (selinux_check_access(sctx, tctx, class, perm, name) == 0)
result = 1;
freecon(tctx);
err:
return result;
}
static int check_control_mac_perms(const char *name, char *sctx)
{
/*
* Create a name prefix out of ctl.<service name>
* The new prefix allows the use of the existing
* property service backend labeling while avoiding
* mislabels based on true property prefixes.
*/
char ctl_name[PROP_VALUE_MAX+4];
int ret = snprintf(ctl_name, sizeof(ctl_name), "ctl.%s", name);
if (ret < 0 || (size_t) ret >= sizeof(ctl_name))
return 0;
return check_mac_perms(ctl_name, sctx);
}
/*
* Checks permissions for starting/stoping system services.
* AID_SYSTEM and AID_ROOT are always allowed.
*
* Returns 1 if uid allowed, 0 otherwise.
*/
static int check_control_perms(const char *name, unsigned int uid, unsigned int gid, char *sctx) {
int i;
if (uid == AID_SYSTEM || uid == AID_ROOT)
return check_control_mac_perms(name, sctx);
/* Search the ACL */
for (i = 0; control_perms[i].service; i++) {
if (strcmp(control_perms[i].service, name) == 0) {
if ((uid && control_perms[i].uid == uid) ||
(gid && control_perms[i].gid == gid)) {
return check_control_mac_perms(name, sctx);
}
}
}
return 0;
}
/*
* Checks permissions for setting system properties.
* Returns 1 if uid allowed, 0 otherwise.
*/
static int check_perms(const char *name, unsigned int uid, unsigned int gid, char *sctx)
{
int i;
unsigned int app_id;
if(!strncmp(name, "ro.", 3))
name +=3;
if (uid == 0)
return check_mac_perms(name, sctx);
app_id = multiuser_get_app_id(uid);
if (app_id == AID_BLUETOOTH) {
uid = app_id;
}
for (i = 0; property_perms[i].prefix; i++) {
if (strncmp(property_perms[i].prefix, name,
strlen(property_perms[i].prefix)) == 0) {
if ((uid && property_perms[i].uid == uid) ||
(gid && property_perms[i].gid == gid)) {
return check_mac_perms(name, sctx);
}
}
}
return 0;
}
.......
void handle_property_set_fd()
{
prop_msg msg;
int s;
int r;
int res;
struct ucred cr;
struct sockaddr_un addr;
socklen_t addr_size = sizeof(addr);
socklen_t cr_size = sizeof(cr);
char * source_ctx = NULL;
if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) {
return;
}
/* Check socket options here */
if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
close(s);
ERROR("Unable to receive socket options\n");
return;
}
r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), 0));
if(r != sizeof(prop_msg)) {
ERROR("sys_prop: mis-match msg size received: %d expected: %d errno: %d\n",
r, sizeof(prop_msg), errno);
close(s);
return;
}
switch(msg.cmd) {
case PROP_MSG_SETPROP:
msg.name[PROP_NAME_MAX-1] = 0;
msg.value[PROP_VALUE_MAX-1] = 0;
if (!is_legal_property_name(msg.name, strlen(msg.name))) {
ERROR("sys_prop: illegal property name. Got: \"%s\"\n", msg.name);
close(s);
return;
}
getpeercon(s, &source_ctx);
if(memcmp(msg.name,"ctl.",4) == 0) {
// Keep the old close-socket-early behavior when handling
// ctl.* properties.
close(s);
if (check_control_perms(msg.value, cr.uid, cr.gid, source_ctx)) {
handle_control_message((char*) msg.name + 4, (char*) msg.value);
} else {
ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d\n",
msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid);
}
} else {
if (check_perms(msg.name, cr.uid, cr.gid, source_ctx)) {
property_set((char*) msg.name, (char*) msg.value);
} else {
ERROR("sys_prop: permission denied uid:%d name:%s\n",
cr.uid, msg.name);
}
// Note: bionic's property client code assumes that the
// property server will not close the socket until *AFTER*
// the property is written to memory.
close(s);
}
freecon(source_ctx);
break;
default:
close(s);
break;
}
}
从上面代码可以看出,有一个白名单列表来判断应用是否有权限来设置属性。一般是uid为AID_SYSTEM的系统级应用才有权限设置属性,而且属性名字前缀也要匹配白名单,比如persist.sys.开头。
安卓5开始
安卓5.0.1 system/core/init/property_service.c源代码:
.......
static int check_mac_perms(const char *name, char *sctx)
{
if (is_selinux_enabled() <= 0)
return 1;
char *tctx = NULL;
const char *class = "property_service";
const char *perm = "set";
int result = 0;
if (!sctx)
goto err;
if (!sehandle_prop)
goto err;
if (selabel_lookup(sehandle_prop, &tctx, name, 1) != 0)
goto err;
if (selinux_check_access(sctx, tctx, class, perm, (void*) name) == 0)
result = 1;
freecon(tctx);
err:
return result;
}
static int check_control_mac_perms(const char *name, char *sctx)
{
/*
* Create a name prefix out of ctl.<service name>
* The new prefix allows the use of the existing
* property service backend labeling while avoiding
* mislabels based on true property prefixes.
*/
char ctl_name[PROP_VALUE_MAX+4];
int ret = snprintf(ctl_name, sizeof(ctl_name), "ctl.%s", name);
if (ret < 0 || (size_t) ret >= sizeof(ctl_name))
return 0;
return check_mac_perms(ctl_name, sctx);
}
/*
* Checks permissions for setting system properties.
* Returns 1 if uid allowed, 0 otherwise.
*/
static int check_perms(const char *name, char *sctx)
{
int i;
unsigned int app_id;
if(!strncmp(name, "ro.", 3))
name +=3;
return check_mac_perms(name, sctx);
}
.......
void handle_property_set_fd()
{
prop_msg msg;
int s;
int r;
int res;
struct ucred cr;
struct sockaddr_un addr;
socklen_t addr_size = sizeof(addr);
socklen_t cr_size = sizeof(cr);
char * source_ctx = NULL;
struct pollfd ufds[1];
const int timeout_ms = 2 * 1000; /* Default 2 sec timeout for caller to send property. */
int nr;
if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) {
return;
}
/* Check socket options here */
if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
close(s);
ERROR("Unable to receive socket options\n");
return;
}
ufds[0].fd = s;
ufds[0].events = POLLIN;
ufds[0].revents = 0;
nr = TEMP_FAILURE_RETRY(poll(ufds, 1, timeout_ms));
if (nr == 0) {
ERROR("sys_prop: timeout waiting for uid=%d to send property message.\n", cr.uid);
close(s);
return;
} else if (nr < 0) {
ERROR("sys_prop: error waiting for uid=%d to send property message. err=%d %s\n", cr.uid, errno, strerror(errno));
close(s);
return;
}
r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), MSG_DONTWAIT));
if(r != sizeof(prop_msg)) {
ERROR("sys_prop: mis-match msg size received: %d expected: %zu errno: %d\n",
r, sizeof(prop_msg), errno);
close(s);
return;
}
switch(msg.cmd) {
case PROP_MSG_SETPROP:
msg.name[PROP_NAME_MAX-1] = 0;
msg.value[PROP_VALUE_MAX-1] = 0;
if (!is_legal_property_name(msg.name, strlen(msg.name))) {
ERROR("sys_prop: illegal property name. Got: \"%s\"\n", msg.name);
close(s);
return;
}
getpeercon(s, &source_ctx);
if(memcmp(msg.name,"ctl.",4) == 0) {
// Keep the old close-socket-early behavior when handling
// ctl.* properties.
close(s);
if (check_control_mac_perms(msg.value, source_ctx)) {
handle_control_message((char*) msg.name + 4, (char*) msg.value);
} else {
ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d\n",
msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid);
}
} else {
if (check_perms(msg.name, source_ctx)) {
property_set((char*) msg.name, (char*) msg.value);
} else {
ERROR("sys_prop: permission denied uid:%d name:%s\n",
cr.uid, msg.name);
}
// Note: bionic's property client code assumes that the
// property server will not close the socket until *AFTER*
// the property is written to memory.
close(s);
}
freecon(source_ctx);
break;
default:
close(s);
break;
}
}
.......
去掉白名单机制,引入selinux,通过selinux来控制权限访问。如果selinux关闭,则任何应用都可以设置,不再限定系统应用。(但selinux可以设置只放行系统级应用)。
例子参考:system.prop添加自己的字段