Skip to content

Commit

Permalink
Merge pull request #216 from microsoft/hapdauthalgs
Browse files Browse the repository at this point in the history
Enable authentication algorithm configuration via hostapd
  • Loading branch information
abeltrano authored Mar 13, 2024
2 parents c883fab + 479c0f0 commit 542cf3d
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 1 deletion.
34 changes: 33 additions & 1 deletion src/linux/wpa-controller/Hostapd.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,38 @@ Hostapd::SetSsid(std::string_view ssid, EnforceConfigurationChange enforceConfig
}
}

void
Hostapd::SetAuthenticationAlgorithms(std::vector<WpaAuthenticationAlgorithm> algorithms, EnforceConfigurationChange enforceConfigurationChange)
{
using AlgorithmsUnderlyingType = std::underlying_type_t<decltype(algorithms)::value_type>;

if (std::empty(algorithms)) {
throw HostapdException("No WPA authentication algorithms were provided");
}

// Convert the authentication algorithm values to an OR'ed unsigned integer value expected by hostapd.
std::string algorithmsPropertyValue;
{
AlgorithmsUnderlyingType algorithmsValue = 0;
for (const auto algorithm : algorithms) {
const auto algorithmValue = WpaAuthenticationAlgorithmPropertyValue(algorithm);
if ((algorithmValue & ~WpaAuthenticationAlgorithmMask) != 0) {
throw HostapdException(std::format("Invalid WPA authentication algorithm value '{}'", algorithmValue));
}
algorithmsValue |= algorithmValue;
}

algorithmsValue &= WpaAuthenticationAlgorithmMask;
algorithmsPropertyValue = std::format("{}", algorithmsValue);
}

try {
SetProperty(ProtocolHostapd::PropertyNameAuthenticationAlgorithms, algorithmsPropertyValue, enforceConfigurationChange);
} catch (const HostapdException& e) {
throw HostapdException(std::format("Failed to set authentication algorithms to '{}' ({})", algorithmsPropertyValue, e.what()));
}
}

void
Hostapd::SetWpaProtocols(std::vector<WpaProtocol> protocols, EnforceConfigurationChange enforceConfigurationChange)
{
Expand All @@ -210,7 +242,7 @@ Hostapd::SetWpaProtocols(std::vector<WpaProtocol> protocols, EnforceConfiguratio

uint32_t protocolsToSet = 0;
for (const auto protocol : protocols) {
const auto protocolValue = std::to_underlying(protocol);
const auto protocolValue = WpaProtocolPropertyValue(protocol);
if ((protocolValue & ~WpaProtocolMask) != 0) {
throw HostapdException(std::format("Invalid WPA protocol value '{}'", protocolValue));
}
Expand Down
9 changes: 9 additions & 0 deletions src/linux/wpa-controller/include/Wpa/Hostapd.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,15 @@ struct Hostapd :
void
SetSsid(std::string_view ssid, EnforceConfigurationChange enforceConfigurationChange = EnforceConfigurationChange::Now) override;

/**
* @brief Set the authentication algorithm(s) for the interface.
*
* @param algorithms The set of allowed authentication algorithms.
* @param enforceConfigurationChange When to enforce the configuration change. A value of 'Now' will trigger a configuration reload.
*/
void
SetAuthenticationAlgorithms(std::vector<WpaAuthenticationAlgorithm> algorithms, EnforceConfigurationChange enforceConfigurationChange) override;

/**
* @brief Set the WPA protocol(s) for the interface.
*
Expand Down
9 changes: 9 additions & 0 deletions src/linux/wpa-controller/include/Wpa/IHostapd.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,15 @@ struct IHostapd
virtual void
SetSsid(std::string_view ssid, EnforceConfigurationChange enforceConfigurationChange) = 0;

/**
* @brief Set the authentication algorithm(s) for the interface.
*
* @param algorithms The set of allowed authentication algorithms.
* @param enforceConfigurationChange When to enforce the configuration change. A value of 'Now' will trigger a configuration reload.
*/
virtual void
SetAuthenticationAlgorithms(std::vector<WpaAuthenticationAlgorithm> algorithms, EnforceConfigurationChange enforceConfigurationChange) = 0;

/**
* @brief Set the WPA protocol(s) for the interface.
*
Expand Down
82 changes: 82 additions & 0 deletions src/linux/wpa-controller/include/Wpa/ProtocolHostapd.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,63 @@ enum class HostapdHwMode {
Ieee80211any,
};

/**
* @brief Initial authentication algorithms supported by hostapd.
*/
enum class WpaAuthenticationAlgorithm : uint32_t {
OpenSystem = 1U << 0U,
SharedKey = 1U << 1U,
Leap = 1U << 2U,
Ft = 1U << 3U,
Sae = 1U << 4U,
Fils = 1U << 5U,
FilsSkPfs = 1U << 6U,
};

/**
* @brief Array of all authentication algorithm values.
*/
inline constexpr std::array<WpaAuthenticationAlgorithm, 7> WpaAuthenticationAlgorithmsAll = {
WpaAuthenticationAlgorithm::OpenSystem,
WpaAuthenticationAlgorithm::SharedKey,
WpaAuthenticationAlgorithm::Leap,
WpaAuthenticationAlgorithm::Ft,
WpaAuthenticationAlgorithm::Sae,
WpaAuthenticationAlgorithm::Fils,
WpaAuthenticationAlgorithm::FilsSkPfs,
};

/**
* @brief Array of all unsupported authentication algorithm values.
*/
inline constexpr std::array<WpaAuthenticationAlgorithm, 5> WpaAuthenticationAlgorithmsUnsupported = {
WpaAuthenticationAlgorithm::Leap,
WpaAuthenticationAlgorithm::Ft,
WpaAuthenticationAlgorithm::Sae,
WpaAuthenticationAlgorithm::Fils,
WpaAuthenticationAlgorithm::FilsSkPfs,
};

/**
* @brief Numerical bitmask of valid WpaAuthenticationAlgorithm values.
*/
inline constexpr std::underlying_type_t<WpaAuthenticationAlgorithm> WpaAuthenticationAlgorithmMask =
std::to_underlying(WpaAuthenticationAlgorithm::OpenSystem) |
std::to_underlying(WpaAuthenticationAlgorithm::SharedKey);

/**
* @brief Determine if the specified WpaAuthenticationAlgorithm is supported by hostapd.
*
* @param wpaProtocol The WpaAuthenticationAlgorithm to check.
* @return true The authentication algorithm is supported.
* @return false The authentication algorithm is not supported.
*/
constexpr bool
IsWpaAuthenticationAlgorithmSupported(WpaAuthenticationAlgorithm wpaAuthenticationAlgorithm) noexcept
{
return !std::ranges::contains(WpaAuthenticationAlgorithmsUnsupported, wpaAuthenticationAlgorithm);
}

/**
* @brief WPA encoding of IEEE 802.11i-2004 protocol types.
*
Expand Down Expand Up @@ -423,6 +480,7 @@ struct ProtocolHostapd :
static constexpr auto PropertyHwModeValueAD = "ad";
static constexpr auto PropertyHwModeValueAny = "any";

static constexpr auto PropertyNameAuthenticationAlgorithms = "auth_algs";
static constexpr auto PropertyNameSsid = "ssid";

static constexpr auto PropertyNameIeee80211N = "ieee80211n";
Expand Down Expand Up @@ -622,6 +680,30 @@ WpaCipherPropertyName(WpaProtocol wpaProtocol) noexcept
return ProtocolHostapd::PropertyNameInvalid;
}
}

/**
* @brief Get the hostapd property value for the specified WpaProtocol.
*
* @param wpaProtocol The WpaProtocol to get the property value for.
* @return constexpr std::underlying_type_t<WpaProtocol>
*/
constexpr std::underlying_type_t<WpaProtocol>
WpaProtocolPropertyValue(WpaProtocol wpaProtocol) noexcept
{
return std::to_underlying(wpaProtocol);
}

/**
* @brief Get the hostapd property value for the specified WpaAuthenticationAlgorithm.
*
* @param wpaAuthenticationAlgorithm The WpaAuthenticationAlgorithm to get the property value for.
* @return constexpr std::underlying_type_t<WpaAuthenticationAlgorithm>
*/
constexpr std::underlying_type_t<WpaAuthenticationAlgorithm>
WpaAuthenticationAlgorithmPropertyValue(WpaAuthenticationAlgorithm wpaAuthenticationAlgorithm) noexcept
{
return std::to_underlying(wpaAuthenticationAlgorithm);
}
} // namespace Wpa

#endif // HOSTAPD_PROTOCOL_HXX
51 changes: 51 additions & 0 deletions tests/unit/linux/wpa-controller/TestHostapd.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -624,3 +624,54 @@ TEST_CASE("Send SetCipherSuites() command (root)", "[wpa][hostapd][client][remot
REQUIRE_NOTHROW(hostapd.SetCipherSuites({ { WpaProtocol::Wpa, { WpaCipher::Aes128Cmac, WpaCipher::Aes128Cmac } } }, EnforceConfigurationChange::Defer));
}
}

TEST_CASE("Send SetAuthenticationAlgorithms() command (root)", "[wpa][hostapd][client][remote]")
{
using namespace Wpa;

Hostapd hostapd(WpaDaemonManager::InterfaceNameDefault);

SECTION("Doesn't throw")
{
REQUIRE_NOTHROW(hostapd.SetAuthenticationAlgorithms({ WpaAuthenticationAlgorithm::OpenSystem }, EnforceConfigurationChange::Now));
REQUIRE_NOTHROW(hostapd.SetAuthenticationAlgorithms({ WpaAuthenticationAlgorithm::OpenSystem }, EnforceConfigurationChange::Defer));
}

SECTION("Fails with empty input")
{
REQUIRE_THROWS_AS(hostapd.SetAuthenticationAlgorithms({}, EnforceConfigurationChange::Now), HostapdException);
REQUIRE_THROWS_AS(hostapd.SetAuthenticationAlgorithms({}, EnforceConfigurationChange::Defer), HostapdException);
}

SECTION("Fails with invalid input")
{
for (const auto wpaAuthenticationAlgorithmUnsupported : WpaAuthenticationAlgorithmsUnsupported) {
REQUIRE_THROWS_AS(hostapd.SetAuthenticationAlgorithms({ wpaAuthenticationAlgorithmUnsupported }, EnforceConfigurationChange::Now), HostapdException);
REQUIRE_THROWS_AS(hostapd.SetAuthenticationAlgorithms({ wpaAuthenticationAlgorithmUnsupported }, EnforceConfigurationChange::Defer), HostapdException);
}
}

SECTION("Succeeds with valid, single input")
{
for (const auto wpaAuthenticationAlgorithm : WpaAuthenticationAlgorithmsAll | std::views::filter(IsWpaAuthenticationAlgorithmSupported)) {
REQUIRE_NOTHROW(hostapd.SetAuthenticationAlgorithms({ wpaAuthenticationAlgorithm }, EnforceConfigurationChange::Now));
REQUIRE_NOTHROW(hostapd.SetAuthenticationAlgorithms({ wpaAuthenticationAlgorithm }, EnforceConfigurationChange::Defer));
}
}

SECTION("Succeeds with valid, multiple inputs")
{
std::vector<WpaAuthenticationAlgorithm> wpaAuthenticationAlgorithms{};
for (const auto wpaAuthenticationAlgorithm : WpaAuthenticationAlgorithmsAll | std::views::filter(IsWpaAuthenticationAlgorithmSupported)) {
wpaAuthenticationAlgorithms.push_back(wpaAuthenticationAlgorithm);
REQUIRE_NOTHROW(hostapd.SetAuthenticationAlgorithms(wpaAuthenticationAlgorithms, EnforceConfigurationChange::Now));
REQUIRE_NOTHROW(hostapd.SetAuthenticationAlgorithms(wpaAuthenticationAlgorithms, EnforceConfigurationChange::Defer));
}
}

SECTION("Succeeds with valid, duplicate inputs")
{
REQUIRE_NOTHROW(hostapd.SetAuthenticationAlgorithms({ WpaAuthenticationAlgorithm::OpenSystem, WpaAuthenticationAlgorithm::OpenSystem }, EnforceConfigurationChange::Now));
REQUIRE_NOTHROW(hostapd.SetAuthenticationAlgorithms({ WpaAuthenticationAlgorithm::OpenSystem, WpaAuthenticationAlgorithm::OpenSystem }, EnforceConfigurationChange::Defer));
}
}

0 comments on commit 542cf3d

Please sign in to comment.