Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 62767fe2 authored by Mark Salyzyn's avatar Mark Salyzyn
Browse files

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
parent 0b034d9d
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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)

init/descriptors.cpp

0 → 100644
+104 −0
Original line number Diff line number Diff line
/*
 * 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;
}

init/descriptors.h

0 → 100644
+78 −0
Original line number Diff line number Diff line
/*
 * 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
+14 −6
Original line number Diff line number Diff line
@@ -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.
+44 −49
Original line number Diff line number Diff line
@@ -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() {
Loading