#include <hooks/hooks_parser.h>
#include <legal_log_log.h>
#include <rotating_file.h>
+#include <util/filesystem.h>
#include <testutils/env_var_wrapper.h>
+#include <testutils/log_utils.h>
#include <gtest/gtest.h>
using namespace isc::hooks;
using namespace isc::legal_log;
using namespace isc::test;
+using namespace isc::dhcp::test;
+using namespace isc::util::file;
using namespace std;
namespace {
DbLogger legal_log_db_logger(legal_log_logger, legal_log_db_message_map);
/// @brief Test fixture
-struct LegalLogMgrTest : ::testing::Test {
+class LegalLogMgrTest : public LogContentTest {
+public:
/// @brief Constructor.
LegalLogMgrTest() : legal_log_dir_env_var_("KEA_LEGAL_LOG_DIR") {
HookLibraryScriptsChecker::getHookScriptsPath(true, TEST_DATA_BUILDDIR);
/// @brief Removes files that may be left over from previous tests
void reset() {
+ PathChecker::enableEnforcement(true);
std::ostringstream stream;
stream << "rm " << TEST_DATA_BUILDDIR << "/" << "*-legal"
<< ".*.txt 2>/dev/null";
std::ostringstream os;
os << "invalid path specified: '/tmp', supported path is '"
<< LegalLogMgr::getLogPath() << "'";
- ASSERT_THROW_MSG(LegalLogMgr::parseFile(params, map), BadValue, os.str());
+ ASSERT_THROW_MSG(LegalLogMgr::parseFile(params, map), SecurityError, os.str());
}
// Verify env variable path override.
std::ostringstream os;
os << "invalid path specified: '/bogus', supported path is '"
<< LegalLogMgr::getLogPath() << "'";
- ASSERT_THROW_MSG(LegalLogMgr::parseFile(params, map), BadValue, os.str());
+ ASSERT_THROW_MSG(LegalLogMgr::parseFile(params, map), SecurityError, os.str());
+}
+
+// Verify path validation when security is disabled.
+TEST_F(LegalLogMgrTest, pathValidationSecurityDisabled) {
+ PathChecker::enableEnforcement(false);
+ isc::db::DatabaseConnection::ParameterMap map;
+ EXPECT_NO_THROW(LegalLogMgr::parseConfig(ConstElementPtr(), map));
+ EXPECT_NO_THROW(LegalLogMgrFactory::addBackend(map));
+ EXPECT_TRUE(LegalLogMgrFactory::instance());
+
+ // Invalid path should be OK but we should get warning.
+ ElementPtr params = Element::createMap();
+ params->set("path", Element::create("/tmp"));
+ ASSERT_NO_THROW_LOG(LegalLogMgr::parseFile(params, map));
+
+ std::ostringstream os;
+ os << "LEGAL_LOG_PATH_SECURITY_WARNING Forensic log path specified is NOT SECURE:"
+ << " invalid path specified: '/tmp', supported path is '"
+ << LegalLogMgr::getLogPath() << "'";
+
+ EXPECT_EQ(1, countFile(os.str()));
}
// Verify that parsing extra parameters for rotate file works
#include <database/db_exceptions.h>
#include <dhcpsrv/cfgmgr.h>
#include <util/encode/encode.h>
+#include <util/filesystem.h>
#include <util/multi_threading_mgr.h>
#include <util/str.h>
#include <string>
using namespace isc::db;
using namespace isc::dhcp;
using namespace isc::util;
+using namespace isc::util::file;
namespace isc {
namespace host_cache {
try {
filename = CfgMgr::instance().validatePath(cmd_args_->stringValue());
+ } catch (const SecurityWarn& ex) {
+ LOG_WARN(host_cache_logger, HOST_CACHE_PATH_SECURITY_WARNING)
+ .arg(ex.what());
+ filename = cmd_args_->stringValue();
} catch (const std::exception& ex) {
isc_throw(BadValue, "parameter is invalid: " << ex.what());
}
extern const isc::log::MessageID HOST_CACHE_GET_ONE_SUBNET_ID_IDENTIFIER = "HOST_CACHE_GET_ONE_SUBNET_ID_IDENTIFIER";
extern const isc::log::MessageID HOST_CACHE_GET_ONE_SUBNET_ID_IDENTIFIER_HOST = "HOST_CACHE_GET_ONE_SUBNET_ID_IDENTIFIER_HOST";
extern const isc::log::MessageID HOST_CACHE_INIT_OK = "HOST_CACHE_INIT_OK";
+extern const isc::log::MessageID HOST_CACHE_PATH_SECURITY_WARNING = "HOST_CACHE_PATH_SECURITY_WARNING";
} // namespace host_cache
} // namespace isc
"HOST_CACHE_GET_ONE_SUBNET_ID_IDENTIFIER", "get one host with %1 reservation for subnet id %2, identified by %3",
"HOST_CACHE_GET_ONE_SUBNET_ID_IDENTIFIER_HOST", "using subnet id %1 and identifier %2, found host: %3",
"HOST_CACHE_INIT_OK", "loading Host Cache hooks library successful",
+ "HOST_CACHE_PATH_SECURITY_WARNING", "Cache file path specified is NOT SECURE: %1",
NULL
};
extern const isc::log::MessageID HOST_CACHE_GET_ONE_SUBNET_ID_IDENTIFIER;
extern const isc::log::MessageID HOST_CACHE_GET_ONE_SUBNET_ID_IDENTIFIER_HOST;
extern const isc::log::MessageID HOST_CACHE_INIT_OK;
+extern const isc::log::MessageID HOST_CACHE_PATH_SECURITY_WARNING;
} // namespace host_cache
} // namespace isc
% HOST_CACHE_INIT_OK loading Host Cache hooks library successful
This info message indicates that the Host Cache hooks library has been
loaded successfully. Enjoy!
+
+% HOST_CACHE_PATH_SECURITY_WARNING Cache file path specified is NOT SECURE: %1
+This warning message is issued when security enforcement is
+disabled and the host cache file path specified does not comply
+with the supported path. The server will still use the specified
+path but is warning that doing so may pose a security risk.
#include <dhcpsrv/cfgmgr.h>
#include <testutils/env_var_wrapper.h>
#include <testutils/multi_threading_utils.h>
+#include <testutils/log_utils.h>
#include <gtest/gtest.h>
#include <fstream>
#include <functional>
using namespace isc::host_cache;
using namespace isc::test;
using namespace isc::util;
+using namespace isc::dhcp::test;
using namespace std;
namespace ph = std::placeholders;
namespace {
/// @brief Test fixture for testing commands for the host-cache library
-class CommandTest : public ::testing::Test {
+//class CommandTest : public ::testing::Test {
+class CommandTest : public LogContentTest {
public:
/// @brief Constructor
CommandTest() {
+ file::PathChecker::enableEnforcement(true);
// Save the pre-test data dir and set it to the test directory.
CfgMgr::instance().clear();
original_datadir_ = CfgMgr::instance().getDataDir();
// Revert to original data directory.
CfgMgr::instance().getDataDir(true, original_datadir_);
+ file::PathChecker::enableEnforcement(true);
}
/// @brief Creates a sample IPv4 host
/// @brief Test cache-write command.
void testWriteCommand();
+ /// @brief Test cache-write command with security disabled.
+ void testWriteCommandSecurityWarning();
+
/// @brief Test cache-load command.
void testLoadCommand();
checkCommand(write_handler, badpath_cmd, 1, 1, exp_error);
}
+// Verifies that cache-write really writes the expected file.
+void
+CommandTest::testWriteCommandSecurityWarning() {
+ // Prepare
+ handlerType write_handler = std::bind(&writeHandler, hcptr_, ph::_1);
+ file::PathChecker::enableEnforcement(false);
+ string badpath = "/tmp/kea-host-cache-test.txt";
+ string write_cmd =
+ "{ \"command\": \"cache-write\", \"arguments\": \"" + badpath + "\" }";
+
+ // Insert an entry.
+ EXPECT_EQ(0, hcptr_->insert(createHost4(), false));
+ EXPECT_EQ(1, hcptr_->size());
+
+ // Write file
+ checkCommand(write_handler, write_cmd, 0, 0,
+ "1 entries dumped to '" + badpath + "'.");
+
+ std::ostringstream oss;
+ oss << "HOST_CACHE_PATH_SECURITY_WARNING Cache file path specified"
+ << " is NOT SECURE: invalid path specified: '/tmp', supported path is '"
+ << CfgMgr::instance().getDataDir() << "'";
+ EXPECT_EQ(1, countFile(oss.str()));
+}
+
// Verifies that cache-load can load a dump file.
void
CommandTest::testLoadCommand() {
testWriteCommand();
}
+TEST_F(CommandTest, writeSecurityWarning) {
+ testWriteCommandSecurityWarning();
+}
+
TEST_F(CommandTest, writeMultiThreading) {
MultiThreadingTest mt(true);
testWriteCommand();
#include <lease_cmds_log.h>
#include <stats/stats_mgr.h>
#include <util/encode/encode.h>
+#include <util/filesystem.h>
#include <util/multi_threading_mgr.h>
#include <boost/scoped_ptr.hpp>
using namespace isc::hooks;
using namespace isc::stats;
using namespace isc::util;
+using namespace isc::util::file;
using namespace isc::log;
using namespace std;
std::string filename;
try {
filename = CfgMgr::instance().validatePath(file->stringValue());
+ } catch (const SecurityWarn& ex) {
+ LOG_WARN(lease_cmds_logger, LEASE_CMDS_PATH_SECURITY_WARNING)
+ .arg(ex.what());
+ filename = file->stringValue();
} catch (const std::exception& ex) {
isc_throw(BadValue, "'filename' parameter is invalid: " << ex.what());
}
extern const isc::log::MessageID LEASE_CMDS_LEASES6_COMMITTED_FAILED = "LEASE_CMDS_LEASES6_COMMITTED_FAILED";
extern const isc::log::MessageID LEASE_CMDS_LEASES6_COMMITTED_LEASE_ERROR = "LEASE_CMDS_LEASES6_COMMITTED_LEASE_ERROR";
extern const isc::log::MessageID LEASE_CMDS_LOAD_ERROR = "LEASE_CMDS_LOAD_ERROR";
+extern const isc::log::MessageID LEASE_CMDS_PATH_SECURITY_WARNING = "LEASE_CMDS_PATH_SECURITY_WARNING";
extern const isc::log::MessageID LEASE_CMDS_RESEND_DDNS4 = "LEASE_CMDS_RESEND_DDNS4";
extern const isc::log::MessageID LEASE_CMDS_RESEND_DDNS4_FAILED = "LEASE_CMDS_RESEND_DDNS4_FAILED";
extern const isc::log::MessageID LEASE_CMDS_RESEND_DDNS6 = "LEASE_CMDS_RESEND_DDNS6";
"LEASE_CMDS_LEASES6_COMMITTED_FAILED", "reason: %1",
"LEASE_CMDS_LEASES6_COMMITTED_LEASE_ERROR", "evaluating binding-variables for lease: %1 for: %2, reason: %3",
"LEASE_CMDS_LOAD_ERROR", "loading Lease Commands hooks library failed: %1",
+ "LEASE_CMDS_PATH_SECURITY_WARNING", "lease file path specified is NOT SECURE: %1",
"LEASE_CMDS_RESEND_DDNS4", "lease4-resend-ddns command successful: %1",
"LEASE_CMDS_RESEND_DDNS4_FAILED", "lease4-resend-ddns command failed: %1",
"LEASE_CMDS_RESEND_DDNS6", "lease6-resend-ddns command successful: %1",
extern const isc::log::MessageID LEASE_CMDS_LEASES6_COMMITTED_FAILED;
extern const isc::log::MessageID LEASE_CMDS_LEASES6_COMMITTED_LEASE_ERROR;
extern const isc::log::MessageID LEASE_CMDS_LOAD_ERROR;
+extern const isc::log::MessageID LEASE_CMDS_PATH_SECURITY_WARNING;
extern const isc::log::MessageID LEASE_CMDS_RESEND_DDNS4;
extern const isc::log::MessageID LEASE_CMDS_RESEND_DDNS4_FAILED;
extern const isc::log::MessageID LEASE_CMDS_RESEND_DDNS6;
% LEASE_CMDS_WIPE6_FAILED lease6-wipe command failed (parameters: %1, reason: %2)
The lease6-wipe command has failed. Both the reason as well as the
parameters passed are logged.
+
+% LEASE_CMDS_PATH_SECURITY_WARNING lease file path specified is NOT SECURE: %1
+This warning message is issued when security enforcement is disabled
+and the path portion of the `filename` parameter of the lease4-write
+or lease6-write command does not comply with the supported path. The
+server will still use the specified path but is warning that doing so
+may pose a security risk.
#include <lease_cmds.h>
#include <lease_cmds_unittest.h>
#include <stats/stats_mgr.h>
+#include <util/filesystem.h>
#include <testutils/user_context_utils.h>
#include <testutils/multi_threading_utils.h>
#include <testutils/gtest_utils.h>
using namespace isc::dhcp_ddns;
using namespace isc::asiolink;
using namespace isc::stats;
+using namespace isc::util::file;
using namespace isc::test;
using namespace isc::lease_cmds;
setFamily(AF_INET);
}
+ /// @brief Destructor.
+ virtual ~Lease4CmdsTest(){};
+
/// @brief Checks if specified response contains IPv4 lease
///
/// @param lease Element tree that represents a lease
/// @brief Check that lease4-write works as expected.
void testLease4Write();
+
+ /// @brief Check that lease4-write works as expected when security
+ /// is disabled.
+ void testLease4WriteSecurityWarn();
};
void Lease4CmdsTest::testLease4AddMissingParams() {
testCommand(txt, CONTROL_RESULT_ERROR, os.str());
}
+void Lease4CmdsTest::testLease4WriteSecurityWarn() {
+ PathChecker::enableEnforcement(false);
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ // Filename must use supported path.
+ std::string cmd =
+ "{\n"
+ " \"command\": \"lease4-write\",\n"
+ " \"arguments\": {"
+ " \"filename\": \"/tmp/kea-lease-write-test.txt\"\n"
+ " }\n"
+ "}";
+
+ std::ostringstream os;
+ os << "LEASE_CMDS_PATH_SECURITY_WARNING lease file path specified is NOT SECURE:"
+ << " invalid path specified: '/tmp', supported path is '"
+ << CfgMgr::instance().getDataDir() << "'";
+
+ testCommand(cmd, CONTROL_RESULT_SUCCESS,
+ "IPv4 lease database into '/tmp/kea-lease-write-test.txt'.");
+
+ EXPECT_EQ(1, countFile(os.str()));
+}
+
TEST_F(Lease4CmdsTest, lease4AddMissingParams) {
testLease4AddMissingParams();
}
testLease4Write();
}
+TEST_F(Lease4CmdsTest, lease4WriteSecurityWarn) {
+ testLease4WriteSecurityWarn();
+}
+
} // end of anonymous namespace
#include <cc/data.h>
#include <process/daemon.h>
#include <stats/stats_mgr.h>
+#include <util/filesystem.h>
#include <testutils/user_context_utils.h>
#include <testutils/multi_threading_utils.h>
+#include <testutils/log_utils.h>
#include <gtest/gtest.h>
constexpr time_t DEC_2030_TIME = 1923222072;
/// @brief Test fixture for testing loading and unloading the flex-id library
-class LibLoadTest : public ::testing::Test {
+class LibLoadTest : public isc::dhcp::test::LogContentTest {
public:
/// @brief Constructor
LibLoadTest(std::string lib_filename)
enableD2();
lmptr_ = 0;
isc::stats::StatsMgr::instance().removeAll();
+ isc::util::file::PathChecker::enableEnforcement(true);
}
/// @brief Destructor
unloadLibs();
lmptr_ = 0;
isc::stats::StatsMgr::instance().removeAll();
+ isc::util::file::PathChecker::enableEnforcement(true);
}
/// @brief Creates an IPv4 lease
subdir_done()
endif
+libs_testutils = [
+ kea_testutils_lib
+]
+
dhcp_lease_cmds_libload_tests = executable(
'dhcp-lease-cmds-libload-tests',
'lease_cmds4_unittest.cc',
dependencies: [GTEST_DEP, CRYPTO_DEP],
include_directories: [include_directories('.'), include_directories('..')] + INCLUDES,
link_with: LIBS_BUILT_SO_FAR,
+ link_with: [libs_testutils] + LIBS_BUILT_SO_FAR,
)
test(
'dhcp-lease-cmds-libload-tests',
extern const isc::log::MessageID COMMAND_SOCKET_READ_FAIL = "COMMAND_SOCKET_READ_FAIL";
extern const isc::log::MessageID COMMAND_SOCKET_WRITE = "COMMAND_SOCKET_WRITE";
extern const isc::log::MessageID COMMAND_SOCKET_WRITE_FAIL = "COMMAND_SOCKET_WRITE_FAIL";
+extern const isc::log::MessageID COMMAND_UNIX_SOCKET_PATH_SECURITY_WARNING = "COMMAND_UNIX_SOCKET_PATH_SECURITY_WARNING";
extern const isc::log::MessageID COMMAND_WATCH_SOCKET_CLEAR_ERROR = "COMMAND_WATCH_SOCKET_CLEAR_ERROR";
extern const isc::log::MessageID COMMAND_WATCH_SOCKET_CLOSE_ERROR = "COMMAND_WATCH_SOCKET_CLOSE_ERROR";
extern const isc::log::MessageID COMMAND_WATCH_SOCKET_MARK_READY_ERROR = "COMMAND_WATCH_SOCKET_MARK_READY_ERROR";
"COMMAND_SOCKET_READ_FAIL", "Encountered error %1 while reading from command socket %2",
"COMMAND_SOCKET_WRITE", "Sent response of %1 bytes (%2 bytes left to send) over command socket %3",
"COMMAND_SOCKET_WRITE_FAIL", "Error while writing to command socket %1 : %2",
+ "COMMAND_UNIX_SOCKET_PATH_SECURITY_WARNING", "unix socket path is NOT SECURE: %1",
"COMMAND_WATCH_SOCKET_CLEAR_ERROR", "watch socket failed to clear: %1",
"COMMAND_WATCH_SOCKET_CLOSE_ERROR", "watch socket failed to close: %1",
"COMMAND_WATCH_SOCKET_MARK_READY_ERROR", "watch socket failed to mark ready: %1",
extern const isc::log::MessageID COMMAND_SOCKET_READ_FAIL;
extern const isc::log::MessageID COMMAND_SOCKET_WRITE;
extern const isc::log::MessageID COMMAND_SOCKET_WRITE_FAIL;
+extern const isc::log::MessageID COMMAND_UNIX_SOCKET_PATH_SECURITY_WARNING;
extern const isc::log::MessageID COMMAND_WATCH_SOCKET_CLEAR_ERROR;
extern const isc::log::MessageID COMMAND_WATCH_SOCKET_CLOSE_ERROR;
extern const isc::log::MessageID COMMAND_WATCH_SOCKET_MARK_READY_ERROR;
% HTTP_COMMAND_MGR_SERVICE_STOPPING Server is stopping %1 service %2
This informational message indicates that the server has stopped
HTTP/HTTPS service. When known the address and port are displayed.
+
+% COMMAND_UNIX_SOCKET_PATH_SECURITY_WARNING unix socket path is NOT SECURE: %1
+This warning message is issued when security enforcement is disabled
+and the path specified for a control channel unix socket-name does
+not comply with the supported path. The server will still use the
+specified path but is warning that doing so may pose a security risk.
#include <testutils/gtest_utils.h>
#include <testutils/test_to_element.h>
#include <testutils/env_var_wrapper.h>
+#include <testutils/log_utils.h>
using namespace isc;
using namespace isc::asiolink;
using namespace isc::http;
using namespace isc::test;
using namespace isc::util;
+using namespace isc::dhcp::test;
using namespace std;
namespace {
/// @brief Test fixture for UNIX control socket configuration.
-class UnixCommandConfigTest : public ::testing::Test {
+//class UnixCommandConfigTest : public ::testing::Test {
+class UnixCommandConfigTest : public LogContentTest {
public:
/// @brief Constructor.
UnixCommandConfigTest() : unix_config_() {
}
}
+// This test verifies security warning of invalid
+// socket paths.
+TEST_F(UnixCommandConfigTest, securityEnforcmentFalse) {
+ file::PathChecker::enableEnforcement(false);
+ std::string config = R"( { "socket-name": "/tmp/mysocket" } )";
+
+ ElementPtr json;
+ ASSERT_NO_THROW(json = Element::fromJSON(config));
+ ASSERT_NO_THROW_LOG(unix_config_.reset(new UnixCommandConfig(json)));
+
+ std::ostringstream oss;
+ oss << "COMMAND_UNIX_SOCKET_PATH_SECURITY_WARNING unix socket path is NOT SECURE:"
+ << " invalid path specified: '/tmp', supported path is '"
+ << UnixCommandConfig::getSocketPath() << "'";
+
+ EXPECT_EQ(1, countFile(oss.str()));
+}
+
} // end of anonymous namespace
#include <asiolink/io_asio_socket.h>
#include <cc/dhcp_config_error.h>
#include <config/command_mgr.h>
+#include <config/config_log.h>
#include <config/unix_command_config.h>
#include <util/filesystem.h>
#include <limits>
getSocketPath();
}
- auto valid_path = socket_path_checker_->validatePath(socket_path);
+ std::string valid_path;
+ try {
+ valid_path = socket_path_checker_->validatePath(socket_path);
+ } catch (const SecurityWarn& ex) {
+ LOG_WARN(command_logger, COMMAND_UNIX_SOCKET_PATH_SECURITY_WARNING)
+ .arg(ex.what());
+ // Skip checking permissions.
+ return(socket_path);
+ }
+
if (!socket_path_checker_->pathHasPermissions(socket_path_perms_)) {
isc_throw (DhcpConfigError,
"socket path:" << socket_path_checker_->getPath()
extern const isc::log::MessageID DHCPSRV_MEMFILE_NEEDS_DOWNGRADING = "DHCPSRV_MEMFILE_NEEDS_DOWNGRADING";
extern const isc::log::MessageID DHCPSRV_MEMFILE_NEEDS_UPGRADING = "DHCPSRV_MEMFILE_NEEDS_UPGRADING";
extern const isc::log::MessageID DHCPSRV_MEMFILE_NO_STORAGE = "DHCPSRV_MEMFILE_NO_STORAGE";
+extern const isc::log::MessageID DHCPSRV_MEMFILE_PATH_SECURITY_WARNING = "DHCPSRV_MEMFILE_PATH_SECURITY_WARNING";
extern const isc::log::MessageID DHCPSRV_MEMFILE_READ_HWADDR_FAIL = "DHCPSRV_MEMFILE_READ_HWADDR_FAIL";
extern const isc::log::MessageID DHCPSRV_MEMFILE_ROLLBACK = "DHCPSRV_MEMFILE_ROLLBACK";
extern const isc::log::MessageID DHCPSRV_MEMFILE_UPDATE_ADDR4 = "DHCPSRV_MEMFILE_UPDATE_ADDR4";
extern const isc::log::MessageID DHCPSRV_TIMERMGR_UNREGISTER_ALL_TIMERS = "DHCPSRV_TIMERMGR_UNREGISTER_ALL_TIMERS";
extern const isc::log::MessageID DHCPSRV_TIMERMGR_UNREGISTER_TIMER = "DHCPSRV_TIMERMGR_UNREGISTER_TIMER";
extern const isc::log::MessageID DHCPSRV_UNKNOWN_DB = "DHCPSRV_UNKNOWN_DB";
+extern const isc::log::MessageID LEGAL_LOG_PATH_SECURITY_WARNING = "LEGAL_LOG_PATH_SECURITY_WARNING";
} // namespace dhcp
} // namespace isc
"DHCPSRV_MEMFILE_NEEDS_DOWNGRADING", "version of lease file: %1 schema is later than version %2",
"DHCPSRV_MEMFILE_NEEDS_UPGRADING", "version of lease file: %1 schema is earlier than version %2",
"DHCPSRV_MEMFILE_NO_STORAGE", "running in non-persistent mode, leases will be lost after restart",
+ "DHCPSRV_MEMFILE_PATH_SECURITY_WARNING", "Lease file path specified is NOT SECURE: %1",
"DHCPSRV_MEMFILE_READ_HWADDR_FAIL", "failed to read hardware address from lease file: %1",
"DHCPSRV_MEMFILE_ROLLBACK", "rolling back memory file database",
"DHCPSRV_MEMFILE_UPDATE_ADDR4", "updating IPv4 lease for address %1",
"DHCPSRV_TIMERMGR_UNREGISTER_ALL_TIMERS", "unregistering all timers",
"DHCPSRV_TIMERMGR_UNREGISTER_TIMER", "unregistering timer: %1",
"DHCPSRV_UNKNOWN_DB", "unknown database type: %1",
+ "LEGAL_LOG_PATH_SECURITY_WARNING", "Forensic log path specified is NOT SECURE: %1",
NULL
};
extern const isc::log::MessageID DHCPSRV_MEMFILE_NEEDS_DOWNGRADING;
extern const isc::log::MessageID DHCPSRV_MEMFILE_NEEDS_UPGRADING;
extern const isc::log::MessageID DHCPSRV_MEMFILE_NO_STORAGE;
+extern const isc::log::MessageID DHCPSRV_MEMFILE_PATH_SECURITY_WARNING;
extern const isc::log::MessageID DHCPSRV_MEMFILE_READ_HWADDR_FAIL;
extern const isc::log::MessageID DHCPSRV_MEMFILE_ROLLBACK;
extern const isc::log::MessageID DHCPSRV_MEMFILE_UPDATE_ADDR4;
extern const isc::log::MessageID DHCPSRV_TIMERMGR_UNREGISTER_ALL_TIMERS;
extern const isc::log::MessageID DHCPSRV_TIMERMGR_UNREGISTER_TIMER;
extern const isc::log::MessageID DHCPSRV_UNKNOWN_DB;
+extern const isc::log::MessageID LEGAL_LOG_PATH_SECURITY_WARNING;
} // namespace dhcp
} // namespace isc
% DHCPSRV_UNKNOWN_DB unknown database type: %1
The database access string specified a database type (given in the
message) that is unknown to the software. This is a configuration error.
+
+% DHCPSRV_MEMFILE_PATH_SECURITY_WARNING Lease file path specified is NOT SECURE: %1
+This warning message is issued when security enforcement is
+disabled and the lease file path specified for does not comply
+with the supported path. The server will still use the specified
+path but is warning that doing so may pose a security risk.
+
+% LEGAL_LOG_PATH_SECURITY_WARNING Forensic log path specified is NOT SECURE: %1
+This warning message is issued when security enforcement is
+disabled and the path specified for forensic logging output
+does not comply with the supported path. The server will
+still use the specified path but is warning that doing so may
+pose a security risk.
#include <database/database_connection.h>
#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/dhcpsrv_log.h>
#include <eval/eval_context.h>
#include <util/reconnect_ctl.h>
#include <util/filesystem.h>
using namespace isc::eval;
using namespace isc::hooks;
using namespace isc::util;
+using namespace isc::util::file;
using namespace std;
namespace {
ConstElementPtr const value(parameters->get(key));
if (value) {
if (key == std::string("path")) {
- auto valid_path = validatePath(value->stringValue());
- file_parameters.emplace(key, valid_path);
+ try {
+ auto valid_path = validatePath(value->stringValue());
+ file_parameters.emplace(key, valid_path);
+ } catch (const SecurityWarn& ex) {
+ LOG_WARN(dhcpsrv_logger, LEGAL_LOG_PATH_SECURITY_WARNING)
+ .arg(ex.what());
+ file_parameters.emplace(key, value->stringValue());
+ }
}
file_parameters.emplace(key, value->stringValue());
#include <stats/stats_mgr.h>
#include <util/multi_threading_mgr.h>
#include <util/pid_file.h>
+#include <util/filesystem.h>
#include <boost/foreach.hpp>
#include <cstdio>
using namespace isc::db;
using namespace isc::util;
using namespace isc::stats;
+using namespace isc::util::file;
namespace isc {
namespace dhcp {
return (getDefaultLeaseFilePath(u));
}
- // If path is invalid this will throw.
- lease_file = CfgMgr::instance().validatePath(lease_file);
+ try {
+ lease_file = CfgMgr::instance().validatePath(lease_file);
+ } catch (const SecurityWarn& ex) {
+ LOG_WARN(dhcpsrv_logger, DHCPSRV_MEMFILE_PATH_SECURITY_WARNING)
+ .arg(ex.what());
+ }
+
return (lease_file);
}
#include <util/range_utilities.h>
#include <util/stopwatch.h>
#include <testutils/env_var_wrapper.h>
+#include <util/filesystem.h>
+#include <testutils/log_utils.h>
#include <gtest/gtest.h>
setExtendedInfoSanityCheck(CfgConsistency::EXTENDED_INFO_CHECK_FIX);
MultiThreadingMgr::instance().setMode(false);
+ file::PathChecker::enableEnforcement(true);
}
/// @brief Reopens the connection to the backend.
// Revert to original data directory.
CfgMgr::instance().getDataDir(true, original_datadir_);
+ file::PathChecker::enableEnforcement(true);
}
/// @brief Remove files being products of Lease File Cleanup.
<< CfgMgr::instance().getDataDir() << "'";
ASSERT_THROW_MSG(lease_mgr.reset(new Memfile_LeaseMgr(pmap)),
- BadValue, os.str());
+ file::SecurityError, os.str());
}
/// @brief Verifies that the supported path may be overridden with
testBigStats();
}
+/// @brief Test fixture which allows log content to be tested.
+class MemfileLeaseMgrLogTest : public LogContentTest {
+public:
+ /// @brief memfile lease mgr test constructor
+ ///
+ /// Creates memfile and stores it in lmptr_ pointer
+ MemfileLeaseMgrLogTest() :
+ data_dir_env_var_("KEA_DHCP_DATA_DIR") {
+
+ // Reset the env variable.
+ data_dir_env_var_.setValue();
+
+ // Save the pre-test data dir and set it to the test directory.
+ CfgMgr::instance().clear();
+ original_datadir_ = CfgMgr::instance().getDataDir();
+ CfgMgr::instance().getDataDir(true, TEST_DATA_BUILDDIR);
+
+ MultiThreadingMgr::instance().setMode(false);
+ file::PathChecker::enableEnforcement(true);
+ }
+
+ /// @brief destructor
+ ///
+ /// destroys lease manager backend.
+ virtual ~MemfileLeaseMgrLogTest() {
+ LeaseMgrFactory::destroy();
+
+ // Disable multi-threading.
+ MultiThreadingMgr::instance().setMode(false);
+
+ // Revert to original data directory.
+ CfgMgr::instance().getDataDir(true, original_datadir_);
+ file::PathChecker::enableEnforcement(true);
+ }
+
+ /// @brief RAII wrapper for KEA_DHCP_DATA_DIR env variable.
+ EnvVarWrapper data_dir_env_var_;
+
+ /// @brief Stores the pre-test DHCP data directory.
+ std::string original_datadir_;
+};
+
+/// @brief Verifies that a warning is logged when given
+/// an invalid path and enforcement is disabled.
+TEST_F(MemfileLeaseMgrLogTest, warnWhenSecurityDisabled) {
+ ASSERT_TRUE(data_dir_env_var_.getValue().empty());
+ file::PathChecker::enableEnforcement(false);
+
+ DatabaseConnection::ParameterMap pmap;
+ pmap["universe"] = "6";
+ pmap["persist"] = "true";
+ pmap["lfc-interval"] = "0";
+ pmap["name"] = "/tmp/leasefile6_1.csv";
+ boost::scoped_ptr<Memfile_LeaseMgr> lease_mgr;
+
+ ASSERT_NO_THROW(lease_mgr.reset(new Memfile_LeaseMgr(pmap)));
+ EXPECT_EQ(lease_mgr->getLeaseFilePath(Memfile_LeaseMgr::V6),
+ "/tmp/leasefile6_1.csv");
+
+ std::ostringstream oss;
+ oss << "DHCPSRV_MEMFILE_PATH_SECURITY_WARNING Lease file path specified is NOT SECURE:"
+ << " invalid path specified: '/tmp', supported path is '"
+ << CfgMgr::instance().getDataDir() << "'";
+
+ EXPECT_EQ(1, countFile(oss.str()));
+}
+
} // namespace
extern const isc::log::MessageID HOOKS_CLOSE_ERROR = "HOOKS_CLOSE_ERROR";
extern const isc::log::MessageID HOOKS_HOOK_LIST_RESET = "HOOKS_HOOK_LIST_RESET";
extern const isc::log::MessageID HOOKS_INCORRECT_VERSION = "HOOKS_INCORRECT_VERSION";
+extern const isc::log::MessageID HOOKS_LIBPATH_SECURITY_WARNING = "HOOKS_LIBPATH_SECURITY_WARNING";
extern const isc::log::MessageID HOOKS_LIBRARY_CLOSED = "HOOKS_LIBRARY_CLOSED";
extern const isc::log::MessageID HOOKS_LIBRARY_LOADED = "HOOKS_LIBRARY_LOADED";
extern const isc::log::MessageID HOOKS_LIBRARY_LOADING = "HOOKS_LIBRARY_LOADING";
"HOOKS_CLOSE_ERROR", "failed to close hook library %1: %2",
"HOOKS_HOOK_LIST_RESET", "the list of hooks has been reset",
"HOOKS_INCORRECT_VERSION", "hook library %1 is at version %2, require version %3",
+ "HOOKS_LIBPATH_SECURITY_WARNING", "Library path specified is NOT SECURE: %1",
"HOOKS_LIBRARY_CLOSED", "hooks library %1 successfully closed",
"HOOKS_LIBRARY_LOADED", "hooks library %1 successfully loaded",
"HOOKS_LIBRARY_LOADING", "loading hooks library %1",
extern const isc::log::MessageID HOOKS_CLOSE_ERROR;
extern const isc::log::MessageID HOOKS_HOOK_LIST_RESET;
extern const isc::log::MessageID HOOKS_INCORRECT_VERSION;
+extern const isc::log::MessageID HOOKS_LIBPATH_SECURITY_WARNING;
extern const isc::log::MessageID HOOKS_LIBRARY_CLOSED;
extern const isc::log::MessageID HOOKS_LIBRARY_LOADED;
extern const isc::log::MessageID HOOKS_LIBRARY_LOADING;
This error message is issued if the version() function in the specified
hooks library was called and generated an exception. The library is
considered unusable and will not be loaded.
+
+% HOOKS_LIBPATH_SECURITY_WARNING Library path specified is NOT SECURE: %1
+This warning message is issued when security enforcement is
+disabled and the library path specified for a given hook library
+does not comply with the supported path. The server will still load
+the hook library but is warning that doing so may pose a security
+risk.
#include <cc/data.h>
#include <cc/dhcp_config_error.h>
#include <hooks/hooks_parser.h>
+#include <hooks/hooks_log.h>
#include <boost/algorithm/string.hpp>
#include <util/filesystem.h>
#include <util/str.h>
getHooksPath();
}
- return (hooks_path_checker_->validatePath(libpath));
+ try {
+ return (hooks_path_checker_->validatePath(libpath));
+ } catch (const SecurityWarn& ex) {
+ LOG_WARN(hooks_logger, HOOKS_LIBPATH_SECURITY_WARNING)
+ .arg(ex.what());
+ return (libpath);
+ }
}
// @todo use the flat style, split into list and item
#include <hooks/server_hooks.h>
#include <util/filesystem.h>
#include <testutils/gtest_utils.h>
+#include <testutils/log_utils.h>
#include <hooks/tests/common_test_class.h>
#define TEST_ASYNC_CALLOUT
using namespace isc::hooks;
using namespace isc::data;
using namespace isc::util::file;
+using namespace isc::dhcp::test;
using namespace std;
namespace {
}
/// @brief Test fixture for hooks parsing.
-class HooksParserTest : public ::testing::Test {
+class HooksParserTest : public LogContentTest {
public:
/// @brief Constructor
HooksParserTest() {
}
/// @brief Destructor
- ~HooksParserTest() {
+ virtual ~HooksParserTest() {
// Restore the original environment path.
if (!original_path_.empty()) {
setenv("KEA_HOOKS_PATH", original_path_.c_str(), 1);
std::string lib_path_;
std::string exp_path_;
std::string exp_error_;
+ bool exp_security_err_;
};
std::list<Scenario> scenarios = {
__LINE__,
"/var/lib/bs/mylib.so",
"",
- string("invalid path specified: '/var/lib/bs', supported path is '" + def_path + "'")
+ string("invalid path specified: '/var/lib/bs', supported path is '" + def_path + "'"),
+ true
},
{
// No file name.
__LINE__,
def_path + "/",
"",
- string ("path: '" + def_path + "/' has no filename")
+ string ("path: '" + def_path + "/' has no filename"),
+ false
},
{
// File name only is valid.
__LINE__,
"mylib.so",
def_path + "/mylib.so",
- ""
+ "",
+ false
},
{
// Valid full path.
__LINE__,
def_path + "/mylib.so",
def_path + "/mylib.so",
- ""
+ "",
+ false
},
{
// Invalid relative path.
"../kea/mylib.so",
"",
string("invalid path specified: '../kea', supported path is '" +
- def_path + "'")
+ def_path + "'"),
+ true
}
};
HooksLibrariesParser::validatePath(scenario.lib_path_));
EXPECT_EQ(validated_path, scenario.exp_path_);
} else {
- ASSERT_THROW_MSG(validated_path =
- HooksLibrariesParser::validatePath(scenario.lib_path_),
- BadValue, scenario.exp_error_);
+ if (scenario.exp_security_err_) {
+ ASSERT_THROW_MSG(validated_path =
+ HooksLibrariesParser::validatePath(scenario.lib_path_),
+ SecurityError, scenario.exp_error_);
+ } else {
+ ASSERT_THROW_MSG(validated_path =
+ HooksLibrariesParser::validatePath(scenario.lib_path_),
+ BadValue, scenario.exp_error_);
+ }
}
}
}
BadValue, scenario.exp_error_);
}
}
+
+ std::ostringstream oss;
+ oss << "HOOKS_LIBPATH_SECURITY_WARNING Library path specified is NOT SECURE:"
+ << " invalid path specified: '/var/lib/bs', supported path is '"
+ << def_path << "'";
+ EXPECT_EQ(1, countFile(oss.str()));
}
// Verifies output of HooksConfig::toElement().
EXPECT_EQ(data::prettyPrint(cfg.toElement()), exp_cfg);
}
-} // Anonymous namespace
+} // Anonymous namespae
configuration: kea_hooks_conf_data,
)
+libs_testutils = [
+ kea_testutils_lib
+]
+
nvl = shared_library(
'nvl',
'no_version_library.cc',
dependencies: [GTEST_DEP],
cpp_args: [f'-DDEFAULT_HOOKS_PATH="@DEFAULT_HOOKS_PATH@"'],
include_directories: [include_directories('.')] + INCLUDES,
- link_with: [kea_util_unittests_lib] + LIBS_BUILT_SO_FAR,
+ link_with: [kea_util_unittests_lib, libs_testutils] + LIBS_BUILT_SO_FAR,
)
test(
'kea-hooks-tests',
getLogPath();
}
- return (log_path_checker_->validatePath(logpath));
+ try {
+ return (log_path_checker_->validatePath(logpath));
+ } catch (const SecurityWarn& ex) {
+ LOG_WARN(dctl_logger, DCTL_LOG_PATH_SECURITY_WARNING)
+ .arg(ex.what());
+ return (logpath);
+ }
}
void LogConfigParser::parseOutputOptions(std::vector<LoggingDestination>& destination,
extern const isc::log::MessageID DCTL_DEVELOPMENT_VERSION = "DCTL_DEVELOPMENT_VERSION";
extern const isc::log::MessageID DCTL_INIT_PROCESS = "DCTL_INIT_PROCESS";
extern const isc::log::MessageID DCTL_INIT_PROCESS_FAIL = "DCTL_INIT_PROCESS_FAIL";
+extern const isc::log::MessageID DCTL_LOG_PATH_SECURITY_WARNING = "DCTL_LOG_PATH_SECURITY_WARNING";
extern const isc::log::MessageID DCTL_NOT_RUNNING = "DCTL_NOT_RUNNING";
extern const isc::log::MessageID DCTL_OPEN_CONFIG_DB = "DCTL_OPEN_CONFIG_DB";
extern const isc::log::MessageID DCTL_PARSER_FAIL = "DCTL_PARSER_FAIL";
"DCTL_DEVELOPMENT_VERSION", "This software is a development branch of Kea. It is not recommended for production use.",
"DCTL_INIT_PROCESS", "%1 initializing the application",
"DCTL_INIT_PROCESS_FAIL", "%1 application initialization failed: %2",
+ "DCTL_LOG_PATH_SECURITY_WARNING", "Log output path specified is NOT SECURE: %1",
"DCTL_NOT_RUNNING", "%1 application instance is not running",
"DCTL_OPEN_CONFIG_DB", "Opening configuration database: %1",
"DCTL_PARSER_FAIL", "Parser error: %1",
extern const isc::log::MessageID DCTL_DEVELOPMENT_VERSION;
extern const isc::log::MessageID DCTL_INIT_PROCESS;
extern const isc::log::MessageID DCTL_INIT_PROCESS_FAIL;
+extern const isc::log::MessageID DCTL_LOG_PATH_SECURITY_WARNING;
extern const isc::log::MessageID DCTL_NOT_RUNNING;
extern const isc::log::MessageID DCTL_OPEN_CONFIG_DB;
extern const isc::log::MessageID DCTL_PARSER_FAIL;
unsupported signal. This is a programming error indicating that the
application has registered to receive the signal but no associated
processing logic has been added.
+
+% DCTL_LOG_PATH_SECURITY_WARNING Log output path specified is NOT SECURE: %1
+This warning message is issued when security enforcement is
+disabled and the output path specified for a given logger does
+not comply with the supported path. The server will still
+use the specified path but is warning that doing so may pose a
+security risk.
#include <cc/data.h>
#include <process/log_parser.h>
#include <process/process_messages.h>
+#include <process/daemon.h>
#include <exceptions/exceptions.h>
#include <log/logger_support.h>
#include <process/d_log.h>
+#include <util/filesystem.h>
#include <testutils/gtest_utils.h>
#include <testutils/io_utils.h>
+#include <testutils/log_utils.h>
#include <gtest/gtest.h>
using namespace isc;
using namespace isc::process;
using namespace isc::data;
+using namespace isc::util;
namespace {
/// each test. Strictly speaking this only resets the testing root logger (which
/// has the name "kea") but as the only other logger mentioned here ("wombat")
/// is not used elsewhere, that is sufficient.
-class LoggingTest : public ::testing::Test {
- public:
+class LoggingTest : public dhcp::test::LogContentTest {
+ public:
/// @brief Constructor
LoggingTest() {
resetLogPath();
/// @brief Resets the log path to default.
void resetLogPath() {
LogConfigParser::getLogPath(true);
+ file::PathChecker::enableEnforcement(true);
}
/// @brief Name of the log file
EXPECT_EQ("syslog:daemon" , storage->getLoggingInfo()[0].destinations_[0].output_);
}
+// Verifies that an invalid output path results in an
+// error security enforcement is enabled.
+TEST_F(LoggingTest, enforceSecurityTrue) {
+ const char* config_txt =
+ "{ \"loggers\": ["
+ " {"
+ " \"name\": \"kea\","
+ " \"output-options\": ["
+ " {"
+ " \"output\": \"/tmp/myfile.log\""
+ " }"
+ " ],"
+ " \"severity\": \"INFO\""
+ " }"
+ "]}";
+
+ ConfigPtr storage(new ConfigBase());
+
+ LogConfigParser parser(storage);
+
+ // We need to parse properly formed JSON and then extract
+ // "loggers" element from it. For some reason fromJSON is
+ // throwing at opening square bracket
+ ConstElementPtr config = Element::fromJSON(config_txt);
+ config = config->get("loggers");
+
+ std::ostringstream oss;
+ oss << "invalid path in `output`: invalid path specified: '/tmp', supported path is '"
+ << LogConfigParser::getLogPath()
+ << "' (<string>:1:82)";
+
+ ASSERT_THROW_MSG(parser.parseConfiguration(config), BadValue,
+ oss.str());
+}
+
+// Verifies that an invalid output path results in a
+// warning when security enforcement is disabled.
+TEST_F(LoggingTest, enforceSecurityFalse) {
+ file::PathChecker::enableEnforcement(false);
+ const char* config_txt =
+ "{ \"loggers\": ["
+ " {"
+ " \"name\": \"kea\","
+ " \"output-options\": ["
+ " {"
+ " \"output\": \"/tmp/myfile.log\""
+ " }"
+ " ],"
+ " \"severity\": \"INFO\""
+ " }"
+ "]}";
+
+ ConfigPtr storage(new ConfigBase());
+
+ LogConfigParser parser(storage);
+
+ // We need to parse properly formed JSON and then extract
+ // "loggers" element from it. For some reason fromJSON is
+ // throwing at opening square bracket
+ ConstElementPtr config = Element::fromJSON(config_txt);
+ config = config->get("loggers");
+
+ ASSERT_NO_THROW_LOG(parser.parseConfiguration(config));
+
+ ASSERT_EQ(1, storage->getLoggingInfo().size());
+
+ EXPECT_EQ("kea", storage->getLoggingInfo()[0].name_);
+ EXPECT_EQ(isc::log::INFO, storage->getLoggingInfo()[0].severity_);
+
+ ASSERT_EQ(1, storage->getLoggingInfo()[0].destinations_.size());
+ EXPECT_EQ("/tmp/myfile.log" , storage->getLoggingInfo()[0].destinations_[0].output_);
+
+ std::ostringstream oss;
+ oss << "DCTL_LOG_PATH_SECURITY_WARNING Log output path specified is NOT SECURE:"
+ << " invalid path specified: '/tmp', supported path is '"
+ << LogConfigParser::getLogPath() << "'";
+
+ EXPECT_EQ(1, countFile(oss.str()));
+}
+
/// @todo Add tests for malformed logging configuration
/// @todo There is no easy way to test applyConfiguration() and defaultLogging().
auto parent_path = input_path.parentPath();
auto parent_dir = input_path.parentDirectory();
if (!parent_dir.empty()) {
- if (!enforce_path) {
- // Security set to lax, let it fly.
- return (input_path_str);
- }
-
// We only allow absolute path equal to default. Catch an invalid path.
if ((parent_path != path_) || (parent_dir == "/")) {
- isc_throw(BadValue, "invalid path specified: '"
- << (parent_path.empty() ? "/" : parent_path)
- << "', supported path is '"
- << path_ << "'");
+ std::ostringstream oss;
+ oss << "invalid path specified: '"
+ << (parent_path.empty() ? "/" : parent_path)
+ << "', supported path is '"
+ << path_ << "'";
+
+ if (enforce_path) {
+ isc_throw(SecurityError, oss.str());
+ } else {
+ isc_throw(SecurityWarn, oss.str());
+ }
}
}
PathChecker::validateDirectory(const std::string input_path_str,
bool enforce_path /* = PathChecker::shouldEnforceSecurity() */) const {
std::string input_copy = trim(input_path_str);
- if (!enforce_path) {
- return(input_copy);
- }
-
// We only allow absolute path equal to default. Catch an invalid path.
if (!input_path_str.empty()) {
std::string input_copy = input_path_str;
}
if (input_copy != path_) {
- isc_throw(BadValue, "invalid path specified: '"
- << input_path_str << "', supported path is '"
- << path_ << "'");
+ std::ostringstream oss;
+ oss << "invalid path specified: '"
+ << input_path_str << "', supported path is '"
+ << path_ << "'";
+
+ if (enforce_path) {
+ isc_throw(SecurityError, oss.str());
+ } else {
+ isc_throw(SecurityWarn, oss.str());
+ }
}
}
#ifndef KEA_UTIL_FILESYSTEM_H
#define KEA_UTIL_FILESYSTEM_H
+#include <exceptions/exceptions.h>
#include <sys/stat.h>
#include <string>
#include <boost/shared_ptr.hpp>
namespace util {
namespace file {
+/// @brief A generic exception that is thrown if a parameter given
+/// violates security check but enforcement is lax.
+class SecurityWarn : public Exception {
+public:
+ SecurityWarn(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+/// @brief A generic exception that is thrown if a parameter given
+/// violates security and enforcement is true.
+class SecurityError : public Exception {
+public:
+ SecurityError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+/// @brief A generic exception that is thrown if a parameter given
+/// violates security check but enfordement is lax.
+
/// @brief Get the content of a regular file.
///
/// @param file_name The file name.
/// returns valid path using the supported path and the input path name.
///
/// @param input_path_str file path to validate.
- /// @param enforce_path enables validation against the supported path. If false
- /// verifies only that the path contains a file name.
+ /// @param enforce_path If true throw SecurityError when validation against the
+ /// supported path fails, if false throw SecurityWarn.
///
/// @return validated path as a string (supported path + input file name)
///
- /// @throw BadValue if the input path does not include a file name or if the
- /// it the parent path does not path the supported path.
+ /// @throw BadValue if the input path does not include a file name.
+ /// SecurityError if the parent path does not path the supported path and
+ /// security is being enforced, SecurityWarn if it is not being enforced.
std::string validatePath(const std::string input_path_str,
bool enforce_path = shouldEnforceSecurity()) const;
/// the supported path. Otherwise it throws an error.
///
/// @param input_path_str file path to validate.
- /// @param enforce_path enables validation against the supported path. If
- /// it simply returns the supported path.
+ /// @param enforce_path If true throw SecurityError when validation against the
+ /// supported path fails, if false throw SecurityWarn.
///
/// @return validated path
///
- /// @throw BadValue if the input directory does not match the supported
- /// path.
+ /// @throw SecurityError if the path does not match the supported path and
+ /// security is being enforced, SecurityWarn if it is not being enforced.
std::string validateDirectory(const std::string input_path_str,
bool enforce_path = shouldEnforceSecurity()) const;
std::string lib_path_;
std::string exp_path_;
std::string exp_error_;
+ bool exp_security_err_;
};
std::list<Scenario> scenarios = {
__LINE__,
"/mylib.so",
"",
- string("invalid path specified: '/', supported path is '" + def_path + "'")
+ string("invalid path specified: '/', supported path is '" + def_path + "'"),
+ true
},
{
// Invalid parent path.
__LINE__,
"/var/lib/bs/mylib.so",
"",
- string("invalid path specified: '/var/lib/bs', supported path is '" + def_path + "'")
+ string("invalid path specified: '/var/lib/bs', supported path is '" + def_path + "'"),
+ true
},
{
// No file name.
__LINE__,
def_path + "/",
"",
- string ("path: '" + def_path + "/' has no filename")
+ string ("path: '" + def_path + "/' has no filename"),
+ false
},
{
// File name only is valid.
__LINE__,
"mylib.so",
def_path + "/mylib.so",
- ""
+ "",
+ false
},
{
// Valid full path.
__LINE__,
def_path + "/mylib.so",
def_path + "/mylib.so",
- ""
+ "",
+ false
},
{
// White space for file name.
__LINE__,
" ",
"",
- string("path: '' has no filename")
+ string("path: '' has no filename"),
+ false
},
{
// Invalid relative path.
"../kea/mylib.so",
"",
string("invalid path specified: '../kea', supported path is '" +
- def_path + "'")
+ def_path + "'"),
+ true
}
};
checker.validatePath(scenario.lib_path_));
EXPECT_EQ(validated_path, scenario.exp_path_);
} else {
- ASSERT_THROW_MSG(validated_path =
- checker.validatePath(scenario.lib_path_),
- BadValue, scenario.exp_error_);
+ if (scenario.exp_security_err_) {
+ ASSERT_THROW_MSG(validated_path =
+ checker.validatePath(scenario.lib_path_),
+ SecurityError, scenario.exp_error_);
+ } else {
+ ASSERT_THROW_MSG(validated_path =
+ checker.validatePath(scenario.lib_path_),
+ BadValue, scenario.exp_error_);
+ }
}
}
}
std::string lib_path_;
std::string exp_path_;
std::string exp_error_;
+ bool exp_security_warn_;
};
std::list<Scenario> scenarios = {
__LINE__,
"/mylib.so",
"/mylib.so",
- "",
+ string("invalid path specified: '/', supported path is '" + def_path + "'"),
+ true
},
{
- // Invalid parent path but shouldn't care.
+ // Invalid parent path.
__LINE__,
"/var/lib/bs/mylib.so",
"/var/lib/bs/mylib.so",
- ""
+ string("invalid path specified: '/var/lib/bs', supported path is '"
+ + def_path + "'"),
+ true
},
{
// No file name.
__LINE__,
def_path + "/",
"",
- string ("path: '" + def_path + "/' has no filename")
+ string ("path: '" + def_path + "/' has no filename"),
+ false
},
{
// File name only is valid.
__LINE__,
"mylib.so",
def_path + "/mylib.so",
- ""
+ "",
+ false
},
{
// Valid full path.
__LINE__,
def_path + "/mylib.so",
def_path + "/mylib.so",
- ""
+ "",
+ false
},
{
// White space for file name.
__LINE__,
" ",
"",
- string("path: '' has no filename")
+ string("path: '' has no filename"),
+ false
}
};
checker.validatePath(scenario.lib_path_));
EXPECT_EQ(validated_path, scenario.exp_path_);
} else {
- ASSERT_THROW_MSG(validated_path =
- checker.validatePath(scenario.lib_path_),
- BadValue, scenario.exp_error_);
+ if (scenario.exp_security_warn_) {
+ ASSERT_THROW_MSG(validated_path =
+ checker.validatePath(scenario.lib_path_),
+ SecurityWarn, scenario.exp_error_);
+ } else {
+ ASSERT_THROW_MSG(validated_path =
+ checker.validatePath(scenario.lib_path_),
+ BadValue, scenario.exp_error_);
+ }
}
}
}
} else {
ASSERT_THROW_MSG(validated_path =
checker.validateDirectory(scenario.lib_path_),
- BadValue, scenario.exp_error_);
+ SecurityError, scenario.exp_error_);
}
}
}
// Invalid parent path but shouldn't care.
__LINE__,
"/var/lib/bs/",
- "/var/lib/bs/",
- ""
+ "",
+ string("invalid path specified: '/var/lib/bs/', supported path is '" + def_path + "'")
},
{
- // File name only is valid.
+ // File name only is invalid.
__LINE__,
"mylib.so",
- "mylib.so",
- ""
+ "",
+ string("invalid path specified: 'mylib.so', supported path is '" + def_path + "'")
},
{
// Valid full path.
__LINE__,
def_path + "/",
- def_path + "/",
+ def_path,
""
},
{
__LINE__,
" ",
"",
- ""
+ string("invalid path specified: ' ', supported path is '" + def_path + "'")
}
};
} else {
ASSERT_THROW_MSG(validated_path =
checker.validateDirectory(scenario.lib_path_),
- BadValue, scenario.exp_error_);
+ SecurityWarn, scenario.exp_error_);
}
}
}