Commit 9f4d7e00 authored by Alex Kelley's avatar Alex Kelley

[IOT-2696] Allow chain of certs in public data

Previously we expected the leaf certificate to be present in public data
and the chain of intermediate CAs to be present in optional data. After
discussion it was agreed to update IoTivity to expect the entire chain
of certificates to be present in public data.

Change-Id: Ib4a53b31451205da4b06c41404b5088568844825
Signed-off-by: default avatarAlex Kelley <alexke@microsoft.com>
parent 65fa6b20
......@@ -109,8 +109,7 @@ OCStackResult OCInternalIsValidCertChain(const uint8_t *buf, size_t bufLen);
* from the /oic/sec/cred resource in a form suitable for the trustedCaCerts and trustedCaCertsLength
* parameters.
*
* @param[in] certificate OicSecKey_t containing the leaf certificate
* @param[in] optData Optional OicSecOpt_t containing intermediate CAs and revocation status
* @param[in] certificateChain OicSecKey_t containing one or more certificates
* @param[in] trustedCaCerts PEM string containing the trusted CAs certificates
* @param[in] trustedCaCertsLength Length of trustedCaCerts (including terminating NULL)
* @param[out] roles Pointer to receive array of OicSecRole_t objects listing roles
......@@ -122,8 +121,7 @@ OCStackResult OCInternalIsValidCertChain(const uint8_t *buf, size_t bufLen);
* OC_STACK_INVALID_PARAM if the certificate is not valid.
* OC_STACK_NO_MEMORY or OC_STACK_ERROR if some other error arose during validation.
*/
OCStackResult OCInternalVerifyRoleCertificate(const OicSecKey_t *certificate, const OicSecOpt_t *optData,
const uint8_t *trustedCaCerts, size_t trustedCaCertsLength,
OicSecRole_t **roles, size_t *rolesLength,
struct tm *notValidAfter);
OCStackResult OCInternalVerifyRoleCertificate(const OicSecKey_t *certificateChain, const uint8_t *trustedCaCerts,
size_t trustedCaCertsLength, OicSecRole_t **roles,
size_t *rolesLength, struct tm *notValidAfter);
#endif
......@@ -33,8 +33,7 @@ extern "C" {
typedef struct RoleCertChain {
uint32_t credId; /**< locally assigned ID number for use with DELETE */
OicSecKey_t certificate; /**< leaf certificate data */
OicSecOpt_t optData; /**< intermediate CA certificates */
OicSecKey_t certificate; /**< certificate chain including leaf and intermediate CA certificates */
struct RoleCertChain *next; /**< next chain in list */
} RoleCertChain_t;
......
......@@ -114,8 +114,7 @@ typedef struct OCPMGetCsrResult
typedef struct OCPMRoleCertChain
{
uint64_t credId; /**< credential ID */
OicSecKey_t certificate; /**< leaf certificate */
OicSecOpt_t optData; /**< intermediate CA certificates (if any) */
OicSecKey_t certificate; /**< certificate chain including leaf and intermediate CA certificates */
} OCPMRoleCertChain_t;
typedef struct OCPMGetRolesResult
......
......@@ -3826,14 +3826,11 @@ static void registerResultForGetRolesResourceCB(GetRolesData_t *getRolesData,
for (i = 0, curr = chains; NULL != curr; curr = curr->next, i++)
{
currentEntry->chains[i].credId = curr->credId;
/* Take ownership of the buffers from certificate and optData, rather than copy. */
/* Take ownership of the buffers from certificate rather than copy. */
currentEntry->chains[i].certificate = curr->certificate;
currentEntry->chains[i].optData = curr->optData;
curr->certificate.data = NULL;
curr->certificate.len = 0;
curr->optData.data = NULL;
curr->optData.len = 0;
}
}
FreeRoleCertChainList(chains);
......@@ -3884,14 +3881,13 @@ static OCStackApplicationResult SRPGetRolesResourceCB(void *ctx, OCDoHandle hand
false);
for (size_t i = 0; i < getRolesData->numOfResults; i++)
{
/* We took ownership of certificate.data and optData.data, so we must free them.
/* We took ownership of certificate.data so we must free it.
* These are allocated internally by tinycbor, which uses malloc and free, so we call
* free directly for those.
*/
for (size_t j = 0; j < getRolesData->resArr[i].chainsLength; j++)
{
free(getRolesData->resArr[i].chains[j].certificate.data);
free(getRolesData->resArr[i].chains[j].optData.data);
}
OICFree(getRolesData->resArr[i].chains);
}
......
......@@ -276,22 +276,12 @@ OCStackResult OCInternalIsValidRoleCertificate(const uint8_t *buf, size_t bufLen
mbedtls_x509_crt_init(&parsedCert);
int mbedRet = mbedtls_x509_crt_parse(&parsedCert, buf, bufLen);
if (0 > mbedRet)
{
OIC_LOG(ERROR, TAG, "Could not parse cert chain");
goto exit;
}
bool valid = false;
/* There should only be one certificate. */
if (NULL != parsedCert.next)
{
OIC_LOG(ERROR, TAG, "Expected only one certificate, but buffer contained more than one.");
res = OC_STACK_INVALID_PARAM;
goto exit;
}
/* We opt to require an EKU extension to be present; all-purposes certs are not allowed.
* mbedtls_x509_crt_check_extended_key_usage will return success if the EKU extension is absent,
* so we check this separately first.
......@@ -319,7 +309,7 @@ OCStackResult OCInternalIsValidRoleCertificate(const uint8_t *buf, size_t bufLen
goto exit;
}
valid = false;
bool valid = false;
/* Check for at least one subjAltName with a role in it. */
for (const mbedtls_x509_general_names *nameCur = &parsedCert.subject_alt_names;
NULL != nameCur;
......@@ -450,12 +440,11 @@ static const mbedtls_x509_crt_profile s_certProfile = {
0 /* RSA minimum key length - not used because we only use EC key pairs */
};
OCStackResult OCInternalVerifyRoleCertificate(const OicSecKey_t *certificate, const OicSecOpt_t *optData,
const uint8_t *trustedCaCerts, size_t trustedCaCertsLength,
OicSecRole_t **roles, size_t *rolesLength,
struct tm *notValidAfter)
OCStackResult OCInternalVerifyRoleCertificate(const OicSecKey_t *certificateChain, const uint8_t *trustedCaCerts,
size_t trustedCaCertsLength, OicSecRole_t **roles,
size_t *rolesLength, struct tm *notValidAfter)
{
VERIFY_NOT_NULL_RETURN(TAG, certificate, ERROR, OC_STACK_INVALID_PARAM);
VERIFY_NOT_NULL_RETURN(TAG, certificateChain, ERROR, OC_STACK_INVALID_PARAM);
VERIFY_NOT_NULL_RETURN(TAG, trustedCaCerts, ERROR, OC_STACK_INVALID_PARAM);
VERIFY_NOT_NULL_RETURN(TAG, roles, ERROR, OC_STACK_INVALID_PARAM);
VERIFY_NOT_NULL_RETURN(TAG, rolesLength, ERROR, OC_STACK_INVALID_PARAM);
......@@ -475,32 +464,21 @@ OCStackResult OCInternalVerifyRoleCertificate(const OicSecKey_t *certificate, co
mbedtls_x509_crt_init(&certChain);
mbedtls_x509_crt_init(&trustedCas);
res = OCInternalIsValidRoleCertificate(certificate->data, certificate->len, NULL, NULL);
res = OCInternalIsValidRoleCertificate(certificateChain->data, certificateChain->len, NULL, NULL);
if (OC_STACK_OK != res)
{
OIC_LOG_V(ERROR, TAG, "Certificate is not valid as a role certificate: %d", res);
goto exit;
}
mbedRet = mbedtls_x509_crt_parse(&certChain, certificate->data, certificate->len);
mbedRet = mbedtls_x509_crt_parse(&certChain, certificateChain->data, certificateChain->len);
if (0 > mbedRet)
{
OIC_LOG_V(ERROR, TAG, "Could not parse certificate: %d", mbedRet);
OIC_LOG_V(ERROR, TAG, "Could not parse certificates: %d", mbedRet);
res = OC_STACK_ERROR;
goto exit;
}
if ((NULL != optData) && (0 != optData->len))
{
mbedRet = mbedtls_x509_crt_parse(&certChain, optData->data, optData->len);
if (0 > mbedRet)
{
OIC_LOG_V(ERROR, TAG, "Could not parse optional data: %d", mbedRet);
res = OC_STACK_ERROR;
goto exit;
}
}
mbedRet = mbedtls_x509_crt_parse(&trustedCas, trustedCaCerts, trustedCaCertsLength);
if (0 > mbedRet)
{
......
......@@ -3517,36 +3517,6 @@ static int cloneSecKey(OicSecKey_t * dst, OicSecKey_t * src)
return 0;
}
static int cloneSecOpt(OicSecOpt_t * dst, OicSecOpt_t * src)
{
if ((src == NULL) || (dst == NULL))
{
return -1;
}
if (src->len > 0)
{
dst->data = OICCalloc(src->len, 1);
if (dst == NULL)
{
OIC_LOG_V(ERROR, TAG, "%s memory allocation failed", __func__);
OICFree(dst);
return -1;
}
memcpy(dst->data, src->data, src->len);
}
else
{
dst->data = NULL;
}
dst->len = src->len;
dst->encoding = src->encoding;
dst->revstat = src->revstat;
return 0;
}
/* Caller must call FreeRoleCertChainList on roleEntries when finished. */
OCStackResult GetAllRoleCerts(RoleCertChain_t ** output)
{
......@@ -3580,12 +3550,6 @@ OCStackResult GetAllRoleCerts(RoleCertChain_t ** output)
OIC_LOG_V(ERROR, TAG, "%s failed to copy certificate data", __func__);
goto error;
}
if (cloneSecOpt(&add->optData, &temp->optionalData) != 0)
{
OIC_LOG_V(ERROR, TAG, "%s failed to copy optional data", __func__);
goto error;
}
}
}
......
......@@ -148,7 +148,6 @@ static void FreeRoleCertChain(RoleCertChain_t *roleCert)
return;
}
OICFree(roleCert->optData.data);
OICFree(roleCert->certificate.data);
OICFree(roleCert);
}
......@@ -290,21 +289,6 @@ static OCStackResult DuplicateRoleCertChain(const RoleCertChain_t *roleCert, Rol
tmp->certificate.encoding = roleCert->certificate.encoding;
memcpy(tmp->certificate.data, roleCert->certificate.data, roleCert->certificate.len);
if (NULL != roleCert->optData.data)
{
tmp->optData.data = (uint8_t *)OICCalloc(1, roleCert->optData.len);
if (NULL == tmp->optData.data)
{
OIC_LOG(ERROR, TAG, "No memory for optional data");
res = OC_STACK_NO_MEMORY;
goto exit;
}
tmp->optData.len = roleCert->optData.len;
tmp->optData.encoding = roleCert->optData.encoding;
tmp->optData.revstat = roleCert->optData.revstat;
memcpy(tmp->optData.data, roleCert->optData.data, roleCert->optData.len);
}
*duplicate = tmp;
res = OC_STACK_OK;
......@@ -326,8 +310,7 @@ static bool RoleCertChainContains(RoleCertChain_t *chain, const RoleCertChain_t*
LL_FOREACH(chain, temp)
{
if (IsSameSecKey(&temp->certificate, &roleCert->certificate) &&
IsSameSecOpt(&temp->optData, &roleCert->optData))
if (IsSameSecKey(&temp->certificate, &roleCert->certificate))
{
return true;
}
......@@ -471,11 +454,6 @@ OCStackResult RolesToCBORPayload(const RoleCertChain_t *roles, uint8_t **cborPay
CborEncoder roleMap;
size_t mapSize = ROLE_MAP_SIZE;
if (NULL != currChain->optData.data)
{
mapSize++;
}
cborEncoderResult = cbor_encoder_create_map(&rolesArray, &roleMap, mapSize);
VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed adding role map");
......@@ -495,13 +473,6 @@ OCStackResult RolesToCBORPayload(const RoleCertChain_t *roles, uint8_t **cborPay
cborEncoderResult = SerializeEncodingToCbor(&roleMap, OIC_JSON_PUBLICDATA_NAME, &currChain->certificate);
VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed adding publicData");
// optionalData
if (NULL != currChain->optData.data)
{
cborEncoderResult = SerializeSecOptToCbor(&roleMap, OIC_JSON_OPTDATA_NAME, &currChain->optData);
VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed adding optional data");
}
// credType - mandatory
cborEncoderResult = cbor_encode_text_string(&roleMap, OIC_JSON_CREDTYPE_NAME, strlen(OIC_JSON_CREDTYPE_NAME));
VERIFY_CBOR_SUCCESS(TAG, cborEncoderResult, "Failed adding credType tag");
......@@ -696,11 +667,6 @@ OCStackResult CBORPayloadToRoles(const uint8_t *cborPayload, size_t size, RoleCe
cborFindResult = DeserializeEncodingFromCbor(&roleMap, &currEntry->certificate);
VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed to read publicData");
}
else if (strcmp(tagName, OIC_JSON_OPTDATA_NAME) == 0)
{
cborFindResult = DeserializeSecOptFromCbor(&roleMap, &currEntry->optData);
VERIFY_CBOR_SUCCESS(TAG, cborFindResult, "Failed to read optionalData");
}
else if (strcmp(tagName, OIC_JSON_CREDTYPE_NAME) == 0)
{
uint64_t credType = 0;
......@@ -854,16 +820,6 @@ static OCEntityHandlerResult HandlePostRequest(OCEntityHandlerRequest *ehRequest
for (curr = chains; NULL != curr; curr = curr->next)
{
if (NULL != curr->optData.data)
{
if (OC_STACK_OK != OCInternalIsValidCertChain(curr->optData.data, curr->optData.len))
{
OIC_LOG(ERROR, TAG, "Optional data is not a valid cert chain");
ehRet = OC_EH_ERROR;
goto exit;
}
}
if (OC_STACK_OK != OCInternalIsValidRoleCertificate(curr->certificate.data, curr->certificate.len,
&pubKey, &pubKeyLength))
{
......@@ -1260,10 +1216,9 @@ OCStackResult GetEndpointRoles(const CAEndpoint_t *endpoint, OicSecRole_t **role
struct tm notValidAfter;
memset(&notValidAfter, 0, sizeof(notValidAfter));
res = OCInternalVerifyRoleCertificate(&chain->certificate, &chain->optData,
trustedCaCerts.data, trustedCaCerts.len,
&currCertRoles, &currCertRolesCount,
&notValidAfter);
res = OCInternalVerifyRoleCertificate(&chain->certificate, trustedCaCerts.data,
trustedCaCerts.len, &currCertRoles,
&currCertRolesCount, &notValidAfter);
if (OC_STACK_OK != res)
{
......
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