Commit 6733dabb authored by Kevin Kane's avatar Kevin Kane Committed by Greg Zaverucha

[IOT-1525] Keep credential resource encrypted on-disk on Windows

The Windows platform provides a way to encrypt and decrypt data
with system-managed keys. Since the cred resource contains
sensitive symmetric and private keys, keep this resource encrypted
on disk.

Change-Id: I88f3cab76f2e782ce778bc64830491822b7d9842
Signed-off-by: default avatarKevin Kane <kkane@microsoft.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/15033Reviewed-by: default avatarAlex Kelley <alexke@microsoft.com>
Tested-by: default avatarjenkins-iotivity <jenkins-iotivity@opendaylight.org>
Reviewed-by: default avatarMike Fenelon <mike.fenelon@microsoft.com>
Reviewed-by: Randeep's avatarRandeep Singh <randeep.s@samsung.com>
Reviewed-by: default avatarDan Mihai <Daniel.Mihai@microsoft.com>
Reviewed-by: default avatarGreg Zaverucha <gregz@microsoft.com>
parent e3c156d2
......@@ -119,7 +119,7 @@ if target_os in ['windows', 'msys_nt']:
else:
liboctbstack_env.Textfile(target = 'octbstack.def', source = [File('octbstack_product.def')])
liboctbstack_env.AppendUnique(LIBS = ['ws2_32', 'advapi32', 'iphlpapi', 'bcrypt'])
liboctbstack_env.AppendUnique(LIBS = ['ws2_32', 'advapi32', 'iphlpapi', 'bcrypt', 'crypt32'])
else:
liboctbstack_env.AppendUnique(LIBS = ['m'])
......
......@@ -84,6 +84,7 @@ if target_os in ['msys_nt', 'windows']:
sptest_env.AppendUnique(LIBS = ['ws2_32',
'advapi32',
'bcrypt',
'crypt32',
'octbstack_static',
'iphlpapi'])
else:
......
......@@ -66,6 +66,11 @@
#define TAG "OIC_SRM_CREDL"
#ifdef HAVE_WINDOWS_H
#include <wincrypt.h>
#endif
/** Max credential types number used for TLS */
#define MAX_TYPE 2
/** Default cbor payload size. This value is increased in case of CborErrorOutOfMemory.
......@@ -735,6 +740,11 @@ OCStackResult CBORPayloadToCred(const uint8_t *cborPayload, size_t size,
CborError cborFindResult = CborNoError;
cbor_parser_init(cborPayload, size, 0, &parser, &credCbor);
if (!cbor_value_is_container(&credCbor))
{
return OC_STACK_ERROR;
}
OicSecCred_t *headCred = (OicSecCred_t *) OICCalloc(1, sizeof(OicSecCred_t));
// Enter CRED Root Map
......@@ -1266,6 +1276,43 @@ exit:
return cred;
}
#ifdef HAVE_WINDOWS_H
/* Helper for UpdatePersistentStorage. */
static OCStackResult CopyPayload(uint8_t **payload, size_t *payloadSize, const DATA_BLOB *source)
{
OCStackResult res = OC_STACK_OK;
if (source->cbData > *payloadSize)
{
/* Need more memory to copy encrypted payload out. */
OICClearMemory(*payload, *payloadSize);
OICFree(*payload);
*payload = OICMalloc(source->cbData);
if (NULL == *payload)
{
OIC_LOG_V(ERROR, TAG, "Failed to OICMalloc for encrypted payload: %u", GetLastError());
res = OC_STACK_NO_MEMORY;
}
}
else if (source->cbData < *payloadSize)
{
/* Zero portion of payload we won't overwrite with the encrypted version. The
* later call to OICClearMemory won't cover this part of the buffer.
*/
OICClearMemory(*payload + source->cbData, *payloadSize - source->cbData);
}
if (OC_STACK_OK == res)
{
memcpy(*payload, source->pbData, source->cbData);
*payloadSize = source->cbData;
}
return res;
}
#endif
static bool UpdatePersistentStorage(const OicSecCred_t *cred)
{
bool ret = false;
......@@ -1282,15 +1329,53 @@ static bool UpdatePersistentStorage(const OicSecCred_t *cred)
int secureFlag = 0;
OCStackResult res = CredToCBORPayload(cred, &payload, &size, secureFlag);
#ifdef HAVE_WINDOWS_H
/* On Windows, keep the credential resource encrypted on disk to protect symmetric and private keys. Only the
* current user on this system will be able to decrypt it later, to help prevent credential theft.
*/
if ((OC_STACK_OK == res) && payload)
{
DATA_BLOB decryptedPayload = { .cbData = size, .pbData = payload };
DATA_BLOB encryptedPayload = { .cbData = 0, .pbData = NULL };
if (CryptProtectData(
&decryptedPayload,
NULL,
NULL,
NULL,
NULL,
CRYPTPROTECT_UI_FORBIDDEN,
&encryptedPayload))
{
res = CopyPayload(&payload, &size, &encryptedPayload);
ret = (OC_STACK_OK == res);
/* For the returned data from CryptProtectData, LocalFree must be used to free. Don't use OICFree. */
if (NULL != LocalFree(encryptedPayload.pbData))
{
OIC_LOG_V(ERROR, TAG, "LocalFree failed on output from CryptProtectData; memory may be corrupted or leaked. Last error: %u.", GetLastError());
assert(!"LocalFree failed");
}
}
else
{
OIC_LOG_V(ERROR, TAG, "Failed to CryptProtectData cred resource: %u", GetLastError());
res = OC_STACK_ERROR;
ret = false;
}
}
#endif
if ((OC_STACK_OK == res) && payload)
{
if (OC_STACK_OK == UpdateSecureResourceInPS(OIC_JSON_CRED_NAME, payload, size))
{
ret = true;
}
OICClearMemory(payload, size);
OICFree(payload);
}
OICClearMemory(payload, size);
OICFree(payload);
}
else //Empty cred list
{
......@@ -1299,6 +1384,7 @@ static bool UpdatePersistentStorage(const OicSecCred_t *cred)
ret = true;
}
}
OIC_LOG(DEBUG, TAG, "OUT Cred UpdatePersistentStorage");
return ret;
}
......@@ -2212,10 +2298,46 @@ OCStackResult InitCredResource()
{
OIC_LOG (DEBUG, TAG, "ReadSVDataFromPS failed");
}
if (data)
if ((ret == OC_STACK_OK) && data)
{
// Read ACL resource from PS
ret = CBORPayloadToCred(data, size, &gCred);
#ifdef HAVE_WINDOWS_H
/* On Windows, if the credential payload isn't cleartext CBOR, it is encrypted. Decrypt and retry. */
if (ret != OC_STACK_OK)
{
DATA_BLOB encryptedPayload = { .cbData = size, .pbData = data };
DATA_BLOB decryptedPayload = { .cbData = 0, .pbData = NULL };
if (CryptUnprotectData(
&encryptedPayload,
NULL,
NULL,
NULL,
NULL,
CRYPTPROTECT_UI_FORBIDDEN,
&decryptedPayload))
{
ret = CBORPayloadToCred(decryptedPayload.pbData, decryptedPayload.cbData, &gCred);
/* For the returned data from CryptUnprotectData, LocalFree must be used to free. Don't use OICFree. */
OICClearMemory(decryptedPayload.pbData, decryptedPayload.cbData);
if (NULL != LocalFree(decryptedPayload.pbData))
{
OIC_LOG_V(ERROR, TAG, "LocalFree failed on output from CryptUnprotectData; memory may be corrupted or leaked. Last error: %u.", GetLastError());
assert(!"LocalFree failed");
}
}
else
{
/* Credential resource is corrupted, or we no longer have access to the encryption key to decrypt it. */
OIC_LOG_V(ERROR, TAG, "Failed to CryptUnprotectData cred resource: %u", GetLastError());
ret = OC_STACK_ERROR;
}
}
#endif
}
/*
......@@ -2610,9 +2732,6 @@ exit:
OCStackResult SetCredRownerId(const OicUuid_t* newROwner)
{
OCStackResult ret = OC_STACK_ERROR;
uint8_t *cborPayload = NULL;
size_t size = 0;
int secureFlag = 0;
OicUuid_t prevId = {.id={0}};
if(NULL == newROwner)
......@@ -2624,27 +2743,19 @@ OCStackResult SetCredRownerId(const OicUuid_t* newROwner)
ret = OC_STACK_NO_RESOURCE;
}
if(newROwner && gCred)
if (newROwner && gCred)
{
memcpy(prevId.id, gCred->rownerID.id, sizeof(prevId.id));
memcpy(gCred->rownerID.id, newROwner->id, sizeof(newROwner->id));
// This added '256' is arbitrary value that is added to cover the name of the resource, map addition and ending
size = GetCredKeyDataSize(gCred);
size += (256 * OicSecCredCount(gCred));
ret = CredToCBORPayload(gCred, &cborPayload, &size, secureFlag);
VERIFY_SUCCESS(TAG, OC_STACK_OK == ret, ERROR);
ret = UpdateSecureResourceInPS(OIC_JSON_CRED_NAME, cborPayload, size);
VERIFY_SUCCESS(TAG, OC_STACK_OK == ret, ERROR);
VERIFY_SUCCESS(TAG, UpdatePersistentStorage(gCred), ERROR);
OICFree(cborPayload);
ret = OC_STACK_OK;
}
return ret;
exit:
OICFree(cborPayload);
memcpy(gCred->rownerID.id, prevId.id, sizeof(prevId.id));
return ret;
}
......
......@@ -51,7 +51,7 @@ tools_env.AppendUnique(RPATH = [tools_env.get('BUILD_DIR')])
if target_os in ['msys_nt', 'windows']:
# octbstack.dll doesn't export all the functions called by this app, so use static LIBs instead.
tools_env.AppendUnique(LIBS = ['ws2_32', 'bcrypt', 'iphlpapi',
tools_env.AppendUnique(LIBS = ['ws2_32', 'bcrypt', 'iphlpapi', 'crypt32',
'coap', 'tinydtls', 'mbedtls', 'mbedx509', 'mbedcrypto',
'octbstack_static', 'ocsrm', 'connectivity_abstraction'])
elif target_os in ['darwin']:
......
......@@ -73,7 +73,7 @@ if srmtest_env.get('MULTIPLE_OWNER') == '1':
if target_os == 'windows':
srmtest_env.AppendUnique(LINKFLAGS = ['/subsystem:CONSOLE'])
srmtest_env.AppendUnique(LIBS = ['advapi32', 'bcrypt', 'kernel32', 'ws2_32', 'iphlpapi', 'octbstack_static'])
srmtest_env.AppendUnique(LIBS = ['advapi32', 'bcrypt', 'crypt32', 'kernel32', 'ws2_32', 'iphlpapi', 'octbstack_static'])
else:
# TODO: Implement feature check.
srmtest_env.AppendUnique(CPPDEFINES = ['HAVE_LOCALTIME_R'])
......
......@@ -59,7 +59,7 @@ if stacktest_env.get('LOGGING'):
stacktest_env.AppendUnique(CPPDEFINES = ['TB_LOG'])
if target_os in ['msys_nt', 'windows']:
stacktest_env.AppendUnique(LIBS = ['ws2_32', 'iphlpapi', 'kernel32', 'bcrypt', 'advapi32'])
stacktest_env.AppendUnique(LIBS = ['ws2_32', 'iphlpapi', 'kernel32', 'bcrypt', 'advapi32', 'crypt32'])
else:
stacktest_env.PrependUnique(LIBS = ['m'])
......
......@@ -47,7 +47,7 @@ unittests_env.PrependUnique(CPPPATH = [
])
if target_os in ['windows']:
unittests_env.AppendUnique(LIBS = ['ws2_32', 'advapi32', 'iphlpapi', 'bcrypt'])
unittests_env.AppendUnique(LIBS = ['ws2_32', 'advapi32', 'iphlpapi', 'bcrypt', 'crypt32'])
unittests_env.AppendUnique(CPPPATH = ['#extlibs/boost/boost'])
if unittests_env.get('SECURED') == '1':
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment