702 lines
21 KiB
C++
702 lines
21 KiB
C++
/*
|
|
SPDX-License-Identifier: MIT
|
|
Copyright © 1991-2025 Amebis
|
|
Copyright © 2016 GÉANT
|
|
*/
|
|
|
|
/// \defgroup WinStdEAPAPI Extensible Authentication Protocol API
|
|
|
|
#pragma once
|
|
|
|
#include "Common.h"
|
|
#include <eaphostpeerconfigapis.h>
|
|
#include <eaptypes.h>
|
|
#include <EapHostPeerTypes.h>
|
|
#include <eapmethodtypes.h>
|
|
#include <eappapis.h>
|
|
#include <WinSock2.h>
|
|
#include <memory>
|
|
|
|
#pragma warning(push)
|
|
#pragma warning(disable: 26812) // Windows EAP API is using unscoped enums
|
|
|
|
#pragma warning(push)
|
|
#pragma warning(disable: 4505) // Don't warn on unused code
|
|
|
|
/// \addtogroup WinStdEAPAPI
|
|
/// @{
|
|
|
|
///
|
|
/// Are EAP method types equal?
|
|
///
|
|
/// \param[in] a First EAP method type
|
|
/// \param[in] b Second EAP method type
|
|
///
|
|
/// \returns
|
|
/// - Non zero when \p a is equal to \p b;
|
|
/// - Zero otherwise.
|
|
///
|
|
static bool operator==(_In_ const EAP_METHOD_TYPE &a, _In_ const EAP_METHOD_TYPE &b) noexcept
|
|
{
|
|
return
|
|
a.eapType.type == b.eapType.type &&
|
|
a.eapType.dwVendorId == b.eapType.dwVendorId &&
|
|
a.eapType.dwVendorType == b.eapType.dwVendorType &&
|
|
a.dwAuthorId == a.dwAuthorId;
|
|
}
|
|
|
|
///
|
|
/// Are EAP method types non-equal?
|
|
///
|
|
/// \param[in] a First EAP method type
|
|
/// \param[in] b Second EAP method type
|
|
///
|
|
/// \returns
|
|
/// - Non zero when \p a is not equal to \p b;
|
|
/// - Zero otherwise.
|
|
///
|
|
static bool operator!=(_In_ const EAP_METHOD_TYPE &a, _In_ const EAP_METHOD_TYPE &b) noexcept
|
|
{
|
|
return !operator==(a, b);
|
|
}
|
|
|
|
/// @}
|
|
|
|
#pragma warning(pop)
|
|
|
|
namespace winstd
|
|
{
|
|
/// \addtogroup WinStdEAPAPI
|
|
/// @{
|
|
|
|
///
|
|
/// EAP method numbers
|
|
///
|
|
/// \sa [Extensible Authentication Protocol (EAP) Registry (Chapter: Method Types)](https://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml#eap-numbers-4)
|
|
///
|
|
#pragma warning(suppress: 4480)
|
|
enum class eap_type_t : unsigned char {
|
|
undefined = 0, ///< Undefined EAP type
|
|
identity = 1, ///< Identity
|
|
notification = 2, ///< Notification
|
|
nak = 3, ///< Legacy Nak
|
|
md5_challenge = 4, ///< MD5-Challenge
|
|
otp = 5, ///< One-Time Password (OTP)
|
|
gtc = 6, ///< Generic Token Card (GTC)
|
|
tls = 13, ///< EAP-TLS
|
|
ttls = 21, ///< EAP-TTLS
|
|
peap = 25, ///< PEAP
|
|
mschapv2 = 26, ///< EAP-MSCHAPv2
|
|
ms_auth_tlv = 33, ///< MS-Authentication-TLV
|
|
|
|
gtcp = 128 + gtc, ///< EAP-GTC using a password
|
|
|
|
legacy_pap = 192, ///< PAP (Not actually an EAP method; Moved to the Unassigned area)
|
|
legacy_mschapv2 = 193, ///< MSCHAPv2 (Not actually an EAP method; Moved to the Unassigned area)
|
|
|
|
start = 1, ///< Start of EAP methods
|
|
end = 192, ///< End of EAP methods (non-inclusive)
|
|
noneap_start = 192, ///< Start of non-EAP methods
|
|
noneap_end = 254, ///< End of non-EAP methods (non-inclusive)
|
|
};
|
|
|
|
///
|
|
/// Deleter for unique_ptr using EapHostPeerFreeMemory
|
|
///
|
|
struct EapHostPeerFreeMemory_delete
|
|
{
|
|
///
|
|
/// Default constructor
|
|
///
|
|
EapHostPeerFreeMemory_delete() noexcept {}
|
|
|
|
///
|
|
/// Delete a pointer
|
|
///
|
|
/// \sa [EapHostPeerFreeMemory function](https://msdn.microsoft.com/en-us/library/windows/desktop/aa363558.aspx)
|
|
///
|
|
template <class _T>
|
|
void operator()(_T *_Ptr) const
|
|
{
|
|
EapHostPeerFreeMemory((BYTE*)_Ptr);
|
|
}
|
|
};
|
|
|
|
///
|
|
/// EapHost BLOB wrapper class
|
|
///
|
|
typedef std::unique_ptr<BYTE[], EapHostPeerFreeMemory_delete> eap_blob;
|
|
|
|
///
|
|
/// Deleter for unique_ptr using EapHostPeerFreeRuntimeMemory
|
|
///
|
|
struct EapHostPeerFreeRuntimeMemory_delete
|
|
{
|
|
///
|
|
/// Default constructor
|
|
///
|
|
EapHostPeerFreeRuntimeMemory_delete() noexcept {}
|
|
|
|
///
|
|
/// Delete a pointer
|
|
///
|
|
template <class _T>
|
|
void operator()(_T *_Ptr) const
|
|
{
|
|
EapHostPeerFreeRuntimeMemory((BYTE*)_Ptr);
|
|
}
|
|
};
|
|
|
|
///
|
|
/// EapHost BLOB wrapper class
|
|
///
|
|
typedef std::unique_ptr<BYTE[], EapHostPeerFreeRuntimeMemory_delete> eap_blob_runtime;
|
|
|
|
///
|
|
/// Deleter for unique_ptr to EAP_ERROR using EapHostPeerFreeErrorMemory
|
|
///
|
|
struct EapHostPeerFreeErrorMemory_delete
|
|
{
|
|
///
|
|
/// Default constructor
|
|
///
|
|
EapHostPeerFreeErrorMemory_delete() noexcept {}
|
|
|
|
///
|
|
/// Delete a pointer
|
|
///
|
|
/// \sa [EapHostPeerFreeErrorMemory function](https://msdn.microsoft.com/en-us/library/windows/desktop/aa363557.aspx)
|
|
///
|
|
void operator()(EAP_ERROR *_Ptr) const noexcept
|
|
{
|
|
EapHostPeerFreeErrorMemory(_Ptr);
|
|
}
|
|
};
|
|
|
|
///
|
|
/// EAP_ERROR wrapper class
|
|
///
|
|
typedef std::unique_ptr<EAP_ERROR, EapHostPeerFreeErrorMemory_delete> eap_error;
|
|
|
|
///
|
|
/// Deleter for unique_ptr to EAP_ERROR using EapHostPeerFreeEapError
|
|
///
|
|
struct EapHostPeerFreeEapError_delete
|
|
{
|
|
///
|
|
/// Default constructor
|
|
///
|
|
EapHostPeerFreeEapError_delete() noexcept {}
|
|
|
|
///
|
|
/// Delete a pointer
|
|
///
|
|
/// \sa [EapHostPeerFreeEapError function](https://msdn.microsoft.com/en-us/library/windows/desktop/aa363556.aspx)
|
|
///
|
|
void operator()(EAP_ERROR *_Ptr) const noexcept
|
|
{
|
|
EapHostPeerFreeEapError(_Ptr);
|
|
}
|
|
};
|
|
|
|
///
|
|
/// EAP_ERROR wrapper class
|
|
///
|
|
typedef std::unique_ptr<EAP_ERROR, EapHostPeerFreeEapError_delete> eap_error_runtime;
|
|
|
|
///
|
|
/// EAP_ATTRIBUTE wrapper class
|
|
///
|
|
#pragma warning(push)
|
|
#pragma warning(disable: 26432) // Copy constructor and assignment operator are also present, but not detected by code analysis as they are using base type source object reference.
|
|
class eap_attr : public EAP_ATTRIBUTE
|
|
{
|
|
public:
|
|
///
|
|
/// Initializes a new EAP attribute set to eatReserved.
|
|
///
|
|
eap_attr() noexcept
|
|
{
|
|
eaType = eatReserved;
|
|
dwLength = 0;
|
|
pValue = NULL;
|
|
}
|
|
|
|
///
|
|
/// Copies an existing EAP attribute.
|
|
///
|
|
eap_attr(_In_ const EAP_ATTRIBUTE &a)
|
|
{
|
|
eaType = a.eaType;
|
|
dwLength = a.dwLength;
|
|
if (a.dwLength) {
|
|
pValue = new BYTE[a.dwLength];
|
|
memcpy(pValue, a.pValue, a.dwLength);
|
|
} else
|
|
pValue = NULL;
|
|
}
|
|
|
|
///
|
|
/// Moves an existing EAP attribute.
|
|
///
|
|
eap_attr(_Inout_ eap_attr &&a) noexcept
|
|
{
|
|
eaType = a.eaType;
|
|
dwLength = a.dwLength;
|
|
if (a.dwLength) {
|
|
pValue = a.pValue;
|
|
a.dwLength = 0;
|
|
a.pValue = NULL;
|
|
} else
|
|
pValue = NULL;
|
|
}
|
|
|
|
///
|
|
/// Destroys the EAP attribute.
|
|
///
|
|
~eap_attr()
|
|
{
|
|
if (pValue)
|
|
delete [] pValue;
|
|
}
|
|
|
|
///
|
|
/// Copies an existing EAP attribute.
|
|
///
|
|
eap_attr& operator=(_In_ const EAP_ATTRIBUTE &a)
|
|
{
|
|
if (this != &a) {
|
|
eaType = a.eaType;
|
|
dwLength = a.dwLength;
|
|
if (a.dwLength) {
|
|
BYTE *pValueNew = new BYTE[a.dwLength];
|
|
if (pValue)
|
|
delete [] pValue;
|
|
memcpy(pValueNew, a.pValue, a.dwLength);
|
|
pValue = pValueNew;
|
|
} else
|
|
pValue = NULL;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
///
|
|
/// Moves an existing EAP attribute.
|
|
///
|
|
eap_attr& operator=(_Inout_ eap_attr &&a) noexcept
|
|
{
|
|
if (this != &a) {
|
|
eaType = a.eaType;
|
|
dwLength = a.dwLength;
|
|
if (pValue)
|
|
delete [] pValue;
|
|
if (a.dwLength) {
|
|
pValue = a.pValue;
|
|
a.dwLength = 0;
|
|
a.pValue = NULL;
|
|
} else
|
|
pValue = NULL;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
///
|
|
/// Creates MS-MPPE-Send-Key or MS-MPPE-Recv-Key
|
|
///
|
|
/// \sa [RADIUS Vendor-Specific](https://tools.ietf.org/html/rfc2865#section-5.26)
|
|
/// \sa [MS-MPPE-Send-Key](https://tools.ietf.org/html/rfc2548#section-2.4.2)
|
|
/// \sa [MS-MPPE-Recv-Key](https://tools.ietf.org/html/rfc2548#section-2.4.3)
|
|
///
|
|
void create_ms_mppe_key(_In_ BYTE bVendorType, _In_count_(nKeySize) LPCBYTE pbKey, _In_ BYTE nKeySize)
|
|
{
|
|
const BYTE nPaddingLength = static_cast<BYTE>((16 - (1 + static_cast<DWORD>(nKeySize))) % 16);
|
|
const DWORD dwLengthNew =
|
|
4 + // Vendor-Id
|
|
1 + // Vendor type
|
|
1 + // Vendor length
|
|
2 + // Salt
|
|
1 + // Key-Length
|
|
nKeySize + // Key
|
|
nPaddingLength; // Padding
|
|
|
|
#pragma warning(push)
|
|
#pragma warning(disable: 6386)
|
|
LPBYTE p = new BYTE[dwLengthNew];
|
|
p[0] = 0x00; // Vendor-Id (0x137 = 311 = Microsoft)
|
|
p[1] = 0x00; // --|
|
|
p[2] = 0x01; // --|
|
|
p[3] = 0x37; // --^
|
|
p[4] = bVendorType; // Vendor type
|
|
p[5] = static_cast<BYTE>(dwLengthNew - 4); // Vendor length
|
|
p[6] = 0x00; // Salt
|
|
p[7] = 0x00; // --^
|
|
p[8] = nKeySize; // Key-Length
|
|
#pragma warning(pop)
|
|
memcpy(p + 9, pbKey, nKeySize); // Key
|
|
memset(p + 9 + nKeySize, 0, nPaddingLength); // Padding
|
|
|
|
if (pValue)
|
|
delete [] pValue;
|
|
|
|
#pragma warning(suppress: 26812) // EAP_ATTRIBUTE_TYPE is unscoped.
|
|
eaType = eatVendorSpecific;
|
|
dwLength = dwLengthNew;
|
|
pValue = p;
|
|
}
|
|
};
|
|
#pragma warning(pop)
|
|
|
|
///
|
|
/// Blank EAP attribute
|
|
///
|
|
static const EAP_ATTRIBUTE blank_eap_attr = {};
|
|
|
|
///
|
|
/// EAP_METHOD_PROPERTY wrapper class
|
|
///
|
|
class eap_method_prop : public EAP_METHOD_PROPERTY
|
|
{
|
|
public:
|
|
///
|
|
/// Constructs a BOOL method property
|
|
///
|
|
/// \param[in] type EAP method property type
|
|
/// \param[in] value Property value
|
|
///
|
|
eap_method_prop(_In_ EAP_METHOD_PROPERTY_TYPE type, _In_ BOOL value) noexcept
|
|
{
|
|
eapMethodPropertyType = type;
|
|
eapMethodPropertyValueType = empvtBool;
|
|
eapMethodPropertyValue.empvBool.length = sizeof(BOOL);
|
|
eapMethodPropertyValue.empvBool.value = value;
|
|
}
|
|
|
|
///
|
|
/// Constructs a DWORD method property
|
|
///
|
|
/// \param[in] type EAP method property type
|
|
/// \param[in] value Property value
|
|
///
|
|
eap_method_prop(_In_ EAP_METHOD_PROPERTY_TYPE type, _In_ DWORD value) noexcept
|
|
{
|
|
eapMethodPropertyType = type;
|
|
eapMethodPropertyValueType = empvtDword;
|
|
eapMethodPropertyValue.empvDword.length = sizeof(DWORD);
|
|
eapMethodPropertyValue.empvDword.value = value;
|
|
}
|
|
|
|
///
|
|
/// Constructs a Unicode string method property
|
|
///
|
|
/// \param[in] type EAP method property type
|
|
/// \param[in] value Property value
|
|
///
|
|
eap_method_prop(_In_ EAP_METHOD_PROPERTY_TYPE type, _In_z_ LPCWSTR value) noexcept
|
|
{
|
|
eapMethodPropertyType = type;
|
|
eapMethodPropertyValueType = empvtString;
|
|
eapMethodPropertyValue.empvString.length = static_cast<DWORD>(sizeof(WCHAR)*(wcslen(value) + 1));
|
|
eapMethodPropertyValue.empvString.value = const_cast<BYTE*>(reinterpret_cast<const BYTE*>(value));
|
|
}
|
|
};
|
|
|
|
///
|
|
/// EapPacket wrapper class
|
|
///
|
|
class eap_packet : public dplhandle<EapPacket*, NULL>
|
|
{
|
|
WINSTD_DPLHANDLE_IMPL(eap_packet, EapPacket*, NULL)
|
|
|
|
public:
|
|
///
|
|
/// Destroys the EAP packet.
|
|
///
|
|
virtual ~eap_packet()
|
|
{
|
|
if (m_h != invalid)
|
|
free_internal();
|
|
}
|
|
|
|
///
|
|
/// Create new EAP packet
|
|
///
|
|
/// \param[in] code EAP code (one of EapCode enum values)
|
|
/// \param[in] id Packet ID
|
|
/// \param[in] size Total packet size in bytes, including header. Must be at least 4B.
|
|
///
|
|
/// \note Packet data (beyond first 4B) is not initialized.
|
|
///
|
|
/// \return
|
|
/// - true when creation succeeds;
|
|
/// - false when creation fails. For extended error information, call `GetLastError()`.
|
|
///
|
|
bool create(_In_ EapCode code, _In_ BYTE id, _In_ WORD size) noexcept
|
|
{
|
|
assert(size >= 4); // EAP packets must contain at least Code, Id, and Length fields: 4B.
|
|
|
|
handle_type h = static_cast<handle_type>(HeapAlloc(GetProcessHeap(), 0, size));
|
|
if (h != NULL) {
|
|
h->Code = static_cast<BYTE>(code);
|
|
h->Id = id;
|
|
*reinterpret_cast<WORD*>(h->Length) = htons(size);
|
|
|
|
attach(h);
|
|
return true;
|
|
} else {
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
///
|
|
/// Returns total EAP packet size in bytes.
|
|
///
|
|
WORD size() const noexcept
|
|
{
|
|
return m_h != NULL ? ntohs(*(WORD*)m_h->Length) : 0;
|
|
}
|
|
|
|
protected:
|
|
///
|
|
/// Destroys the EAP packet.
|
|
///
|
|
void free_internal() noexcept override
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, m_h);
|
|
}
|
|
|
|
///
|
|
/// Duplicates the EAP packet.
|
|
///
|
|
handle_type duplicate_internal(_In_ handle_type h) const override
|
|
{
|
|
assert(h);
|
|
const WORD n = ntohs(*reinterpret_cast<WORD*>(h->Length));
|
|
handle_type h2 = static_cast<handle_type>(HeapAlloc(GetProcessHeap(), 0, n));
|
|
if (h2 != invalid) {
|
|
_Analysis_assume_(h2 != NULL); // VS2022 can't figure out `invalid` is `NULL`
|
|
memcpy(h2, h, n);
|
|
return h2;
|
|
}
|
|
throw std::bad_alloc();
|
|
}
|
|
};
|
|
|
|
///
|
|
/// EAP_METHOD_INFO_ARRAY wrapper class
|
|
///
|
|
class eap_method_info_array : public EAP_METHOD_INFO_ARRAY
|
|
{
|
|
WINSTD_NONCOPYABLE(eap_method_info_array)
|
|
|
|
public:
|
|
///
|
|
/// Constructs an empty array
|
|
///
|
|
eap_method_info_array() noexcept
|
|
{
|
|
dwNumberOfMethods = 0;
|
|
pEapMethods = NULL;
|
|
}
|
|
|
|
///
|
|
/// Move constructor
|
|
///
|
|
/// \param[inout] other A rvalue reference of another object
|
|
///
|
|
eap_method_info_array(_Inout_ eap_method_info_array &&other) noexcept
|
|
{
|
|
dwNumberOfMethods = other.dwNumberOfMethods;
|
|
pEapMethods = other.pEapMethods;
|
|
other.dwNumberOfMethods = 0;
|
|
other.pEapMethods = NULL;
|
|
}
|
|
|
|
///
|
|
/// Destructor
|
|
///
|
|
~eap_method_info_array()
|
|
{
|
|
if (pEapMethods)
|
|
free_internal();
|
|
}
|
|
|
|
///
|
|
/// Move assignment
|
|
///
|
|
/// \param[inout] other A rvalue reference of another object
|
|
///
|
|
eap_method_info_array& operator=(_Inout_ eap_method_info_array &&other) noexcept
|
|
{
|
|
if (this != std::addressof(other)) {
|
|
if (pEapMethods)
|
|
free_internal();
|
|
dwNumberOfMethods = other.dwNumberOfMethods;
|
|
pEapMethods = other.pEapMethods;
|
|
other.dwNumberOfMethods = 0;
|
|
other.pEapMethods = NULL;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
protected:
|
|
/// \cond internal
|
|
|
|
void free_internal() noexcept
|
|
{
|
|
for (DWORD i = 0; i < dwNumberOfMethods; i++)
|
|
free_internal(pEapMethods + i);
|
|
|
|
EapHostPeerFreeMemory(reinterpret_cast<BYTE*>(pEapMethods));
|
|
}
|
|
|
|
static void free_internal(_In_ EAP_METHOD_INFO *pMethodInfo) noexcept
|
|
{
|
|
if (pMethodInfo->pInnerMethodInfo)
|
|
free_internal(pMethodInfo->pInnerMethodInfo);
|
|
|
|
EapHostPeerFreeMemory(reinterpret_cast<BYTE*>(pMethodInfo->pwszAuthorName));
|
|
EapHostPeerFreeMemory(reinterpret_cast<BYTE*>(pMethodInfo->pwszFriendlyName));
|
|
}
|
|
|
|
/// \endcond
|
|
};
|
|
|
|
/// @}
|
|
|
|
/// \addtogroup WinStdExceptions
|
|
/// @{
|
|
|
|
///
|
|
/// EapHost runtime error
|
|
///
|
|
/// \sa [EAP_ERROR structure](https://msdn.microsoft.com/en-us/library/windows/desktop/aa363699.aspx)
|
|
///
|
|
class eap_runtime_error : public win_runtime_error
|
|
{
|
|
public:
|
|
///
|
|
/// Constructs an exception
|
|
///
|
|
/// \param[in] err EapHost error descriptor
|
|
/// \param[in] msg Error message
|
|
///
|
|
eap_runtime_error(_In_ const EAP_ERROR &err, _In_ const std::string& msg) :
|
|
m_type (err.type ),
|
|
m_reason (err.dwReasonCode ),
|
|
m_root_cause_id (err.rootCauseGuid ),
|
|
m_root_cause_desc(err.pRootCauseString ),
|
|
m_repair_id (err.repairGuid ),
|
|
m_repair_desc (err.pRepairString ),
|
|
m_help_link_id (err.helpLinkGuid ),
|
|
win_runtime_error(err.dwWinError, msg.c_str())
|
|
{}
|
|
|
|
///
|
|
/// Constructs an exception
|
|
///
|
|
/// \param[in] err EapHost error descriptor
|
|
///
|
|
eap_runtime_error(_In_ const EAP_ERROR &err) :
|
|
m_type (err.type ),
|
|
m_reason (err.dwReasonCode ),
|
|
m_root_cause_id (err.rootCauseGuid ),
|
|
m_root_cause_desc(err.pRootCauseString),
|
|
m_repair_id (err.repairGuid ),
|
|
m_repair_desc (err.pRepairString ),
|
|
m_help_link_id (err.helpLinkGuid ),
|
|
win_runtime_error(err.dwWinError )
|
|
{}
|
|
|
|
///
|
|
/// Constructs an exception
|
|
///
|
|
/// \param[in] err EapHost error descriptor
|
|
/// \param[in] msg Error message
|
|
///
|
|
eap_runtime_error(_In_ const EAP_ERROR &err, _In_z_ const char *msg) :
|
|
m_type (err.type ),
|
|
m_reason (err.dwReasonCode ),
|
|
m_root_cause_id (err.rootCauseGuid ),
|
|
m_root_cause_desc(err.pRootCauseString),
|
|
m_repair_id (err.repairGuid ),
|
|
m_repair_desc (err.pRepairString ),
|
|
m_help_link_id (err.helpLinkGuid ),
|
|
win_runtime_error(err.dwWinError, msg )
|
|
{}
|
|
|
|
///
|
|
/// Returns EAP method type
|
|
///
|
|
const EAP_METHOD_TYPE& type() const noexcept
|
|
{
|
|
return m_type;
|
|
}
|
|
|
|
///
|
|
/// Returns the reason code for error
|
|
///
|
|
DWORD reason() const noexcept
|
|
{
|
|
return m_reason;
|
|
}
|
|
|
|
///
|
|
/// Returns root cause ID
|
|
///
|
|
const GUID& root_cause_id() const noexcept
|
|
{
|
|
return m_root_cause_id;
|
|
}
|
|
|
|
///
|
|
/// Returns root cause ID
|
|
///
|
|
const wchar_t* root_cause() const noexcept
|
|
{
|
|
return m_root_cause_desc.c_str();
|
|
}
|
|
|
|
///
|
|
/// Returns repair ID
|
|
///
|
|
const GUID& repair_id() const noexcept
|
|
{
|
|
return m_repair_id;
|
|
}
|
|
|
|
///
|
|
/// Returns root cause ID
|
|
///
|
|
const wchar_t* repair() const noexcept
|
|
{
|
|
return m_repair_desc.c_str();
|
|
}
|
|
|
|
///
|
|
/// Returns help_link ID
|
|
///
|
|
const GUID& help_link_id() const noexcept
|
|
{
|
|
return m_help_link_id;
|
|
}
|
|
|
|
protected:
|
|
EAP_METHOD_TYPE m_type; ///< Structure that identifies the EAP method that raised the error
|
|
|
|
DWORD m_reason; ///< The reason code for the error
|
|
|
|
GUID m_root_cause_id; ///< A unique ID that identifies cause of error in EAPHost
|
|
std::wstring m_root_cause_desc; ///< A localized and readable string that describes the root cause of the error
|
|
|
|
GUID m_repair_id; ///< A unique ID that maps to a localizable string that identifies the repair action that can be taken to fix the reported error
|
|
std::wstring m_repair_desc; ///< A localized and readable string that describes the possible repair action
|
|
|
|
GUID m_help_link_id; ///< A unique ID that maps to a localizable string that specifies an URL for a page that contains additional information about an error or repair message
|
|
};
|
|
|
|
/// @}
|
|
}
|
|
|
|
#pragma warning(pop)
|