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
This commit is contained in:
parent
0b034d9d7b
commit
62767fe29f
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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.
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue