[CR2390] Identity spoofing/privelege escalation 71/25771/6
authorOleksandr Andrieiev <o.andrieiev@samsung.com>
Fri, 8 Jun 2018 13:54:23 +0000 (16:54 +0300)
committerNathan Heldt-Sheller <nathan.heldt-sheller@intel.com>
Thu, 14 Jun 2018 16:22:51 +0000 (16:22 +0000)
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 <o.andrieiev@samsung.com>
resource/csdk/connectivity/api/casecurityinterface.h
resource/csdk/connectivity/src/adapter_util/ca_adapter_net_ssl.c
resource/csdk/connectivity/src/caconnectivitymanager.c
resource/csdk/security/include/internal/credresource.h
resource/csdk/security/src/credresource.c
resource/csdk/security/src/secureresourcemanager.c

index dbf0044..6bce2c4 100644 (file)
@@ -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.
index d47cb8b..e325595 100644 (file)
@@ -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");
index fb8c240..8660791 100644 (file)
@@ -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,
index e461f33..a862588 100644 (file)
@@ -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.
  *
index 6361eda..08c5721 100644 (file)
@@ -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)
index 8853c0e..0133d0d 100644 (file)
@@ -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.");