diff options
| -rw-r--r-- | include/cutils/files.h | 37 | ||||
| -rw-r--r-- | init/Android.mk | 3 | ||||
| -rw-r--r-- | init/descriptors.cpp | 104 | ||||
| -rw-r--r-- | init/descriptors.h | 78 | ||||
| -rw-r--r-- | init/readme.txt | 20 | ||||
| -rw-r--r-- | init/service.cpp | 93 | ||||
| -rw-r--r-- | init/service.h | 21 | ||||
| -rw-r--r-- | init/util.cpp | 88 | ||||
| -rw-r--r-- | init/util.h | 2 | ||||
| -rw-r--r-- | init/util_test.cpp | 56 | ||||
| -rw-r--r-- | libcutils/Android.bp | 1 | ||||
| -rw-r--r-- | libcutils/files.cpp | 111 | ||||
| -rw-r--r-- | libcutils/fs_config.c | 26 | ||||
| -rw-r--r-- | libcutils/klog.cpp | 7 | ||||
| -rw-r--r-- | libcutils/sockets.cpp | 84 | ||||
| -rw-r--r-- | libcutils/tests/Android.bp | 2 | ||||
| -rw-r--r-- | libcutils/tests/files_test.cpp | 46 | ||||
| -rw-r--r-- | libcutils/tests/sockets_test.cpp | 41 | ||||
| -rw-r--r-- | logd/Android.mk | 2 | ||||
| -rw-r--r-- | logd/logd.rc | 7 | ||||
| -rw-r--r-- | logd/main.cpp | 181 |
21 files changed, 845 insertions, 165 deletions
diff --git a/include/cutils/files.h b/include/cutils/files.h new file mode 100644 index 000000000..0210e3084 --- /dev/null +++ b/include/cutils/files.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CUTILS_FILES_H +#define __CUTILS_FILES_H + +#define ANDROID_FILE_ENV_PREFIX "ANDROID_FILE_" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * android_get_control_file - simple helper function to get the file + * descriptor of our init-managed file. `path' is the filename path as + * given in init.rc. Returns -1 on error. + */ +int android_get_control_file(const char* path); + +#ifdef __cplusplus +} +#endif + +#endif /* __CUTILS_FILES_H */ diff --git a/init/Android.mk b/init/Android.mk index 4bfd743dd..442a5f301 100644 --- a/init/Android.mk +++ b/init/Android.mk @@ -46,6 +46,7 @@ LOCAL_CPPFLAGS := $(init_cflags) LOCAL_SRC_FILES:= \ action.cpp \ capabilities.cpp \ + descriptors.cpp \ import_parser.cpp \ init_parser.cpp \ log.cpp \ @@ -125,8 +126,10 @@ LOCAL_SRC_FILES := \ LOCAL_SHARED_LIBRARIES += \ libcutils \ libbase \ + libselinux \ LOCAL_STATIC_LIBRARIES := libinit LOCAL_SANITIZE := integer LOCAL_CLANG := true +LOCAL_CPPFLAGS := -Wall -Wextra -Werror include $(BUILD_NATIVE_TEST) diff --git a/init/descriptors.cpp b/init/descriptors.cpp new file mode 100644 index 000000000..10aae8882 --- /dev/null +++ b/init/descriptors.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "descriptors.h" + +#include <ctype.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <android-base/stringprintf.h> +#include <cutils/files.h> +#include <cutils/sockets.h> + +#include "init.h" +#include "log.h" +#include "util.h" + +DescriptorInfo::DescriptorInfo(const std::string& name, const std::string& type, uid_t uid, + gid_t gid, int perm, const std::string& context) + : name_(name), type_(type), uid_(uid), gid_(gid), perm_(perm), context_(context) { +} + +DescriptorInfo::~DescriptorInfo() { +} + +std::ostream& operator<<(std::ostream& os, const DescriptorInfo& info) { + return os << " descriptors " << info.name_ << " " << info.type_ << " " << std::oct << info.perm_; +} + +bool DescriptorInfo::operator==(const DescriptorInfo& other) const { + return name_ == other.name_ && type_ == other.type_ && key() == other.key(); +} + +void DescriptorInfo::CreateAndPublish(const std::string& globalContext) const { + // Create + const std::string& contextStr = context_.empty() ? globalContext : context_; + int fd = Create(contextStr); + if (fd < 0) return; + + // Publish + std::string publishedName = key() + name_; + std::for_each(publishedName.begin(), publishedName.end(), + [] (char& c) { c = isalnum(c) ? c : '_'; }); + + std::string val = android::base::StringPrintf("%d", fd); + add_environment(publishedName.c_str(), val.c_str()); + + // make sure we don't close on exec + fcntl(fd, F_SETFD, 0); +} + +void DescriptorInfo::Clean() const { +} + +SocketInfo::SocketInfo(const std::string& name, const std::string& type, uid_t uid, + gid_t gid, int perm, const std::string& context) + : DescriptorInfo(name, type, uid, gid, perm, context) { +} + +void SocketInfo::Clean() const { + unlink(android::base::StringPrintf(ANDROID_SOCKET_DIR "/%s", name().c_str()).c_str()); +} + +int SocketInfo::Create(const std::string& context) const { + int flags = ((type() == "stream" ? SOCK_STREAM : + (type() == "dgram" ? SOCK_DGRAM : + SOCK_SEQPACKET))); + return create_socket(name().c_str(), flags, perm(), uid(), gid(), context.c_str()); +} + +const std::string SocketInfo::key() const { + return ANDROID_SOCKET_ENV_PREFIX; +} + +FileInfo::FileInfo(const std::string& name, const std::string& type, uid_t uid, + gid_t gid, int perm, const std::string& context) + : DescriptorInfo(name, type, uid, gid, perm, context) { +} + +int FileInfo::Create(const std::string& context) const { + int flags = ((type() == "r" ? O_RDONLY : + (type() == "w" ? (O_WRONLY | O_CREAT) : + (O_RDWR | O_CREAT)))); + return create_file(name().c_str(), flags, perm(), uid(), gid(), context.c_str()); +} + +const std::string FileInfo::key() const { + return ANDROID_FILE_ENV_PREFIX; +} diff --git a/init/descriptors.h b/init/descriptors.h new file mode 100644 index 000000000..ff276fbc0 --- /dev/null +++ b/init/descriptors.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef _INIT_DESCRIPTORS_H +#define _INIT_DESCRIPTORS_H + +#include <sys/types.h> + +#include <string> + +class DescriptorInfo { + public: + DescriptorInfo(const std::string& name, const std::string& type, uid_t uid, + gid_t gid, int perm, const std::string& context); + virtual ~DescriptorInfo(); + + friend std::ostream& operator<<(std::ostream& os, const class DescriptorInfo& info); + bool operator==(const DescriptorInfo& other) const; + + void CreateAndPublish(const std::string& globalContext) const; + virtual void Clean() const; + + protected: + const std::string& name() const { return name_; } + const std::string& type() const { return type_; } + uid_t uid() const { return uid_; } + gid_t gid() const { return gid_; } + int perm() const { return perm_; } + const std::string& context() const { return context_; } + + private: + std::string name_; + std::string type_; + uid_t uid_; + gid_t gid_; + int perm_; + std::string context_; + + virtual int Create(const std::string& globalContext) const = 0; + virtual const std::string key() const = 0; +}; + +std::ostream& operator<<(std::ostream& os, const DescriptorInfo& info); + +class SocketInfo : public DescriptorInfo { + public: + SocketInfo(const std::string& name, const std::string& type, uid_t uid, + gid_t gid, int perm, const std::string& context); + void Clean() const override; + private: + virtual int Create(const std::string& context) const override; + virtual const std::string key() const override; +}; + +class FileInfo : public DescriptorInfo { + public: + FileInfo(const std::string& name, const std::string& type, uid_t uid, + gid_t gid, int perm, const std::string& context); + private: + virtual int Create(const std::string& context) const override; + virtual const std::string key() const override; +}; + +#endif diff --git a/init/readme.txt b/init/readme.txt index e7e369c5a..500b1d81d 100644 --- a/init/readme.txt +++ b/init/readme.txt @@ -141,12 +141,20 @@ setenv <name> <value> Set the environment variable <name> to <value> in the launched process. socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ] - Create a unix domain socket named /dev/socket/<name> and pass - its fd to the launched process. <type> must be "dgram", "stream" or "seqpacket". - User and group default to 0. - 'seclabel' is the SELinux security context for the socket. - It defaults to the service security context, as specified by seclabel or - computed based on the service executable file security context. + Create a unix domain socket named /dev/socket/<name> and pass its fd to the + launched process. <type> must be "dgram", "stream" or "seqpacket". User and + group default to 0. 'seclabel' is the SELinux security context for the + socket. It defaults to the service security context, as specified by + seclabel or computed based on the service executable file security context. + For native executables see libcutils android_get_control_socket(). + +file <path> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ] + Open/Create a file path and pass its fd to the launched process. <type> must + be "r", "w" or "rw". User and group default to 0. 'seclabel' is the SELinux + security context for the file if it must be created. It defaults to the + service security context, as specified by seclabel or computed based on the + service executable file security context. For native executables see + libcutils android_get_control_file(). user <username> Change to 'username' before exec'ing this service. diff --git a/init/service.cpp b/init/service.cpp index e052b45d3..9fa11b85b 100644 --- a/init/service.cpp +++ b/init/service.cpp @@ -36,7 +36,6 @@ #include <android-base/stringprintf.h> #include <android-base/strings.h> #include <cutils/android_reboot.h> -#include <cutils/sockets.h> #include <system/thread_defs.h> #include <processgroup/processgroup.h> @@ -145,14 +144,6 @@ static void ExpandArgs(const std::vector<std::string>& args, std::vector<char*>* strs->push_back(nullptr); } -SocketInfo::SocketInfo() : uid(0), gid(0), perm(0) { -} - -SocketInfo::SocketInfo(const std::string& name, const std::string& type, uid_t uid, - gid_t gid, int perm, const std::string& socketcon) - : name(name), type(type), uid(uid), gid(gid), perm(perm), socketcon(socketcon) { -} - ServiceEnvironmentInfo::ServiceEnvironmentInfo() { } @@ -213,20 +204,6 @@ void Service::KillProcessGroup(int signal) { } } -void Service::CreateSockets(const std::string& context) { - for (const auto& si : sockets_) { - int socket_type = ((si.type == "stream" ? SOCK_STREAM : - (si.type == "dgram" ? SOCK_DGRAM : - SOCK_SEQPACKET))); - const char* socketcon = !si.socketcon.empty() ? si.socketcon.c_str() : context.c_str(); - - int s = create_socket(si.name.c_str(), socket_type, si.perm, si.uid, si.gid, socketcon); - if (s >= 0) { - PublishSocket(si.name, s); - } - } -} - void Service::SetProcessAttributes() { // Keep capabilites on uid change. if (capabilities_.any() && uid_) { @@ -273,11 +250,9 @@ bool Service::Reap() { KillProcessGroup(SIGKILL); } - // Remove any sockets we may have created. - for (const auto& si : sockets_) { - std::string tmp = StringPrintf(ANDROID_SOCKET_DIR "/%s", si.name.c_str()); - unlink(tmp.c_str()); - } + // Remove any descriptor resources we may have created. + std::for_each(descriptors_.begin(), descriptors_.end(), + std::bind(&DescriptorInfo::Clean, std::placeholders::_1)); if (flags_ & SVC_EXEC) { LOG(INFO) << "SVC_EXEC pid " << pid_ << " finished..."; @@ -330,9 +305,8 @@ void Service::DumpState() const { LOG(INFO) << "service " << name_; LOG(INFO) << " class '" << classname_ << "'"; LOG(INFO) << " exec "<< android::base::Join(args_, " "); - for (const auto& si : sockets_) { - LOG(INFO) << " socket " << si.name << " " << si.type << " " << std::oct << si.perm; - } + std::for_each(descriptors_.begin(), descriptors_.end(), + [] (const auto& info) { LOG(INFO) << *info; }); } bool Service::ParseCapabilities(const std::vector<std::string>& args, std::string* err) { @@ -469,20 +443,48 @@ bool Service::ParseSetenv(const std::vector<std::string>& args, std::string* err return true; } -/* name type perm [ uid gid context ] */ +template <typename T> +bool Service::AddDescriptor(const std::vector<std::string>& args, std::string* err) { + int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1; + uid_t uid = args.size() > 4 ? decode_uid(args[4].c_str()) : 0; + gid_t gid = args.size() > 5 ? decode_uid(args[5].c_str()) : 0; + std::string context = args.size() > 6 ? args[6] : ""; + + auto descriptor = std::make_unique<T>(args[1], args[2], uid, gid, perm, context); + + auto old = + std::find_if(descriptors_.begin(), descriptors_.end(), + [&descriptor] (const auto& other) { return descriptor.get() == other.get(); }); + + if (old != descriptors_.end()) { + *err = "duplicate descriptor " + args[1] + " " + args[2]; + return false; + } + + descriptors_.emplace_back(std::move(descriptor)); + return true; +} + +// name type perm [ uid gid context ] bool Service::ParseSocket(const std::vector<std::string>& args, std::string* err) { if (args[2] != "dgram" && args[2] != "stream" && args[2] != "seqpacket") { *err = "socket type must be 'dgram', 'stream' or 'seqpacket'"; return false; } + return AddDescriptor<SocketInfo>(args, err); +} - int perm = std::strtoul(args[3].c_str(), 0, 8); - uid_t uid = args.size() > 4 ? decode_uid(args[4].c_str()) : 0; - gid_t gid = args.size() > 5 ? decode_uid(args[5].c_str()) : 0; - std::string socketcon = args.size() > 6 ? args[6] : ""; - - sockets_.emplace_back(args[1], args[2], uid, gid, perm, socketcon); - return true; +// name type perm [ uid gid context ] +bool Service::ParseFile(const std::vector<std::string>& args, std::string* err) { + if (args[2] != "r" && args[2] != "w" && args[2] != "rw") { + *err = "file type must be 'r', 'w' or 'rw'"; + return false; + } + if ((args[1][0] != '/') || (args[1].find("../") != std::string::npos)) { + *err = "file name must not be relative"; + return false; + } + return AddDescriptor<FileInfo>(args, err); } bool Service::ParseUser(const std::vector<std::string>& args, std::string* err) { @@ -524,6 +526,7 @@ Service::OptionParserMap::Map& Service::OptionParserMap::map() const { {"seclabel", {1, 1, &Service::ParseSeclabel}}, {"setenv", {2, 2, &Service::ParseSetenv}}, {"socket", {3, 6, &Service::ParseSocket}}, + {"file", {2, 6, &Service::ParseFile}}, {"user", {1, 1, &Service::ParseUser}}, {"writepid", {1, kMax, &Service::ParseWritepid}}, }; @@ -613,7 +616,8 @@ bool Service::Start() { add_environment(ei.name.c_str(), ei.value.c_str()); } - CreateSockets(scon); + std::for_each(descriptors_.begin(), descriptors_.end(), + std::bind(&DescriptorInfo::CreateAndPublish, std::placeholders::_1, scon)); std::string pid_str = StringPrintf("%d", getpid()); for (const auto& file : writepid_files_) { @@ -787,15 +791,6 @@ void Service::OpenConsole() const { close(fd); } -void Service::PublishSocket(const std::string& name, int fd) const { - std::string key = StringPrintf(ANDROID_SOCKET_ENV_PREFIX "%s", name.c_str()); - std::string val = StringPrintf("%d", fd); - add_environment(key.c_str(), val.c_str()); - - /* make sure we don't close-on-exec */ - fcntl(fd, F_SETFD, 0); -} - int ServiceManager::exec_count_ = 0; ServiceManager::ServiceManager() { diff --git a/init/service.h b/init/service.h index 7f035eff2..d9e8f57c3 100644 --- a/init/service.h +++ b/init/service.h @@ -27,6 +27,7 @@ #include "action.h" #include "capabilities.h" +#include "descriptors.h" #include "init_parser.h" #include "keyword_map.h" @@ -48,18 +49,6 @@ class Action; class ServiceManager; -struct SocketInfo { - SocketInfo(); - SocketInfo(const std::string& name, const std::string& type, uid_t uid, - gid_t gid, int perm, const std::string& socketcon); - std::string name; - std::string type; - uid_t uid; - gid_t gid; - int perm; - std::string socketcon; -}; - struct ServiceEnvironmentInfo { ServiceEnvironmentInfo(); ServiceEnvironmentInfo(const std::string& name, const std::string& value); @@ -113,9 +102,7 @@ private: void StopOrReset(int how); void ZapStdio() const; void OpenConsole() const; - void PublishSocket(const std::string& name, int fd) const; void KillProcessGroup(int signal); - void CreateSockets(const std::string& scon); void SetProcessAttributes(); bool ParseCapabilities(const std::vector<std::string>& args, std::string *err); @@ -134,9 +121,13 @@ private: bool ParseSeclabel(const std::vector<std::string>& args, std::string* err); bool ParseSetenv(const std::vector<std::string>& args, std::string* err); bool ParseSocket(const std::vector<std::string>& args, std::string* err); + bool ParseFile(const std::vector<std::string>& args, std::string* err); bool ParseUser(const std::vector<std::string>& args, std::string* err); bool ParseWritepid(const std::vector<std::string>& args, std::string* err); + template <typename T> + bool AddDescriptor(const std::vector<std::string>& args, std::string* err); + std::string name_; std::string classname_; std::string console_; @@ -155,7 +146,7 @@ private: std::string seclabel_; - std::vector<SocketInfo> sockets_; + std::vector<std::unique_ptr<DescriptorInfo>> descriptors_; std::vector<ServiceEnvironmentInfo> envvars_; Action onrestart_; // Commands to execute on restart. diff --git a/init/util.cpp b/init/util.cpp index e451edd72..ab6837d71 100644 --- a/init/util.cpp +++ b/init/util.cpp @@ -14,32 +14,32 @@ * limitations under the License. */ +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <ftw.h> +#include <pwd.h> #include <stdarg.h> #include <stdlib.h> #include <stdio.h> #include <string.h> -#include <fcntl.h> -#include <ctype.h> -#include <errno.h> #include <time.h> -#include <ftw.h> -#include <pwd.h> +#include <unistd.h> -#include <selinux/label.h> #include <selinux/android.h> +#include <selinux/label.h> +#include <sys/socket.h> #include <sys/stat.h> #include <sys/types.h> -#include <sys/socket.h> #include <sys/un.h> #include <android-base/file.h> #include <android-base/logging.h> +#include <android-base/stringprintf.h> #include <android-base/strings.h> - /* for ANDROID_SOCKET_* */ #include <cutils/sockets.h> -#include <android-base/stringprintf.h> #include "init.h" #include "log.h" @@ -164,6 +164,76 @@ out_close: return -1; } +/* + * create_file - opens and creates a file as dictated in init.rc. + * This file is inherited by the daemon. We communicate the file + * descriptor's value via the environment variable ANDROID_FILE_<basename> + */ +int create_file(const char *path, int flags, mode_t perm, uid_t uid, + gid_t gid, const char *filecon) +{ + char *secontext = NULL; + int ret; + + if (filecon) { + if (setsockcreatecon(filecon) == -1) { + PLOG(ERROR) << "setsockcreatecon(\"" << filecon << "\") failed"; + return -1; + } + } else if (sehandle) { + ret = selabel_lookup(sehandle, &secontext, path, perm); + if (ret != -1) { + ret = setfscreatecon(secontext); + if (ret == -1) { + freecon(secontext); + PLOG(ERROR) << "setfscreatecon(\"" << secontext << "\") failed"; + return -1; + } + } + } + + int fd = TEMP_FAILURE_RETRY(open(path, flags | O_NDELAY, perm)); + + if (filecon) { + setsockcreatecon(NULL); + lsetfilecon(path, filecon); + } else { + setfscreatecon(NULL); + freecon(secontext); + } + + if (fd == -1) { + PLOG(ERROR) << "Failed to open/create file '" << path << "'"; + goto out_close; + } + + if (!(flags & O_NDELAY)) fcntl(fd, F_SETFD, flags); + + ret = lchown(path, uid, gid); + if (ret) { + PLOG(ERROR) << "Failed to lchown file '" << path << "'"; + goto out_close; + } + if (perm != static_cast<mode_t>(-1)) { + ret = fchmodat(AT_FDCWD, path, perm, AT_SYMLINK_NOFOLLOW); + if (ret) { + PLOG(ERROR) << "Failed to fchmodat file '" << path << "'"; + goto out_close; + } + } + + LOG(INFO) << "Created file '" << path << "'" + << ", mode " << std::oct << perm << std::dec + << ", user " << uid + << ", group " << gid; + + return fd; + +out_close: + if (fd >= 0) close(fd); + return -1; +} + bool read_file(const char* path, std::string* content) { content->clear(); diff --git a/init/util.h b/init/util.h index 5fcbdf070..12ab17359 100644 --- a/init/util.h +++ b/init/util.h @@ -27,6 +27,8 @@ int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid, const char *socketcon); +int create_file(const char *path, int mode, mode_t perm, + uid_t uid, gid_t gid, const char *filecon); bool read_file(const char* path, std::string* content); int write_file(const char* path, const char* content); diff --git a/init/util_test.cpp b/init/util_test.cpp index 228954b44..6ecbf908c 100644 --- a/init/util_test.cpp +++ b/init/util_test.cpp @@ -17,7 +17,15 @@ #include "util.h" #include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <cutils/files.h> #include <gtest/gtest.h> +#include <selinux/android.h> TEST(util, read_file_ENOENT) { std::string s("hello"); @@ -41,3 +49,51 @@ TEST(util, decode_uid) { EXPECT_EQ(UINT_MAX, decode_uid("toot")); EXPECT_EQ(123U, decode_uid("123")); } + +struct selabel_handle *sehandle; + +TEST(util, create_file) { + if (!sehandle) sehandle = selinux_android_file_context_handle(); + + static const char path[] = "/data/local/tmp/util.create_file.test"; + static const char key[] = ANDROID_FILE_ENV_PREFIX "_data_local_tmp_util_create_file_test"; + EXPECT_EQ(unsetenv(key), 0); + unlink(path); + + int fd; + uid_t uid = decode_uid("logd"); + gid_t gid = decode_uid("system"); + mode_t perms = S_IRWXU | S_IWGRP | S_IRGRP | S_IROTH; + static const char context[] = "u:object_r:misc_logd_file:s0"; + EXPECT_GE(fd = create_file(path, O_RDWR | O_CREAT, perms, uid, gid, context), 0); + if (fd < 0) return; + static const char hello[] = "hello world\n"; + static const ssize_t len = strlen(hello); + EXPECT_EQ(write(fd, hello, len), len); + char buffer[sizeof(hello)]; + memset(buffer, 0, sizeof(buffer)); + EXPECT_GE(lseek(fd, 0, SEEK_SET), 0); + EXPECT_EQ(read(fd, buffer, sizeof(buffer)), len); + EXPECT_EQ(strcmp(hello, buffer), 0); + char val[32]; + snprintf(val, sizeof(val), "%d", fd); + EXPECT_EQ(android_get_control_file(path), -1); + setenv(key, val, true); + EXPECT_EQ(android_get_control_file(path), fd); + close(fd); + EXPECT_EQ(android_get_control_file(path), -1); + EXPECT_EQ(unsetenv(key), 0); + struct stat st; + EXPECT_EQ(stat(path, &st), 0); + EXPECT_EQ(st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO), perms); + EXPECT_EQ(st.st_uid, uid); + EXPECT_EQ(st.st_gid, gid); + security_context_t con; + EXPECT_GE(getfilecon(path, &con), 0); + EXPECT_NE(con, static_cast<security_context_t>(NULL)); + if (con) { + EXPECT_EQ(context, std::string(con)); + } + freecon(con); + EXPECT_EQ(unlink(path), 0); +} diff --git a/libcutils/Android.bp b/libcutils/Android.bp index 943926b9b..f7b497d9d 100644 --- a/libcutils/Android.bp +++ b/libcutils/Android.bp @@ -34,6 +34,7 @@ cc_library { host_supported: true, srcs: [ "config_utils.c", + "files.cpp", "fs_config.c", "canned_fs_config.c", "hashmap.c", diff --git a/libcutils/files.cpp b/libcutils/files.cpp new file mode 100644 index 000000000..bf15b4283 --- /dev/null +++ b/libcutils/files.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +// This file contains files implementation that can be shared between +// platforms as long as the correct headers are included. +#define _GNU_SOURCE 1 // for asprintf + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <cutils/files.h> + +#ifndef TEMP_FAILURE_RETRY // _WIN32 does not define +#define TEMP_FAILURE_RETRY(exp) (exp) +#endif + +int android_get_control_file(const char* path) { + if (!path) return -1; + + char *key = NULL; + if (asprintf(&key, ANDROID_FILE_ENV_PREFIX "%s", path) < 0) return -1; + if (!key) return -1; + + char *cp = key; + while (*cp) { + if (!isalnum(*cp)) *cp = '_'; + ++cp; + } + + const char* val = getenv(key); + free(key); + if (!val) return -1; + + errno = 0; + long fd = strtol(val, NULL, 10); + if (errno) return -1; + + // validity checking + if ((fd < 0) || (fd > INT_MAX)) return -1; +#if defined(_SC_OPEN_MAX) + if (fd >= sysconf(_SC_OPEN_MAX)) return -1; +#elif defined(OPEN_MAX) + if (fd >= OPEN_MAX) return -1; +#elif defined(_POSIX_OPEN_MAX) + if (fd >= _POSIX_OPEN_MAX) return -1; +#endif + +#if defined(F_GETFD) + if (TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD)) < 0) return -1; +#elif defined(F_GETFL) + if (TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL)) < 0) return -1; +#else + struct stat s; + if (TEMP_FAILURE_RETRY(fstat(fd, &s)) < 0) return -1; +#endif + +#if defined(__linux__) + char *proc = NULL; + if (asprintf(&proc, "/proc/self/fd/%ld", fd) < 0) return -1; + if (!proc) return -1; + + size_t len = strlen(path); + char *buf = static_cast<char *>(calloc(1, len + 2)); + if (!buf) { + free(proc); + return -1; + } + ssize_t ret = TEMP_FAILURE_RETRY(readlink(proc, buf, len + 1)); + free(proc); + int cmp = (len != static_cast<size_t>(ret)) || strcmp(buf, path); + free(buf); + if (ret < 0) return -1; + if (cmp != 0) return -1; +#endif + + // It is what we think it is + return static_cast<int>(fd); +} diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c index 3c3ceb4a9..60a389b0d 100644 --- a/libcutils/fs_config.c +++ b/libcutils/fs_config.c @@ -139,23 +139,35 @@ static const struct fs_path_config android_files[] = { { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/procmem" }, /* the following files have enhanced capabilities and ARE included in user builds. */ - { 00750, AID_ROOT, AID_SHELL, CAP_MASK_LONG(CAP_SETUID) | CAP_MASK_LONG(CAP_SETGID), "system/bin/run-as" }, - { 00700, AID_SYSTEM, AID_SHELL, CAP_MASK_LONG(CAP_BLOCK_SUSPEND), "system/bin/inputflinger" }, + { 00550, AID_LOGD, AID_LOGD, CAP_MASK_LONG(CAP_SYSLOG) | + CAP_MASK_LONG(CAP_AUDIT_CONTROL) | + CAP_MASK_LONG(CAP_SETGID), + "system/bin/logd" }, + { 00750, AID_ROOT, AID_SHELL, CAP_MASK_LONG(CAP_SETUID) | + CAP_MASK_LONG(CAP_SETGID), + "system/bin/run-as" }, + { 00700, AID_SYSTEM, AID_SHELL, CAP_MASK_LONG(CAP_BLOCK_SUSPEND), + "system/bin/inputflinger" }, /* Support hostapd administering a network interface. */ - { 00755, AID_WIFI, AID_WIFI, CAP_MASK_LONG(CAP_NET_ADMIN) | - CAP_MASK_LONG(CAP_NET_RAW), "system/bin/hostapd" }, + { 00755, AID_WIFI, AID_WIFI, CAP_MASK_LONG(CAP_NET_ADMIN) | + CAP_MASK_LONG(CAP_NET_RAW), + "system/bin/hostapd" }, /* Support wifi_hal_legacy administering a network interface. */ - { 00755, AID_WIFI, AID_WIFI, CAP_MASK_LONG(CAP_NET_ADMIN) | CAP_MASK_LONG(CAP_NET_RAW), "system/bin/hw/android.hardware.wifi@1.0-service" }, + { 00755, AID_WIFI, AID_WIFI, CAP_MASK_LONG(CAP_NET_ADMIN) | + CAP_MASK_LONG(CAP_NET_RAW), + "system/bin/hw/android.hardware.wifi@1.0-service" }, /* A non-privileged zygote that spawns isolated processes for web rendering. */ { 0750, AID_ROOT, AID_ROOT, CAP_MASK_LONG(CAP_SETUID) | CAP_MASK_LONG(CAP_SETGID) | - CAP_MASK_LONG(CAP_SETPCAP), "system/bin/webview_zygote32" }, + CAP_MASK_LONG(CAP_SETPCAP), + "system/bin/webview_zygote32" }, { 0750, AID_ROOT, AID_ROOT, CAP_MASK_LONG(CAP_SETUID) | CAP_MASK_LONG(CAP_SETGID) | - CAP_MASK_LONG(CAP_SETPCAP), "system/bin/webview_zygote64" }, + CAP_MASK_LONG(CAP_SETPCAP), + "system/bin/webview_zygote64" }, { 00750, AID_ROOT, AID_ROOT, 0, "system/bin/uncrypt" }, { 00750, AID_ROOT, AID_ROOT, 0, "system/bin/install-recovery.sh" }, diff --git a/libcutils/klog.cpp b/libcutils/klog.cpp index 061af1bf4..9d823cfae 100644 --- a/libcutils/klog.cpp +++ b/libcutils/klog.cpp @@ -24,6 +24,7 @@ #include <sys/types.h> #include <unistd.h> +#include <cutils/files.h> #include <cutils/klog.h> static int klog_level = KLOG_DEFAULT_LEVEL; @@ -37,7 +38,11 @@ void klog_set_level(int level) { } static int __open_klog(void) { - return TEMP_FAILURE_RETRY(open("/dev/kmsg", O_WRONLY | O_CLOEXEC)); + static const char kmsg_device[] = "/dev/kmsg"; + + int ret = android_get_control_file(kmsg_device); + if (ret >= 0) return ret; + return TEMP_FAILURE_RETRY(open(kmsg_device, O_WRONLY | O_CLOEXEC)); } #define LOG_BUF_MAX 512 diff --git a/libcutils/sockets.cpp b/libcutils/sockets.cpp index bba63ac16..63761a2c5 100644 --- a/libcutils/sockets.cpp +++ b/libcutils/sockets.cpp @@ -28,12 +28,32 @@ // This file contains socket implementation that can be shared between // platforms as long as the correct headers are included. +#define _GNU_SOURCE 1 // For asprintf -#include <cutils/sockets.h> - +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> #if !defined(_WIN32) #include <netinet/in.h> #endif +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#if !defined(_WIN32) +#include <sys/un.h> +#endif +#include <unistd.h> + +#include <string> + +#include <cutils/sockets.h> + +#ifndef TEMP_FAILURE_RETRY // _WIN32 does not define +#define TEMP_FAILURE_RETRY(exp) (exp) +#endif int socket_get_local_port(cutils_socket_t sock) { sockaddr_storage addr; @@ -47,22 +67,56 @@ int socket_get_local_port(cutils_socket_t sock) { } int android_get_control_socket(const char* name) { - char key[64]; - snprintf(key, sizeof(key), ANDROID_SOCKET_ENV_PREFIX "%s", name); + char *key = NULL; + if (asprintf(&key, ANDROID_SOCKET_ENV_PREFIX "%s", name) < 0) return -1; + if (!key) return -1; - const char* val = getenv(key); - if (!val) { - return -1; + char *cp = key; + while (*cp) { + if (!isalnum(*cp)) *cp = '_'; + ++cp; } + const char* val = getenv(key); + free(key); + if (!val) return -1; + errno = 0; - long ret = strtol(val, NULL, 10); - if (errno) { - return -1; - } - if (ret < 0 || ret > INT_MAX) { - return -1; - } + long fd = strtol(val, NULL, 10); + if (errno) return -1; + + // validity checking + if ((fd < 0) || (fd > INT_MAX)) return -1; +#if defined(_SC_OPEN_MAX) + if (fd >= sysconf(_SC_OPEN_MAX)) return -1; +#elif defined(OPEN_MAX) + if (fd >= OPEN_MAX) return -1; +#elif defined(_POSIX_OPEN_MAX) + if (fd >= _POSIX_OPEN_MAX) return -1; +#endif + +#if defined(F_GETFD) + if (TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD)) < 0) return -1; +#elif defined(F_GETFL) + if (TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL)) < 0) return -1; +#else + struct stat s; + if (TEMP_FAILURE_RETRY(fstat(fd, &s)) < 0) return -1; +#endif + +#if !defined(_WIN32) + struct sockaddr_un addr; + socklen_t addrlen = sizeof(addr); + int ret = TEMP_FAILURE_RETRY(getsockname(fd, (struct sockaddr *)&addr, &addrlen)); + if (ret < 0) return -1; + char *path = NULL; + if (asprintf(&path, ANDROID_SOCKET_DIR"/%s", name) < 0) return -1; + if (!path) return -1; + int cmp = strcmp(addr.sun_path, path); + free(path); + if (cmp != 0) return -1; +#endif - return static_cast<int>(ret); + // It is what we think it is + return static_cast<int>(fd); } diff --git a/libcutils/tests/Android.bp b/libcutils/tests/Android.bp index 06d0e284e..bd354129d 100644 --- a/libcutils/tests/Android.bp +++ b/libcutils/tests/Android.bp @@ -14,7 +14,7 @@ cc_defaults { name: "libcutils_test_default", - srcs: ["sockets_test.cpp"], + srcs: ["sockets_test.cpp", "files_test.cpp"], target: { android: { diff --git a/libcutils/tests/files_test.cpp b/libcutils/tests/files_test.cpp new file mode 100644 index 000000000..1a7d67386 --- /dev/null +++ b/libcutils/tests/files_test.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <time.h> + +#include <cutils/files.h> +#include <gtest/gtest.h> + +TEST(FilesTest, android_get_control_file) { + static const char key[] = ANDROID_FILE_ENV_PREFIX "_dev_kmsg"; + static const char name[] = "/dev/kmsg"; + + EXPECT_EQ(unsetenv(key), 0); + EXPECT_EQ(android_get_control_file(name), -1); + + int fd; + ASSERT_GE(fd = open(name, O_RDONLY | O_CLOEXEC), 0); + EXPECT_EQ(android_get_control_file(name), -1); + + char val[32]; + snprintf(val, sizeof(val), "%d", fd); + EXPECT_EQ(setenv(key, val, true), 0); + + EXPECT_EQ(android_get_control_file(name), fd); + close(fd); + EXPECT_EQ(android_get_control_file(name), -1); + EXPECT_EQ(unsetenv(key), 0); + EXPECT_EQ(android_get_control_file(name), -1); +} diff --git a/libcutils/tests/sockets_test.cpp b/libcutils/tests/sockets_test.cpp index 0f682a2fa..82961a48f 100644 --- a/libcutils/tests/sockets_test.cpp +++ b/libcutils/tests/sockets_test.cpp @@ -18,10 +18,14 @@ // IPv6 capabilities. These tests assume that no UDP packets are lost, which // should be the case for loopback communication, but is not guaranteed. -#include <cutils/sockets.h> - +#include <stdio.h> +#include <stdlib.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/un.h> #include <time.h> +#include <cutils/sockets.h> #include <gtest/gtest.h> // Makes sure the passed sockets are valid, sends data between them, and closes @@ -185,3 +189,36 @@ TEST(SocketsTest, TestTcpReceiveTimeout) { TEST(SocketsTest, TestSocketSendBuffersFailure) { EXPECT_EQ(-1, socket_send_buffers(INVALID_SOCKET, nullptr, 0)); } + +TEST(SocketsTest, android_get_control_socket) { + static const char key[] = ANDROID_SOCKET_ENV_PREFIX "SocketsTest.android_get_control_socket"; + static const char* name = key + strlen(ANDROID_SOCKET_ENV_PREFIX); + + EXPECT_EQ(unsetenv(key), 0); + EXPECT_EQ(android_get_control_socket(name), -1); + + int fd; + ASSERT_GE(fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0), 0); + EXPECT_EQ(android_get_control_socket(name), -1); + + struct sockaddr_un addr; + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s", name); + unlink(addr.sun_path); + + EXPECT_EQ(bind(fd, (struct sockaddr*)&addr, sizeof(addr)), 0); + EXPECT_EQ(android_get_control_socket(name), -1); + + char val[32]; + snprintf(val, sizeof(val), "%d", fd); + EXPECT_EQ(setenv(key, val, true), 0); + + EXPECT_EQ(android_get_control_socket(name), fd); + socket_close(fd); + EXPECT_EQ(android_get_control_socket(name), -1); + EXPECT_EQ(unlink(addr.sun_path), 0); + EXPECT_EQ(android_get_control_socket(name), -1); + EXPECT_EQ(unsetenv(key), 0); + EXPECT_EQ(android_get_control_socket(name), -1); +} diff --git a/logd/Android.mk b/logd/Android.mk index 81637d21b..7fe48d746 100644 --- a/logd/Android.mk +++ b/logd/Android.mk @@ -29,7 +29,7 @@ LOCAL_SHARED_LIBRARIES := \ libcutils \ libbase \ libpackagelistparser \ - libminijail + libcap # This is what we want to do: # event_logtags = $(shell \ diff --git a/logd/logd.rc b/logd/logd.rc index 31ed4df34..54349dd67 100644 --- a/logd/logd.rc +++ b/logd/logd.rc @@ -2,10 +2,15 @@ service logd /system/bin/logd socket logd stream 0666 logd logd socket logdr seqpacket 0666 logd logd socket logdw dgram 0222 logd logd - group root system readproc + file /proc/kmsg r + file /dev/kmsg w + user logd + group logd system readproc writepid /dev/cpuset/system-background/tasks service logd-reinit /system/bin/logd --reinit oneshot disabled + user logd + group logd writepid /dev/cpuset/system-background/tasks diff --git a/logd/main.cpp b/logd/main.cpp index 1ac1415ed..d698976d0 100644 --- a/logd/main.cpp +++ b/logd/main.cpp @@ -39,13 +39,12 @@ #include <android-base/macros.h> #include <cutils/properties.h> #include <cutils/sched_policy.h> +#include <cutils/files.h> #include <cutils/sockets.h> -#include <libminijail.h> #include <log/event_tag_map.h> #include <packagelistparser/packagelistparser.h> #include <private/android_filesystem_config.h> #include <private/android_logger.h> -#include <scoped_minijail.h> #include <utils/threads.h> #include "CommandListener.h" @@ -90,34 +89,81 @@ // logd // -static int drop_privs() { +static int drop_privs(bool klogd, bool auditd) { + // Tricky, if ro.build.type is "eng" then this is true because of the + // side effect that ro.debuggable == 1 as well, else it is false. + bool eng = __android_logger_property_get_bool("ro.build.type", BOOL_DEFAULT_FALSE); + struct sched_param param; memset(¶m, 0, sizeof(param)); if (set_sched_policy(0, SP_BACKGROUND) < 0) { - return -1; + android::prdebug("failed to set background scheduling policy"); + if (!eng) return -1; } if (sched_setscheduler((pid_t) 0, SCHED_BATCH, ¶m) < 0) { - return -1; + android::prdebug("failed to set batch scheduler"); + if (!eng) return -1; } if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) { - return -1; + android::prdebug("failed to set background cgroup"); + if (!eng) return -1; } - if (prctl(PR_SET_DUMPABLE, 0) < 0) { + if (!eng && (prctl(PR_SET_DUMPABLE, 0) < 0)) { android::prdebug("failed to clear PR_SET_DUMPABLE"); return -1; } + if (prctl(PR_SET_KEEPCAPS, 1) < 0) { + android::prdebug("failed to set PR_SET_KEEPCAPS"); + if (!eng) return -1; + } + + std::unique_ptr<struct _cap_struct, int(*)(void *)> caps(cap_init(), cap_free); + if (cap_clear(caps.get()) < 0) return -1; + cap_value_t cap_value[] = { + CAP_SETGID, // must be first for below + klogd ? CAP_SYSLOG : CAP_SETGID, + auditd ? CAP_AUDIT_CONTROL : CAP_SETGID + }; + if (cap_set_flag(caps.get(), CAP_PERMITTED, + arraysize(cap_value), cap_value, + CAP_SET) < 0) return -1; + if (cap_set_flag(caps.get(), CAP_EFFECTIVE, + arraysize(cap_value), cap_value, + CAP_SET) < 0) return -1; + if (cap_set_proc(caps.get()) < 0) { + android::prdebug("failed to set CAP_SETGID, CAP_SYSLOG or CAP_AUDIT_CONTROL (%d)", errno); + if (!eng) return -1; + } + gid_t groups[] = { AID_READPROC }; - ScopedMinijail j(minijail_new()); - minijail_set_supplementary_gids(j.get(), arraysize(groups), groups); - minijail_change_uid(j.get(), AID_LOGD); - minijail_change_gid(j.get(), AID_LOGD); - minijail_use_caps(j.get(), CAP_TO_MASK(CAP_SYSLOG) | CAP_TO_MASK(CAP_AUDIT_CONTROL)); - minijail_enter(j.get()); + + if (setgroups(arraysize(groups), groups) == -1) { + android::prdebug("failed to set AID_READPROC groups"); + if (!eng) return -1; + } + + if (setgid(AID_LOGD) != 0) { + android::prdebug("failed to set AID_LOGD gid"); + if (!eng) return -1; + } + + if (setuid(AID_LOGD) != 0) { + android::prdebug("failed to set AID_LOGD uid"); + if (!eng) return -1; + } + + if (cap_set_flag(caps.get(), CAP_PERMITTED, 1, cap_value, CAP_CLEAR) < 0) return -1; + if (cap_set_flag(caps.get(), CAP_EFFECTIVE, 1, cap_value, CAP_CLEAR) < 0) return -1; + if (cap_set_proc(caps.get()) < 0) { + android::prdebug("failed to clear CAP_SETGID (%d)", errno); + if (!eng) return -1; + } + return 0; } @@ -189,11 +235,16 @@ static void *reinit_thread_start(void * /*obj*/) { set_sched_policy(0, SP_BACKGROUND); setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND); - // If we are AID_ROOT, we should drop to AID_SYSTEM, if we are anything - // else, we have even lesser privileges and accept our fate. Not worth - // checking for error returns setting this thread's privileges. - (void)setgid(AID_SYSTEM); - (void)setuid(AID_SYSTEM); + cap_t caps = cap_init(); + (void)cap_clear(caps); + (void)cap_set_proc(caps); + (void)cap_free(caps); + + // If we are AID_ROOT, we should drop to AID_LOGD+AID_SYSTEM, if we are + // anything else, we have even lesser privileges and accept our fate. Not + // worth checking for error returns setting this thread's privileges. + (void)setgid(AID_SYSTEM); // readonly access to /data/system/packages.list + (void)setuid(AID_LOGD); // access to everything logd. while (reinit_running && !sem_wait(&reinit) && reinit_running) { @@ -311,6 +362,39 @@ static void readDmesg(LogAudit *al, LogKlog *kl) { } } +static int issueReinit() { + cap_t caps = cap_init(); + (void)cap_clear(caps); + (void)cap_set_proc(caps); + (void)cap_free(caps); + + int sock = TEMP_FAILURE_RETRY( + socket_local_client("logd", + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_STREAM)); + if (sock < 0) return -errno; + + static const char reinitStr[] = "reinit"; + ssize_t ret = TEMP_FAILURE_RETRY(write(sock, reinitStr, sizeof(reinitStr))); + if (ret < 0) return -errno; + + struct pollfd p; + memset(&p, 0, sizeof(p)); + p.fd = sock; + p.events = POLLIN; + ret = TEMP_FAILURE_RETRY(poll(&p, 1, 1000)); + if (ret < 0) return -errno; + if ((ret == 0) || !(p.revents & POLLIN)) return -ETIME; + + static const char success[] = "success"; + char buffer[sizeof(success) - 1]; + memset(buffer, 0, sizeof(buffer)); + ret = TEMP_FAILURE_RETRY(read(sock, buffer, sizeof(buffer))); + if (ret < 0) return -errno; + + return strncmp(buffer, success, sizeof(success) - 1) != 0; +} + // Foreground waits for exit of the main persistent threads // that are started here. The threads are created to manage // UNIX domain client sockets for writing, reading and @@ -318,6 +402,17 @@ static void readDmesg(LogAudit *al, LogKlog *kl) { // logging plugins like auditd and restart control. Additional // transitory per-client threads are created for each reader. int main(int argc, char *argv[]) { + // issue reinit command. KISS argument parsing. + if ((argc > 1) && argv[1] && !strcmp(argv[1], "--reinit")) { + return issueReinit(); + } + + static const char dev_kmsg[] = "/dev/kmsg"; + fdDmesg = android_get_control_file(dev_kmsg); + if (fdDmesg < 0) { + fdDmesg = TEMP_FAILURE_RETRY(open(dev_kmsg, O_WRONLY | O_CLOEXEC)); + } + int fdPmesg = -1; bool klogd = __android_logger_property_get_bool("logd.kernel", BOOL_DEFAULT_TRUE | @@ -325,43 +420,13 @@ int main(int argc, char *argv[]) { BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE); if (klogd) { - fdPmesg = open("/proc/kmsg", O_RDONLY | O_NDELAY); - } - fdDmesg = open("/dev/kmsg", O_WRONLY); - - // issue reinit command. KISS argument parsing. - if ((argc > 1) && argv[1] && !strcmp(argv[1], "--reinit")) { - int sock = TEMP_FAILURE_RETRY( - socket_local_client("logd", - ANDROID_SOCKET_NAMESPACE_RESERVED, - SOCK_STREAM)); - if (sock < 0) { - return -errno; + static const char proc_kmsg[] = "/proc/kmsg"; + fdPmesg = android_get_control_file(proc_kmsg); + if (fdPmesg < 0) { + fdPmesg = TEMP_FAILURE_RETRY(open(proc_kmsg, + O_RDONLY | O_NDELAY | O_CLOEXEC)); } - static const char reinit[] = "reinit"; - ssize_t ret = TEMP_FAILURE_RETRY(write(sock, reinit, sizeof(reinit))); - if (ret < 0) { - return -errno; - } - struct pollfd p; - memset(&p, 0, sizeof(p)); - p.fd = sock; - p.events = POLLIN; - ret = TEMP_FAILURE_RETRY(poll(&p, 1, 1000)); - if (ret < 0) { - return -errno; - } - if ((ret == 0) || !(p.revents & POLLIN)) { - return -ETIME; - } - static const char success[] = "success"; - char buffer[sizeof(success) - 1]; - memset(buffer, 0, sizeof(buffer)); - ret = TEMP_FAILURE_RETRY(read(sock, buffer, sizeof(buffer))); - if (ret < 0) { - return -errno; - } - return strncmp(buffer, success, sizeof(success) - 1) != 0; + if (fdPmesg < 0) android::prdebug("Failed to open %s\n", proc_kmsg); } // Reinit Thread @@ -386,7 +451,10 @@ int main(int argc, char *argv[]) { pthread_attr_destroy(&attr); } - if (drop_privs() != 0) { + bool auditd = __android_logger_property_get_bool("logd.auditd", + BOOL_DEFAULT_TRUE | + BOOL_DEFAULT_FLAG_PERSIST); + if (drop_privs(klogd, auditd) != 0) { return -1; } @@ -441,9 +509,6 @@ int main(int argc, char *argv[]) { // initiated log messages. New log entries are added to LogBuffer // and LogReader is notified to send updates to connected clients. - bool auditd = __android_logger_property_get_bool("logd.auditd", - BOOL_DEFAULT_TRUE | - BOOL_DEFAULT_FLAG_PERSIST); LogAudit *al = NULL; if (auditd) { al = new LogAudit(logBuf, reader, |
