From c9bb0338bcb0cbf4425eef7bf04fe6cb61c767d4 Mon Sep 17 00:00:00 2001 From: Dimitry Ivanov Date: Tue, 24 Jan 2017 20:43:58 +0000 Subject: [PATCH] Revert "Revert "New protocol for property_service"" This reverts commit 70c4ecf2b562ec643c270d65e0585fe8506d064c. Bug: http://b/33926793 Bug: http://b/34670529 Test: run bionic-unit-tests --gtest_filter=prop* Change-Id: I75b28146805ff37fbdcf0543ccb515ee5895eb2a --- init/property_service.cpp | 335 +++++++++++++++++++++++++++----------- init/property_service.h | 14 +- 2 files changed, 244 insertions(+), 105 deletions(-) diff --git a/init/property_service.cpp b/init/property_service.cpp index 72fcb5b80..b2f641127 100644 --- a/init/property_service.cpp +++ b/init/property_service.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -48,6 +49,7 @@ #include #include +#include #include "bootimg.h" #include "property_service.h" @@ -70,30 +72,30 @@ void property_init() { } } -static int check_mac_perms(const char *name, char *sctx, struct ucred *cr) -{ - char *tctx = NULL; - int result = 0; +static bool check_mac_perms(const std::string& name, char* sctx, struct ucred* cr) { + + if (!sctx) { + return false; + } + + if (!sehandle_prop) { + return false; + } + + char* tctx = nullptr; + if (selabel_lookup(sehandle_prop, &tctx, name.c_str(), 1) != 0) { + return false; + } + property_audit_data audit_data; - if (!sctx) - goto err; - - if (!sehandle_prop) - goto err; - - if (selabel_lookup(sehandle_prop, &tctx, name, 1) != 0) - goto err; - - audit_data.name = name; + audit_data.name = name.c_str(); audit_data.cr = cr; - if (selinux_check_access(sctx, tctx, "property_service", "set", reinterpret_cast(&audit_data)) == 0) - result = 1; + bool has_access = (selinux_check_access(sctx, tctx, "property_service", "set", &audit_data) == 0); freecon(tctx); - err: - return result; + return has_access; } static int check_control_mac_perms(const char *name, char *sctx, struct ucred *cr) @@ -142,11 +144,9 @@ static void write_persistent_property(const char *name, const char *value) } } -bool is_legal_property_name(const std::string &name) -{ +bool is_legal_property_name(const std::string& name) { size_t namelen = name.size(); - if (namelen >= PROP_NAME_MAX) return false; if (namelen < 1) return false; if (name[0] == '.') return false; if (name[namelen - 1] == '.') return false; @@ -169,58 +169,217 @@ bool is_legal_property_name(const std::string &name) return true; } -int property_set(const char* name, const char* value) { - size_t valuelen = strlen(value); +uint32_t property_set(const std::string& name, const std::string& value) { + size_t valuelen = value.size(); if (!is_legal_property_name(name)) { LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: bad name"; - return -1; + return PROP_ERROR_INVALID_NAME; } + if (valuelen >= PROP_VALUE_MAX) { LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: " << "value too long"; - return -1; + return PROP_ERROR_INVALID_VALUE; } - if (strcmp("selinux.restorecon_recursive", name) == 0 && valuelen > 0) { - if (restorecon(value, SELINUX_ANDROID_RESTORECON_RECURSE) != 0) { + if (name == "selinux.restorecon_recursive" && valuelen > 0) { + if (restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) { LOG(ERROR) << "Failed to restorecon_recursive " << value; } } - prop_info* pi = (prop_info*) __system_property_find(name); + prop_info* pi = (prop_info*) __system_property_find(name.c_str()); if (pi != nullptr) { // ro.* properties are actually "write-once". - if (!strncmp(name, "ro.", 3)) { + if (android::base::StartsWith(name, "ro.")) { LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: " << "property already set"; - return -1; + return PROP_ERROR_READ_ONLY_PROPERTY; } - __system_property_update(pi, value, valuelen); + __system_property_update(pi, value.c_str(), valuelen); } else { - int rc = __system_property_add(name, strlen(name), value, valuelen); + int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen); if (rc < 0) { LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: " << "__system_property_add failed"; - return rc; + return PROP_ERROR_SET_FAILED; } } // Don't write properties to disk until after we have read all default // properties to prevent them from being overwritten by default values. - if (persistent_properties_loaded && strncmp("persist.", name, strlen("persist.")) == 0) { - write_persistent_property(name, value); + if (persistent_properties_loaded && android::base::StartsWith(name, "persist.")) { + write_persistent_property(name.c_str(), value.c_str()); } - property_changed(name, value); - return 0; + property_changed(name.c_str(), value.c_str()); + return PROP_SUCCESS; } -static void handle_property_set_fd() -{ - prop_msg msg; - int r; - char * source_ctx = NULL; +class SocketConnection { + public: + SocketConnection(int socket, const struct ucred& cred) + : socket_(socket), cred_(cred) {} + + ~SocketConnection() { + close(socket_); + } + + bool RecvUint32(uint32_t* value, uint32_t* timeout_ms) { + return RecvFully(value, sizeof(*value), timeout_ms); + } + + bool RecvChars(char* chars, size_t size, uint32_t* timeout_ms) { + return RecvFully(chars, size, timeout_ms); + } + + bool RecvString(std::string* value, uint32_t* timeout_ms) { + uint32_t len = 0; + if (!RecvUint32(&len, timeout_ms)) { + return false; + } + + if (len == 0) { + *value = ""; + return true; + } + + std::vector chars(len); + if (!RecvChars(&chars[0], len, timeout_ms)) { + return false; + } + + *value = std::string(&chars[0], len); + return true; + } + + bool SendUint32(uint32_t value) { + int result = TEMP_FAILURE_RETRY(send(socket_, &value, sizeof(value), 0)); + return result == sizeof(value); + } + + int socket() { + return socket_; + } + + const struct ucred& cred() { + return cred_; + } + + private: + bool PollIn(uint32_t* timeout_ms) { + struct pollfd ufds[1]; + ufds[0].fd = socket_; + ufds[0].events = POLLIN; + ufds[0].revents = 0; + while (*timeout_ms > 0) { + Timer timer; + int nr = poll(ufds, 1, *timeout_ms); + uint64_t millis = timer.duration_ns()/1000000; + *timeout_ms = (millis > *timeout_ms) ? 0 : *timeout_ms - millis; + + if (nr > 0) { + return true; + } + + if (nr == 0) { + // Timeout + break; + } + + if (nr < 0 && errno != EINTR) { + PLOG(ERROR) << "sys_prop: error waiting for uid " << cred_.uid << " to send property message"; + return false; + } else { // errno == EINTR + // Timer rounds milliseconds down in case of EINTR we want it to be rounded up + // to avoid slowing init down by causing EINTR with under millisecond timeout. + if (*timeout_ms > 0) { + --(*timeout_ms); + } + } + } + + LOG(ERROR) << "sys_prop: timeout waiting for uid " << cred_.uid << " to send property message."; + return false; + } + + bool RecvFully(void* data_ptr, size_t size, uint32_t* timeout_ms) { + size_t bytes_left = size; + char* data = static_cast(data_ptr); + while (*timeout_ms > 0 && bytes_left > 0) { + if (!PollIn(timeout_ms)) { + return false; + } + + int result = TEMP_FAILURE_RETRY(recv(socket_, data, bytes_left, MSG_DONTWAIT)); + if (result <= 0) { + return false; + } + + bytes_left -= result; + data += result; + } + + return bytes_left == 0; + } + + int socket_; + struct ucred cred_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(SocketConnection); +}; + +static void handle_property_set(SocketConnection& socket, + const std::string& name, + const std::string& value, + bool legacy_protocol) { + const char* cmd_name = legacy_protocol ? "PROP_MSG_SETPROP" : "PROP_MSG_SETPROP2"; + if (!is_legal_property_name(name)) { + LOG(ERROR) << "sys_prop(" << cmd_name << "): illegal property name \"" << name << "\""; + socket.SendUint32(PROP_ERROR_INVALID_NAME); + return; + } + + struct ucred cr = socket.cred(); + char* source_ctx = nullptr; + getpeercon(socket.socket(), &source_ctx); + + if (android::base::StartsWith(name, "ctl.")) { + if (check_control_mac_perms(value.c_str(), source_ctx, &cr)) { + handle_control_message(name.c_str() + 4, value.c_str()); + if (!legacy_protocol) { + socket.SendUint32(PROP_SUCCESS); + } + } else { + LOG(ERROR) << "sys_prop(" << cmd_name << "): Unable to " << (name.c_str() + 4) + << " service ctl [" << value << "]" + << " uid:" << cr.uid + << " gid:" << cr.gid + << " pid:" << cr.pid; + if (!legacy_protocol) { + socket.SendUint32(PROP_ERROR_HANDLE_CONTROL_MESSAGE); + } + } + } else { + if (check_mac_perms(name, source_ctx, &cr)) { + uint32_t result = property_set(name, value); + if (!legacy_protocol) { + socket.SendUint32(result); + } + } else { + LOG(ERROR) << "sys_prop(" << cmd_name << "): permission denied uid:" << cr.uid << " name:" << name; + if (!legacy_protocol) { + socket.SendUint32(PROP_ERROR_PERMISSION_DENIED); + } + } + } + + freecon(source_ctx); +} + +static void handle_property_set_fd() { + static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */ int s = accept(property_set_fd, nullptr, nullptr); if (s == -1) { @@ -236,72 +395,50 @@ static void handle_property_set_fd() return; } - static constexpr int timeout_ms = 2 * 1000; /* Default 2 sec timeout for caller to send property. */ - struct pollfd ufds[1]; - ufds[0].fd = s; - ufds[0].events = POLLIN; - ufds[0].revents = 0; - int nr = TEMP_FAILURE_RETRY(poll(ufds, 1, timeout_ms)); - if (nr == 0) { - LOG(ERROR) << "sys_prop: timeout waiting for uid " << cr.uid << " to send property message."; - close(s); - return; - } else if (nr < 0) { - PLOG(ERROR) << "sys_prop: error waiting for uid " << cr.uid << " to send property message"; - close(s); + SocketConnection socket(s, cr); + uint32_t timeout_ms = kDefaultSocketTimeout; + + uint32_t cmd = 0; + + if (!socket.RecvUint32(&cmd, &timeout_ms)) { + PLOG(ERROR) << "sys_prop: error while reading command from the socket"; + socket.SendUint32(PROP_ERROR_READ_CMD); return; } - r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), MSG_DONTWAIT)); - if(r != sizeof(prop_msg)) { - PLOG(ERROR) << "sys_prop: mis-match msg size received: " << r << " expected: " << sizeof(prop_msg); - close(s); - return; - } + switch(cmd) { + case PROP_MSG_SETPROP: { + char prop_name[PROP_NAME_MAX]; + char prop_value[PROP_VALUE_MAX]; - 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)) { - LOG(ERROR) << "sys_prop: illegal property name \"" << msg.name << "\""; - close(s); - return; + if (!socket.RecvChars(prop_name, PROP_NAME_MAX, &timeout_ms) || + !socket.RecvChars(prop_value, PROP_VALUE_MAX, &timeout_ms)) { + PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP): error while reading name/value from the socket"; + return; } - getpeercon(s, &source_ctx); + prop_name[PROP_NAME_MAX-1] = 0; + prop_value[PROP_VALUE_MAX-1] = 0; - 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, &cr)) { - handle_control_message((char*) msg.name + 4, (char*) msg.value); - } else { - LOG(ERROR) << "sys_prop: Unable to " << (msg.name + 4) - << " service ctl [" << msg.value << "]" - << " uid:" << cr.uid - << " gid:" << cr.gid - << " pid:" << cr.pid; - } - } else { - if (check_mac_perms(msg.name, source_ctx, &cr)) { - property_set((char*) msg.name, (char*) msg.value); - } else { - LOG(ERROR) << "sys_prop: permission denied uid:" << cr.uid << " name:" << 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); + handle_property_set(socket, prop_value, prop_value, true); break; + } + case PROP_MSG_SETPROP2: { + std::string name; + std::string value; + if (!socket.RecvString(&name, &timeout_ms) || + !socket.RecvString(&value, &timeout_ms)) { + PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP2): error while reading name/value from the socket"; + socket.SendUint32(PROP_ERROR_READ_DATA); + return; + } + + handle_property_set(socket, name, value, false); + break; + } default: - close(s); + socket.SendUint32(PROP_ERROR_INVALID_CMD); break; } } @@ -510,6 +647,8 @@ void load_system_props() { } void start_property_service() { + property_set("ro.property_service.version", "2"); + property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0666, 0, 0, NULL); if (property_set_fd == -1) { diff --git a/init/property_service.h b/init/property_service.h index e3a2acb8f..5d5947325 100644 --- a/init/property_service.h +++ b/init/property_service.h @@ -27,14 +27,14 @@ struct property_audit_data { const char* name; }; -extern void property_init(void); -extern void property_load_boot_defaults(void); -extern void load_persist_props(void); -extern void load_system_props(void); -extern void start_property_service(void); +void property_init(void); +void property_load_boot_defaults(void); +void load_persist_props(void); +void load_system_props(void); +void start_property_service(void); std::string property_get(const char* name); -extern int property_set(const char *name, const char *value); -extern bool is_legal_property_name(const std::string &name); +uint32_t property_set(const std::string& name, const std::string& value); +bool is_legal_property_name(const std::string& name); #endif /* _INIT_PROPERTY_H */