polardbxengine/router/tests/helpers/filesystem_utils.cc

189 lines
6.7 KiB
C++

/*
Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2.0,
as published by the Free Software Foundation.
This program is also distributed with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation. The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have included with MySQL.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <gmock/gmock.h>
#include "common.h"
#include "gtest_consoleoutput.h"
#include "mysql/harness/filesystem.h"
/** @file
* Stuff here could be considered an extension of stuff in
* mysql/harness/filesystem.h, except stuff here is only used to help in
* testing, and hasn't been tested itself.
*/
namespace {
#ifdef _WIN32
void check_ace_access_rights_local_service(const std::string &file_name,
ACCESS_ALLOWED_ACE *access_ace,
const bool read_only,
bool &is_local_service_ace) {
is_local_service_ace = false;
SID *sid = reinterpret_cast<SID *>(&access_ace->SidStart);
DWORD sid_size = SECURITY_MAX_SID_SIZE;
std::unique_ptr<SID, decltype(&free)> local_service_sid(
static_cast<SID *>(std::malloc(sid_size)), &free);
if (CreateWellKnownSid(WinLocalServiceSid, nullptr, local_service_sid.get(),
&sid_size) == FALSE) {
throw std::runtime_error("CreateWellKnownSid() failed: " +
std::to_string(GetLastError()));
}
if (EqualSid(sid, local_service_sid.get())) {
if (access_ace->Mask & (FILE_EXECUTE)) {
FAIL() << "Invalid file access rights for file " << file_name
<< " (Execute privilege granted to Local Service user).";
}
const auto read_perm = FILE_READ_DATA | FILE_READ_EA | FILE_READ_ATTRIBUTES;
if ((access_ace->Mask & read_perm) != read_perm) {
FAIL() << "Invalid file access rights for file " << file_name
<< "(Read privilege for Local Service user missing).";
}
const auto write_perm =
FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES;
if (read_only) {
if ((access_ace->Mask & write_perm) != 0) {
FAIL() << "Invalid file access rights for file " << file_name
<< "(Write privilege for Local Service user not expected).";
}
} else {
if ((access_ace->Mask & write_perm) != write_perm) {
FAIL() << "Invalid file access rights for file " << file_name
<< "(Write privilege for Local Service user missing).";
}
}
is_local_service_ace = true;
}
}
void check_acl_access_rights_local_service(const std::string &file_name,
ACL *dacl, const bool read_only) {
ACL_SIZE_INFORMATION dacl_size_info;
if (GetAclInformation(dacl, &dacl_size_info, sizeof(dacl_size_info),
AclSizeInformation) == FALSE) {
throw std::runtime_error("GetAclInformation() failed: " +
std::to_string(GetLastError()));
}
bool checked_local_service_ace = false;
for (DWORD ace_idx = 0; ace_idx < dacl_size_info.AceCount; ++ace_idx) {
LPVOID ace = nullptr;
if (GetAce(dacl, ace_idx, &ace) == FALSE) {
throw std::runtime_error("GetAce() failed: " +
std::to_string(GetLastError()));
continue;
}
if (static_cast<ACE_HEADER *>(ace)->AceType == ACCESS_ALLOWED_ACE_TYPE) {
bool is_local_service_ace;
check_ace_access_rights_local_service(
file_name, static_cast<ACCESS_ALLOWED_ACE *>(ace), read_only,
is_local_service_ace);
checked_local_service_ace |= is_local_service_ace;
}
}
if (!checked_local_service_ace)
FAIL() << "Permissions not set for user 'LocalService' on file '"
<< file_name << "'.";
}
void check_security_descriptor_access_rights(
const std::string &file_name,
std::unique_ptr<SECURITY_DESCRIPTOR, decltype(&free)> &sec_desc,
bool read_only) {
BOOL dacl_present;
ACL *dacl;
BOOL dacl_defaulted;
if (GetSecurityDescriptorDacl(sec_desc.get(), &dacl_present, &dacl,
&dacl_defaulted) == FALSE) {
throw std::runtime_error("GetSecurityDescriptorDacl() failed: " +
std::to_string(GetLastError()));
}
if (!dacl_present) {
// No DACL means: no access allowed. That's not good.
FAIL() << "No access allowed to file: " << file_name;
return;
}
if (!dacl) {
// Empty DACL means: all access allowed.
FAIL() << "Invalid file " << file_name
<< " access rights "
"(Everyone has full access rights).";
}
check_acl_access_rights_local_service(file_name, dacl, read_only);
}
#endif
} // namespace
void check_config_file_access_rights(const std::string &file_name,
const bool read_only) {
#ifdef _WIN32
std::unique_ptr<SECURITY_DESCRIPTOR, decltype(&free)> sec_descr(nullptr,
&free);
try {
sec_descr = mysql_harness::get_security_descriptor(file_name);
} catch (const std::system_error &) {
// that means that the system does not support ACL, in that case nothing
// really to check
return;
}
check_security_descriptor_access_rights(file_name, sec_descr, read_only);
#else
// on other OSes we ALWAYS expect 600 access rights for the config file
// weather it's static or dynamic one
(void)read_only;
struct stat status;
if (stat(file_name.c_str(), &status) != 0) {
if (errno == ENOENT) return;
FAIL() << "stat() failed (" << file_name
<< "): " << mysql_harness::get_strerror(errno);
}
static constexpr mode_t kFullAccessMask = (S_IRWXU | S_IRWXG | S_IRWXO);
static constexpr mode_t kRequiredAccessMask = (S_IRUSR | S_IWUSR);
if ((status.st_mode & kFullAccessMask) != kRequiredAccessMask)
FAIL() << "Config file (" << file_name
<< ") has file permissions that are not strict enough"
" (only RW for file's owner is allowed).";
#endif
}