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;
+ }
+ }
+}