Reset access to system properties on reinitialization

Treat subsequent calls to __system_properties_init() as a
reinitialization of system properties and revoke access to prop files
that have been previously mapped but that the process's current context
does not have access to.  Additionally reset the no_access_ flag in
case permissions have loosened and previously unaccessible files can now
be accessed.

This is meant to work around an issue that setcon() does not revoke
mmap() mappings, so we must manually revoke them after a successful
setcon() call.

Bug 26114086

Change-Id: I4d690abb6817283ca64ac26ea4c1dad398a98fbc
This commit is contained in:
Tom Cherry 2015-12-09 15:48:15 -08:00
parent e371ae68ac
commit b417169d80

View File

@ -200,6 +200,7 @@ static char property_filename[PROP_FILENAME_MAX] = PROP_FILENAME;
static bool compat_mode = false;
static size_t pa_data_size;
static size_t pa_size;
static bool initialized = false;
// NOTE: This isn't static because system_properties_compat.c
// requires it.
@ -642,22 +643,33 @@ bool prop_area::foreach(void (*propfn)(const prop_info* pi, void* cookie), void*
return foreach_property(root_node(), propfn, cookie);
}
struct context_node {
context_node(struct context_node* next, const char* context, prop_area* pa)
: context(strdup(context)), pa(pa), checked_access(false), next(next) {
lock.init(false);
class context_node {
public:
context_node(context_node* next, const char* context, prop_area* pa)
: next(next), context_(strdup(context)), pa_(pa), no_access_(false) {
lock_.init(false);
}
~context_node() {
if (pa) {
munmap(pa, pa_size);
}
free(context);
unmap();
free(context_);
}
Lock lock;
char* context;
prop_area* pa;
bool checked_access;
struct context_node* next;
bool open(bool access_rw, bool* fsetxattr_failed);
bool check_access_and_open();
void reset_access();
const char* context() const { return context_; }
prop_area* pa() { return pa_; }
context_node* next;
private:
bool check_access();
void unmap();
Lock lock_;
char* context_;
prop_area* pa_;
bool no_access_;
};
struct prefix_node {
@ -733,32 +745,50 @@ static context_node* contexts = nullptr;
* allocation of memory for each filename.
*/
static bool open_prop_file(context_node* cnode, bool access_rw, bool* fsetxattr_failed) {
cnode->lock.lock();
if (cnode->pa) {
cnode->lock.unlock();
bool context_node::open(bool access_rw, bool* fsetxattr_failed) {
lock_.lock();
if (pa_) {
lock_.unlock();
return true;
}
char filename[PROP_FILENAME_MAX];
int len = snprintf(filename, sizeof(filename), "%s/%s", property_filename, cnode->context);
int len = snprintf(filename, sizeof(filename), "%s/%s", property_filename, context_);
if (len < 0 || len > PROP_FILENAME_MAX) {
cnode->lock.unlock();
lock_.unlock();
return false;
}
if (access_rw) {
cnode->pa = map_prop_area_rw(filename, cnode->context, fsetxattr_failed);
pa_ = map_prop_area_rw(filename, context_, fsetxattr_failed);
} else {
cnode->pa = map_prop_area(filename, false);
pa_ = map_prop_area(filename, false);
}
cnode->lock.unlock();
return cnode->pa;
lock_.unlock();
return pa_;
}
static bool check_access(context_node* cnode) {
bool context_node::check_access_and_open() {
if (!pa_ && !no_access_) {
if (!check_access() || !open(false, nullptr)) {
no_access_ = true;
}
}
return pa_;
}
void context_node::reset_access() {
if (!check_access()) {
unmap();
no_access_ = true;
} else {
no_access_ = false;
}
}
bool context_node::check_access() {
char filename[PROP_FILENAME_MAX];
int len = snprintf(filename, sizeof(filename), "%s/%s", property_filename, cnode->context);
int len = snprintf(filename, sizeof(filename), "%s/%s", property_filename, context_);
if (len < 0 || len > PROP_FILENAME_MAX) {
return false;
}
@ -766,6 +796,18 @@ static bool check_access(context_node* cnode) {
return access(filename, R_OK) == 0;
}
void context_node::unmap() {
if (!pa_) {
return;
}
munmap(pa_, pa_size);
if (pa_ == __system_property_area__) {
__system_property_area__ = nullptr;
}
pa_ = nullptr;
}
static bool map_system_property_area(bool access_rw, bool* fsetxattr_failed) {
char filename[PROP_FILENAME_MAX];
int len = snprintf(filename, sizeof(filename), "%s/properties_serial", property_filename);
@ -792,10 +834,15 @@ static prop_area* get_prop_area_for_name(const char* name) {
}
auto cnode = entry->context;
if (!cnode->pa) {
open_prop_file(cnode, false, nullptr);
if (!cnode->pa()) {
/*
* We explicitly do not check no_access_ in this case because unlike the
* case of foreach(), we want to generate an selinux audit for each
* non-permitted property access in this function.
*/
cnode->open(false, nullptr);
}
return cnode->pa;
return cnode->pa();
}
/*
@ -892,9 +939,6 @@ static int read_spec_entries(char *line_buf, int num_args, ...)
}
static bool initialize_properties() {
list_free(&prefixes);
list_free(&contexts);
FILE* file = fopen("/property_contexts", "re");
if (!file) {
@ -927,7 +971,7 @@ static bool initialize_properties() {
}
auto old_context = list_find(
contexts, [context](context_node* l) { return !strcmp(l->context, context); });
contexts, [context](context_node* l) { return !strcmp(l->context(), context); });
if (old_context) {
list_add_after_len(&prefixes, prop_prefix, old_context);
} else {
@ -951,15 +995,27 @@ static bool is_dir(const char* pathname) {
return S_ISDIR(info.st_mode);
}
static void free_and_unmap_contexts() {
list_free(&prefixes);
list_free(&contexts);
if (__system_property_area__) {
munmap(__system_property_area__, pa_size);
__system_property_area__ = nullptr;
}
}
int __system_properties_init()
{
if (initialized) {
list_foreach(contexts, [](context_node* l) { l->reset_access(); });
return 0;
}
if (is_dir(property_filename)) {
if (!initialize_properties()) {
return -1;
}
if (!map_system_property_area(false, nullptr)) {
list_free(&prefixes);
list_free(&contexts);
free_and_unmap_contexts();
return -1;
}
} else {
@ -970,6 +1026,7 @@ int __system_properties_init()
list_add(&contexts, "legacy_system_prop_area", __system_property_area__);
list_add_after_len(&prefixes, "*", contexts);
}
initialized = true;
return 0;
}
@ -985,22 +1042,23 @@ int __system_property_set_filename(const char *filename)
int __system_property_area_init()
{
free_and_unmap_contexts();
mkdir(property_filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
if (!initialize_properties()) {
return -1;
}
bool open_prop_file_failed = false;
bool open_failed = false;
bool fsetxattr_failed = false;
list_foreach(contexts, [&fsetxattr_failed, &open_prop_file_failed](context_node* l) {
if (!open_prop_file(l, true, &fsetxattr_failed)) {
open_prop_file_failed = true;
list_foreach(contexts, [&fsetxattr_failed, &open_failed](context_node* l) {
if (!l->open(true, &fsetxattr_failed)) {
open_failed = true;
}
});
if (open_prop_file_failed || !map_system_property_area(true, &fsetxattr_failed)) {
list_free(&prefixes);
list_free(&contexts);
if (open_failed || !map_system_property_area(true, &fsetxattr_failed)) {
free_and_unmap_contexts();
return -1;
}
initialized = true;
return fsetxattr_failed ? -2 : 0;
}
@ -1226,14 +1284,8 @@ int __system_property_foreach(void (*propfn)(const prop_info *pi, void *cookie),
}
list_foreach(contexts, [propfn, cookie](context_node* l) {
if (!l->pa && !l->checked_access) {
if (check_access(l)) {
open_prop_file(l, false, nullptr);
}
l->checked_access = true;
}
if (l->pa) {
l->pa->foreach(propfn, cookie);
if (l->check_access_and_open()) {
l->pa()->foreach(propfn, cookie);
}
});
return 0;