diff options
Diffstat (limited to 'init/property_service.c')
| -rwxr-xr-x[-rw-r--r--] | init/property_service.c | 245 |
1 files changed, 137 insertions, 108 deletions
diff --git a/init/property_service.c b/init/property_service.c index 059db97..86e35f1 100644..100755 --- a/init/property_service.c +++ b/init/property_service.c @@ -40,6 +40,9 @@ #include <sys/atomics.h> #include <private/android_filesystem_config.h> +#include <selinux/selinux.h> +#include <selinux/label.h> + #include "property_service.h" #include "init.h" #include "util.h" @@ -76,8 +79,10 @@ struct { { "sys.", AID_SYSTEM, 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 }, @@ -85,6 +90,8 @@ struct { { "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 } }; @@ -116,7 +123,7 @@ static int init_workspace(workspace *w, size_t size) /* dev is a tmpfs that we can use to carve a shared workspace * out of, so let's do that... */ - fd = open("/dev/__properties__", O_RDWR | O_CREAT, 0600); + fd = open("/dev/__properties__", O_RDWR | O_CREAT | O_NOFOLLOW, 0600); if (fd < 0) return -1; @@ -129,7 +136,7 @@ static int init_workspace(workspace *w, size_t size) close(fd); - fd = open("/dev/__properties__", O_RDONLY); + fd = open("/dev/__properties__", O_RDONLY | O_NOFOLLOW); if (fd < 0) return -1; @@ -145,23 +152,11 @@ out: return -1; } -/* (8 header words + 247 toc words) = 1020 bytes */ -/* 1024 bytes header and toc + 247 prop_infos @ 128 bytes = 32640 bytes */ - -#define PA_COUNT_MAX 247 -#define PA_INFO_START 1024 -#define PA_SIZE 32768 - static workspace pa_workspace; -static prop_info *pa_info_array; - -extern prop_area *__system_property_area__; static int init_property_area(void) { - prop_area *pa; - - if(pa_info_array) + if (property_area_inited) return -1; if(init_workspace(&pa_workspace, PA_SIZE)) @@ -169,25 +164,54 @@ static int init_property_area(void) fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC); - pa_info_array = (void*) (((char*) pa_workspace.data) + PA_INFO_START); + __system_property_area_init(pa_workspace.data); - pa = pa_workspace.data; - memset(pa, 0, PA_SIZE); - pa->magic = PROP_AREA_MAGIC; - pa->version = PROP_AREA_VERSION; - - /* plug into the lib property services */ - __system_property_area__ = pa; property_area_inited = 1; return 0; } -static void update_prop_info(prop_info *pi, const char *value, unsigned len) +static int check_mac_perms(const char *name, char *sctx) { - pi->serial = pi->serial | 1; - memcpy(pi->value, value, len + 1); - pi->serial = (len << 24) | ((pi->serial + 1) & 0xffffff); - __futex_wake(&pi->serial, INT32_MAX); + 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); } /* @@ -196,17 +220,18 @@ static void update_prop_info(prop_info *pi, const char *value, unsigned len) * * Returns 1 if uid allowed, 0 otherwise. */ -static int check_control_perms(const char *name, unsigned int uid, unsigned int gid) { +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 1; + 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 1; + return check_control_mac_perms(name, sctx); } } } @@ -217,22 +242,22 @@ static int check_control_perms(const char *name, unsigned int uid, unsigned int * 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) +static int check_perms(const char *name, unsigned int uid, unsigned int gid, char *sctx) { int i; - if (uid == 0) - return 1; - if(!strncmp(name, "ro.", 3)) name +=3; + if (uid == 0) + return check_mac_perms(name, sctx); + for (i = 0; property_perms[i].prefix; i++) { - int tmp; 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 1; + + return check_mac_perms(name, sctx); } } } @@ -240,30 +265,19 @@ static int check_perms(const char *name, unsigned int uid, unsigned int gid) return 0; } -const char* property_get(const char *name) +int __property_get(const char *name, char *value) { - prop_info *pi; - - if(strlen(name) >= PROP_NAME_MAX) return 0; - - pi = (prop_info*) __system_property_find(name); - - if(pi != 0) { - return pi->value; - } else { - return 0; - } + return __system_property_get(name, value); } static void write_persistent_property(const char *name, const char *value) { - const char *tempPath = PERSISTENT_PROPERTY_DIR "/.temp"; + char tempPath[PATH_MAX]; char path[PATH_MAX]; - int fd, length; - - snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, name); + int fd; - fd = open(tempPath, O_WRONLY|O_CREAT|O_TRUNC, 0600); + snprintf(tempPath, sizeof(tempPath), "%s/.temp.XXXXXX", PERSISTENT_PROPERTY_DIR); + fd = mkstemp(tempPath); if (fd < 0) { ERROR("Unable to write persistent property to temp file %s errno: %d\n", tempPath, errno); return; @@ -271,6 +285,7 @@ static void write_persistent_property(const char *name, const char *value) write(fd, value, strlen(value)); close(fd); + snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, name); if (rename(tempPath, path)) { unlink(tempPath); ERROR("Unable to rename persistent property file %s to %s\n", tempPath, path); @@ -279,11 +294,11 @@ static void write_persistent_property(const char *name, const char *value) int property_set(const char *name, const char *value) { - prop_area *pa; prop_info *pi; + int ret; - int namelen = strlen(name); - int valuelen = strlen(value); + size_t namelen = strlen(name); + size_t valuelen = strlen(value); if(namelen >= PROP_NAME_MAX) return -1; if(valuelen >= PROP_VALUE_MAX) return -1; @@ -295,25 +310,13 @@ int property_set(const char *name, const char *value) /* ro.* properties may NEVER be modified once set */ if(!strncmp(name, "ro.", 3)) return -1; - pa = __system_property_area__; - update_prop_info(pi, value, valuelen); - pa->serial++; - __futex_wake(&pa->serial, INT32_MAX); + __system_property_update(pi, value, valuelen); } else { - pa = __system_property_area__; - if(pa->count == PA_COUNT_MAX) return -1; - - pi = pa_info_array + pa->count; - pi->serial = (valuelen << 24); - memcpy(pi->name, name, namelen + 1); - memcpy(pi->value, value, valuelen + 1); - - pa->toc[pa->count] = - (namelen << 24) | (((unsigned) pi) - ((unsigned) pa)); - - pa->count++; - pa->serial++; - __futex_wake(&pa->serial, INT32_MAX); + ret = __system_property_add(name, namelen, value, valuelen); + if (ret < 0) { + ERROR("Failed to set '%s'='%s'", name, value); + return ret; + } } /* If name starts with "net." treat as a DNS property. */ if (strncmp("net.", name, strlen("net.")) == 0) { @@ -333,26 +336,14 @@ int property_set(const char *name, const char *value) * to prevent them from being overwritten by default values. */ write_persistent_property(name, value); + } else if (strcmp("selinux.reload_policy", name) == 0 && + strcmp("1", value) == 0) { + selinux_reload_policy(); } property_changed(name, value); return 0; } -static int property_list(void (*propfn)(const char *key, const char *value, void *cookie), - void *cookie) -{ - char name[PROP_NAME_MAX]; - char value[PROP_VALUE_MAX]; - const prop_info *pi; - unsigned n; - - for(n = 0; (pi = __system_property_find_nth(n)); n++) { - __system_property_read(pi, name, value); - propfn(name, value, cookie); - } - return 0; -} - void handle_property_set_fd() { prop_msg msg; @@ -363,6 +354,7 @@ void handle_property_set_fd() 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; @@ -371,13 +363,13 @@ void handle_property_set_fd() /* Check socket options here */ if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) { close(s); - ERROR("Unable to recieve socket options\n"); + 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 recieved: %d expected: %d errno: %d\n", + ERROR("sys_prop: mis-match msg size received: %d expected: %d errno: %d\n", r, sizeof(prop_msg), errno); close(s); return; @@ -388,18 +380,20 @@ void handle_property_set_fd() msg.name[PROP_NAME_MAX-1] = 0; msg.value[PROP_VALUE_MAX-1] = 0; + 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)) { + 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)) { + 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", @@ -411,6 +405,7 @@ void handle_property_set_fd() // the property is written to memory. close(s); } + freecon(source_ctx); break; default: @@ -468,12 +463,14 @@ static void load_properties_from_file(const char *fn) static void load_persistent_properties() { DIR* dir = opendir(PERSISTENT_PROPERTY_DIR); + int dir_fd; struct dirent* entry; - char path[PATH_MAX]; char value[PROP_VALUE_MAX]; int fd, length; + struct stat sb; if (dir) { + dir_fd = dirfd(dir); while ((entry = readdir(dir)) != NULL) { if (strncmp("persist.", entry->d_name, strlen("persist."))) continue; @@ -482,20 +479,39 @@ static void load_persistent_properties() continue; #endif /* open the file and read the property value */ - snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, entry->d_name); - fd = open(path, O_RDONLY); - if (fd >= 0) { - length = read(fd, value, sizeof(value) - 1); - if (length >= 0) { - value[length] = 0; - property_set(entry->d_name, value); - } else { - ERROR("Unable to read persistent property file %s errno: %d\n", path, errno); - } + fd = openat(dir_fd, entry->d_name, O_RDONLY | O_NOFOLLOW); + if (fd < 0) { + ERROR("Unable to open persistent property file \"%s\" errno: %d\n", + entry->d_name, errno); + continue; + } + if (fstat(fd, &sb) < 0) { + ERROR("fstat on property file \"%s\" failed errno: %d\n", entry->d_name, errno); + close(fd); + continue; + } + + // File must not be accessible to others, be owned by root/root, and + // not be a hard link to any other file. + if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) + || (sb.st_uid != 0) + || (sb.st_gid != 0) + || (sb.st_nlink != 1)) { + ERROR("skipping insecure property file %s (uid=%lu gid=%lu nlink=%d mode=%o)\n", + entry->d_name, sb.st_uid, sb.st_gid, sb.st_nlink, sb.st_mode); close(fd); + continue; + } + + length = read(fd, value, sizeof(value) - 1); + if (length >= 0) { + value[length] = 0; + property_set(entry->d_name, value); } else { - ERROR("Unable to open persistent property file %s errno: %d\n", path, errno); + ERROR("Unable to read persistent property file %s errno: %d\n", + entry->d_name, errno); } + close(fd); } closedir(dir); } else { @@ -520,6 +536,19 @@ int properties_inited(void) return property_area_inited; } +static void load_override_properties() { +#ifdef ALLOW_LOCAL_PROP_OVERRIDE + char debuggable[PROP_VALUE_MAX]; + int ret; + + ret = property_get("ro.debuggable", debuggable); + if (ret && (strcmp(debuggable, "1") == 0)) { + load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE); + } +#endif /* ALLOW_LOCAL_PROP_OVERRIDE */ +} + + /* When booting an encrypted system, /data is not mounted when the * property service is started, so any properties stored there are * not loaded. Vold triggers init to load these properties once it @@ -527,7 +556,7 @@ int properties_inited(void) */ void load_persist_props(void) { - load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE); + load_override_properties(); /* Read persistent properties after all default values have been loaded. */ load_persistent_properties(); } @@ -538,7 +567,7 @@ void start_property_service(void) load_properties_from_file(PROP_PATH_SYSTEM_BUILD); load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT); - load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE); + load_override_properties(); /* Read persistent properties after all default values have been loaded. */ load_persistent_properties(); |
