From: Razvan Becheriu Date: Tue, 20 May 2025 10:06:53 +0000 (+0300) Subject: [#3839] backport #3831 to v2_4 X-Git-Tag: Kea-2.4.2~9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f7061c4e9711f395fbc940b0cf0ddbde87e0fc13;p=thirdparty%2Fkea.git [#3839] backport #3831 to v2_4 --- diff --git a/ChangeLog b/ChangeLog index 3dbe84667b..a2d4b2fc7c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,4 @@ -2262. [sec]* fdupont +2171. [sec]* fdupont Change the umask to no group write and no other access at the entry of Kea server/agent binaries. CVE:2025-32803 diff --git a/doc/examples/agent/comments.json b/doc/examples/agent/comments.json index 10c82e3fa3..6803165ae0 100644 --- a/doc/examples/agent/comments.json +++ b/doc/examples/agent/comments.json @@ -43,7 +43,7 @@ { "comment": "control socket for DHCPv4 server", "socket-type": "unix", - "socket-name": "/tmp/kea4-ctrl-socket" + "socket-name": "kea4-ctrl-socket" } }, diff --git a/doc/examples/agent/simple.json b/doc/examples/agent/simple.json index a7227de04f..155dd627cf 100644 --- a/doc/examples/agent/simple.json +++ b/doc/examples/agent/simple.json @@ -86,21 +86,21 @@ { "comment": "socket to DHCPv4 server", "socket-type": "unix", - "socket-name": "/tmp/kea4-ctrl-socket" + "socket-name": "kea4-ctrl-socket" }, // Location of the DHCPv6 command channel socket. "dhcp6": { "socket-type": "unix", - "socket-name": "/tmp/kea6-ctrl-socket" + "socket-name": "kea6-ctrl-socket" }, // Location of the D2 command channel socket. "d2": { "socket-type": "unix", - "socket-name": "/tmp/kea-ddns-ctrl-socket", + "socket-name": "kea-ddns-ctrl-socket", "user-context": { "in-use": false } } }, diff --git a/doc/examples/ddns/all-keys-netconf.json b/doc/examples/ddns/all-keys-netconf.json index 2ea6fad859..f8ea63bd78 100644 --- a/doc/examples/ddns/all-keys-netconf.json +++ b/doc/examples/ddns/all-keys-netconf.json @@ -40,7 +40,7 @@ // Location of the UNIX domain socket file the DHCP-DDNS server uses // to receive control commands from the Kea Control Agent or the // local server administrator. - "socket-name": "/tmp/kea-ddns-ctrl-socket", + "socket-name": "kea-ddns-ctrl-socket", // Control socket type used by the Kea DHCP-DDNS server. // The 'unix' socket is currently the only supported type. diff --git a/doc/examples/ddns/all-keys.json b/doc/examples/ddns/all-keys.json index 674c9860cf..f24d343550 100644 --- a/doc/examples/ddns/all-keys.json +++ b/doc/examples/ddns/all-keys.json @@ -40,7 +40,7 @@ // Location of the UNIX domain socket file the DHCP-DDNS server uses // to receive control commands from the Kea Control Agent or the // local server administrator. - "socket-name": "/tmp/kea-ddns-ctrl-socket", + "socket-name": "kea-ddns-ctrl-socket", // Control socket type used by the Kea DHCP-DDNS server. // The 'unix' socket is currently the only supported type. diff --git a/doc/examples/ddns/comments.json b/doc/examples/ddns/comments.json index a7717d17c8..750c27d099 100644 --- a/doc/examples/ddns/comments.json +++ b/doc/examples/ddns/comments.json @@ -16,7 +16,7 @@ { "comment": "Control channel", "socket-type": "unix", - "socket-name": "/tmp/kea-ddns-ctrl-socket" + "socket-name": "kea-ddns-ctrl-socket" }, "forward-ddns": diff --git a/doc/examples/ddns/sample1.json b/doc/examples/ddns/sample1.json index 7ff4960c9a..4d6a3046c4 100644 --- a/doc/examples/ddns/sample1.json +++ b/doc/examples/ddns/sample1.json @@ -32,7 +32,7 @@ "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea-ddns-ctrl-socket" + "socket-name": "kea-ddns-ctrl-socket" }, // ----------------- Hooks Libraries ----------------- diff --git a/doc/examples/ddns/template.json b/doc/examples/ddns/template.json index c4da2508e5..74dffdede5 100644 --- a/doc/examples/ddns/template.json +++ b/doc/examples/ddns/template.json @@ -19,7 +19,7 @@ // "control-socket": // { // "socket-type": "unix", -// "socket-name": "/tmp/kea-ddns-ctrl-socket" +// "socket-name": "kea-ddns-ctrl-socket" // }, // ----------------- Forward DDNS ------------------ diff --git a/doc/examples/kea4/advanced.json b/doc/examples/kea4/advanced.json index 9191226aaf..daeec26886 100644 --- a/doc/examples/kea4/advanced.json +++ b/doc/examples/kea4/advanced.json @@ -89,7 +89,7 @@ // Guide for list of supported commands. "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea4-ctrl-socket" + "socket-name": "kea4-ctrl-socket" }, // Addresses will be assigned with a lifetime of 4000 seconds. diff --git a/doc/examples/kea4/all-keys-netconf.json b/doc/examples/kea4/all-keys-netconf.json index ef4a9e8e0d..15fdd266da 100644 --- a/doc/examples/kea4/all-keys-netconf.json +++ b/doc/examples/kea4/all-keys-netconf.json @@ -147,7 +147,7 @@ // Location of the UNIX domain socket file the DHCPv4 server uses // to receive control commands from the Kea Control Agent or the // local server administrator. - "socket-name": "/tmp/kea4-ctrl-socket", + "socket-name": "kea4-ctrl-socket", // Control socket type used by the Kea DHCPv4 server. The 'unix' // socket is currently the only supported type. diff --git a/doc/examples/kea4/all-keys.json b/doc/examples/kea4/all-keys.json index 6c6dbb90f3..38c23367ff 100644 --- a/doc/examples/kea4/all-keys.json +++ b/doc/examples/kea4/all-keys.json @@ -147,7 +147,7 @@ // Location of the UNIX domain socket file the DHCPv4 server uses // to receive control commands from the Kea Control Agent or the // local server administrator. - "socket-name": "/tmp/kea4-ctrl-socket", + "socket-name": "kea4-ctrl-socket", // Control socket type used by the Kea DHCPv4 server. The 'unix' // socket is currently the only supported type. diff --git a/doc/examples/kea4/comments.json b/doc/examples/kea4/comments.json index a5cfbdcd5c..c8f4175fd3 100644 --- a/doc/examples/kea4/comments.json +++ b/doc/examples/kea4/comments.json @@ -54,7 +54,7 @@ // In control socket (more for the agent) "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea4-ctrl-socket", + "socket-name": "kea4-ctrl-socket", "user-context": { "comment": "Indirect comment" } }, diff --git a/doc/examples/kea4/config-backend.json b/doc/examples/kea4/config-backend.json index 272243a1ff..4dc4842923 100644 --- a/doc/examples/kea4/config-backend.json +++ b/doc/examples/kea4/config-backend.json @@ -52,7 +52,7 @@ // details. "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea4-ctrl-socket" + "socket-name": "kea4-ctrl-socket" }, // Hooks libraries that enable configuration backend are loaded. diff --git a/doc/examples/kea4/dhcpv4-over-dhcpv6.json b/doc/examples/kea4/dhcpv4-over-dhcpv6.json index c383004a7d..c76427eec6 100644 --- a/doc/examples/kea4/dhcpv4-over-dhcpv6.json +++ b/doc/examples/kea4/dhcpv4-over-dhcpv6.json @@ -36,7 +36,7 @@ "name": "kea-dhcp4", "output_options": [ { - "output": "/tmp/kea-dhcp4.log" + "output": "kea-dhcp4.log" } ], "severity": "DEBUG", diff --git a/doc/examples/kea4/ha-load-balancing-server1-mt-with-tls.json b/doc/examples/kea4/ha-load-balancing-server1-mt-with-tls.json index 5e6802647c..9ac988d001 100644 --- a/doc/examples/kea4/ha-load-balancing-server1-mt-with-tls.json +++ b/doc/examples/kea4/ha-load-balancing-server1-mt-with-tls.json @@ -25,7 +25,7 @@ // The Control Agent is used only to handle user commands. "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea4-ctrl-socket" + "socket-name": "kea4-ctrl-socket" }, // Multi-threading parameters. diff --git a/doc/examples/kea4/ha-load-balancing-server2-mt.json b/doc/examples/kea4/ha-load-balancing-server2-mt.json index ddfadb0cfd..b084095cd2 100644 --- a/doc/examples/kea4/ha-load-balancing-server2-mt.json +++ b/doc/examples/kea4/ha-load-balancing-server2-mt.json @@ -24,7 +24,7 @@ // The Control Agent is used only to handle user commands. "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea4-ctrl-socket" + "socket-name": "kea4-ctrl-socket" }, // Multi-threading parameters. diff --git a/doc/examples/kea6/advanced.json b/doc/examples/kea6/advanced.json index 995f693715..c2b2686834 100644 --- a/doc/examples/kea6/advanced.json +++ b/doc/examples/kea6/advanced.json @@ -80,7 +80,7 @@ // Guide for list of supported commands. "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea6-ctrl-socket" + "socket-name": "kea6-ctrl-socket" }, // Addresses will be assigned with preferred and valid lifetimes diff --git a/doc/examples/kea6/all-keys-netconf.json b/doc/examples/kea6/all-keys-netconf.json index 16eb541d82..8b52a57452 100644 --- a/doc/examples/kea6/all-keys-netconf.json +++ b/doc/examples/kea6/all-keys-netconf.json @@ -98,7 +98,7 @@ // Location of the UNIX domain socket file the DHCPv6 server uses // to receive control commands from the Kea Control Agent or the // local server administrator. - "socket-name": "/tmp/kea6-ctrl-socket", + "socket-name": "kea6-ctrl-socket", // Control socket type used by the Kea DHCPv6 server. The 'unix' // socket is currently the only supported type. diff --git a/doc/examples/kea6/all-keys.json b/doc/examples/kea6/all-keys.json index f990aadd54..bc71d66cef 100644 --- a/doc/examples/kea6/all-keys.json +++ b/doc/examples/kea6/all-keys.json @@ -98,7 +98,7 @@ // Location of the UNIX domain socket file the DHCPv6 server uses // to receive control commands from the Kea Control Agent or the // local server administrator. - "socket-name": "/tmp/kea6-ctrl-socket", + "socket-name": "kea6-ctrl-socket", // Control socket type used by the Kea DHCPv6 server. The 'unix' // socket is currently the only supported type. diff --git a/doc/examples/kea6/comments.json b/doc/examples/kea6/comments.json index 88cdd41376..65ad2f0e25 100644 --- a/doc/examples/kea6/comments.json +++ b/doc/examples/kea6/comments.json @@ -54,7 +54,7 @@ // In control socket (more for the agent) "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea6-ctrl-socket", + "socket-name": "kea6-ctrl-socket", "user-context": { "comment": "Indirect comment" } }, diff --git a/doc/examples/kea6/config-backend.json b/doc/examples/kea6/config-backend.json index 4793ee2e0d..e169249bec 100644 --- a/doc/examples/kea6/config-backend.json +++ b/doc/examples/kea6/config-backend.json @@ -52,7 +52,7 @@ // details. "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea6-ctrl-socket" + "socket-name": "kea6-ctrl-socket" }, // Hooks libraries that enable configuration backend are loaded. diff --git a/doc/examples/kea6/dhcpv4-over-dhcpv6.json b/doc/examples/kea6/dhcpv4-over-dhcpv6.json index 983b4898db..b9dc35fb89 100644 --- a/doc/examples/kea6/dhcpv4-over-dhcpv6.json +++ b/doc/examples/kea6/dhcpv4-over-dhcpv6.json @@ -46,7 +46,7 @@ "name": "kea-dhcp6", "output_options": [ { - "output": "/tmp/kea-dhcp6.log" + "output": "kea-dhcp6.log" } ], "severity": "DEBUG", diff --git a/doc/examples/kea6/ha-hot-standby-server1-with-tls.json b/doc/examples/kea6/ha-hot-standby-server1-with-tls.json index 3a586f9c16..8833cd94ad 100644 --- a/doc/examples/kea6/ha-hot-standby-server1-with-tls.json +++ b/doc/examples/kea6/ha-hot-standby-server1-with-tls.json @@ -24,7 +24,7 @@ // API between the HA peers. "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea6-ctrl-socket" + "socket-name": "kea6-ctrl-socket" }, // Use Memfile lease database backend to store leases in a CSV file. diff --git a/doc/examples/kea6/ha-hot-standby-server2.json b/doc/examples/kea6/ha-hot-standby-server2.json index 43c751bf12..bb0f7bbffb 100644 --- a/doc/examples/kea6/ha-hot-standby-server2.json +++ b/doc/examples/kea6/ha-hot-standby-server2.json @@ -23,7 +23,7 @@ // API between the HA peers. "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea6-ctrl-socket" + "socket-name": "kea6-ctrl-socket" }, // Use Memfile lease database backend to store leases in a CSV file. diff --git a/doc/examples/netconf/comments.json b/doc/examples/netconf/comments.json index 99014e7850..12e1276efc 100644 --- a/doc/examples/netconf/comments.json +++ b/doc/examples/netconf/comments.json @@ -20,7 +20,7 @@ { "comment": "using unix/local socket", "socket-type": "unix", - "socket-name": "/tmp/kea4-ctrl-socket" + "socket-name": "kea4-ctrl-socket" } } }, diff --git a/doc/examples/netconf/simple-dhcp4.json b/doc/examples/netconf/simple-dhcp4.json index 309badf862..cbcd0a7c0b 100644 --- a/doc/examples/netconf/simple-dhcp4.json +++ b/doc/examples/netconf/simple-dhcp4.json @@ -57,7 +57,7 @@ "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea4-ctrl-socket" + "socket-name": "kea4-ctrl-socket" }, // Comment is optional. You can put some notes here. diff --git a/doc/examples/netconf/simple-dhcp6.json b/doc/examples/netconf/simple-dhcp6.json index 91e57bece3..967e3dbdbe 100644 --- a/doc/examples/netconf/simple-dhcp6.json +++ b/doc/examples/netconf/simple-dhcp6.json @@ -58,7 +58,7 @@ "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea6-ctrl-socket" + "socket-name": "kea6-ctrl-socket" }, // Comment is optional. You can put some notes here. diff --git a/doc/sphinx/arm/agent.rst b/doc/sphinx/arm/agent.rst index 31be5f73e2..4e30d6a22a 100644 --- a/doc/sphinx/arm/agent.rst +++ b/doc/sphinx/arm/agent.rst @@ -135,6 +135,16 @@ for the DHCPv4 server and the CA (for that server) must match. Consult :ref:`d2-ctrl-channel` to learn how the socket configuration is specified for the DHCPv4, DHCPv6, and D2 services. +.. note:: + + As of Kea 2.4.2, control sockets may only reside in the directory + determined during compilation as ``"[kea-install-dir]/var/run/kea"``. This + path may be overridden at startup by setting the environment variable + ``KEA_CONTROL_SOCKET_DIR`` to the desired path. If a path other than + this value is used in ``socket-name``, Kea will emit an error and refuse to + start or, if already running, log an unrecoverable error. For ease of use in + simply omit the path component from ``socket-name``. + User contexts can store arbitrary data as long as they are in valid JSON syntax and their top-level element is a map (i.e. the data must be enclosed in curly brackets). Some hook libraries may expect specific diff --git a/doc/sphinx/arm/ddns.rst b/doc/sphinx/arm/ddns.rst index b559255070..fd972c2438 100644 --- a/doc/sphinx/arm/ddns.rst +++ b/doc/sphinx/arm/ddns.rst @@ -295,6 +295,16 @@ operating system, i.e. the size of the ``sun_path`` field in the different operating systems, between 91 and 107 characters. Typical values are 107 on Linux and 103 on FreeBSD. +.. note:: + + As of Kea 2.4.2, control sockets may only reside in the directory + determined during compilation as ``"[kea-install-dir]/var/run/kea"``. This + path may be overridden at startup by setting the environment variable + ``KEA_CONTROL_SOCKET_DIR`` to the desired path. If a path other than + this value is used in ``socket-name``, Kea will emit an error and refuse to + start or, if already running, log an unrecoverable error. For ease of use in + simply omit the path component from ``socket-name``. + Communication over the control channel is conducted using JSON structures. See the `Control Channel section in the Kea Developer's Guide `__ diff --git a/doc/sphinx/arm/dhcp4-srv.rst b/doc/sphinx/arm/dhcp4-srv.rst index c3839f857a..9792ec544e 100644 --- a/doc/sphinx/arm/dhcp4-srv.rst +++ b/doc/sphinx/arm/dhcp4-srv.rst @@ -7443,6 +7443,16 @@ operating system, i.e. the size of the ``sun_path`` field in the different operating systems, between 91 and 107 characters. Typical values are 107 on Linux and 103 on FreeBSD. +.. note:: + + As of Kea 2.4.2, control sockets may only reside in the directory + determined during compilation as ``"[kea-install-dir]/var/run/kea"``. This + path may be overridden at startup by setting the environment variable + ``KEA_CONTROL_SOCKET_DIR`` to the desired path. If a path other than + this value is used in ``socket-name``, Kea will emit an error and refuse to + start or, if already running, log an unrecoverable error. For ease of use in + simply omit the path component from ``socket-name``. + Communication over the control channel is conducted using JSON structures. See the `Control Channel section in the Kea Developer's Guide diff --git a/doc/sphinx/arm/dhcp6-srv.rst b/doc/sphinx/arm/dhcp6-srv.rst index 39a742dc41..9bfbd4622e 100644 --- a/doc/sphinx/arm/dhcp6-srv.rst +++ b/doc/sphinx/arm/dhcp6-srv.rst @@ -7225,6 +7225,16 @@ operating system, i.e. the size of the ``sun_path`` field in the different operating systems, between 91 and 107 characters. Typical values are 107 on Linux and 103 on FreeBSD. +.. note:: + + As of Kea 2.4.2, control sockets may only reside in the directory + determined during compilation as ``"[kea-install-dir]/var/run/kea"``. This + path may be overridden at startup by setting the environment variable + ``KEA_CONTROL_SOCKET_DIR`` to the desired path. If a path other than + this value is used in ``socket-name``, Kea will emit an error and refuse to + start or, if already running, log an unrecoverable error. For ease of use in + simply omit the path component from ``socket-name``. + Communication over the control channel is conducted using JSON structures. See the `Control Channel section in the Kea Developer's Guide diff --git a/doc/sphinx/arm/hooks.rst b/doc/sphinx/arm/hooks.rst index 156a094d80..7675032179 100644 --- a/doc/sphinx/arm/hooks.rst +++ b/doc/sphinx/arm/hooks.rst @@ -218,7 +218,7 @@ configuration would be: As of Kea 2.4.2, hook libraries may only be loaded from the default installation directory determined during compilation and shown in the config report as -"Hooks directory". This value may be overridden at startup by setting the +"Hooks directory". This value may be overridden at startup by setting the environment variable ``KEA_HOOKS_PATH`` to the desired path. If a path other than this value is used in a ``library`` element Kea will emit an error and refuse to load the library. For ease of use ``library`` elements may simply omit path diff --git a/src/bin/agent/ca_cfg_mgr.cc b/src/bin/agent/ca_cfg_mgr.cc index f2f8caa92a..a3bbb27744 100644 --- a/src/bin/agent/ca_cfg_mgr.cc +++ b/src/bin/agent/ca_cfg_mgr.cc @@ -200,10 +200,14 @@ CtrlAgentCfgContext::toElement() const { ca->set("hooks-libraries", hooks_config_.toElement()); // Set control-sockets ElementPtr control_sockets = Element::createMap(); - for (auto si = ctrl_sockets_.cbegin(); si != ctrl_sockets_.cend(); ++si) { - ConstElementPtr socket = UserContext::toElement(si->second); - control_sockets->set(si->first, socket); + for (auto const& si : ctrl_sockets_) { + // Remove validated_path. + auto mutable_socket_info = boost::const_pointer_cast( + UserContext::toElement(si.second)); + mutable_socket_info->remove("validated-socket-name"); + control_sockets->set(si.first, mutable_socket_info); } + ca->set("control-sockets", control_sockets); // Set Control-agent ElementPtr result = Element::createMap(); diff --git a/src/bin/agent/ca_command_mgr.cc b/src/bin/agent/ca_command_mgr.cc index 848753d333..4cd26cdf3f 100644 --- a/src/bin/agent/ca_command_mgr.cc +++ b/src/bin/agent/ca_command_mgr.cc @@ -220,9 +220,15 @@ CtrlAgentCommandMgr::forwardCommand(const std::string& service, " for the server type " << service); } - // If the configuration does its job properly the socket-name must be - // specified and must be a string value. - std::string socket_name = socket_info->get("socket-name")->stringValue(); + // If the configuration does its job properly the validated-socket-name + // should be present. + if (!socket_info->get("validated-socket-name")) { + isc_throw(Unexpected, "validated-socket-name missing from " + << " socket_info: " << socket_info->str() + << " for the server type " << service); + } + + auto socket_name = socket_info->get("validated-socket-name")->stringValue(); // Forward command and receive reply. IOServicePtr io_service(new IOService());; diff --git a/src/bin/agent/simple_parser.cc b/src/bin/agent/simple_parser.cc index 218ee21c56..a19b6f9c88 100644 --- a/src/bin/agent/simple_parser.cc +++ b/src/bin/agent/simple_parser.cc @@ -9,12 +9,14 @@ #include #include #include +#include #include #include #include -#include using namespace isc::data; +using namespace isc::asiolink; +using namespace isc::config; namespace isc { namespace agent { @@ -146,9 +148,16 @@ AgentSimpleParser::parse(const CtrlAgentCfgContextPtr& ctx, // Control sockets are third. ConstElementPtr ctrl_sockets = config->get("control-sockets"); if (ctrl_sockets) { - auto sockets_map = ctrl_sockets->mapValue(); - for (auto cs = sockets_map.cbegin(); cs != sockets_map.cend(); ++cs) { - ctx->setControlSocketInfo(cs->second, cs->first); + auto const& sockets_map = ctrl_sockets->mapValue(); + for (auto const& cs : sockets_map) { + // Add a validated socket name so we can suppress it in + // toElement() but don't have to revalidate it every time we + // want to use it. + auto mutable_socket_info = boost::const_pointer_cast(cs.second); + std::string socket_name = mutable_socket_info->get("socket-name")->stringValue(); + auto validated_name = UnixCommandConfig::validatePath(socket_name); + mutable_socket_info->set("validated-socket-name", Element::create(validated_name)); + ctx->setControlSocketInfo(mutable_socket_info, cs.first); } } diff --git a/src/bin/agent/tests/ca_cfg_mgr_unittests.cc b/src/bin/agent/tests/ca_cfg_mgr_unittests.cc index 73f5a85cd6..4828ce00dc 100644 --- a/src/bin/agent/tests/ca_cfg_mgr_unittests.cc +++ b/src/bin/agent/tests/ca_cfg_mgr_unittests.cc @@ -7,11 +7,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -23,6 +25,8 @@ using namespace isc::data; using namespace isc::hooks; using namespace isc::http; using namespace isc::process; +using namespace isc::config; +using namespace isc::util; namespace { @@ -239,8 +243,8 @@ TEST(CtrlAgentCfgMgr, contextAuthConfigFile) { auth->setRealm("foobar"); auth->setDirectory("/tmp"); - auth->add("", "/tmp/foo", "", "/tmp/bar"); - auth->add("", "/tmp/test", "", "/tmp/pwd"); + auth->add("", "foo", "", "bar"); + auth->add("", "test", "", "pwd"); const HttpAuthConfigPtr& stored_auth = ctx.getAuthConfig(); ASSERT_TRUE(stored_auth); @@ -265,7 +269,7 @@ const char* AGENT_CONFIGS[] = { " \"http-port\": 8001,\n" " \"control-sockets\": {\n" " \"dhcp4\": {\n" - " \"socket-name\": \"/tmp/socket-v4\"\n" + " \"socket-name\": \"socket-v4\"\n" " }\n" " }\n" "}", @@ -276,13 +280,13 @@ const char* AGENT_CONFIGS[] = { " \"http-port\": 8001,\n" " \"control-sockets\": {\n" " \"dhcp4\": {\n" - " \"socket-name\": \"/tmp/socket-v4\"\n" + " \"socket-name\": \"socket-v4\"\n" " },\n" " \"dhcp6\": {\n" - " \"socket-name\": \"/tmp/socket-v6\"\n" + " \"socket-name\": \"socket-v6\"\n" " },\n" " \"d2\": {\n" - " \"socket-name\": \"/tmp/socket-d2\"\n" + " \"socket-name\": \"socket-d2\"\n" " }\n" " }\n" "}", @@ -295,7 +299,7 @@ const char* AGENT_CONFIGS[] = { " \"http-port\": 8001,\n" " \"control-sockets\": {\n" " \"dhcp4\": {\n" - " \"socket-name\": \"/tmp/socket-v4\"\n" + " \"socket-name\": \"socket-v4\"\n" " }\n" " },\n" " \"hooks-libraries\": [" @@ -314,7 +318,7 @@ const char* AGENT_CONFIGS[] = { " \"http-port\": 8001,\n" " \"control-sockets\": {\n" " \"d2\": {\n" - " \"socket-name\": \"/tmp/socket-d2\"\n" + " \"socket-name\": \"socket-d2\"\n" " }\n" " }\n" "}", @@ -325,7 +329,7 @@ const char* AGENT_CONFIGS[] = { " \"http-port\": 8001,\n" " \"control-sockets\": {\n" " \"dhcp6\": {\n" - " \"socket-name\": \"/tmp/socket-v6\"\n" + " \"socket-name\": \"socket-v6\"\n" " }\n" " }\n" "}", @@ -349,7 +353,7 @@ const char* AGENT_CONFIGS[] = { " },\n" " \"control-sockets\": {\n" " \"dhcp4\": {\n" - " \"socket-name\": \"/tmp/socket-v4\"\n" + " \"socket-name\": \"socket-v4\"\n" " }\n" " }\n" "}", @@ -377,10 +381,10 @@ const char* AGENT_CONFIGS[] = { " \"control-sockets\": {\n" " \"dhcp4\": {\n" " \"comment\": \"dhcp4 socket\",\n" - " \"socket-name\": \"/tmp/socket-v4\"\n" + " \"socket-name\": \"socket-v4\"\n" " },\n" " \"dhcp6\": {\n" - " \"socket-name\": \"/tmp/socket-v6\",\n" + " \"socket-name\": \"socket-v6\",\n" " \"user-context\": { \"version\": 1 }\n" " }\n" " }\n" @@ -415,7 +419,7 @@ const char* AGENT_CONFIGS[] = { " },\n" " \"control-sockets\": {\n" " \"dhcp4\": {\n" - " \"socket-name\": \"/tmp/socket-v4\"\n" + " \"socket-name\": \"socket-v4\"\n" " }\n" " }\n" "}" @@ -426,10 +430,12 @@ class AgentParserTest : public isc::process::ConfigParseTest { public: virtual void SetUp() { resetHooksPath(); + setSocketTestPath(); } virtual void TearDown() { resetHooksPath(); + resetSocketPath(); } /// @brief Sets the Hooks path from which hooks can be loaded. @@ -445,6 +451,20 @@ public: HooksLibrariesParser::getHooksPath(true); } + /// @brief Sets the path in which the socket can be created. + /// @param explicit_path path to use as the socket path. + void setSocketTestPath(const std::string explicit_path = "") { + auto path = UnixCommandConfig::getSocketPath(true, (!explicit_path.empty() ? + explicit_path : TEST_DATA_BUILDDIR)); + UnixCommandConfig::setSocketPathPerms(file::getPermissions(path)); + } + + /// @brief Resets the socket path to the default. + void resetSocketPath() { + UnixCommandConfig::getSocketPath(true); + UnixCommandConfig::setSocketPathPerms(); + } + /// @brief Tries to load input text as a configuration /// /// @param config text containing input configuration @@ -468,6 +488,20 @@ public: return (txt); } + /// @brief Make expected contents of socket info with socket path added. + /// + /// @param name name of the socket + /// @return expected string + std::string makeSocketStr(const std::string& name) { + std::ostringstream os; + os << "{ \"socket-name\": \"" + << name << "\", \"socket-type\": \"unix\"," + << " \"validated-socket-name\": \"" + << UnixCommandConfig::getSocketPath() << "/" << name + << "\" }"; + return (os.str()); + } + /// Configuration Manager (used in tests) NakedAgentCfgMgr cfg_mgr_; }; @@ -483,8 +517,6 @@ TEST_F(AgentParserTest, DISABLED_configParseEmpty) { // This test checks if a config with only HTTP parameters is parsed properly. TEST_F(AgentParserTest, configParseHttpOnly) { - setHooksTestPath(); - configParse(AGENT_CONFIGS[1], 0); CtrlAgentCfgContextPtr ctx = cfg_mgr_.getCtrlAgentCfgContext(); @@ -503,8 +535,7 @@ TEST_F(AgentParserTest, configParseSocketDhcp4) { ASSERT_TRUE(ctx); ConstElementPtr socket = ctx->getControlSocketInfo("dhcp4"); ASSERT_TRUE(socket); - EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-v4\", \"socket-type\": \"unix\" }", - socket->str()); + EXPECT_EQ(makeSocketStr("socket-v4"), socket->str()); EXPECT_FALSE(ctx->getControlSocketInfo("dhcp6")); EXPECT_FALSE(ctx->getControlSocketInfo("d2")); } @@ -519,9 +550,7 @@ TEST_F(AgentParserTest, configParseSocketD2) { ASSERT_TRUE(ctx); ConstElementPtr socket = ctx->getControlSocketInfo("d2"); ASSERT_TRUE(socket); - EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-d2\", \"socket-type\": \"unix\" }", - socket->str()); - + EXPECT_EQ(makeSocketStr("socket-d2"), socket->str()); EXPECT_FALSE(ctx->getControlSocketInfo("dhcp4")); EXPECT_FALSE(ctx->getControlSocketInfo("dhcp6")); } @@ -536,8 +565,7 @@ TEST_F(AgentParserTest, configParseSocketDhcp6) { ASSERT_TRUE(ctx); ConstElementPtr socket = ctx->getControlSocketInfo("dhcp6"); ASSERT_TRUE(socket); - EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-v6\", \"socket-type\": \"unix\" }", - socket->str()); + EXPECT_EQ(makeSocketStr("socket-v6"), socket->str()); EXPECT_FALSE(ctx->getControlSocketInfo("dhcp4")); EXPECT_FALSE(ctx->getControlSocketInfo("d2")); } @@ -552,14 +580,11 @@ TEST_F(AgentParserTest, configParse3Sockets) { ConstElementPtr socket4 = ctx->getControlSocketInfo("dhcp4"); ConstElementPtr socket6 = ctx->getControlSocketInfo("dhcp6"); ASSERT_TRUE(socket2); - EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-d2\", \"socket-type\": \"unix\" }", - socket2->str()); + EXPECT_EQ(makeSocketStr("socket-d2"), socket2->str()); ASSERT_TRUE(socket4); - EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-v4\", \"socket-type\": \"unix\" }", - socket4->str()); + EXPECT_EQ(makeSocketStr("socket-v4"), socket4->str()); ASSERT_TRUE(socket6); - EXPECT_EQ("{ \"socket-name\": \"/tmp/socket-v6\", \"socket-type\": \"unix\" }", - socket6->str()); + EXPECT_EQ(makeSocketStr("socket-v6"), socket6->str()); } // This test checks that the config file with hook library specified can be diff --git a/src/bin/agent/tests/ca_command_mgr_unittests.cc b/src/bin/agent/tests/ca_command_mgr_unittests.cc index 6353916be1..b5bb9a587b 100644 --- a/src/bin/agent/tests/ca_command_mgr_unittests.cc +++ b/src/bin/agent/tests/ca_command_mgr_unittests.cc @@ -15,7 +15,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -28,6 +30,8 @@ using namespace isc::agent; using namespace isc::asiolink; using namespace isc::data; using namespace isc::process; +using namespace isc::config; +using namespace isc::util; namespace { @@ -50,6 +54,7 @@ public: : DControllerTest(CtrlAgentController::instance), mgr_(CtrlAgentCommandMgr::instance()) { mgr_.deregisterAll(); + setSocketTestPath(); removeUnixSocketFile(); initProcess(); } @@ -60,6 +65,7 @@ public: virtual ~CtrlAgentCommandMgrTest() { mgr_.deregisterAll(); removeUnixSocketFile(); + resetSocketPath(); } /// @brief Verifies received answer @@ -106,19 +112,22 @@ public: } /// @brief Returns socket file path. - /// - /// If the KEA_SOCKET_TEST_DIR environment variable is specified, the - /// socket file is created in the location pointed to by this variable. - /// Otherwise, it is created in the build directory. std::string unixSocketFilePath() { - std::string socket_path; - const char* env = getenv("KEA_SOCKET_TEST_DIR"); - if (env) { - socket_path = std::string(env) + "/test-socket"; - } else { - socket_path = sandbox.join("test-socket"); - } - return (socket_path); + return (UnixCommandConfig::getSocketPath() + "/test-socket"); + } + + /// @brief Sets the path in which the socket can be created. + /// @param explicit_path path to use as the socket path. + void setSocketTestPath(const std::string explicit_path = "") { + auto path = UnixCommandConfig::getSocketPath(true, (!explicit_path.empty() ? + explicit_path : TEST_DATA_BUILDDIR)); + UnixCommandConfig::setSocketPathPerms(file::getPermissions(path)); + } + + /// @brief Resets the socket path to the default. + void resetSocketPath() { + UnixCommandConfig::getSocketPath(true); + UnixCommandConfig::setSocketPathPerms(); } /// @brief Removes unix socket descriptor. @@ -158,7 +167,7 @@ public: ASSERT_TRUE(ctx); ElementPtr control_socket = Element::createMap(); - control_socket->set("socket-name", + control_socket->set("validated-socket-name", Element::create(unixSocketFilePath())); ctx->setControlSocketInfo(control_socket, service); } diff --git a/src/bin/agent/tests/ca_controller_unittests.cc b/src/bin/agent/tests/ca_controller_unittests.cc index 54881d4a77..57c2eb29e0 100644 --- a/src/bin/agent/tests/ca_controller_unittests.cc +++ b/src/bin/agent/tests/ca_controller_unittests.cc @@ -12,9 +12,15 @@ #include #include #include +#include #include +#include +#include + #include + #include + #include using namespace isc::asiolink::test; @@ -22,6 +28,8 @@ using namespace isc::agent; using namespace isc::data; using namespace isc::http; using namespace isc::process; +using namespace isc::config; +using namespace isc::util; using namespace boost::posix_time; using namespace std; @@ -35,11 +43,11 @@ const char* valid_agent_config = " \"control-sockets\": {" " \"dhcp4\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/first/dhcp4/socket\"" + " \"socket-name\": \"first_socket4\"" " }," " \"dhcp6\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/first/dhcp6/socket\"" + " \"socket-name\": \"first_socket6\"" " }" " }" "}"; @@ -56,6 +64,12 @@ public: /// @brief Constructor. CtrlAgentControllerTest() : DControllerTest(CtrlAgentController::instance) { + setSocketTestPath(); + } + + /// @brief Destructor. + virtual ~CtrlAgentControllerTest() { + resetSocketPath(); } /// @brief Returns pointer to CtrlAgentProcess instance. @@ -81,6 +95,20 @@ public: return (p); } + /// @brief Sets the path in which the socket can be created. + /// @param explicit_path path to use as the socket path. + void setSocketTestPath(const std::string explicit_path = "") { + auto path = UnixCommandConfig::getSocketPath(true, (!explicit_path.empty() ? + explicit_path : TEST_DATA_BUILDDIR)); + UnixCommandConfig::setSocketPathPerms(file::getPermissions(path)); + } + + /// @brief Resets the socket path to the default. + void resetSocketPath() { + UnixCommandConfig::getSocketPath(true); + UnixCommandConfig::setSocketPathPerms(); + } + /// @brief Tests that socket info structure contains 'unix' socket-type /// value and the expected socket-name. /// @@ -96,8 +124,7 @@ public: ASSERT_TRUE(sock_info->contains("socket-type")); EXPECT_EQ("unix", sock_info->get("socket-type")->stringValue()); ASSERT_TRUE(sock_info->contains("socket-name")); - EXPECT_EQ(exp_socket_name, - sock_info->get("socket-name")->stringValue()); + EXPECT_EQ(exp_socket_name, sock_info->get("socket-name")->stringValue()); } /// @brief Compares the status in the given parse result to a given value. @@ -184,7 +211,6 @@ TEST_F(CtrlAgentControllerTest, basicInstanceTesting) { EXPECT_FALSE(checkProcess()); } - // Tests basic command line processing. // Verifies that: // 1. Standard command line options are supported. @@ -276,11 +302,11 @@ TEST_F(CtrlAgentControllerTest, successfulConfigUpdate) { " \"control-sockets\": {" " \"dhcp4\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/second/dhcp4/socket\"" + " \"socket-name\": \"second_socket4\"" " }," " \"dhcp6\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/second/dhcp6/socket\"" + " \"socket-name\": \"second_socket6\"" " }" " }" "}"; @@ -319,8 +345,8 @@ TEST_F(CtrlAgentControllerTest, successfulConfigUpdate) { EXPECT_EQ(8080, ctx->getHttpPort()); // The forwarding configuration should have been updated too. - testUnixSocketInfo("dhcp4", "/second/dhcp4/socket"); - testUnixSocketInfo("dhcp6", "/second/dhcp6/socket"); + testUnixSocketInfo("dhcp4", "second_socket4"); + testUnixSocketInfo("dhcp6", "second_socket6"); // After the shutdown the HTTP listener no longer exists. CtrlAgentProcessPtr process = getCtrlAgentProcess(); @@ -342,11 +368,11 @@ TEST_F(CtrlAgentControllerTest, unsuccessfulConfigUpdate) { " \"control-sockets\": {" " \"dhcp4\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/second/dhcp4/socket\"" + " \"socket-name\": \"second_socket4\"" " }," " \"dhcp6\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/second/dhcp6/socket\"" + " \"socket-name\": \"second_socket6\"" " }" " }" "}"; @@ -385,8 +411,8 @@ TEST_F(CtrlAgentControllerTest, unsuccessfulConfigUpdate) { EXPECT_EQ(8081, ctx->getHttpPort()); // Same for forwarding. - testUnixSocketInfo("dhcp4", "/first/dhcp4/socket"); - testUnixSocketInfo("dhcp6", "/first/dhcp6/socket"); + testUnixSocketInfo("dhcp4", "first_socket4"); + testUnixSocketInfo("dhcp6", "first_socket6"); // After the shutdown the HTTP listener no longer exists. CtrlAgentProcessPtr process = getCtrlAgentProcess(); @@ -408,11 +434,11 @@ TEST_F(CtrlAgentControllerTest, noListenerChange) { " \"control-sockets\": {" " \"dhcp4\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/second/dhcp4/socket\"" + " \"socket-name\": \"second_socket4\"" " }," " \"dhcp6\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/second/dhcp6/socket\"" + " \"socket-name\": \"second_socket6\"" " }" " }" "}"; @@ -450,8 +476,8 @@ TEST_F(CtrlAgentControllerTest, noListenerChange) { EXPECT_EQ(8081, ctx->getHttpPort()); // The forwarding configuration should have been updated. - testUnixSocketInfo("dhcp4", "/second/dhcp4/socket"); - testUnixSocketInfo("dhcp6", "/second/dhcp6/socket"); + testUnixSocketInfo("dhcp4", "second_socket4"); + testUnixSocketInfo("dhcp6", "second_socket6"); CtrlAgentProcessPtr process = getCtrlAgentProcess(); ASSERT_TRUE(process); @@ -727,7 +753,6 @@ TEST_F(CtrlAgentControllerTest, configReloadFileValid) { answer = CtrlAgentCommandMgr::instance().handleCommand("config-reload", params, cmd); - // Verify the reload was successful. string expected = "{ \"result\": 0, \"text\": " "\"Configuration applied successfully.\" }"; @@ -832,7 +857,6 @@ TEST_F(CtrlAgentControllerTest, shutdown) { ctrl->deregisterCommands(); } - TEST_F(CtrlAgentControllerTest, shutdownExitValue) { ASSERT_NO_THROW(initProcess()); EXPECT_TRUE(checkProcess()); diff --git a/src/bin/agent/tests/get_config_unittest.cc b/src/bin/agent/tests/get_config_unittest.cc index e5f741925f..a0848415e8 100644 --- a/src/bin/agent/tests/get_config_unittest.cc +++ b/src/bin/agent/tests/get_config_unittest.cc @@ -8,11 +8,13 @@ #include #include +#include #include #include #include #include #include +#include #include #include @@ -28,8 +30,10 @@ using namespace isc::agent; using namespace isc::config; using namespace isc::data; using namespace isc::process; -using namespace isc::test; using namespace isc::hooks; +using namespace isc::test; +using namespace isc::config; +using namespace isc::util; namespace { @@ -140,11 +144,13 @@ public: srv_.reset(new NakedAgentCfgMgr()); // Create fresh context. resetConfiguration(); + setSocketTestPath(); } ~CtrlAgentGetCfgTest() { resetConfiguration(); resetHooksPath(); + resetSocketPath(); } /// @brief Sets the Hooks path from which hooks can be loaded. @@ -160,6 +166,20 @@ public: HooksLibrariesParser::getHooksPath(true); } + /// @brief Sets the path in which the socket can be created. + /// @param explicit_path path to use as the socket path. + void setSocketTestPath(const std::string explicit_path = "") { + auto path = UnixCommandConfig::getSocketPath(true, (!explicit_path.empty() ? + explicit_path : TEST_DATA_BUILDDIR)); + UnixCommandConfig::setSocketPathPerms(file::getPermissions(path)); + } + + /// @brief Resets the socket path to the default. + void resetSocketPath() { + UnixCommandConfig::getSocketPath(true); + UnixCommandConfig::setSocketPathPerms(); + } + /// @brief Parse and Execute configuration /// /// Parses a configuration and executes a configuration of the server. diff --git a/src/bin/agent/tests/parser_unittests.cc b/src/bin/agent/tests/parser_unittests.cc index df80ab250a..d4335942aa 100644 --- a/src/bin/agent/tests/parser_unittests.cc +++ b/src/bin/agent/tests/parser_unittests.cc @@ -142,15 +142,15 @@ TEST(ParserTest, keywordAgent) { " \"control-sockets\": {" " \"dhcp4\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/tmp/kea4-ctrl-socket\"" + " \"socket-name\": \"kea4-ctrl-socket\"" " }," " \"dhcp6\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/tmp/kea6-ctrl-socket\"" + " \"socket-name\": \"kea6-ctrl-socket\"" " }," " \"d2\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/tmp/kea-ddns-ctrl-socket\"" + " \"socket-name\": \"kea-ddns-ctrl-socket\"" " }" " }," " \"hooks-libraries\": [" @@ -179,15 +179,15 @@ TEST(ParserTest, keywordSubAgent) { " \"control-sockets\": {" " \"dhcp4\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/tmp/kea4-ctrl-socket\"" + " \"socket-name\": \"kea4-ctrl-socket\"" " }," " \"dhcp6\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/tmp/kea6-ctrl-socket\"" + " \"socket-name\": \"kea6-ctrl-socket\"" " }," " \"d2\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/tmp/kea-ddns-ctrl-socket\"" + " \"socket-name\": \"kea-ddns-ctrl-socket\"" " }" " }," " \"hooks-libraries\": [" @@ -884,15 +884,15 @@ TEST_F(TrailingCommasTest, tests) { "Control-agent": { "control-sockets": { "d2": { - "socket-name": "/tmp/kea-dhcp-ddns-ctrl.sock", + "socket-name": "kea-dhcp-ddns-ctrl.sock", "socket-type": "unix", }, "dhcp4": { - "socket-name": "/tmp/kea-dhcp4-ctrl.sock", + "socket-name": "kea-dhcp4-ctrl.sock", "socket-type": "unix", }, "dhcp6": { - "socket-name": "/tmp/kea-dhcp6-ctrl.sock", + "socket-name": "kea-dhcp6-ctrl.sock", "socket-type": "unix", }, }, diff --git a/src/bin/agent/tests/testdata/get_config.json b/src/bin/agent/tests/testdata/get_config.json index 3117d66fc3..775732a155 100644 --- a/src/bin/agent/tests/testdata/get_config.json +++ b/src/bin/agent/tests/testdata/get_config.json @@ -23,21 +23,21 @@ }, "control-sockets": { "d2": { - "socket-name": "/tmp/kea-ddns-ctrl-socket", + "socket-name": "kea-ddns-ctrl-socket", "socket-type": "unix", "user-context": { "in-use": false } }, "dhcp4": { - "socket-name": "/tmp/kea4-ctrl-socket", + "socket-name": "kea4-ctrl-socket", "socket-type": "unix", "user-context": { "comment": "socket to DHCPv4 server" } }, "dhcp6": { - "socket-name": "/tmp/kea6-ctrl-socket", + "socket-name": "kea6-ctrl-socket", "socket-type": "unix" } }, diff --git a/src/bin/d2/tests/d2_cfg_mgr_unittests.cc b/src/bin/d2/tests/d2_cfg_mgr_unittests.cc index 88af349e3e..fd9dbb4413 100644 --- a/src/bin/d2/tests/d2_cfg_mgr_unittests.cc +++ b/src/bin/d2/tests/d2_cfg_mgr_unittests.cc @@ -6,6 +6,7 @@ #include +#include #include #include #include @@ -17,6 +18,7 @@ #include #include #include +#include #include #include @@ -27,6 +29,8 @@ using namespace isc; using namespace isc::d2; using namespace isc::hooks; using namespace isc::process; +using namespace isc::config; +using namespace isc::util; namespace { @@ -50,11 +54,13 @@ public: /// @brief Constructor D2CfgMgrTest():cfg_mgr_(new D2CfgMgr()), d2_params_() { resetHooksPath(); + resetSocketPath(); } /// @brief Destructor ~D2CfgMgrTest() { resetHooksPath(); + resetSocketPath(); } /// @brief Sets the Hooks path from which hooks can be loaded. @@ -70,6 +76,22 @@ public: HooksLibrariesParser::getHooksPath(true); } + /// @brief Sets the path in which the socket can be created. + /// @param explicit_path path to use as the socket path. + void setSocketTestPath(const std::string explicit_path = "") { + UnixCommandConfig::getSocketPath(true, (!explicit_path.empty() ? + explicit_path : TEST_DATA_BUILDDIR)); + + auto path = UnixCommandConfig::getSocketPath(); + UnixCommandConfig::setSocketPathPerms(file::getPermissions(path)); + } + + /// @brief Resets the socket path to the default. + void resetSocketPath() { + UnixCommandConfig::getSocketPath(true); + UnixCommandConfig::setSocketPathPerms(); + } + /// @brief Configuration manager instance. D2CfgMgrPtr cfg_mgr_; @@ -487,6 +509,7 @@ TEST(D2CfgMgr, construction) { /// event. TEST_F(D2CfgMgrTest, fullConfig) { setHooksTestPath(); + setSocketTestPath(); // Create a configuration with all of application level parameters, plus // both the forward and reverse ddns managers. Both managers have two @@ -499,7 +522,7 @@ TEST_F(D2CfgMgrTest, fullConfig) { "\"ncr-format\": \"JSON\", " "\"control-socket\" : {" " \"socket-type\" : \"unix\" ," - " \"socket-name\" : \"/tmp/d2-ctrl-channel\" " + " \"socket-name\" : \"d2-ctrl-channel\" " "}," "\"hooks-libraries\": [" "{" @@ -586,7 +609,7 @@ TEST_F(D2CfgMgrTest, fullConfig) { ASSERT_TRUE(ctrl_sock->get("socket-type")); EXPECT_EQ("\"unix\"", ctrl_sock->get("socket-type")->str()); ASSERT_TRUE(ctrl_sock->get("socket-name")); - EXPECT_EQ("\"/tmp/d2-ctrl-channel\"", ctrl_sock->get("socket-name")->str()); + EXPECT_EQ("\"d2-ctrl-channel\"", ctrl_sock->get("socket-name")->str()); // Verify that the hooks libraries can be retrieved. const HookLibsCollection libs = context->getHooksConfig().get(); @@ -997,6 +1020,7 @@ TEST_F(D2CfgMgrTest, configPermutations) { /// @brief Tests comments. TEST_F(D2CfgMgrTest, comments) { + setSocketTestPath(); std::string config = "{ " "\"comment\": \"D2 config\" , " "\"ip-address\" : \"192.168.1.33\" , " @@ -1004,7 +1028,7 @@ TEST_F(D2CfgMgrTest, comments) { "\"control-socket\": {" " \"comment\": \"Control channel\" , " " \"socket-type\": \"unix\" ," - " \"socket-name\": \"/tmp/d2-ctrl-channel\" " + " \"socket-name\": \"d2-ctrl-channel\" " "}," "\"tsig-keys\": [" "{" diff --git a/src/bin/d2/tests/d2_command_unittest.cc b/src/bin/d2/tests/d2_command_unittest.cc index 1f3a84bd33..7eb2ac47c5 100644 --- a/src/bin/d2/tests/d2_command_unittest.cc +++ b/src/bin/d2/tests/d2_command_unittest.cc @@ -10,12 +10,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -33,6 +35,7 @@ using namespace isc::d2; using namespace isc::data; using namespace isc::dhcp::test; using namespace isc::process; +using namespace isc::util; using namespace boost::asio; namespace ph = std::placeholders; @@ -122,12 +125,7 @@ public: /// Sets socket path to its default value. CtrlChannelD2Test() : server_(NakedD2Controller::instance()) { - const char* env = getenv("KEA_SOCKET_TEST_DIR"); - if (env) { - socket_path_ = string(env) + "/d2.sock"; - } else { - socket_path_ = sandbox.join("d2.sock"); - } + setSocketTestPath(); ::remove(socket_path_.c_str()); } @@ -143,6 +141,7 @@ public: // Reset command manager. CommandMgr::instance().deregisterAll(); CommandMgr::instance().setConnectionTimeout(TIMEOUT_DHCP_SERVER_RECEIVE_COMMAND); + resetSocketPath(); } /// @brief Returns pointer to the server's IO service. @@ -153,6 +152,23 @@ public: return (server_ ? d2Controller()->getIOService() : IOServicePtr()); } + /// @brief Sets the path in which the socket can be created. + /// @param explicit_path path to use as the socket path. + void setSocketTestPath(const std::string explicit_path = "") { + UnixCommandConfig::getSocketPath(true, (!explicit_path.empty() ? + explicit_path : TEST_DATA_BUILDDIR)); + + auto path = UnixCommandConfig::getSocketPath(); + UnixCommandConfig::setSocketPathPerms(file::getPermissions(path)); + socket_path_ = path + "/d2.sock"; + } + + /// @brief Resets the socket path to the default. + void resetSocketPath() { + UnixCommandConfig::getSocketPath(true); + UnixCommandConfig::setSocketPathPerms(); + } + /// @brief Runs parser in DHCPDDNS mode /// /// @param config input configuration diff --git a/src/bin/d2/tests/get_config_unittest.cc b/src/bin/d2/tests/get_config_unittest.cc index 7ca53b86c4..2dc7044fbd 100644 --- a/src/bin/d2/tests/get_config_unittest.cc +++ b/src/bin/d2/tests/get_config_unittest.cc @@ -8,12 +8,14 @@ #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -28,8 +30,9 @@ using namespace isc::config; using namespace isc::d2; using namespace isc::data; using namespace isc::process; -using namespace isc::hooks; using namespace isc::test; +using namespace isc::hooks; +using namespace isc::util; namespace { @@ -117,6 +120,7 @@ public: D2GetConfigTest() : rcode_(-1) { resetHooksPath(); + resetSocketPath(); srv_.reset(new D2CfgMgr()); // Enforce not verbose mode. Daemon::setVerbose(false); @@ -127,6 +131,7 @@ public: ~D2GetConfigTest() { resetConfiguration(); resetHooksPath(); + resetSocketPath(); } /// @brief Sets the Hooks path from which hooks can be loaded. @@ -142,6 +147,22 @@ public: HooksLibrariesParser::getHooksPath(true); } + /// @brief Sets the path in which the socket can be created. + /// @param explicit_path path to use as the socket path. + void setSocketTestPath(const std::string explicit_path = "") { + UnixCommandConfig::getSocketPath(true, (!explicit_path.empty() ? + explicit_path : TEST_DATA_BUILDDIR)); + + auto path = UnixCommandConfig::getSocketPath(); + UnixCommandConfig::setSocketPathPerms(file::getPermissions(path)); + } + + /// @brief Resets the socket path to the default. + void resetSocketPath() { + UnixCommandConfig::getSocketPath(true); + UnixCommandConfig::setSocketPathPerms(); + } + /// @brief Parse and Execute configuration /// /// Parses a configuration and executes a configuration of the server. @@ -248,6 +269,7 @@ public: /// Test a configuration TEST_F(D2GetConfigTest, sample1) { setHooksTestPath(); + setSocketTestPath(); // get the sample1 configuration std::string sample1_file = string(CFG_EXAMPLES) + "/" + "sample1.json"; diff --git a/src/bin/d2/tests/testdata/get_config.json b/src/bin/d2/tests/testdata/get_config.json index d9613a3610..8c2159d4e6 100644 --- a/src/bin/d2/tests/testdata/get_config.json +++ b/src/bin/d2/tests/testdata/get_config.json @@ -1,7 +1,7 @@ { "DhcpDdns": { "control-socket": { - "socket-name": "/tmp/kea-ddns-ctrl-socket", + "socket-name": "kea-ddns-ctrl-socket", "socket-type": "unix" }, "dns-server-timeout": 1000, diff --git a/src/bin/dhcp4/tests/config_parser_unittest.cc b/src/bin/dhcp4/tests/config_parser_unittest.cc index 8765795e78..b2c2801ab0 100644 --- a/src/bin/dhcp4/tests/config_parser_unittest.cc +++ b/src/bin/dhcp4/tests/config_parser_unittest.cc @@ -58,6 +58,8 @@ using namespace isc::data; using namespace isc::dhcp; using namespace isc::dhcp::test; using namespace isc::hooks; +using namespace isc::test; +using namespace isc::util; using namespace std; namespace { @@ -206,7 +208,7 @@ const char* PARSER_CONFIGS[] = { " ]," " \"control-socket\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/tmp/kea4-ctrl-socket\"," + " \"socket-name\": \"kea4-ctrl-socket\"," " \"user-context\": { \"comment\": \"Indirect comment\" }" " }," " \"shared-networks\": [ {" @@ -279,7 +281,6 @@ protected: virtual void SetUp() { std::vector libraries = HooksManager::getLibraryNames(); ASSERT_TRUE(libraries.empty()); - resetHooksPath(); } public: @@ -292,6 +293,7 @@ public: // Create fresh context. resetConfiguration(); resetHooksPath(); + Dhcpv4SrvTest::resetSocketPath(); } public: @@ -376,6 +378,9 @@ public: // ... and delete the hooks library marker files if present static_cast(remove(LOAD_MARKER_FILE)); static_cast(remove(UNLOAD_MARKER_FILE)); + + resetHooksPath(); + Dhcpv4SrvTest::resetSocketPath(); }; /// @brief Returns an interface configuration used by the most of the @@ -6932,6 +6937,7 @@ TEST_F(Dhcp4ParserTest, hostsDatabases) { // This test checks comments. Please keep it last. TEST_F(Dhcp4ParserTest, comments) { + Dhcpv4SrvTest::setSocketTestPath(); string config = PARSER_CONFIGS[5]; extractConfig(config); @@ -7029,7 +7035,7 @@ TEST_F(Dhcp4ParserTest, comments) { ASSERT_TRUE(socket->get("socket-type")); EXPECT_EQ("\"unix\"", socket->get("socket-type")->str()); ASSERT_TRUE(socket->get("socket-name")); - EXPECT_EQ("\"/tmp/kea4-ctrl-socket\"", socket->get("socket-name")->str()); + EXPECT_EQ("\"kea4-ctrl-socket\"", socket->get("socket-name")->str()); // Check control socket comment and user context. ConstElementPtr ctx_socket = socket->get("user-context"); diff --git a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc index 0634b68b21..bcfa7faa63 100644 --- a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc +++ b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -116,12 +117,7 @@ public: /// Sets socket path to its default value. CtrlChannelDhcpv4SrvTest() : interfaces_("\"*\"") { resetLogPath(); - const char* env = getenv("KEA_SOCKET_TEST_DIR"); - if (env) { - socket_path_ = string(env) + "/kea4.sock"; - } else { - socket_path_ = sandbox.join("kea4.sock"); - } + setSocketTestPath(); reset(); IfaceMgr::instance().setTestMode(false); IfaceMgr::instance().setDetectCallback(std::bind(&IfaceMgr::checkDetectIfaces, @@ -131,6 +127,7 @@ public: /// @brief Destructor ~CtrlChannelDhcpv4SrvTest() { resetLogPath(); + Dhcpv4SrvTest::resetSocketPath(); LeaseMgrFactory::destroy(); StatsMgr::instance().removeAll(); @@ -159,6 +156,13 @@ public: LogConfigParser::getLogPath(true); } + /// @brief Sets the path in which the socket can be created. + /// @param explicit_path path to use as the socket path. + void setSocketTestPath(const std::string explicit_path = "") { + Dhcpv4SrvTest::setSocketTestPath(explicit_path); + socket_path_ = UnixCommandConfig::getSocketPath() + "/kea4.sock"; + } + /// @brief Returns pointer to the server's IO service. /// /// @return Pointer to the server's IO service or null pointer if the server diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc index 7a1c4604a2..80c8ae91fc 100644 --- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc +++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc @@ -2955,6 +2955,7 @@ class DBInitializer { void Dhcpv4SrvTest::checkConfigFiles() { + setSocketTestPath(); DBInitializer dbi; IfaceMgrTestConfig test_config(true); string path = CFG_EXAMPLES; diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.cc b/src/bin/dhcp4/tests/dhcp4_test_utils.cc index fb6fa567a5..fe1fd83ab2 100644 --- a/src/bin/dhcp4/tests/dhcp4_test_utils.cc +++ b/src/bin/dhcp4/tests/dhcp4_test_utils.cc @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,7 @@ using namespace std; using namespace isc::asiolink; using namespace isc::data; using namespace isc::util; +using namespace isc::config; namespace isc { namespace dhcp { @@ -88,6 +90,7 @@ Dhcpv4SrvTest::Dhcpv4SrvTest() } Dhcpv4SrvTest::~Dhcpv4SrvTest() { + resetSocketPath(); // Make sure that we revert to default value CfgMgr::instance().clear(); @@ -100,6 +103,21 @@ Dhcpv4SrvTest::~Dhcpv4SrvTest() { MultiThreadingMgr::instance().apply(false, 0, 0); } +void +Dhcpv4SrvTest::setSocketTestPath(const std::string explicit_path /* = "" */) { + UnixCommandConfig::getSocketPath(true, (!explicit_path.empty() ? + explicit_path : TEST_DATA_BUILDDIR)); + + auto path = UnixCommandConfig::getSocketPath(); + UnixCommandConfig::setSocketPathPerms(file::getPermissions(path)); +} + +void +Dhcpv4SrvTest::resetSocketPath() { + UnixCommandConfig::getSocketPath(true); + UnixCommandConfig::setSocketPathPerms(); +} + void Dhcpv4SrvTest::addPrlOption(Pkt4Ptr& pkt) { OptionUint8ArrayPtr option_prl = diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.h b/src/bin/dhcp4/tests/dhcp4_test_utils.h index fa4cf7e0c7..9bb8bee0ab 100644 --- a/src/bin/dhcp4/tests/dhcp4_test_utils.h +++ b/src/bin/dhcp4/tests/dhcp4_test_utils.h @@ -350,6 +350,13 @@ public: /// Removes existing configuration. virtual ~Dhcpv4SrvTest(); + /// @brief Sets the path in which the socket can be created. + /// @param explicit_path path to use as the socket path. + static void setSocketTestPath(const std::string explicit_path = ""); + + /// @brief Resets the socket path to the default. + static void resetSocketPath(); + /// @brief Add 'Parameter Request List' option to the packet. /// /// This function adds PRL option comprising the following option codes: diff --git a/src/bin/dhcp4/tests/get_config_unittest.cc b/src/bin/dhcp4/tests/get_config_unittest.cc index 9ae42adb7d..92a982b4bd 100644 --- a/src/bin/dhcp4/tests/get_config_unittest.cc +++ b/src/bin/dhcp4/tests/get_config_unittest.cc @@ -2103,7 +2103,7 @@ const char* EXTRACTED_CONFIGS[] = { " }\n" " ],\n" " \"control-socket\": {\n" -" \"socket-name\": \"/tmp/kea4-ctrl-socket\",\n" +" \"socket-name\": \"kea4-ctrl-socket\",\n" " \"socket-type\": \"unix\",\n" " \"user-context\": {\n" " \"comment\": \"Indirect comment\"\n" @@ -11077,7 +11077,7 @@ const char* UNPARSED_CONFIGS[] = { " }\n" " ],\n" " \"control-socket\": {\n" -" \"socket-name\": \"/tmp/kea4-ctrl-socket\",\n" +" \"socket-name\": \"kea4-ctrl-socket\",\n" " \"socket-type\": \"unix\",\n" " \"user-context\": {\n" " \"comment\": \"Indirect comment\"\n" @@ -12280,10 +12280,12 @@ public: srv_.reset(new ControlledDhcpv4Srv(0)); // Create fresh context. resetConfiguration(); + Dhcpv4SrvTest::setSocketTestPath(); } ~Dhcp4GetConfigTest() { resetConfiguration(); + Dhcpv4SrvTest::resetSocketPath(); }; /// @brief Parse and Execute configuration diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc index 293f8567fc..56365df869 100644 --- a/src/bin/dhcp6/tests/config_parser_unittest.cc +++ b/src/bin/dhcp6/tests/config_parser_unittest.cc @@ -314,7 +314,7 @@ const char* PARSER_CONFIGS[] = { " ]," " \"control-socket\": {" " \"socket-type\": \"unix\"," - " \"socket-name\": \"/tmp/kea6-ctrl-socket\"," + " \"socket-name\": \"kea6-ctrl-socket\"," " \"user-context\": { \"comment\": \"Indirect comment\" }" " }," " \"shared-networks\": [ {" @@ -7477,6 +7477,7 @@ TEST_F(Dhcp6ParserTest, hostsDatabases) { // This test checks comments. Please keep it last. TEST_F(Dhcp6ParserTest, comments) { + setSocketTestPath(); string config = PARSER_CONFIGS[9]; extractConfig(config); @@ -7586,7 +7587,7 @@ TEST_F(Dhcp6ParserTest, comments) { ASSERT_TRUE(socket->get("socket-type")); EXPECT_EQ("\"unix\"", socket->get("socket-type")->str()); ASSERT_TRUE(socket->get("socket-name")); - EXPECT_EQ("\"/tmp/kea6-ctrl-socket\"", socket->get("socket-name")->str()); + EXPECT_EQ("\"kea6-ctrl-socket\"", socket->get("socket-name")->str()); // Check control socket comment and user context. ConstElementPtr ctx_socket = socket->get("user-context"); diff --git a/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc index f6654669e9..d8e96460ef 100644 --- a/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -148,16 +149,12 @@ public: /// /// Sets socket path to its default value. CtrlChannelDhcpv6SrvTest() : interfaces_("\"*\"") { - const char* env = getenv("KEA_SOCKET_TEST_DIR"); - if (env) { - socket_path_ = string(env) + "/kea6.sock"; - } else { - socket_path_ = sandbox.join("/kea6.sock"); - } reset(); IfaceMgr::instance().setTestMode(false); IfaceMgr::instance().setDetectCallback(std::bind(&IfaceMgr::checkDetectIfaces, IfaceMgr::instancePtr().get(), ph::_1)); + setSocketTestPath(); + socket_path_ = UnixCommandConfig::getSocketPath() + "/kea6.sock"; } /// @brief Destructor @@ -170,7 +167,7 @@ public: IfaceMgr::instance().clearIfaces(); IfaceMgr::instance().closeSockets(); IfaceMgr::instance().detectIfaces(); - }; + } /// @brief Returns pointer to the server's IO service. /// diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc index 40ee751cae..8ddd8dd7f3 100644 --- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc @@ -324,6 +324,7 @@ class DBInitializer { void Dhcpv6SrvTest::checkConfigFiles() { + setSocketTestPath(); DBInitializer dbi; IfaceMgrTestConfig test_config(true); string path = CFG_EXAMPLES; diff --git a/src/bin/dhcp6/tests/dhcp6_test_utils.cc b/src/bin/dhcp6/tests/dhcp6_test_utils.cc index e885b9ee6a..11a62888f1 100644 --- a/src/bin/dhcp6/tests/dhcp6_test_utils.cc +++ b/src/bin/dhcp6/tests/dhcp6_test_utils.cc @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,7 @@ using namespace isc::asiolink; using namespace isc::stats; using namespace isc::util; using namespace isc::process; +using namespace isc::config; namespace isc { namespace dhcp { @@ -39,6 +41,7 @@ BaseServerTest::BaseServerTest() { original_datadir_ = CfgMgr::instance().getDataDir(); CfgMgr::instance().getDataDir(true, TEST_DATA_BUILDDIR); resetLogPath(); + resetSocketPath(); } BaseServerTest::~BaseServerTest() { @@ -58,6 +61,7 @@ BaseServerTest::~BaseServerTest() { // Revert to unit test logging, in case the test reconfigured it. isc::log::initLogger(); resetLogPath(); + resetSocketPath(); } void @@ -71,6 +75,22 @@ BaseServerTest::resetLogPath() { LogConfigParser::getLogPath(true); } +void +BaseServerTest::setSocketTestPath(const std::string explicit_path /* = "" */) { + UnixCommandConfig::getSocketPath(true, + (!explicit_path.empty() ? + explicit_path : TEST_DATA_BUILDDIR)); + + auto path = UnixCommandConfig::getSocketPath(); + UnixCommandConfig::setSocketPathPerms(file::getPermissions(path)); +} + +void +BaseServerTest::resetSocketPath() { + UnixCommandConfig::getSocketPath(true); + UnixCommandConfig::setSocketPathPerms(); +} + Dhcpv6SrvTest::Dhcpv6SrvTest() : NakedDhcpv6SrvTest(), srv_(0), multi_threading_(false) { subnet_ = Subnet6::create(isc::asiolink::IOAddress("2001:db8:1::"), diff --git a/src/bin/dhcp6/tests/dhcp6_test_utils.h b/src/bin/dhcp6/tests/dhcp6_test_utils.h index d2b2d45af6..6983823d07 100644 --- a/src/bin/dhcp6/tests/dhcp6_test_utils.h +++ b/src/bin/dhcp6/tests/dhcp6_test_utils.h @@ -130,6 +130,13 @@ public: /// @brief Resets the log path to TEST_DATA_BUILDDIR. void resetLogPath(); + /// @brief Sets the path in which the socket can be created. + /// @param explicit_path path to use as the socket path. + static void setSocketTestPath(const std::string explicit_path = ""); + + /// @brief Resets the socket path to the default. + static void resetSocketPath(); + private: /// @brief Holds the original data directory. diff --git a/src/bin/dhcp6/tests/get_config_unittest.cc b/src/bin/dhcp6/tests/get_config_unittest.cc index c22f4a7adf..9e09df5b51 100644 --- a/src/bin/dhcp6/tests/get_config_unittest.cc +++ b/src/bin/dhcp6/tests/get_config_unittest.cc @@ -1838,7 +1838,7 @@ const char* EXTRACTED_CONFIGS[] = { " }\n" " ],\n" " \"control-socket\": {\n" -" \"socket-name\": \"/tmp/kea6-ctrl-socket\",\n" +" \"socket-name\": \"kea6-ctrl-socket\",\n" " \"socket-type\": \"unix\",\n" " \"user-context\": {\n" " \"comment\": \"Indirect comment\"\n" @@ -9863,7 +9863,7 @@ const char* UNPARSED_CONFIGS[] = { " }\n" " ],\n" " \"control-socket\": {\n" -" \"socket-name\": \"/tmp/kea6-ctrl-socket\",\n" +" \"socket-name\": \"kea6-ctrl-socket\",\n" " \"socket-type\": \"unix\",\n" " \"user-context\": {\n" " \"comment\": \"Indirect comment\"\n" @@ -10932,11 +10932,13 @@ public: // Reset configuration for each test. resetConfiguration(); + BaseServerTest::setSocketTestPath(); } ~Dhcp6GetConfigTest() { // Reset configuration database after each test. resetConfiguration(); + BaseServerTest::resetSocketPath(); }; /// @brief Parse and Execute configuration diff --git a/src/bin/keactrl/kea-ctrl-agent.conf.pre b/src/bin/keactrl/kea-ctrl-agent.conf.pre index 873dfd7aff..c4e8ee1312 100644 --- a/src/bin/keactrl/kea-ctrl-agent.conf.pre +++ b/src/bin/keactrl/kea-ctrl-agent.conf.pre @@ -66,15 +66,15 @@ "control-sockets": { "dhcp4": { "socket-type": "unix", - "socket-name": "/tmp/kea4-ctrl-socket" + "socket-name": "kea4-ctrl-socket" }, "dhcp6": { "socket-type": "unix", - "socket-name": "/tmp/kea6-ctrl-socket" + "socket-name": "kea6-ctrl-socket" }, "d2": { "socket-type": "unix", - "socket-name": "/tmp/kea-ddns-ctrl-socket" + "socket-name": "kea-ddns-ctrl-socket" } }, diff --git a/src/bin/keactrl/kea-dhcp-ddns.conf.pre b/src/bin/keactrl/kea-dhcp-ddns.conf.pre index f88a3a393f..524fc81b87 100644 --- a/src/bin/keactrl/kea-dhcp-ddns.conf.pre +++ b/src/bin/keactrl/kea-dhcp-ddns.conf.pre @@ -23,7 +23,7 @@ "port": 53001, "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea-ddns-ctrl-socket" + "socket-name": "kea-ddns-ctrl-socket" }, "tsig-keys": [], "forward-ddns" : {}, diff --git a/src/bin/keactrl/kea-dhcp4.conf.pre b/src/bin/keactrl/kea-dhcp4.conf.pre index adb5647914..ed1d3f97c1 100644 --- a/src/bin/keactrl/kea-dhcp4.conf.pre +++ b/src/bin/keactrl/kea-dhcp4.conf.pre @@ -49,7 +49,7 @@ // more. For detailed description, see Sections 8.8, 16 and 15. "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea4-ctrl-socket" + "socket-name": "kea4-ctrl-socket" }, // Use Memfile lease database backend to store leases in a CSV file. diff --git a/src/bin/keactrl/kea-dhcp6.conf.pre b/src/bin/keactrl/kea-dhcp6.conf.pre index ae2f01f88a..5632110c86 100644 --- a/src/bin/keactrl/kea-dhcp6.conf.pre +++ b/src/bin/keactrl/kea-dhcp6.conf.pre @@ -43,7 +43,7 @@ // description, see Sections 9.12, 16 and 15. "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea6-ctrl-socket" + "socket-name": "kea6-ctrl-socket" }, // Use Memfile lease database backend to store leases in a CSV file. diff --git a/src/bin/keactrl/kea-netconf.conf.pre b/src/bin/keactrl/kea-netconf.conf.pre index 80372eb266..2e47b4d27f 100644 --- a/src/bin/keactrl/kea-netconf.conf.pre +++ b/src/bin/keactrl/kea-netconf.conf.pre @@ -30,13 +30,13 @@ "dhcp4": { "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea4-ctrl-socket" + "socket-name": "kea4-ctrl-socket" } }, "dhcp6": { "control-socket": { "socket-type": "unix", - "socket-name": "/tmp/kea6-ctrl-socket" + "socket-name": "kea6-ctrl-socket" } } }, diff --git a/src/bin/netconf/tests/testdata/get_config.json b/src/bin/netconf/tests/testdata/get_config.json index e2f5d31486..1875333698 100644 --- a/src/bin/netconf/tests/testdata/get_config.json +++ b/src/bin/netconf/tests/testdata/get_config.json @@ -6,7 +6,7 @@ "dhcp4": { "boot-update": true, "control-socket": { - "socket-name": "/tmp/kea4-ctrl-socket", + "socket-name": "kea4-ctrl-socket", "socket-type": "unix", "socket-url": "http://127.0.0.1:8000/" }, diff --git a/src/hooks/dhcp/high_availability/ha_config_parser.cc b/src/hooks/dhcp/high_availability/ha_config_parser.cc index 0dcad36e6c..7c00b0bfde 100644 --- a/src/hooks/dhcp/high_availability/ha_config_parser.cc +++ b/src/hooks/dhcp/high_availability/ha_config_parser.cc @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/lib/asiolink/common_tls.cc b/src/lib/asiolink/common_tls.cc index 35ca637bdc..4363be4878 100644 --- a/src/lib/asiolink/common_tls.cc +++ b/src/lib/asiolink/common_tls.cc @@ -10,7 +10,7 @@ #include #include -#include +#include using namespace isc::cryptolink; using namespace isc::util; diff --git a/src/lib/config/Makefile.am b/src/lib/config/Makefile.am index a38f1bd4d7..168f323683 100644 --- a/src/lib/config/Makefile.am +++ b/src/lib/config/Makefile.am @@ -2,6 +2,8 @@ SUBDIRS = . tests AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib AM_CPPFLAGS += $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES) +control_socket_dir = @runstatedir@/@PACKAGE@ +AM_CPPFLAGS += -DCONTROL_SOCKET_DIR=\"$(control_socket_dir)\" AM_CXXFLAGS = $(KEA_CXXFLAGS) @@ -17,6 +19,7 @@ libkea_cfgclient_la_SOURCES += timeouts.h libkea_cfgclient_la_SOURCES += cmd_http_listener.cc cmd_http_listener.h libkea_cfgclient_la_SOURCES += cmd_response_creator.cc cmd_response_creator.h libkea_cfgclient_la_SOURCES += cmd_response_creator_factory.h +libkea_cfgclient_la_SOURCES += unix_command_config.cc unix_command_config.h libkea_cfgclient_la_LIBADD = $(top_builddir)/src/lib/http/libkea-http.la libkea_cfgclient_la_LIBADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la diff --git a/src/lib/config/command_mgr.cc b/src/lib/config/command_mgr.cc index 831d56e78c..c61005699d 100644 --- a/src/lib/config/command_mgr.cc +++ b/src/lib/config/command_mgr.cc @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -547,7 +548,11 @@ CommandMgrImpl::openCommandSocket(const isc::data::ConstElementPtr& socket_info) isc_throw(BadSocketInfo, "'socket-name' parameter expected to be a string"); } - socket_name_ = name->stringValue(); + try { + socket_name_ = UnixCommandConfig::validatePath(name->stringValue()); + } catch (const std::exception& ex) { + isc_throw(BadSocketInfo, "'socket-name' is invalid: " << ex.what()); + } // First let's open lock file. std::string lock_name = getLockName(); @@ -587,6 +592,8 @@ CommandMgrImpl::openCommandSocket(const isc::data::ConstElementPtr& socket_info) doAccept(); } catch (const std::exception& ex) { + close(lock_fd); + static_cast(::remove(getLockName().c_str())); isc_throw(SocketError, ex.what()); } } diff --git a/src/lib/config/tests/Makefile.am b/src/lib/config/tests/Makefile.am index 546c35748a..2a8357d5f8 100644 --- a/src/lib/config/tests/Makefile.am +++ b/src/lib/config/tests/Makefile.am @@ -4,6 +4,8 @@ AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib AM_CPPFLAGS += $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES) AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/config/tests\" AM_CPPFLAGS += -DTEST_CA_DIR=\"$(srcdir)/../../asiolink/testutils/ca\" +control_socket_dir = @runstatedir@/@PACKAGE@ +AM_CPPFLAGS += -DCONTROL_SOCKET_DIR=\"$(control_socket_dir)\" AM_CXXFLAGS = $(KEA_CXXFLAGS) @@ -25,6 +27,7 @@ run_unittests_SOURCES += command_mgr_unittests.cc run_unittests_SOURCES += cmd_http_listener_unittests.cc run_unittests_SOURCES += cmd_response_creator_unittests.cc run_unittests_SOURCES += cmd_response_creator_factory_unittests.cc +run_unittests_SOURCES += unix_command_config_unittests.cc run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) run_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS) @@ -39,6 +42,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la run_unittests_LDADD += $(top_builddir)/src/lib/dns/libkea-dns++.la run_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la run_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la +run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libkea-testutils.la run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la run_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la diff --git a/src/lib/config/tests/command_mgr_unittests.cc b/src/lib/config/tests/command_mgr_unittests.cc index b607d3e414..f74c51f7d3 100644 --- a/src/lib/config/tests/command_mgr_unittests.cc +++ b/src/lib/config/tests/command_mgr_unittests.cc @@ -12,11 +12,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -24,6 +26,7 @@ using namespace isc::asiolink; using namespace isc::config; using namespace isc::data; using namespace isc::hooks; +using namespace isc::util; using namespace std; // Test class for Command Manager @@ -34,6 +37,7 @@ public: /// Default constructor CommandMgrTest() : io_service_(new IOService()) { + resetSocketPath(); CommandMgr::instance().setIOService(io_service_); @@ -55,19 +59,24 @@ public: CommandMgr::instance().deregisterAll(); CommandMgr::instance().closeCommandSocket(); resetCalloutIndicators(); + resetSocketPath(); } - /// @brief Returns socket path (using either hardcoded path or env variable) - /// @return path to the unix socket - std::string getSocketPath() { - std::string socket_path; - const char* env = getenv("KEA_SOCKET_TEST_DIR"); - if (env) { - socket_path = std::string(env) + "/test-socket"; - } else { - socket_path = sandbox.join("test-socket"); - } - return (socket_path); + /// @brief Sets the path in which the socket can be created. + /// @param explicit_path path to use as the hooks path. + void setSocketTestPath(const std::string explicit_path = "") { + UnixCommandConfig::getSocketPath(true, + (!explicit_path.empty() ? + explicit_path : TEST_DATA_BUILDDIR)); + + auto path = UnixCommandConfig::getSocketPath(); + UnixCommandConfig::setSocketPathPerms(file::getPermissions(path)); + } + + /// @brief Resets the socket path to the default. + void resetSocketPath() { + UnixCommandConfig::getSocketPath(true); + UnixCommandConfig::setSocketPathPerms(); } /// @brief Resets indicators related to callout invocation. @@ -431,6 +440,7 @@ TEST_F(CommandMgrTest, delegateListCommands) { // This test verifies that a Unix socket can be opened properly and that input // parameters (socket-type and socket-name) are verified. TEST_F(CommandMgrTest, unixCreate) { + setSocketTestPath(); // Null pointer is obviously a bad idea. EXPECT_THROW(CommandMgr::instance().openCommandSocket(ConstElementPtr()), isc::config::BadSocketInfo); @@ -449,7 +459,7 @@ TEST_F(CommandMgrTest, unixCreate) { EXPECT_THROW(CommandMgr::instance().openCommandSocket(socket_info), isc::config::BadSocketInfo); - socket_info->set("socket-name", Element::create(getSocketPath())); + socket_info->set("socket-name", Element::create("test-socket")); EXPECT_NO_THROW(CommandMgr::instance().openCommandSocket(socket_info)); EXPECT_GE(CommandMgr::instance().getControlSocketFD(), 0); @@ -459,8 +469,9 @@ TEST_F(CommandMgrTest, unixCreate) { // This test checks that when unix path is too long, the socket cannot be opened. TEST_F(CommandMgrTest, unixCreateTooLong) { + setSocketTestPath(); ElementPtr socket_info = Element::fromJSON("{ \"socket-type\": \"unix\"," - "\"socket-name\": \"/tmp/toolongtoolongtoolongtoolongtoolongtoolong" + "\"socket-name\": \"toolongtoolongtoolongtoolongtoolongtoolong" "toolongtoolongtoolongtoolongtoolongtoolongtoolongtoolongtoolong" "\" }"); @@ -546,10 +557,11 @@ TEST_F(CommandMgrTest, commandProcessedHookReplaceResponse) { // Verifies that a socket cannot be concurrently opened more than once. TEST_F(CommandMgrTest, exclusiveOpen) { + setSocketTestPath(); // Pass in valid parameters. ElementPtr socket_info = Element::createMap(); socket_info->set("socket-type", Element::create("unix")); - socket_info->set("socket-name", Element::create(getSocketPath())); + socket_info->set("socket-name", Element::create("test-socket")); EXPECT_NO_THROW(CommandMgr::instance().openCommandSocket(socket_info)); EXPECT_GE(CommandMgr::instance().getControlSocketFD(), 0); diff --git a/src/lib/config/tests/unix_command_config_unittests.cc b/src/lib/config/tests/unix_command_config_unittests.cc new file mode 100644 index 0000000000..f5b3f70c3e --- /dev/null +++ b/src/lib/config/tests/unix_command_config_unittests.cc @@ -0,0 +1,65 @@ +// Copyright (C) 2025 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include +#include +#include +#include + +using namespace isc; +using namespace isc::config; +using namespace isc::test; +using namespace isc::util; +using namespace std; + +namespace { + +/// @brief Test fixture for UNIX control socket configuration. +class UnixCommandConfigTest : public ::testing::Test { +public: + /// @brief Constructor. + UnixCommandConfigTest() { + setSocketTestPath(); + } + + /// @brief Destructor. + ~UnixCommandConfigTest() { + resetSocketPath(); + } + + /// @brief Sets the path in which the socket can be created. + /// @param explicit_path path to use as the hooks path. + void setSocketTestPath(const std::string explicit_path = "") { + UnixCommandConfig::getSocketPath(true, + (!explicit_path.empty() ? + explicit_path : TEST_DATA_BUILDDIR)); + + auto path = UnixCommandConfig::getSocketPath(); + UnixCommandConfig::setSocketPathPerms(file::getPermissions(path)); + } + + /// @brief Resets the socket path to the default. + void resetSocketPath() { + UnixCommandConfig::getSocketPath(true); + UnixCommandConfig::setSocketPathPerms(); + } +}; + +TEST(SocketPathTest, socketDir) { + EnvVarWrapper env("KEA_CONTROL_SOCKET_DIR"); + env.setValue(""); + + auto path = UnixCommandConfig::getSocketPath(true); + ASSERT_EQ(path, std::string(CONTROL_SOCKET_DIR)); + + env.setValue(TEST_DATA_BUILDDIR); + path = UnixCommandConfig::getSocketPath(true); + ASSERT_EQ(path, std::string(TEST_DATA_BUILDDIR)); +} + +} // end of anonymous namespace diff --git a/src/lib/config/unix_command_config.cc b/src/lib/config/unix_command_config.cc new file mode 100644 index 0000000000..7c7d5b3fcc --- /dev/null +++ b/src/lib/config/unix_command_config.cc @@ -0,0 +1,75 @@ +// Copyright (C) 2025 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include +#include +#include + +using namespace isc; +using namespace isc::config; +using namespace isc::util::file; +using namespace std; + +namespace isc { +namespace config { + +namespace { + // Singleton PathChecker to set and hold valid unix socket path. + PathCheckerPtr socket_path_checker_; + +}; + +const mode_t UnixCommandConfig::DEFAULT_SOCKET_PATH_PERMS = (S_IRWXU | S_IRGRP | S_IXGRP); + +mode_t UnixCommandConfig::socket_path_perms_ = UnixCommandConfig::DEFAULT_SOCKET_PATH_PERMS; + +std::string +UnixCommandConfig::getSocketPath(bool reset /* = false */, + const std::string explicit_path /* = "" */) { + if (!socket_path_checker_ || reset) { + socket_path_checker_.reset(new PathChecker(CONTROL_SOCKET_DIR, + "KEA_CONTROL_SOCKET_DIR")); + if (!explicit_path.empty()) { + socket_path_checker_->getPath(true, explicit_path); + } + } + + return (socket_path_checker_->getPath()); +} + +std::string +UnixCommandConfig::validatePath(const std::string socket_path, + bool enforce /* = true */) { + if (!socket_path_checker_) { + getSocketPath(); + } + + auto valid_path = socket_path_checker_->validatePath(socket_path, enforce); + if (enforce && !(socket_path_checker_->pathHasPermissions(socket_path_perms_))) { + isc_throw (BadValue, + "socket path:" << socket_path_checker_->getPath() + << " does not exist or does not have permssions = " + << std::oct << socket_path_perms_); + } + + return (valid_path); +} + +mode_t +UnixCommandConfig::getSocketPathPerms() { + return (socket_path_perms_); +} + +void +UnixCommandConfig::setSocketPathPerms(mode_t perms + /* = DEFAULT_SOCKET_PATH_PERMS */) { + socket_path_perms_ = perms; +} + +} // end of isc::config +} // end of isc diff --git a/src/lib/config/unix_command_config.h b/src/lib/config/unix_command_config.h new file mode 100644 index 0000000000..ca2aaa4421 --- /dev/null +++ b/src/lib/config/unix_command_config.h @@ -0,0 +1,72 @@ +// Copyright (C) 2025 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef UNIX_COMMAND_CONFIG_H +#define UNIX_COMMAND_CONFIG_H + +#include +#include + +namespace isc { +namespace config { + +/// @brief UNIX command config aka UNIX control socket info class. +class UnixCommandConfig { +public: + /// @brief Defines the default permissions for unix socket parent directory. + static const mode_t DEFAULT_SOCKET_PATH_PERMS; + + /// @brief Stores the default permissions for unix socket parent directory. + static mode_t socket_path_perms_; + + /// @brief Constructor. + UnixCommandConfig() = default; + + /// @brief Virtual destructor. + ~UnixCommandConfig() = default; + + /// @brief Fetches the supported control socket path. + /// + /// The first call to this function with no arguments will set the default + /// path to either the value of CONTROL_SOCKET_DIR or the environment + /// variable KEA_CONTROL_SOCKET_DIR if it is defined. Subsequent calls with + /// no arguments will simply return this value. + /// + /// @param reset recalculate when true, defaults to false. + /// @param explicit_path set the default socket path to this value. This is + /// for testing purposes only. + /// + /// @return String containing the default socket path. + static std::string getSocketPath(bool reset = false, + const std::string explicit_path = ""); + + /// @brief Validates a path against the supported path for unix control + /// sockets. + /// + /// @param socket_path path to validate. + /// @param enforce enables validation against the supported path and + /// permissions. + /// If false simply returns the input path. + /// + /// @return validated path + static std::string validatePath(const std::string socket_path, + bool enforce = true); + + /// @brief Fetches the required socket path permissions mask + /// + /// @return permissions mask + static mode_t getSocketPathPerms(); + + /// @brief Sets the required socket path permissions mask + /// + /// This is for testing purposes only. + /// @param perms permissions mask to use + static void setSocketPathPerms(mode_t perms = DEFAULT_SOCKET_PATH_PERMS); +}; + +} // end of isc::config namespace +} // end of isc namespace +#endif diff --git a/src/lib/http/basic_auth_config.cc b/src/lib/http/basic_auth_config.cc index 0c98a4a3c3..6e33ea63eb 100644 --- a/src/lib/http/basic_auth_config.cc +++ b/src/lib/http/basic_auth_config.cc @@ -8,7 +8,7 @@ #include #include -#include +#include #include using namespace isc; diff --git a/src/lib/mysql/mysql_connection.cc b/src/lib/mysql/mysql_connection.cc index 652793e64d..6acbe03a76 100644 --- a/src/lib/mysql/mysql_connection.cc +++ b/src/lib/mysql/mysql_connection.cc @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include diff --git a/src/lib/util/Makefile.am b/src/lib/util/Makefile.am index f631523d0c..f4438f460c 100644 --- a/src/lib/util/Makefile.am +++ b/src/lib/util/Makefile.am @@ -15,7 +15,6 @@ libkea_util_la_SOURCES += chrono_time_utils.h chrono_time_utils.cc libkea_util_la_SOURCES += csv_file.h csv_file.cc libkea_util_la_SOURCES += dhcp_space.h dhcp_space.cc libkea_util_la_SOURCES += doubles.h -libkea_util_la_SOURCES += file_utilities.h file_utilities.cc libkea_util_la_SOURCES += filename.h filename.cc libkea_util_la_SOURCES += hash.h libkea_util_la_SOURCES += labeled_value.h labeled_value.cc @@ -66,7 +65,6 @@ libkea_util_include_HEADERS = \ csv_file.h \ dhcp_space.h \ doubles.h \ - file_utilities.h \ filename.h \ hash.h \ io_utilities.h \ diff --git a/src/lib/util/file_utilities.cc b/src/lib/util/file_utilities.cc deleted file mode 100644 index dfe5e89324..0000000000 --- a/src/lib/util/file_utilities.cc +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (C) 2021-2022 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#include - -#include -#include -#include -#include -#include -#include - -using namespace std; - -namespace isc { -namespace util { -namespace file { - -string -getContent(const string& file_name) { - // Open the file. - int fd = ::open(file_name.c_str(), O_RDONLY); - if (fd < 0) { - isc_throw(BadValue, "can't open file '" << file_name << "': " - << std::strerror(errno)); - } - try { - struct stat stats; - if (fstat(fd, &stats) < 0) { - isc_throw(BadValue, "can't stat file '" << file_name << "': " - << std::strerror(errno)); - } - if ((stats.st_mode & S_IFMT) != S_IFREG) { - isc_throw(BadValue, "'" << file_name - << "' must be a regular file"); - } - string content(stats.st_size, ' '); - ssize_t got = ::read(fd, &content[0], stats.st_size); - if (got < 0) { - isc_throw(BadValue, "can't read file '" << file_name << "': " - << std::strerror(errno)); - } - if (got != stats.st_size) { - isc_throw(BadValue, "can't read whole file '" << file_name - << "' (got " << got << " of " << stats.st_size << ")"); - } - static_cast(close(fd)); - return (content); - } catch (const std::exception&) { - static_cast(close(fd)); - throw; - } -} - -bool -isDir(const string& name) { - struct stat stats; - if (::stat(name.c_str(), &stats) < 0) { - return (false); - } - return ((stats.st_mode & S_IFMT) == S_IFDIR); -} - -} // namespace file -} // namespace log -} // namespace isc diff --git a/src/lib/util/file_utilities.h b/src/lib/util/file_utilities.h deleted file mode 100644 index 8c34822de6..0000000000 --- a/src/lib/util/file_utilities.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (C) 2021-2022 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#ifndef FILE_UTILITIES_H -#define FILE_UTILITIES_H - -#include - -namespace isc { -namespace util { -namespace file { - -/// @brief Get the content of a regular file. -/// -/// @param file_name The file name. -/// @return The content of the file_name file. -/// @throw BadValue when the file can't be opened or is not a regular one. -std::string getContent(const std::string& file_name); - -/// @brief Is a directory predicate. -/// -/// @param name The file or directory name. -/// @return True if the name points to a directory, false otherwise including -/// if the pointed location does not exist. -bool isDir(const std::string& name); - -} // namespace file -} // namespace util -} // namespace isc - -#endif // FILE_UTILITIES_H diff --git a/src/lib/util/filesystem.cc b/src/lib/util/filesystem.cc index a28cf3ddf1..cc5a87de54 100644 --- a/src/lib/util/filesystem.cc +++ b/src/lib/util/filesystem.cc @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -27,12 +28,53 @@ namespace isc { namespace util { namespace file { +string +getContent(string const& file_name) { + if (!exists(file_name)) { + isc_throw(BadValue, "Expected a file at path '" << file_name << "'"); + } + if (!isFile(file_name)) { + isc_throw(BadValue, "Expected '" << file_name << "' to be a regular file"); + } + ifstream file(file_name, ios::in); + if (!file.is_open()) { + isc_throw(BadValue, "Cannot open '" << file_name); + } + string content; + file >> content; + return (content); +} + bool exists(string const& path) { struct stat statbuf; return (::stat(path.c_str(), &statbuf) == 0); } +mode_t +getPermissions(const std::string path) { + struct stat statbuf; + if (::stat(path.c_str(), &statbuf) < 0) { + return (0); + } + + return (statbuf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); +} + +bool +hasPermissions(const std::string path, const mode_t& permissions) { + return (getPermissions(path) == permissions); +} + +bool +isDir(string const& path) { + struct stat statbuf; + if (::stat(path.c_str(), &statbuf) < 0) { + return (false); + } + return ((statbuf.st_mode & S_IFMT) == S_IFDIR); +} + bool isFile(string const& path) { struct stat statbuf; @@ -256,6 +298,11 @@ PathChecker::validatePath(const std::string input_path_str, return (valid_path); } +bool +PathChecker::pathHasPermissions(mode_t permissions) { + return(hasPermissions(path_, permissions)); +} + } // namespace file } // namespace util } // namespace isc diff --git a/src/lib/util/filesystem.h b/src/lib/util/filesystem.h index 8db8fecb41..3b36f3d5bd 100644 --- a/src/lib/util/filesystem.h +++ b/src/lib/util/filesystem.h @@ -15,6 +15,15 @@ namespace isc { namespace util { namespace file { +/// @brief Get the content of a regular file. +/// +/// @param file_name The file name. +/// +/// @return The content of the file. +/// @throw BadValue when the file can't be opened or is not a regular one. +std::string +getContent(const std::string& file_name); + /// @brief Check if there is a file or directory at the given path. /// /// @param path The path being checked. @@ -23,6 +32,31 @@ namespace file { bool exists(const std::string& path); +/// @brief Check if there is a directory at the given path. +/// +/// @param path The path being checked. +/// +/// @return True if the path points to a directory, false otherwise including +/// if the pointed location does not exist. +bool +isDir(const std::string& path); + +/// @brief Fetches the file permissions mask. +/// +/// @param path The path being checked. +/// @return File permissios mask or 0 if the path does not exist. +mode_t +getPermissions(const std::string path); + +/// @brief Check if there if file or directory has the given permissions. +/// +/// @param path The path being checked. +/// @param permissions mask of expected permissions. +/// +/// @return True if the path points to a file or a directory, false otherwise. +bool +hasPermissions(const std::string path, const mode_t& permissions); + /// @brief Check if there is a file at the given path. /// /// @param path The path being checked. @@ -186,6 +220,13 @@ public: std::string validatePath(const std::string input_path_str, bool enforce_path = true) const; + /// @brief Tests that the supported path has the given permissions. + /// + /// @param permissions mode_t mask of required permissions. + /// @return True if the path's permissions exactly match the permissions + /// parameter. + bool pathHasPermissions(mode_t permissions); + /// @brief Fetches the default path. std::string getDefaultPath() const { return (default_path_); diff --git a/src/lib/util/tests/Makefile.am b/src/lib/util/tests/Makefile.am index 935ec09712..3b6e902441 100644 --- a/src/lib/util/tests/Makefile.am +++ b/src/lib/util/tests/Makefile.am @@ -35,7 +35,6 @@ run_unittests_SOURCES += dhcp_space_unittest.cc run_unittests_SOURCES += doubles_unittest.cc run_unittests_SOURCES += fd_share_tests.cc run_unittests_SOURCES += fd_tests.cc -run_unittests_SOURCES += file_utilities_unittest.cc run_unittests_SOURCES += filename_unittest.cc run_unittests_SOURCES += hash_unittest.cc run_unittests_SOURCES += hex_unittest.cc diff --git a/src/lib/util/tests/file_utilities_unittest.cc b/src/lib/util/tests/file_utilities_unittest.cc deleted file mode 100644 index 4ee9093c28..0000000000 --- a/src/lib/util/tests/file_utilities_unittest.cc +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (C) 2015-2022 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#include - -#include -#include -#include -#include - -using namespace isc; -using namespace isc::util::file; -using namespace std; - -namespace { - -/// @brief Test fixture class for testing operations on files. -class FileUtilTest : public ::testing::Test { -public: - - /// @brief Destructor. - /// - /// Deletes the test file if any. - virtual ~FileUtilTest(); -}; - -FileUtilTest::~FileUtilTest() { - string test_file_name = string(TEST_DATA_BUILDDIR) + "/fu.test"; - static_cast(remove(test_file_name.c_str())); -} - -/// @brief Check an error is returned by getContent on not existent file. -TEST_F(FileUtilTest, notExists) { - string file_name("/this/does/not/exists"); - try { - string c = getContent(file_name); - FAIL() << "this test must throw before this line"; - } catch (const BadValue& ex) { - string expected = "can't open file '" + file_name; - expected += "': No such file or directory"; - EXPECT_EQ(string(ex.what()), expected); - } catch (const std::exception& ex) { - FAIL() << "unexpected exception: " << ex.what(); - } -} - -/// @note No easy can't stat. - -/// @brief Check an error is returned by getContent on not regular file. -TEST_F(FileUtilTest, notRegular) { - string file_name("/"); - try { - string c = getContent(file_name); - FAIL() << "this test must throw before this line"; - } catch (const BadValue& ex) { - string expected = "'" + file_name + "' must be a regular file"; - EXPECT_EQ(string(ex.what()), expected); - } catch (const std::exception& ex) { - FAIL() << "unexpected exception: " << ex.what(); - } -} - -/// @brief Check getContent works. -TEST_F(FileUtilTest, basic) { - string file_name = string(TEST_DATA_BUILDDIR) + "/fu.test"; - ofstream fs(file_name.c_str(), ofstream::out | ofstream::trunc); - ASSERT_TRUE(fs.is_open()); - fs << "abdc"; - fs.close(); - string content; - EXPECT_NO_THROW(content = getContent(file_name)); - EXPECT_EQ("abdc", content); -} - -/// @brief Check isDir works. -TEST_F(FileUtilTest, isDir) { - EXPECT_TRUE(isDir("/dev")); - EXPECT_FALSE(isDir("/dev/null")); - EXPECT_FALSE(isDir("/this/does/not/exists")); - EXPECT_FALSE(isDir("/etc/hosts")); -} - -} diff --git a/src/lib/util/tests/filesystem_unittests.cc b/src/lib/util/tests/filesystem_unittests.cc index 88d84fe5a7..7da8d3cfd5 100644 --- a/src/lib/util/tests/filesystem_unittests.cc +++ b/src/lib/util/tests/filesystem_unittests.cc @@ -22,6 +22,54 @@ using namespace std; namespace { +/// @brief Test fixture class for testing operations on files. +struct FileUtilTest : ::testing::Test { + /// @brief Destructor. + /// + /// Deletes the test file if any. + virtual ~FileUtilTest() { + string test_file_name(TEST_DATA_BUILDDIR "/fu.test"); + static_cast(remove(test_file_name.c_str())); + } +}; + +/// @brief Check that an error is returned by getContent on non-existent file. +TEST_F(FileUtilTest, notExist) { + EXPECT_THROW_MSG(getContent("/does/not/exist"), BadValue, + "Expected a file at path '/does/not/exist'"); +} + +/// @brief Check that an error is returned by getContent on not regular file. +TEST_F(FileUtilTest, notRegular) { + EXPECT_THROW_MSG(getContent("/"), BadValue, "Expected '/' to be a regular file"); +} + +/// @brief Check getContent. +TEST_F(FileUtilTest, getContent) { + string file_name(TEST_DATA_BUILDDIR "/fu.test"); + ofstream fs(file_name.c_str(), ofstream::out | ofstream::trunc); + ASSERT_TRUE(fs.is_open()); + fs << "abdc"; + fs.close(); + string content; + EXPECT_NO_THROW_LOG(content = getContent(file_name)); + EXPECT_EQ("abdc", content); +} + +/// @brief Check isDir. +TEST_F(FileUtilTest, isDir) { + EXPECT_TRUE(isDir("/dev")); + EXPECT_FALSE(isDir("/dev/null")); + EXPECT_FALSE(isDir("/this/does/not/exist")); + EXPECT_FALSE(isDir("/etc/hosts")); +} + +/// @brief Check isFile. +TEST_F(FileUtilTest, isFile) { + EXPECT_TRUE(isFile(ABS_SRCDIR "/filesystem_unittests.cc")); + EXPECT_FALSE(isFile(TEST_DATA_BUILDDIR)); +} + /// @brief Test fixture class for testing operations on umask. struct UMaskUtilTest : ::testing::Test { /// @brief Constructor. @@ -376,4 +424,14 @@ TEST(PathChecker, validatePathEnforcePathFalse) { } } +/// @brief Check hasPermissions. +TEST_F(FileUtilTest, hasPermissions) { + const std::string path = ABS_SRCDIR "/filesystem_unittests.cc"; + ASSERT_TRUE(isFile(path)); + mode_t current_permissions = getPermissions(path); + EXPECT_TRUE(hasPermissions(path, current_permissions)); + current_permissions = ~current_permissions; + EXPECT_FALSE(hasPermissions(path, current_permissions)); +} + } // namespace