Commit 8e30527a 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: Ic766fa2256d548c99ed4a5dd76f6f3c53b5250a9
Signed-off-by: Oleksandr Andrieiev's avatarOleksandr Andrieiev <o.andrieiev@samsung.com>
parent d23d1287
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "cacommon.h" #include "cacommon.h"
#include "experimental/ocrandom.h"
#include "experimental/byte_array.h" #include "experimental/byte_array.h"
#ifdef __cplusplus #ifdef __cplusplus
...@@ -125,6 +126,41 @@ typedef struct ...@@ -125,6 +126,41 @@ typedef struct
} PkiInfo_t; } PkiInfo_t;
#if defined(__WITH_DTLS__) || defined(__WITH_TLS__) #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. * 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. * If set, this callback will be invoked during handshake after certificate verification.
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include "cacommon.h" #include "cacommon.h"
#include "caipinterface.h" #include "caipinterface.h"
#include "oic_malloc.h" #include "oic_malloc.h"
#include "utlist.h"
#include "experimental/ocrandom.h" #include "experimental/ocrandom.h"
#include "experimental/byte_array.h" #include "experimental/byte_array.h"
#include "octhread.h" #include "octhread.h"
...@@ -406,6 +407,12 @@ static CAgetCredentialTypesHandler g_getCredentialTypesCallback = NULL; ...@@ -406,6 +407,12 @@ static CAgetCredentialTypesHandler g_getCredentialTypesCallback = NULL;
* @brief callback to get X.509-based Public Key Infrastructure * @brief callback to get X.509-based Public Key Infrastructure
*/ */
static CAgetPkixInfoHandler g_getPkixInfoCallback = NULL; static CAgetPkixInfoHandler g_getPkixInfoCallback = NULL;
/**
* @var g_getIdentityCallback
*
* @brief callback to retrieve acceptable UUID list
*/
static CAgetIdentityHandler g_getIdentityCallback = NULL;
/** /**
* @var g_dtlsContextMutex * @var g_dtlsContextMutex
...@@ -467,6 +474,13 @@ void CAsetPkixInfoCallback(CAgetPkixInfoHandler infoCallback) ...@@ -467,6 +474,13 @@ void CAsetPkixInfoCallback(CAgetPkixInfoHandler infoCallback)
OIC_LOG_V(DEBUG, NET_SSL_TAG, "Out %s", __func__); 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) void CAsetCredentialTypesCallback(CAgetCredentialTypesHandler credTypesCallback)
{ {
OIC_LOG_V(DEBUG, NET_SSL_TAG, "In %s", __func__); OIC_LOG_V(DEBUG, NET_SSL_TAG, "In %s", __func__);
...@@ -1322,6 +1336,67 @@ void CAcloseSslConnectionAll(CATransportAdapter_t transportType) ...@@ -1322,6 +1336,67 @@ void CAcloseSslConnectionAll(CATransportAdapter_t transportType)
OIC_LOG_V(DEBUG, NET_SSL_TAG, "Out %s", __func__); OIC_LOG_V(DEBUG, NET_SSL_TAG, "Out %s", __func__);
return; 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. * Creates session for endpoint.
* *
...@@ -1348,6 +1423,10 @@ static SslEndPoint_t * NewSslEndPoint(const CAEndpoint_t * endpoint, mbedtls_ssl ...@@ -1348,6 +1423,10 @@ static SslEndPoint_t * NewSslEndPoint(const CAEndpoint_t * endpoint, mbedtls_ssl
tep->sep.endpoint = *endpoint; tep->sep.endpoint = *endpoint;
tep->sep.endpoint.flags = (CATransportFlags_t)(tep->sep.endpoint.flags | CA_SECURE); tep->sep.endpoint.flags = (CATransportFlags_t)(tep->sep.endpoint.flags | CA_SECURE);
if (g_getIdentityCallback != NULL)
{
mbedtls_ssl_conf_verify(config, verifyIdentity, NULL);
}
if(0 != mbedtls_ssl_setup(&tep->ssl, config)) if(0 != mbedtls_ssl_setup(&tep->ssl, config))
{ {
OIC_LOG(ERROR, NET_SSL_TAG, "Setup failed"); OIC_LOG(ERROR, NET_SSL_TAG, "Setup failed");
......
...@@ -52,6 +52,7 @@ static bool g_isInitialized = false; ...@@ -52,6 +52,7 @@ static bool g_isInitialized = false;
extern void CAsetPkixInfoCallback(CAgetPkixInfoHandler infCallback); extern void CAsetPkixInfoCallback(CAgetPkixInfoHandler infCallback);
extern void CAsetPskCredentialsCallback(CAgetPskCredentialsHandler credCallback); extern void CAsetPskCredentialsCallback(CAgetPskCredentialsHandler credCallback);
extern void CAsetCredentialTypesCallback(CAgetCredentialTypesHandler credCallback); extern void CAsetCredentialTypesCallback(CAgetCredentialTypesHandler credCallback);
extern void CAsetIdentityCallback(CAgetIdentityHandler credCallback);
#endif // __WITH_DTLS__ or __WITH_TLS__ #endif // __WITH_DTLS__ or __WITH_TLS__
...@@ -240,6 +241,19 @@ CAResult_t CAregisterGetCredentialTypesHandler(CAgetCredentialTypesHandler getCr ...@@ -240,6 +241,19 @@ CAResult_t CAregisterGetCredentialTypesHandler(CAgetCredentialTypesHandler getCr
OIC_LOG_V(DEBUG, TAG, "Out %s", __func__); OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
return CA_STATUS_OK; 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__ #endif // __WITH_DTLS__ or __WITH_TLS__
CAResult_t CACreateEndpoint(CATransportFlags_t flags, CAResult_t CACreateEndpoint(CATransportFlags_t flags,
......
...@@ -179,6 +179,17 @@ int32_t GetDtlsPskCredentials( CADtlsPskCredType_t type, ...@@ -179,6 +179,17 @@ int32_t GetDtlsPskCredentials( CADtlsPskCredType_t type,
const unsigned char *desc, size_t desc_len, const unsigned char *desc, size_t desc_len,
unsigned char *result, size_t result_length); 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. * Add temporal PSK to PIN based OxM.
* *
......
...@@ -526,6 +526,10 @@ static void SetResult(OTMContext_t* otmCtx, const OCStackResult res) ...@@ -526,6 +526,10 @@ static void SetResult(OTMContext_t* otmCtx, const OCStackResult res)
{ {
OIC_LOG(WARNING, TAG, "Failed to revert PkixInfoHandler."); OIC_LOG(WARNING, TAG, "Failed to revert PkixInfoHandler.");
} }
if(CA_STATUS_OK != CAregisterIdentityHandler(GetIdentityHandler))
{
OIC_LOG(WARNING, TAG, "Failed to set IdentityHandler.");
}
if(CA_STATUS_OK != CAregisterGetCredentialTypesHandler(InitCipherSuiteList)) if(CA_STATUS_OK != CAregisterGetCredentialTypesHandler(InitCipherSuiteList))
{ {
OIC_LOG(WARNING, TAG, "Failed to revert CredentialTypesHandler."); OIC_LOG(WARNING, TAG, "Failed to revert CredentialTypesHandler.");
......
...@@ -116,6 +116,12 @@ OCStackResult PrepareMCertificateCallback(OTMContext_t *otmCtx) ...@@ -116,6 +116,12 @@ OCStackResult PrepareMCertificateCallback(OTMContext_t *otmCtx)
return OC_STACK_ERROR; return OC_STACK_ERROR;
} }
if (CA_STATUS_OK != CAregisterIdentityHandler(NULL))
{
OIC_LOG(ERROR, TAG, "Failed to register IdentityHandler");
return OC_STACK_ERROR;
}
if (CA_STATUS_OK != CAregisterGetCredentialTypesHandler(InitManufacturerCipherSuiteList)) if (CA_STATUS_OK != CAregisterGetCredentialTypesHandler(InitManufacturerCipherSuiteList))
{ {
OIC_LOG(ERROR, TAG, "Failed to register CredentialTypesHandler"); OIC_LOG(ERROR, TAG, "Failed to register CredentialTypesHandler");
......
...@@ -3251,6 +3251,88 @@ static int ConvertDerCertToPem(const uint8_t* der, size_t derLen, uint8_t** pem) ...@@ -3251,6 +3251,88 @@ static int ConvertDerCertToPem(const uint8_t* der, size_t derLen, uint8_t** pem)
return 0; 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))
{
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 || 0 != memcmp(der, crt, crtLen))
{
if (der != cred->publicData.data)
{
OICFree(der);
}
continue;
}
if (der != cred->publicData.data)
{
OICFree(der);
}
UuidInfo_t* node = (UuidInfo_t*) OICMalloc(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__);
OICFree(node);
}
}
}
static OCStackResult GetCaCert(ByteArray_t * crt, const char * usage, OicEncodingType_t desiredEncoding) static OCStackResult GetCaCert(ByteArray_t * crt, const char * usage, OicEncodingType_t desiredEncoding)
{ {
OIC_LOG_V(DEBUG, TAG, "In %s", __func__); OIC_LOG_V(DEBUG, TAG, "In %s", __func__);
......
...@@ -1518,6 +1518,7 @@ OCEntityHandlerResult HandleDoxmPostRequestMfg(OicSecDoxm_t *newDoxm, ...@@ -1518,6 +1518,7 @@ OCEntityHandlerResult HandleDoxmPostRequestMfg(OicSecDoxm_t *newDoxm,
OIC_LOG(DEBUG, TAG, "No ciphersuite preferred"); OIC_LOG(DEBUG, TAG, "No ciphersuite preferred");
VERIFY_SUCCESS(TAG, CA_STATUS_OK == CAregisterPkixInfoHandler(GetManufacturerPkixInfo), ERROR); VERIFY_SUCCESS(TAG, CA_STATUS_OK == CAregisterPkixInfoHandler(GetManufacturerPkixInfo), ERROR);
VERIFY_SUCCESS(TAG, CA_STATUS_OK == CAregisterIdentityHandler(NULL), ERROR);
VERIFY_SUCCESS(TAG, CA_STATUS_OK == CAregisterGetCredentialTypesHandler( VERIFY_SUCCESS(TAG, CA_STATUS_OK == CAregisterGetCredentialTypesHandler(
InitManufacturerCipherSuiteList), ERROR); InitManufacturerCipherSuiteList), ERROR);
exit: exit:
......
...@@ -451,6 +451,7 @@ OCStackResult SRMInitSecureResources() ...@@ -451,6 +451,7 @@ OCStackResult SRMInitSecureResources()
ret = OC_STACK_ERROR; ret = OC_STACK_ERROR;
} }
CAregisterPkixInfoHandler(GetPkixInfo); CAregisterPkixInfoHandler(GetPkixInfo);
CAregisterIdentityHandler(GetIdentityHandler);
CAregisterGetCredentialTypesHandler(InitCipherSuiteList); CAregisterGetCredentialTypesHandler(InitCipherSuiteList);
#endif // __WITH_DTLS__ or __WITH_TLS__ #endif // __WITH_DTLS__ or __WITH_TLS__
return ret; return ret;
......
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