(Pre-shared) client certificates are no longer maintained by hash only

This commit is contained in:
2016-06-16 00:29:56 +02:00
parent cda81dd696
commit d430b63829
5 changed files with 164 additions and 133 deletions

View File

@@ -34,14 +34,14 @@ eap::credentials_tls::credentials_tls(_In_ module &mod) : credentials(mod)
eap::credentials_tls::credentials_tls(_In_ const credentials_tls &other) :
m_cert_hash(other.m_cert_hash),
m_cert(other.m_cert),
credentials(other)
{
}
eap::credentials_tls::credentials_tls(_Inout_ credentials_tls &&other) :
m_cert_hash(std::move(other.m_cert_hash)),
m_cert(std::move(other.m_cert)),
credentials(std::move(other))
{
}
@@ -51,7 +51,7 @@ eap::credentials_tls& eap::credentials_tls::operator=(_In_ const credentials_tls
{
if (this != &other) {
(credentials&)*this = other;
m_cert_hash = other.m_cert_hash;
m_cert = other.m_cert;
}
return *this;
@@ -62,7 +62,7 @@ eap::credentials_tls& eap::credentials_tls::operator=(_Inout_ credentials_tls &&
{
if (this != &other) {
(credentials&)*this = std::move(other);
m_cert_hash = std::move(other.m_cert_hash);
m_cert = std::move(other.m_cert);
}
return *this;
@@ -78,13 +78,13 @@ eap::config* eap::credentials_tls::clone() const
void eap::credentials_tls::clear()
{
credentials::clear();
m_cert_hash.clear();
m_cert.free();
}
bool eap::credentials_tls::empty() const
{
return credentials::empty() && m_cert_hash.empty();
return credentials::empty() && !m_cert;
}
@@ -92,13 +92,35 @@ bool eap::credentials_tls::save(_In_ IXMLDOMDocument *pDoc, _In_ IXMLDOMNode *pC
{
const bstr bstrNamespace(L"urn:ietf:params:xml:ns:yang:ietf-eap-metadata");
DWORD dwResult;
HRESULT hr;
if (!credentials::save(pDoc, pConfigRoot, ppEapError))
// Don't save m_identity. We rebuild it on every load.
//if (!credentials::save(pDoc, pConfigRoot, ppEapError))
// return false;
// <ClientCertificate>
com_obj<IXMLDOMElement> pXmlElClientCertificate;
if ((dwResult = eapxml::create_element(pDoc, bstr(L"ClientCertificate"), bstrNamespace, &pXmlElClientCertificate))) {
*ppEapError = m_module.make_error(dwResult, 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" Error creating <ClientCertificate> element."), NULL);
return false;
}
// <CertHash>
if ((dwResult = eapxml::put_element_hex(pDoc, pConfigRoot, bstr(L"CertHash"), bstrNamespace, m_cert_hash.data(), m_cert_hash.size())) != ERROR_SUCCESS) {
*ppEapError = m_module.make_error(dwResult, 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" Error creating <CertHash> element."), NULL);
if (m_cert) {
// <ClientCertificate>/<format>
if ((dwResult = eapxml::put_element_value(pDoc, pXmlElClientCertificate, bstr(L"format"), bstrNamespace, bstr(L"PEM"))) != ERROR_SUCCESS) {
*ppEapError = m_module.make_error(dwResult, 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" Error creating <format> element."), NULL);
return false;
}
// <ClientCertificate>/<cert-data>
if ((dwResult = eapxml::put_element_base64(pDoc, pXmlElClientCertificate, bstr(L"cert-data"), bstrNamespace, m_cert->pbCertEncoded, m_cert->cbCertEncoded)) != ERROR_SUCCESS) {
*ppEapError = m_module.make_error(dwResult, 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" Error creating <cert-data> element."), NULL);
return false;
}
}
if (FAILED(hr = pConfigRoot->appendChild(pXmlElClientCertificate, NULL))) {
*ppEapError = m_module.make_error(HRESULT_CODE(hr), 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" Error appending <ClientCertificate> element."), NULL);
return false;
}
@@ -111,15 +133,35 @@ bool eap::credentials_tls::load(_In_ IXMLDOMNode *pConfigRoot, _Out_ EAP_ERROR *
assert(pConfigRoot);
DWORD dwResult;
if (!credentials::load(pConfigRoot, ppEapError))
return false;
// Don't load m_identity. We rebuild it on load.
//if (!credentials::load(pConfigRoot, ppEapError))
// return false;
// <CertHash>
if ((dwResult = eapxml::get_element_hex(pConfigRoot, bstr(L"eap-metadata:CertHash"), m_cert_hash)) != ERROR_SUCCESS) {
*ppEapError = m_module.make_error(dwResult, 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" Error reading <CertHash> element."), NULL);
m_identity.clear();
m_cert.free();
// <ClientCertificate>
com_obj<IXMLDOMElement> pXmlElClientCertificate;
if ((dwResult = eapxml::select_element(pConfigRoot, bstr(L"eap-metadata:ClientCertificate"), &pXmlElClientCertificate)) != ERROR_SUCCESS) {
*ppEapError = m_module.make_error(dwResult, 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" Error reading <ClientCertificate> element."), NULL);
return false;
}
// <ClientCertificate>/<format>
bstr bstrFormat;
if ((dwResult = eapxml::get_element_value(pXmlElClientCertificate, 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) {
// <ClientCertificate>/<cert-data>
vector<unsigned char> aData;
if ((dwResult = eapxml::get_element_base64(pXmlElClientCertificate, bstr(L"eap-metadata:cert-data"), aData)) == ERROR_SUCCESS) {
if (m_cert.create(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, aData.data(), (DWORD)aData.size())) {
// Generate identity. TODO: Find which CERT_NAME_... constant returns valid identity (username@domain or DOMAIN\Username).
CertGetNameString(m_cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, m_identity);
}
}
}
}
return true;
}
@@ -128,11 +170,36 @@ bool eap::credentials_tls::store(_In_ LPCTSTR pszTargetName, _Out_ EAP_ERROR **p
{
assert(pszTargetName);
assert(ppEapError);
string cert_enc;
// Prepare cryptographics provider.
crypt_prov cp;
if (!cp.create(NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {
*ppEapError = m_module.make_error(GetLastError(), 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" CryptAcquireContext failed."), NULL);
return false;
}
// Encrypt certificate.
vector<unsigned char> cert;
if (!m_module.encrypt_md5(cp, m_cert->pbCertEncoded, m_cert->cbCertEncoded, cert, ppEapError))
return false;
// Convert encrypted certificate to Base64, since CredProtectA() fail for binary strings.
string cert_base64;
base64_enc enc;
enc.encode(cert_base64, cert.data(), cert.size());
// Encrypt the certificate using user's key.
CRED_PROTECTION_TYPE cpt;
if (!CredProtectA(TRUE, cert_base64.c_str(), (DWORD)cert_base64.length(), cert_enc, &cpt)) {
*ppEapError = m_module.make_error(GetLastError(), 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" CredProtect failed."), NULL);
return false;
}
tstring target(target_name(pszTargetName));
// Write credentials.
assert(m_cert_hash.size() < CRED_MAX_CREDENTIAL_BLOB_SIZE);
assert(cert_enc.size() < CRED_MAX_CREDENTIAL_BLOB_SIZE);
assert(m_identity.length() < CRED_MAX_USERNAME_LENGTH );
CREDENTIAL cred = {
0, // Flags
@@ -140,8 +207,8 @@ bool eap::credentials_tls::store(_In_ LPCTSTR pszTargetName, _Out_ EAP_ERROR **p
(LPTSTR)target.c_str(), // TargetName
_T(""), // Comment
{ 0, 0 }, // LastWritten
(DWORD)m_cert_hash.size(), // CredentialBlobSize
(LPBYTE)m_cert_hash.data(), // CredentialBlob
(DWORD)cert_enc.size(), // CredentialBlobSize
(LPBYTE)cert_enc.data(), // CredentialBlob
CRED_PERSIST_ENTERPRISE, // Persist
0, // AttributeCount
NULL, // Attributes
@@ -159,7 +226,7 @@ bool eap::credentials_tls::store(_In_ LPCTSTR pszTargetName, _Out_ EAP_ERROR **p
bool eap::credentials_tls::retrieve(_In_ LPCTSTR pszTargetName, _Out_ EAP_ERROR **ppEapError)
{
assert(pszTargetName && _tcslen(pszTargetName) < CRED_MAX_GENERIC_TARGET_NAME_LENGTH);
assert(pszTargetName);
// Read credentials.
unique_ptr<CREDENTIAL, CredFree_delete<CREDENTIAL> > cred;
@@ -168,12 +235,38 @@ bool eap::credentials_tls::retrieve(_In_ LPCTSTR pszTargetName, _Out_ EAP_ERROR
return false;
}
if (cred->UserName)
m_identity = cred->UserName;
else
m_identity.clear();
// Decrypt the certificate using user's key.
string cert_base64;
if (!CredUnprotectA(TRUE, (LPCSTR)(cred->CredentialBlob), cred->CredentialBlobSize, cert_base64)) {
*ppEapError = m_module.make_error(GetLastError(), 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" CredUnprotect failed."), NULL);
return false;
}
m_cert_hash.assign(cred->CredentialBlob, cred->CredentialBlob + cred->CredentialBlobSize);
// Convert Base64 to binary encrypted certificate, since CredProtectA() fail for binary strings.
vector<unsigned char> cert;
base64_dec dec;
bool is_last;
dec.decode(cert, is_last, cert_base64.c_str(), cert_base64.length());
// Prepare cryptographics provider.
crypt_prov cp;
if (!cp.create(NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {
*ppEapError = m_module.make_error(GetLastError(), 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" CryptAcquireContext failed."), NULL);
return false;
}
// Decrypt certificate.
vector<unsigned char, sanitizing_allocator<unsigned char> > _cert;
if (!m_module.decrypt_md5(cp, cert.data(), cert.size(), _cert, ppEapError))
return false;
if (!m_cert.create(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, _cert.data(), (DWORD)_cert.size())) {
*ppEapError = m_module.make_error(GetLastError(), 0, NULL, NULL, NULL, _T(__FUNCTION__) _T(" Error loading certificate."), NULL);
return false;
}
// Generate identity. TODO: Find which CERT_NAME_... constant returns valid identity (username@domain or DOMAIN\Username).
CertGetNameString(m_cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, m_identity);
return true;
}