init: service file keyword
Solve one more issue where privilege is required to open a file and
we do not want to grant such to the service. This is the service side
of the picture, android_get_control_file() in libcutils is the client.
The file's descriptor is placed into the environment as
"ANDROID_FILE_<path>". For socket and files where non-alpha and
non-numeric characters in the <name/path> are replaced with _. There
was an accompanying change in android_get_control_socket() to match
in commit 'libcutils: add android_get_control_socket() test'
Add a gTest unit test for this that tests create_file and
android_get_control_file().
Test: gTest init_tests --gtest_filter=util.create_file
Bug: 32450474
Change-Id: I96eb970c707db6d51a9885873329ba1cb1f23140
diff --git a/init/service.cpp b/init/service.cpp
index e052b45..9fa11b8 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 @@
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::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 @@
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 @@
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 @@
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 @@
{"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 @@
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 @@
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() {