ueventd: Run external handler as non-root group

The external firmware handler always has root group privileges because
it is forked/executed without setgid() by ueventd which has root
privileges. This patch calls setgid() with group ID specified in
ueventd.rc before execv().

Test: atest CtsInitTestCases
Signed-off-by: Suchang Woo <suchang.woo@samsung.com>
Change-Id: Id1430e783b0e409d55ac80fe213e81ba099729e2
This commit is contained in:
Suchang Woo 2021-05-13 18:56:31 +09:00
parent fc7852b741
commit 10c6374e81
5 changed files with 63 additions and 11 deletions

View file

@ -123,7 +123,10 @@ not present.
The exact firmware file to be served can be customized by running an external program by a
`external_firmware_handler` line in a ueventd.rc file. This line takes the format of
external_firmware_handler <devpath> <user name to run as> <path to external program>
external_firmware_handler <devpath> <user [group]> <path to external program>
The handler will be run as the given user, or if a group is provided, as the given user and group.
For example
external_firmware_handler /devices/leds/red/firmware/coeffs.bin system /vendor/bin/led_coeffs.bin

View file

@ -19,6 +19,7 @@
#include <fcntl.h>
#include <fnmatch.h>
#include <glob.h>
#include <grp.h>
#include <pwd.h>
#include <signal.h>
#include <stdlib.h>
@ -81,9 +82,9 @@ static bool IsBooting() {
return access("/dev/.booting", F_OK) == 0;
}
ExternalFirmwareHandler::ExternalFirmwareHandler(std::string devpath, uid_t uid,
ExternalFirmwareHandler::ExternalFirmwareHandler(std::string devpath, uid_t uid, gid_t gid,
std::string handler_path)
: devpath(std::move(devpath)), uid(uid), handler_path(std::move(handler_path)) {
: devpath(std::move(devpath)), uid(uid), gid(gid), handler_path(std::move(handler_path)) {
auto wildcard_position = this->devpath.find('*');
if (wildcard_position != std::string::npos) {
if (wildcard_position == this->devpath.length() - 1) {
@ -97,13 +98,17 @@ ExternalFirmwareHandler::ExternalFirmwareHandler(std::string devpath, uid_t uid,
}
}
ExternalFirmwareHandler::ExternalFirmwareHandler(std::string devpath, uid_t uid,
std::string handler_path)
: ExternalFirmwareHandler(devpath, uid, 0, handler_path) {}
FirmwareHandler::FirmwareHandler(std::vector<std::string> firmware_directories,
std::vector<ExternalFirmwareHandler> external_firmware_handlers)
: firmware_directories_(std::move(firmware_directories)),
external_firmware_handlers_(std::move(external_firmware_handlers)) {}
Result<std::string> FirmwareHandler::RunExternalHandler(const std::string& handler, uid_t uid,
const Uevent& uevent) const {
gid_t gid, const Uevent& uevent) const {
unique_fd child_stdout;
unique_fd parent_stdout;
if (!Socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, &child_stdout, &parent_stdout)) {
@ -140,6 +145,13 @@ Result<std::string> FirmwareHandler::RunExternalHandler(const std::string& handl
}
c_args.emplace_back(nullptr);
if (gid != 0) {
if (setgid(gid) != 0) {
fprintf(stderr, "setgid() failed: %s", strerror(errno));
_exit(EXIT_FAILURE);
}
}
if (setuid(uid) != 0) {
fprintf(stderr, "setuid() failed: %s", strerror(errno));
_exit(EXIT_FAILURE);
@ -196,8 +208,8 @@ std::string FirmwareHandler::GetFirmwarePath(const Uevent& uevent) const {
<< "' for devpath: '" << uevent.path << "' firmware: '" << uevent.firmware
<< "'";
auto result =
RunExternalHandler(external_handler.handler_path, external_handler.uid, uevent);
auto result = RunExternalHandler(external_handler.handler_path, external_handler.uid,
external_handler.gid, uevent);
if (!result.ok()) {
LOG(ERROR) << "Using default firmware; External firmware handler failed: "
<< result.error();

View file

@ -16,6 +16,7 @@
#pragma once
#include <grp.h>
#include <pwd.h>
#include <functional>
@ -31,9 +32,11 @@ namespace init {
struct ExternalFirmwareHandler {
ExternalFirmwareHandler(std::string devpath, uid_t uid, std::string handler_path);
ExternalFirmwareHandler(std::string devpath, uid_t uid, gid_t gid, std::string handler_path);
std::string devpath;
uid_t uid;
gid_t gid;
std::string handler_path;
std::function<bool(const std::string&)> match;
@ -51,7 +54,7 @@ class FirmwareHandler : public UeventHandler {
friend void FirmwareTestWithExternalHandler(const std::string& test_name,
bool expect_new_firmware);
Result<std::string> RunExternalHandler(const std::string& handler, uid_t uid,
Result<std::string> RunExternalHandler(const std::string& handler, uid_t uid, gid_t gid,
const Uevent& uevent) const;
std::string GetFirmwarePath(const Uevent& uevent) const;
void ProcessFirmwareEvent(const std::string& root, const std::string& firmware) const;

View file

@ -101,8 +101,8 @@ Result<void> ParseFirmwareDirectoriesLine(std::vector<std::string>&& args,
Result<void> ParseExternalFirmwareHandlerLine(
std::vector<std::string>&& args,
std::vector<ExternalFirmwareHandler>* external_firmware_handlers) {
if (args.size() != 4) {
return Error() << "external_firmware_handler lines must have exactly 3 parameters";
if (args.size() != 4 && args.size() != 5) {
return Error() << "external_firmware_handler lines must have 3 or 4 parameters";
}
if (std::find_if(external_firmware_handlers->begin(), external_firmware_handlers->end(),
@ -117,7 +117,19 @@ Result<void> ParseExternalFirmwareHandlerLine(
return ErrnoError() << "invalid handler uid'" << args[2] << "'";
}
ExternalFirmwareHandler handler(std::move(args[1]), pwd->pw_uid, std::move(args[3]));
gid_t gid = 0;
int handler_index = 3;
if (args.size() == 5) {
struct group* grp = getgrnam(args[3].c_str());
if (!grp) {
return ErrnoError() << "invalid handler gid '" << args[3] << "'";
}
gid = grp->gr_gid;
handler_index = 4;
}
ExternalFirmwareHandler handler(std::move(args[1]), pwd->pw_uid, gid,
std::move(args[handler_index]));
external_firmware_handlers->emplace_back(std::move(handler));
return {};

View file

@ -49,6 +49,7 @@ void TestExternalFirmwareHandler(const ExternalFirmwareHandler& expected,
const ExternalFirmwareHandler& test) {
EXPECT_EQ(expected.devpath, test.devpath) << expected.devpath;
EXPECT_EQ(expected.uid, test.uid) << expected.uid;
EXPECT_EQ(expected.gid, test.gid) << expected.gid;
EXPECT_EQ(expected.handler_path, test.handler_path) << expected.handler_path;
}
@ -157,39 +158,59 @@ external_firmware_handler /devices/path/firmware/something002.bin radio "/vendor
external_firmware_handler /devices/path/firmware/* root "/vendor/bin/firmware_handler.sh"
external_firmware_handler /devices/path/firmware/something* system "/vendor/bin/firmware_handler.sh"
external_firmware_handler /devices/path/*/firmware/something*.bin radio "/vendor/bin/firmware_handler.sh"
external_firmware_handler /devices/path/firmware/something003.bin system system /vendor/bin/firmware_handler.sh
external_firmware_handler /devices/path/firmware/something004.bin radio radio "/vendor/bin/firmware_handler.sh --has --arguments"
)";
auto external_firmware_handlers = std::vector<ExternalFirmwareHandler>{
{
"devpath",
AID_ROOT,
AID_ROOT,
"handler_path",
},
{
"/devices/path/firmware/something001.bin",
AID_SYSTEM,
AID_ROOT,
"/vendor/bin/firmware_handler.sh",
},
{
"/devices/path/firmware/something002.bin",
AID_RADIO,
AID_ROOT,
"/vendor/bin/firmware_handler.sh --has --arguments",
},
{
"/devices/path/firmware/",
AID_ROOT,
AID_ROOT,
"/vendor/bin/firmware_handler.sh",
},
{
"/devices/path/firmware/something",
AID_SYSTEM,
AID_ROOT,
"/vendor/bin/firmware_handler.sh",
},
{
"/devices/path/*/firmware/something*.bin",
AID_RADIO,
AID_ROOT,
"/vendor/bin/firmware_handler.sh",
},
{
"/devices/path/firmware/something003.bin",
AID_SYSTEM,
AID_SYSTEM,
"/vendor/bin/firmware_handler.sh",
},
{
"/devices/path/firmware/something004.bin",
AID_RADIO,
AID_RADIO,
"/vendor/bin/firmware_handler.sh --has --arguments",
},
};
TestUeventdFile(ueventd_file, {{}, {}, {}, {}, external_firmware_handlers});
@ -205,6 +226,7 @@ external_firmware_handler devpath root handler_path2
{
"devpath",
AID_ROOT,
AID_ROOT,
"handler_path",
},
};
@ -305,7 +327,7 @@ parallel_restorecon enabled
};
auto external_firmware_handlers = std::vector<ExternalFirmwareHandler>{
{"/devices/path/firmware/firmware001.bin", AID_ROOT, "/vendor/bin/touch.sh"},
{"/devices/path/firmware/firmware001.bin", AID_ROOT, AID_ROOT, "/vendor/bin/touch.sh"},
};
size_t uevent_socket_rcvbuf_size = 6 * 1024 * 1024;