Commit bc8c9fdc authored by Oleksandr Andrieiev's avatar Oleksandr Andrieiev Committed by Nathan Heldt-Sheller

[CR2390] Identity spoofing/privelege escalation

For secure connections that use certificates the SubjectUUID
is retrieved from leaf certificate's CN. However, there is
no binding mechanism between Root CA and Device Id that it
can generate certificates for. Root CAs can issue certificates
with arbitrary UUIDs, which can be used to impersonate another
Device.

The fix adds callback to the certificate chain validation
function. This callback collects single-linked list of all
UUIDs associated with the certificate in cred entries.
When leaf certificate is reached, UUID of Device is retrieved
and matched against static list. If no matching UUID is
found, connection should be rejected.

Bug: https://jira.iotivity.org/browse/IOT-3087
Change-Id: I20333c980226dc6a0c257dc36aab1502202993d9
Signed-off-by: Oleksandr Andrieiev's avatarOleksandr Andrieiev <o.andrieiev@samsung.com>
parent 2b88e4e4
......@@ -29,6 +29,7 @@
#include "cacommon.h"
#include "experimental/ocrandom.h"
#include "experimental/byte_array.h"
#ifdef __cplusplus
......@@ -125,6 +126,41 @@ typedef struct
} PkiInfo_t;
#if defined(__WITH_DTLS__) || defined(__WITH_TLS__)
/**
* Node structure for UUID linked list
*/
typedef struct UuidInfo_s
{
char uuid[UUID_STRING_SIZE];
struct UuidInfo_s * next;
} UuidInfo_t;
/**
* Context with UUID linked list to populate
*/
typedef struct
{
UuidInfo_t* list;
} UuidContext_t;
/**
* Populates UUID linked list in the context with all
* UUIDs retrieved from OCF Device /oic/sec/cred/ entries.
* This is done to match allowed UUIDs against presented
* UUID in the leaf certificate during TLS handshake
*/
typedef void (*CAgetIdentityHandler)(UuidContext_t* list, unsigned char* p, size_t len);
/**
* Registers UUID population callback
*/
CAResult_t CAregisterIdentityHandler(CAgetIdentityHandler getIdentityHandler);
/**
* Callback is used by application layer to check peer's certificate CN field.
* If set, this callback will be invoked during handshake after certificate verification.
......
......@@ -34,6 +34,7 @@
#include "caipinterface.h"
#include "cacertprofile.h"
#include "oic_malloc.h"
#include "utlist.h"
#include "experimental/ocrandom.h"
#include "experimental/byte_array.h"
#include "octhread.h"
......@@ -409,6 +410,12 @@ static CAgetCredentialTypesHandler g_getCredentialTypesCallback = NULL;
* @brief callback to get X.509-based Public Key Infrastructure
*/
static CAgetPkixInfoHandler g_getPkixInfoCallback = NULL;
/**
* @var g_getIdentityCallback
*
* @brief callback to retrieve acceptable UUID list
*/
static CAgetIdentityHandler g_getIdentityCallback = NULL;
/**
* @var g_dtlsContextMutex
......@@ -470,6 +477,13 @@ void CAsetPkixInfoCallback(CAgetPkixInfoHandler infoCallback)
OIC_LOG_V(DEBUG, NET_SSL_TAG, "Out %s", __func__);
}
void CAsetIdentityCallback(CAgetIdentityHandler identityCallback)
{
OIC_LOG_V(DEBUG, NET_SSL_TAG, "In %s", __func__);
g_getIdentityCallback = identityCallback;
OIC_LOG_V(DEBUG, NET_SSL_TAG, "Out %s", __func__);
}
void CAsetCredentialTypesCallback(CAgetCredentialTypesHandler credTypesCallback)
{
OIC_LOG_V(DEBUG, NET_SSL_TAG, "In %s", __func__);
......@@ -1367,6 +1381,67 @@ void CAcloseSslConnectionAll(CATransportAdapter_t transportType)
OIC_LOG_V(DEBUG, NET_SSL_TAG, "Out %s", __func__);
return;
}
const char UUID_WILDCARD[UUID_STRING_SIZE] = "2a000000-0000-0000-0000-000000000000"; // conversion result for '*' to UUID, possible collision with real UUID
static int verifyIdentity( void *data, mbedtls_x509_crt *crt, int depth, uint32_t *flags ) {
OC_UNUSED(data); // no need to pass extra data
OC_UNUSED(flags); // we do not remove any flags
static UuidContext_t ctx = { NULL };
g_getIdentityCallback(&ctx, crt->raw.p, crt->raw.len);
if (0 == depth) // leaf certificate
{
const mbedtls_x509_name * name = NULL;
/* Find the CN component of the subject name. */
for (name = &crt->subject; NULL != name; name = name->next)
{
if (name->oid.p &&
(name->oid.len <= MBEDTLS_OID_SIZE(MBEDTLS_OID_AT_CN)) &&
(0 == memcmp(MBEDTLS_OID_AT_CN, name->oid.p, name->oid.len)))
{
break;
}
}
if (NULL == name)
{
OIC_LOG(ERROR, NET_SSL_TAG, "Could not retrieve identity information from leaf certificate");
return -1;
}
const size_t uuidBufLen = UUID_STRING_SIZE - 1;
const unsigned char * uuidPos = (const unsigned char*)memmem(name->val.p, name->val.len, UUID_PREFIX, sizeof(UUID_PREFIX) - 1);
/* If UUID_PREFIX is present, ensure there's enough data for the prefix plus an entire
* UUID, to make sure we don't read past the end of the buffer.
*/
char uuid[UUID_STRING_SIZE] = { 0 };
if ((NULL != uuidPos) && (name->val.len >= ((uuidPos - name->val.p) + (sizeof(UUID_PREFIX) - 1) + uuidBufLen)))
{
memcpy(uuid, uuidPos + sizeof(UUID_PREFIX) - 1, uuidBufLen);
}
else
{
OIC_LOG(ERROR, NET_SSL_TAG, "Could not retrieve UUID from leaf certificate");
return -1;
}
bool isMatched = false;
UuidInfo_t* node = NULL;
UuidInfo_t* tmpNode = NULL;
LL_FOREACH(ctx.list, node)
{
isMatched = isMatched || (0 == memcmp(node->uuid, uuid, UUID_STRING_SIZE));
isMatched = isMatched || (0 == memcmp(node->uuid, UUID_WILDCARD, UUID_STRING_SIZE));
}
LL_FOREACH_SAFE(ctx.list, node, tmpNode)
{
free(node);
}
ctx.list = NULL;
return isMatched ? 0 : -1;
}
return 0;
}
/**
* Creates session for endpoint.
*
......@@ -1393,6 +1468,7 @@ static SslEndPoint_t * NewSslEndPoint(const CAEndpoint_t * endpoint, mbedtls_ssl
tep->sep.endpoint = *endpoint;
tep->sep.endpoint.flags = (CATransportFlags_t)(tep->sep.endpoint.flags | CA_SECURE);
mbedtls_ssl_conf_verify(config, verifyIdentity, NULL);
if(0 != mbedtls_ssl_setup(&tep->ssl, config))
{
OIC_LOG(ERROR, NET_SSL_TAG, "Setup failed");
......
......@@ -52,6 +52,7 @@ static bool g_isInitialized = false;
extern void CAsetPkixInfoCallback(CAgetPkixInfoHandler infCallback);
extern void CAsetPskCredentialsCallback(CAgetPskCredentialsHandler credCallback);
extern void CAsetCredentialTypesCallback(CAgetCredentialTypesHandler credCallback);
extern void CAsetIdentityCallback(CAgetIdentityHandler credCallback);
#endif // __WITH_DTLS__ or __WITH_TLS__
......@@ -240,6 +241,19 @@ CAResult_t CAregisterGetCredentialTypesHandler(CAgetCredentialTypesHandler getCr
OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
return CA_STATUS_OK;
}
CAResult_t CAregisterIdentityHandler(CAgetIdentityHandler getIdentityHandler)
{
OIC_LOG_V(DEBUG, TAG, "In %s", __func__);
if (!g_isInitialized)
{
return CA_STATUS_NOT_INITIALIZED;
}
CAsetIdentityCallback(getIdentityHandler);
OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
return CA_STATUS_OK;
}
#endif // __WITH_DTLS__ or __WITH_TLS__
CAResult_t CACreateEndpoint(CATransportFlags_t flags,
......
......@@ -209,6 +209,17 @@ int32_t GetDtlsPskCredentials( CADtlsPskCredType_t type,
const unsigned char *desc, size_t desc_len,
unsigned char *result, size_t result_length);
/*
* This internal callback is used to retrieve UUIDs from CRED
* entries that have matching publicData.
*
* @param ctx context holding UUID list
* @param p pointer to the DER-encoded certificate
* @param len length of the DER-encoded certificate
*/
void GetIdentityHandler(UuidContext_t* ctx, unsigned char* p, size_t len);
/**
* Add temporal PSK to PIN based OxM.
*
......
......@@ -3293,6 +3293,85 @@ static int ConvertDerCertToPem(const uint8_t* der, size_t derLen, uint8_t** pem)
return 0;
}
void GetIdentityHandler(UuidContext_t* ctx, unsigned char* crt, size_t crtLen)
{
UuidInfo_t* cur = ctx->list;
if (NULL != ctx->list)
{
while (NULL != cur->next)
{
cur = cur->next;
}
}
OicSecCred_t *cred = NULL;
LL_FOREACH(gCred, cred)
{
if (SIGNED_ASYMMETRIC_KEY != cred->credType)
{
continue;
}
if (0 == strcmp(cred->credUsage, TRUST_CA) && 0 == strcmp(cred->credUsage, MF_TRUST_CA))
{
continue;
}
uint8_t *der = NULL;
size_t derLen = 0;
if ((OIC_ENCODING_BASE64 == cred->publicData.encoding) ||
(OIC_ENCODING_PEM == cred->publicData.encoding))
{
int ret = ConvertPemCertToDer((const char*)cred->publicData.data, cred->publicData.len, &der, &derLen);
if (0 > ret)
{
OIC_LOG_V(ERROR, TAG, "%s: Failed converting PEM cert to DER: %d", __func__, ret);
continue;
}
}
else
{
der = cred->publicData.data;
derLen = cred->publicData.len;
}
if (derLen != crtLen)
{
continue;
}
if (0 != memcmp(der, crt, crtLen))
{
continue;
}
UuidInfo_t* node = (UuidInfo_t*) malloc(sizeof(UuidInfo_t));
if (NULL == node)
{
OIC_LOG_V(ERROR, TAG, "%s: Could not allocate new UUID node", __func__);
continue;
}
node->next = NULL;
if (OCConvertUuidToString(cred->subject.id, node->uuid))
{
if (NULL == ctx->list)
{
ctx->list = node;
}
else
{
cur->next = node;
}
cur = node;
}
else
{
OIC_LOG_V(ERROR, TAG, "%s: Failed to convert subjectuuid to string", __func__);
free(node);
}
}
}
#ifndef NDEBUG
void LogCert(uint8_t *data, size_t len, OicEncodingType_t encoding, const char* tag)
......
......@@ -472,6 +472,7 @@ OCStackResult SRMInitSecureResources(void)
InitSecureResources();
OCStackResult ret = OC_STACK_OK;
#if defined(__WITH_DTLS__) || defined(__WITH_TLS__)
CAregisterIdentityHandler(GetIdentityHandler);
if (CA_STATUS_OK != CAregisterPskCredentialsHandler(GetDtlsPskCredentials))
{
OIC_LOG(ERROR, TAG, "Failed to revert TLS credential handler.");
......
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