diff --git a/EAPMethods/build/EAPTTLS.vcxproj b/EAPMethods/build/EAPTTLS.vcxproj index 9128812..d52c307 100644 --- a/EAPMethods/build/EAPTTLS.vcxproj +++ b/EAPMethods/build/EAPTTLS.vcxproj @@ -81,6 +81,7 @@ + @@ -88,6 +89,7 @@ + diff --git a/EAPMethods/build/EAPTTLS.vcxproj.filters b/EAPMethods/build/EAPTTLS.vcxproj.filters index 4794cf5..ba6bfa7 100644 --- a/EAPMethods/build/EAPTTLS.vcxproj.filters +++ b/EAPMethods/build/EAPTTLS.vcxproj.filters @@ -33,6 +33,9 @@ Header Files + + Header Files + @@ -50,6 +53,9 @@ Source Files + + Source Files + diff --git a/EAPMethods/build/EAPTTLSUI.vcxproj b/EAPMethods/build/EAPTTLSUI.vcxproj index 59f50da..17a1eab 100644 --- a/EAPMethods/build/EAPTTLSUI.vcxproj +++ b/EAPMethods/build/EAPTTLSUI.vcxproj @@ -80,11 +80,13 @@ + + diff --git a/EAPMethods/build/EAPTTLSUI.vcxproj.filters b/EAPMethods/build/EAPTTLSUI.vcxproj.filters index bcacebb..812bb54 100644 --- a/EAPMethods/build/EAPTTLSUI.vcxproj.filters +++ b/EAPMethods/build/EAPTTLSUI.vcxproj.filters @@ -24,6 +24,9 @@ Header Files + + Header Files + @@ -41,6 +44,9 @@ Source Files + + Source Files + diff --git a/EAPMethods/include/EAPMethods.h b/EAPMethods/include/EAPMethods.h index 5b1859b..920aaf5 100644 Binary files a/EAPMethods/include/EAPMethods.h and b/EAPMethods/include/EAPMethods.h differ diff --git a/EAPMethods/include/EAPTLS.h b/EAPMethods/include/EAPTLS.h new file mode 100644 index 0000000..e677d42 --- /dev/null +++ b/EAPMethods/include/EAPTLS.h @@ -0,0 +1,181 @@ +/* + Copyright 2015-2016 Amebis + Copyright 2016 GÉANT + + This file is part of GÉANTLink. + + GÉANTLink is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + GÉANTLink is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GÉANTLink. If not, see . +*/ + +#include "EAPMethods.h" +#include "EAPSerialize.h" + +namespace eap +{ + class config_tls; +} + +namespace eapserial +{ + inline void pack(_Inout_ unsigned char *&cursor, _In_ const eap::config_tls &val); + inline size_t get_pk_size(const eap::config_tls &val); + inline void unpack(_Inout_ const unsigned char *&cursor, _Out_ eap::config_tls &val); +} + +#pragma once + + +namespace eap +{ + /// + /// TLS configuration + /// + class config_tls : public config_method + { + public: + /// + /// Constructs configuration + /// + /// \param[in] mod Reference of the EAP module to use for global services + /// + config_tls(_In_ module &mod); + + /// + /// Copies configuration + /// + /// \param[in] other Configuration to copy from + /// + config_tls(_In_ const config_tls &other); + + /// + /// Moves configuration + /// + /// \param[in] other Configuration to move from + /// + config_tls(_Inout_ config_tls &&other); + + /// + /// Copies configuration + /// + /// \param[in] other Configuration to copy from + /// + /// \returns Reference to this object + /// + config_tls& operator=(_In_ const config_tls &other); + + /// + /// Moves configuration + /// + /// \param[in] other Configuration to move from + /// + /// \returns Reference to this object + /// + config_tls& operator=(_Inout_ config_tls &&other); + + /// \name XML configuration management + /// @{ + + /// + /// Save configuration to XML document + /// + /// \param[in] pDoc XML document + /// \param[in] pConfigRoot Suggested root element for saving configuration + /// \param[out] ppEapError Pointer to error descriptor in case of failure. Free using `module::free_memory()`. + /// + /// \returns + /// - \c ERROR_SUCCESS if succeeded + /// - error code otherwise + /// + virtual DWORD save(_In_ IXMLDOMDocument *pDoc, _In_ IXMLDOMNode *pConfigRoot, _Out_ EAP_ERROR **ppEapError) const; + + /// + /// Load configuration from XML document + /// + /// \param[in] pConfigRoot Root element for loading configuration + /// \param[out] ppEapError Pointer to error descriptor in case of failure. Free using `module::free_memory()`. + /// + /// \returns + /// - \c ERROR_SUCCESS if succeeded + /// - error code otherwise + /// + virtual DWORD load(_In_ IXMLDOMNode *pConfigRoot, _Out_ EAP_ERROR **ppEapError); + + /// @} + + /// + /// Returns EAP method type of this configuration + /// + /// \returns `eap::type_tls` + /// + virtual eap::type_t get_method_id() { return eap::type_tls; } + + /// + /// Adds CA to the list of trusted root CA's + /// + /// \sa [CertCreateCertificateContext function](https://msdn.microsoft.com/en-us/library/windows/desktop/aa376033.aspx) + /// + bool add_trusted_ca(_In_ DWORD dwCertEncodingType, _In_ const BYTE *pbCertEncoded, _In_ DWORD cbCertEncoded); + + public: + std::list m_trusted_root_ca; ///< Trusted root CAs + std::list m_server_names; ///< Acceptable authenticating server names + }; +} + + +namespace eapserial +{ + /// + /// Packs a TLS method configuration + /// + /// \param[inout] cursor Memory cursor + /// \param[in] val Configuration to pack + /// + inline void pack(_Inout_ unsigned char *&cursor, _In_ const eap::config_tls &val) + { + pack(cursor, (const eap::config_method&)val); + pack(cursor, val.m_trusted_root_ca ); + pack(cursor, val.m_server_names ); + } + + + /// + /// Returns packed size of a TLS method configuration + /// + /// \param[in] val Configuration to pack + /// + /// \returns Size of data when packed (in bytes) + /// + inline size_t get_pk_size(const eap::config_tls &val) + { + return + get_pk_size((const eap::config_method&)val) + + get_pk_size(val.m_trusted_root_ca ) + + get_pk_size(val.m_server_names ); + } + + + /// + /// Unpacks a TLS method configuration + /// + /// \param[inout] cursor Memory cursor + /// \param[out] val Configuration to unpack to + /// + inline void unpack(_Inout_ const unsigned char *&cursor, _Out_ eap::config_tls &val) + { + unpack(cursor, (eap::config_method&)val ); + unpack(cursor, val.m_trusted_root_ca); + unpack(cursor, val.m_server_names ); + } +} diff --git a/EAPMethods/include/EAPTTLS.h b/EAPMethods/include/EAPTTLS.h index 599cd01..6a22281 100644 Binary files a/EAPMethods/include/EAPTTLS.h and b/EAPMethods/include/EAPTTLS.h differ diff --git a/EAPMethods/include/StdAfx.h b/EAPMethods/include/StdAfx.h index f41f58d..9a831f9 100644 Binary files a/EAPMethods/include/StdAfx.h and b/EAPMethods/include/StdAfx.h differ diff --git a/EAPMethods/src/EAPMethods.cpp b/EAPMethods/src/EAPMethods.cpp index ba41a35..132b229 100644 Binary files a/EAPMethods/src/EAPMethods.cpp and b/EAPMethods/src/EAPMethods.cpp differ diff --git a/EAPMethods/src/EAPTLS.cpp b/EAPMethods/src/EAPTLS.cpp new file mode 100644 index 0000000..724a59d --- /dev/null +++ b/EAPMethods/src/EAPTLS.cpp @@ -0,0 +1,201 @@ +/* + Copyright 2015-2016 Amebis + Copyright 2016 GÉANT + + This file is part of GÉANTLink. + + GÉANTLink is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + GÉANTLink is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GÉANTLink. If not, see . +*/ + +#include + +using namespace std; +using namespace winstd; + + +////////////////////////////////////////////////////////////////////// +// eap::config_tls +////////////////////////////////////////////////////////////////////// + +eap::config_tls::config_tls(_In_ module &mod) : config_method(mod) +{ +} + + +eap::config_tls::config_tls(_In_ const config_tls &other) : + m_trusted_root_ca(other.m_trusted_root_ca), + m_server_names(other.m_server_names), + config_method(other) +{ +} + + +eap::config_tls::config_tls(_Inout_ config_tls &&other) : + m_trusted_root_ca(std::move(other.m_trusted_root_ca)), + m_server_names(std::move(other.m_server_names)), + config_method(std::move(other)) +{ +} + + +eap::config_tls& eap::config_tls::operator=(_In_ const eap::config_tls &other) +{ + if (this != &other) { + (config_method&)*this = other; + m_trusted_root_ca = other.m_trusted_root_ca; + m_server_names = other.m_server_names; + } + + return *this; +} + + +eap::config_tls& eap::config_tls::operator=(_Inout_ eap::config_tls &&other) +{ + if (this != &other) { + (config_method&&)*this = std::move(other); + m_trusted_root_ca = std::move(other.m_trusted_root_ca); + m_server_names = std::move(other.m_server_names); + } + + return *this; +} + + +DWORD eap::config_tls::save(_In_ IXMLDOMDocument *pDoc, _In_ IXMLDOMNode *pConfigRoot, _Out_ EAP_ERROR **ppEapError) const +{ + const bstr bstrNamespace(L"urn:ietf:params:xml:ns:yang:ietf-eap-metadata"); + DWORD dwResult; + HRESULT hr; + + // + com_obj pXmlElServerSideCredential; + if ((dwResult = eapxml::create_element(pDoc, pConfigRoot, bstr(L"eap-metadata:ServerSideCredential"), bstr(L"ServerSideCredential"), bstrNamespace, &pXmlElServerSideCredential)) != ERROR_SUCCESS) { + *ppEapError = m_module.make_error(dwResult, 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" Error creating element."), NULL); + return dwResult; + } + + for (list::const_iterator i = m_trusted_root_ca.begin(), i_end = m_trusted_root_ca.end(); i != i_end; ++i) { + // + com_obj pXmlElCA; + if ((dwResult = eapxml::create_element(pDoc, bstr(L"CA"), bstrNamespace, &pXmlElCA))) { + *ppEapError = m_module.make_error(dwResult, 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" Error creating element."), NULL); + return dwResult; + } + + // / + if ((dwResult = eapxml::put_element_value(pDoc, pXmlElCA, bstr(L"format"), bstrNamespace, bstr(L"PEM"))) != ERROR_SUCCESS) { + *ppEapError = m_module.make_error(dwResult, 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" Error creating element."), NULL); + return dwResult; + } + + // / + const cert_context &cc = *i; + if ((dwResult = eapxml::put_element_base64(pDoc, pXmlElCA, bstr(L"cert-data"), bstrNamespace, cc->pbCertEncoded, cc->cbCertEncoded)) != ERROR_SUCCESS) { + *ppEapError = m_module.make_error(dwResult, 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" Error creating element."), NULL); + return dwResult; + } + + if (FAILED(hr = pXmlElServerSideCredential->appendChild(pXmlElCA, NULL))) { + *ppEapError = m_module.make_error(dwResult = HRESULT_CODE(hr), 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" Error appending element."), NULL); + return dwResult; + } + } + + // + for (list::const_iterator i = m_server_names.begin(), i_end = m_server_names.end(); i != i_end; ++i) { + wstring str; + MultiByteToWideChar(CP_UTF8, 0, i->c_str(), (int)i->length(), str); + if ((dwResult = eapxml::put_element_value(pDoc, pXmlElServerSideCredential, bstr(L"ServerName"), bstrNamespace, bstr(str))) != ERROR_SUCCESS) { + *ppEapError = m_module.make_error(dwResult, 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" Error creating element."), NULL); + return dwResult; + } + } + + return config_method::save(pDoc, pConfigRoot, ppEapError); +} + + +DWORD eap::config_tls::load(_In_ IXMLDOMNode *pConfigRoot, _Out_ EAP_ERROR **ppEapError) +{ + m_trusted_root_ca.clear(); + m_server_names.clear(); + + // + com_obj pXmlElServerSideCredential; + if (eapxml::select_element(pConfigRoot, bstr(L"eap-metadata:ServerSideCredential"), &pXmlElServerSideCredential) == ERROR_SUCCESS) { + // + com_obj pXmlListCAs; + long lCACount = 0; + if (eapxml::select_nodes(pXmlElServerSideCredential, bstr(L"eap-metadata:CA"), &pXmlListCAs) == ERROR_SUCCESS && SUCCEEDED(pXmlListCAs->get_length(&lCACount))) { + for (long j = 0; j < lCACount; j++) { + // Load CA certificate. + com_obj pXmlElCA; + pXmlListCAs->get_item(j, &pXmlElCA); + bstr bstrFormat; + if (eapxml::get_element_value(pXmlElCA, bstr(L"eap-metadata:format"), &bstrFormat) == ERROR_SUCCESS) { + if (CompareStringEx(LOCALE_NAME_INVARIANT, NORM_IGNORECASE, bstrFormat, bstrFormat.length(), L"PEM", -1, NULL, NULL, 0) == CSTR_EQUAL) { + vector aData; + if (eapxml::get_element_base64(pXmlElCA, bstr(L"eap-metadata:cert-data"), aData) == ERROR_SUCCESS) + add_trusted_ca(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, aData.data(), (DWORD)aData.size()); + } + } + } + } + + // + com_obj pXmlListServerIDs; + long lServerIDCount = 0; + if (eapxml::select_nodes(pXmlElServerSideCredential, bstr(L"eap-metadata:ServerName"), &pXmlListServerIDs) == ERROR_SUCCESS && SUCCEEDED(pXmlListServerIDs->get_length(&lServerIDCount))) { + for (long j = 0; j < lServerIDCount; j++) { + // Load server name (). + com_obj pXmlElServerID; + pXmlListServerIDs->get_item(j, &pXmlElServerID); + bstr bstrServerID; + pXmlElServerID->get_text(&bstrServerID); + + // Server names (FQDNs) are always ASCII. Hopefully. Convert them to UTF-8 anyway for consistent comparison. CP_ANSI varies. + string str; + WideCharToMultiByte(CP_UTF8, 0, bstrServerID, bstrServerID.length(), str, NULL, NULL); + + m_server_names.push_back(str); + } + } + } + + return config_method::load(pConfigRoot, ppEapError); +} + + +bool eap::config_tls::add_trusted_ca(_In_ DWORD dwCertEncodingType, _In_ const BYTE *pbCertEncoded, _In_ DWORD cbCertEncoded) +{ + cert_context cert; + if (!cert.create(dwCertEncodingType, pbCertEncoded, cbCertEncoded)) { + // Invalid or unsupported certificate. + return false; + } + + for (list::const_iterator i = m_trusted_root_ca.cbegin(), i_end = m_trusted_root_ca.cend();; ++i) { + if (i != i_end) { + if (*i == cert) { + // This certificate is already on the list. + return false; + } + } else { + // End of list reached. Append certificate. + m_trusted_root_ca.push_back(std::move(cert)); + return true; + } + } +}