From 0768ad59edfacc203db8f047c2321472a2c243e8 Mon Sep 17 00:00:00 2001 From: Dennis Potman <45659984+dennis-adlink@users.noreply.github.com> Date: Fri, 20 Mar 2020 13:44:27 +0100 Subject: [PATCH] Remove duplicated code in authentication plugin (#442) * Remove duplicated code in authentication plugin Signed-off-by: Dennis Potman * Fix build warnings Signed-off-by: Dennis Potman * Fix memory leak and call create_validate_asymmetrical_signature directly from create_validate_signature_impl Signed-off-by: Dennis Potman * Fix refcount issue (assert in openssl) for identity cert in hs remote info Signed-off-by: Dennis Potman * Refactoring of validate_handshake_token function Co-authored-by: Erik Boasson Signed-off-by: Dennis Potman --- .../authentication/src/auth_utils.c | 84 +- .../authentication/src/auth_utils.h | 4 +- .../authentication/src/authentication.c | 802 ++++++------------ .../dds/security/core/dds_security_utils.h | 12 +- src/security/core/src/dds_security_utils.c | 73 +- 5 files changed, 348 insertions(+), 627 deletions(-) diff --git a/src/security/builtin_plugins/authentication/src/auth_utils.c b/src/security/builtin_plugins/authentication/src/auth_utils.c index 54e94b2..e79a951 100644 --- a/src/security/builtin_plugins/authentication/src/auth_utils.c +++ b/src/security/builtin_plugins/authentication/src/auth_utils.c @@ -956,94 +956,54 @@ DDS_Security_ValidationResult_t get_trusted_ca_list(const char *trusted_ca_dir, return failed ? DDS_SECURITY_VALIDATION_FAILED : DDS_SECURITY_VALIDATION_OK; } -DDS_Security_ValidationResult_t create_asymmetrical_signature(EVP_PKEY *pkey, const unsigned char *data, const size_t dataLen, +DDS_Security_ValidationResult_t create_validate_asymmetrical_signature(bool create, EVP_PKEY *pkey, const unsigned char *data, const size_t dataLen, unsigned char **signature, size_t *signatureLen, DDS_Security_SecurityException *ex) { EVP_MD_CTX *mdctx = NULL; EVP_PKEY_CTX *kctx = NULL; - if (!(mdctx = EVP_MD_CTX_create())) { - DDS_Security_Exception_set_with_openssl_error(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "Failed to create signing context: "); - goto err_create_ctx; + DDS_Security_Exception_set_with_openssl_error(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "Failed to create digest context: "); + return DDS_SECURITY_VALIDATION_FAILED; } - if (EVP_DigestSignInit(mdctx, &kctx, EVP_sha256(), NULL, pkey) != 1) + if ((create ? EVP_DigestSignInit(mdctx, &kctx, EVP_sha256(), NULL, pkey) : EVP_DigestVerifyInit(mdctx, &kctx, EVP_sha256(), NULL, pkey)) != 1) { - DDS_Security_Exception_set_with_openssl_error(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "Failed to initialize signing context: "); - goto err_sign; + DDS_Security_Exception_set_with_openssl_error(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "Failed to initialize digest context: "); + goto err; } if (EVP_PKEY_id(pkey) == EVP_PKEY_RSA) { if (EVP_PKEY_CTX_set_rsa_padding(kctx, RSA_PKCS1_PSS_PADDING) < 1) { - DDS_Security_Exception_set_with_openssl_error(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "Failed to initialize signing context: "); - goto err_sign; + DDS_Security_Exception_set_with_openssl_error(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "Failed to initialize digest context: "); + goto err; } } - if (EVP_DigestSignUpdate(mdctx, data, dataLen) != 1) + if ((create ? EVP_DigestSignUpdate(mdctx, data, dataLen) : EVP_DigestVerifyUpdate(mdctx, data, dataLen)) != 1) { - DDS_Security_Exception_set_with_openssl_error(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "Failed to update signing context: "); - goto err_sign; + DDS_Security_Exception_set_with_openssl_error(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "Failed to update digest context: "); + goto err; } - if (EVP_DigestSignFinal(mdctx, NULL, signatureLen) != 1) + if (create) { - DDS_Security_Exception_set_with_openssl_error(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "Failed to finalize signing context: "); - goto err_sign; - } - *signature = ddsrt_malloc(sizeof(unsigned char) * (*signatureLen)); - if (EVP_DigestSignFinal(mdctx, *signature, signatureLen) != 1) - { - DDS_Security_Exception_set_with_openssl_error(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "Failed to finalize signing context: "); - ddsrt_free(*signature); - goto err_sign; - } - EVP_MD_CTX_destroy(mdctx); - return DDS_SECURITY_VALIDATION_OK; - -err_sign: - EVP_MD_CTX_destroy(mdctx); -err_create_ctx: - return DDS_SECURITY_VALIDATION_FAILED; -} - -DDS_Security_ValidationResult_t validate_asymmetrical_signature(EVP_PKEY *pkey, const unsigned char *data, const size_t dataLen, - const unsigned char *signature, const size_t signatureLen, DDS_Security_SecurityException *ex) -{ - EVP_MD_CTX *mdctx = NULL; - EVP_PKEY_CTX *kctx = NULL; - if (!(mdctx = EVP_MD_CTX_create())) - { - DDS_Security_Exception_set_with_openssl_error(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "Failed to create verify context: "); - goto err_create_ctx; - } - if (EVP_DigestVerifyInit(mdctx, &kctx, EVP_sha256(), NULL, pkey) != 1) - { - DDS_Security_Exception_set_with_openssl_error(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "Failed to initialize verify context: "); - goto err_verify; - } - if (EVP_PKEY_id(pkey) == EVP_PKEY_RSA) - { - if (EVP_PKEY_CTX_set_rsa_padding(kctx, RSA_PKCS1_PSS_PADDING) < 1) + if (EVP_DigestSignFinal(mdctx, NULL, signatureLen) != 1) { - DDS_Security_Exception_set_with_openssl_error(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "Failed to initialize signing context: "); - goto err_verify; + DDS_Security_Exception_set_with_openssl_error(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "Failed to finalize digest context: "); + goto err; } + *signature = ddsrt_malloc(sizeof(unsigned char) * (*signatureLen)); } - if (EVP_DigestVerifyUpdate(mdctx, data, dataLen) != 1) + if ((create ? EVP_DigestSignFinal(mdctx, *signature, signatureLen) : EVP_DigestVerifyFinal(mdctx, *signature, *signatureLen)) != 1) { - DDS_Security_Exception_set_with_openssl_error(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "Failed to update verify context: "); - goto err_verify; - } - if (EVP_DigestVerifyFinal(mdctx, signature, signatureLen) != 1) - { - DDS_Security_Exception_set_with_openssl_error(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "Failed to finalize verify context: "); - goto err_verify; + DDS_Security_Exception_set_with_openssl_error(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "Failed to finalize digest context: "); + if (create) + ddsrt_free(*signature); + goto err; } EVP_MD_CTX_destroy(mdctx); return DDS_SECURITY_VALIDATION_OK; -err_verify: +err: EVP_MD_CTX_destroy(mdctx); -err_create_ctx: return DDS_SECURITY_VALIDATION_FAILED; } diff --git a/src/security/builtin_plugins/authentication/src/auth_utils.h b/src/security/builtin_plugins/authentication/src/auth_utils.h index bbde1ba..52ca1f7 100644 --- a/src/security/builtin_plugins/authentication/src/auth_utils.h +++ b/src/security/builtin_plugins/authentication/src/auth_utils.h @@ -100,7 +100,7 @@ AuthConfItemPrefix_t get_conf_item_type(const char *str, char **data); void free_ca_list_contents(X509Seq *ca_list); DDS_Security_ValidationResult_t get_trusted_ca_list(const char* trusted_ca_dir, X509Seq *ca_list, DDS_Security_SecurityException *ex); char * string_from_data(const unsigned char *data, uint32_t size); -DDS_Security_ValidationResult_t create_asymmetrical_signature(EVP_PKEY *pkey, const unsigned char *data, const size_t dataLen, unsigned char **signature, size_t *signatureLen, DDS_Security_SecurityException *ex); -DDS_Security_ValidationResult_t validate_asymmetrical_signature(EVP_PKEY *pkey, const unsigned char *data, const size_t dataLen, const unsigned char *signature, const size_t signatureLen, DDS_Security_SecurityException *ex); +DDS_Security_ValidationResult_t create_validate_asymmetrical_signature(bool create, EVP_PKEY *pkey, const unsigned char *data, const size_t dataLen, + unsigned char **signature, size_t *signatureLen, DDS_Security_SecurityException *ex); #endif /* AUTH_UTILS_H */ diff --git a/src/security/builtin_plugins/authentication/src/authentication.c b/src/security/builtin_plugins/authentication/src/authentication.c index 79f03bd..951e07e 100644 --- a/src/security/builtin_plugins/authentication/src/authentication.c +++ b/src/security/builtin_plugins/authentication/src/authentication.c @@ -570,40 +570,33 @@ static void get_hash_binary_property_seq(const DDS_Security_BinaryPropertySeq *s DDS_Security_Serializer_free(serializer); } -static DDS_Security_ValidationResult_t create_signature(EVP_PKEY *pkey, const DDS_Security_BinaryProperty_t **binary_properties, - const uint32_t binary_properties_length, unsigned char **signature, size_t *signatureLen, DDS_Security_SecurityException *ex) +static DDS_Security_ValidationResult_t create_validate_signature_impl(bool create, EVP_PKEY *pkey, const DDS_Security_BinaryProperty_t **bprops, + const uint32_t n_bprops, unsigned char **signature, size_t *signature_len, DDS_Security_SecurityException *ex) { DDS_Security_ValidationResult_t result; unsigned char *buffer; size_t size; DDS_Security_Serializer serializer = DDS_Security_Serializer_new(4096, 4096); - - DDS_Security_Serialize_BinaryPropertyArray(serializer, binary_properties, binary_properties_length); + DDS_Security_Serialize_BinaryPropertyArray(serializer, bprops, n_bprops); DDS_Security_Serializer_buffer(serializer, &buffer, &size); - - result = create_asymmetrical_signature(pkey, buffer, size, signature, signatureLen, ex); + result = create_validate_asymmetrical_signature(create, pkey, buffer, size, signature, signature_len, ex); ddsrt_free(buffer); DDS_Security_Serializer_free(serializer); - return result; } -static DDS_Security_ValidationResult_t validate_signature(EVP_PKEY *pkey, const DDS_Security_BinaryProperty_t **properties, - const uint32_t properties_length, unsigned char *signature, size_t signatureLen, DDS_Security_SecurityException *ex) +static DDS_Security_ValidationResult_t create_signature(EVP_PKEY *pkey, const DDS_Security_BinaryProperty_t **bprops, + const uint32_t n_bprops, unsigned char **signature, size_t *signature_len, DDS_Security_SecurityException *ex) { - DDS_Security_ValidationResult_t result; - unsigned char *buffer; - size_t size; - DDS_Security_Serializer serializer = DDS_Security_Serializer_new(4096, 4096); + return create_validate_signature_impl(true, pkey, bprops, n_bprops, signature, signature_len, ex); +} - DDS_Security_Serialize_BinaryPropertyArray(serializer, properties, properties_length); - DDS_Security_Serializer_buffer(serializer, &buffer, &size); - - result = validate_asymmetrical_signature(pkey, buffer, size, signature, signatureLen, ex); - ddsrt_free(buffer); - DDS_Security_Serializer_free(serializer); - - return result; +static DDS_Security_ValidationResult_t validate_signature(EVP_PKEY *pkey, const DDS_Security_BinaryProperty_t **bprops, + const uint32_t n_bprops, const unsigned char *signature, size_t signature_len, DDS_Security_SecurityException *ex) +{ + unsigned char *s = (unsigned char *)signature; + size_t s_len = signature_len; + return create_validate_signature_impl(false, pkey, bprops, n_bprops, &s, &s_len, ex); } static DDS_Security_ValidationResult_t compute_hash_value(HashValue_t value, const DDS_Security_BinaryProperty_t **properties, @@ -1089,19 +1082,12 @@ DDS_Security_ValidationResult_t validate_remote_identity(dds_security_authentica ddsrt_free(lchallenge); } } - ddsrt_mutex_unlock(&impl->lock); if (!remote_auth_request_token) - { - /* Create local_auth_request_token with contents set to the challenge */ fill_auth_request_token(local_auth_request_token, relation->lchallenge); - } else - { - /* Set local_auth_request token to TokenNil */ DDS_Security_set_token_nil(local_auth_request_token); - } *remote_identity_handle = IDENTITY_HANDLE(remoteIdent); return memcmp(&localIdent->adjustedGUID, &remoteIdent->guid, sizeof(DDS_Security_GUID_t)) < 0 ? @@ -1206,6 +1192,7 @@ DDS_Security_ValidationResult_t begin_handshake_request(dds_security_authenticat DDS_Security_BinaryProperty_set_by_string(c_dsign_algo, "c.dsign_algo", get_dsign_algo(localIdent->dsignAlgoKind)); DDS_Security_BinaryProperty_set_by_string(c_kagree_algo, "c.kagree_algo", get_kagree_algo(localIdent->kagreeAlgoKind)); + /* Todo: including hash_c1 is optional (conform spec); add a configuration option to leave it out */ { DDS_Security_BinaryPropertySeq bseq = { ._length = 5, ._buffer = tokens }; get_hash_binary_property_seq(&bseq, handshake->hash_c1); @@ -1285,549 +1272,314 @@ failed_deser: return DDS_SECURITY_VALIDATION_FAILED; } -static DDS_Security_ValidationResult_t validate_handshake_request_token(const DDS_Security_HandshakeMessageToken *token, HandshakeInfo *handshake, X509Seq *trusted_ca_list, DDS_Security_SecurityException *ex) +enum handshake_token_type { - IdentityRelation *relation = handshake->relation; - X509 *identityCert; - const DDS_Security_BinaryProperty_t *c_id, *c_perm, *c_pdata, *c_dsign_algo, *c_kagree_algo, *dh1, *challenge, *hash_c1; - EVP_PKEY *pdhkey = NULL; - AuthenticationAlgoKind_t dsignAlgoKind, kagreeAlgoKind; + HS_TOKEN_REQ, + HS_TOKEN_REPLY, + HS_TOKEN_FINAL +}; - assert(relation); +static DDS_Security_ValidationResult_t set_exception (DDS_Security_SecurityException *ex, const char *fmt, ...) + ddsrt_attribute_format ((printf, 2, 3)) ddsrt_attribute_warn_unused_result; - if (!token->class_id || strncmp(AUTH_HANDSHAKE_REQUEST_TOKEN_ID, token->class_id, strlen(AUTH_HANDSHAKE_REQUEST_TOKEN_ID)) != 0) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "begin_handshake_reply: HandshakeMessageToken incorrect class_id: %s (expected %s)", token->class_id ? token->class_id : "NULL", AUTH_HANDSHAKE_REQUEST_TOKEN_ID); - goto err_inv_class_id; - } - - /* Check presents of mandatory properties: c.i, c.perm, c.pdata, c.dsign_algo, c.kagree_algo, dh1, challenge1 */ - c_id = DDS_Security_DataHolder_find_binary_property(token, "c.id"); - if (!c_id || c_id->value._length == 0 || c_id->value._buffer == NULL) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "begin_handshake_reply: HandshakeMessageToken property c.id missing"); - goto err_no_c_id; - } - - if (load_X509_certificate_from_data((char *)c_id->value._buffer, (int)c_id->value._length, &identityCert, ex) != DDS_SECURITY_VALIDATION_OK) - goto err_identity_cert_load; - - { - DDS_Security_ValidationResult_t result = DDS_SECURITY_VALIDATION_OK; - if (trusted_ca_list->length == 0) - result = verify_certificate(identityCert, relation->localIdentity->identityCA, ex); - else - { - DDS_Security_Exception_clean(ex); - for (unsigned i = 0; i < trusted_ca_list->length; ++i) - { - DDS_Security_Exception_reset(ex); - if ((result = verify_certificate(identityCert, trusted_ca_list->buffer[i], ex)) == DDS_SECURITY_VALIDATION_OK) - break; - } - } - if (result != DDS_SECURITY_VALIDATION_OK) - goto err_inv_identity_cert; - } - - if (check_certificate_expiry(identityCert, ex) != DDS_SECURITY_VALIDATION_OK) - goto err_inv_identity_cert; - - if (!(c_perm = DDS_Security_DataHolder_find_binary_property(token, "c.perm"))) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "begin_handshake_reply: HandshakeMessageToken property c.perm missing"); - goto err_no_c_perm; - } - if (c_perm->value._length > 0) - { - ddsrt_free(relation->remoteIdentity->permissionsDocument); - relation->remoteIdentity->permissionsDocument = string_from_data(c_perm->value._buffer, c_perm->value._length); - } - - if (!(c_pdata = DDS_Security_DataHolder_find_binary_property(token, "c.pdata"))) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "begin_handshake_reply: HandshakeMessageToken property c.pdata missing"); - goto err_no_c_pdata; - } - if (validate_pdata(&c_pdata->value, identityCert, ex) != DDS_SECURITY_VALIDATION_OK) - goto err_inv_pdata; - - if (!(c_dsign_algo = DDS_Security_DataHolder_find_binary_property(token, "c.dsign_algo"))) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "begin_handshake_reply: HandshakeMessageToken property c.dsign_algo missing"); - goto err_no_c_dsign_algo; - } - - if ((dsignAlgoKind = get_dsign_algo_from_string((const char *)c_dsign_algo->value._buffer)) == AUTH_ALGO_KIND_UNKNOWN) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "begin_handshake_reply: HandshakeMessageToken property c.dsign_algo not supported"); - goto err_no_c_dsign_algo; - } - - if (!(c_kagree_algo = DDS_Security_DataHolder_find_binary_property(token, "c.kagree_algo"))) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "begin_handshake_reply: HandshakeMessageToken property c.kagree_algo missing"); - goto err_no_c_kagree_algo; - } - - if ((kagreeAlgoKind = get_kagree_algo_from_string((const char *)c_kagree_algo->value._buffer)) == AUTH_ALGO_KIND_UNKNOWN) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "begin_handshake_reply: HandshakeMessageToken property c.kagree_algo not support"); - goto err_no_c_kagree_algo; - } - - dh1 = DDS_Security_DataHolder_find_binary_property(token, "dh1"); - if (!dh1 || dh1->value._length == 0 || dh1->value._buffer == NULL) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "begin_handshake_reply: HandshakeMessageToken property dh1 missing"); - goto err_no_dh; - } - if (dh_oct_to_public_key(&pdhkey, kagreeAlgoKind, dh1->value._buffer, dh1->value._length, ex) != DDS_SECURITY_VALIDATION_OK) - goto err_no_dh; - - if (!(challenge = DDS_Security_DataHolder_find_binary_property(token, "challenge1"))) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "begin_handshake_reply: HandshakeMessageToken property challenge1 missing"); - goto err_no_challenge; - } - if (challenge->value._length != sizeof(AuthenticationChallenge) || challenge->value._buffer == NULL) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "begin_handshake_reply: HandshakeMessageToken property challenge1 invalid"); - goto err_no_challenge; - } - - /* When validate_remote_identity was provided with a remote_auth_request_token then the future_challenge in the remote identity was set and the challenge1 - property of the handshake_request_token should be the same as the future_challenge stored in the remote identity. */ - if (relation->rchallenge) - { - if (memcmp(relation->rchallenge->value, challenge->value._buffer, sizeof(AuthenticationChallenge)) != 0) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "begin_handshake_reply: HandshakeMessageToken property challenge1 does not match future_challenge"); - goto err_no_challenge; - } - } - else - { - if (challenge->value._length == sizeof(relation->rchallenge->value)) - { - relation->rchallenge = ddsrt_malloc(sizeof(AuthenticationChallenge)); - memcpy(relation->rchallenge, challenge->value._buffer, challenge->value._length); - } - else - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "begin_handshake_reply: HandshakeMessageToken property challenge1 invalid (incorrect size)"); - goto err_no_challenge; - } - } - - { - const DDS_Security_BinaryProperty_t *binary_properties[] = { c_id, c_perm, c_pdata, c_dsign_algo, c_kagree_algo }; - (void)compute_hash_value(&handshake->hash_c1[0], binary_properties, 5, NULL); - } - if ((hash_c1 = DDS_Security_DataHolder_find_binary_property(token, "hash_c1"))) - { - if (hash_c1->value._length != sizeof(HashValue_t) || memcmp(hash_c1->value._buffer, &handshake->hash_c1, sizeof(HashValue_t)) != 0) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "begin_handshake_reply: HandshakeMessageToken property hash_c1 invalid (incorrect size)"); - goto err_inv_hash_c1; - } - } - - if (relation->remoteIdentity->identityCert) - X509_free(relation->remoteIdentity->identityCert); - relation->remoteIdentity->identityCert = identityCert; - relation->remoteIdentity->dsignAlgoKind = dsignAlgoKind; - relation->remoteIdentity->kagreeAlgoKind = kagreeAlgoKind; - DDS_Security_OctetSeq_copy(&relation->remoteIdentity->pdata, &c_pdata->value); - handshake->rdh = pdhkey; - return DDS_SECURITY_VALIDATION_OK; - -err_inv_hash_c1: -err_no_challenge: - EVP_PKEY_free(pdhkey); -err_no_dh: -err_no_c_kagree_algo: -err_no_c_dsign_algo: -err_inv_pdata: -err_no_c_pdata: -err_no_c_perm: -err_inv_identity_cert: - X509_free(identityCert); -err_identity_cert_load: -err_no_c_id: -err_inv_class_id: +static DDS_Security_ValidationResult_t set_exception (DDS_Security_SecurityException *ex, const char *fmt, ...) +{ + va_list ap; + va_start (ap, fmt); + DDS_Security_Exception_vset (ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, fmt, ap); + va_end (ap); return DDS_SECURITY_VALIDATION_FAILED; } -static DDS_Security_ValidationResult_t validate_handshake_reply_token(const DDS_Security_HandshakeMessageToken *token, HandshakeInfo *handshake, EVP_PKEY **pdhkey, X509Seq *trusted_ca_list, DDS_Security_SecurityException *ex) +static const DDS_Security_BinaryProperty_t *find_required_binprop (const DDS_Security_HandshakeMessageToken *token, const char *name, DDS_Security_SecurityException *ex) { - IdentityRelation *relation = handshake->relation; - X509 *identityCert; - EVP_PKEY *public_key; - const DDS_Security_BinaryProperty_t *c_id, *c_perm, *c_pdata, *c_dsign_algo, *c_kagree_algo, *dh1, *dh2, *hash_c1, *hash_c2, *challenge1, *challenge2, *signature; - AuthenticationAlgoKind_t dsignAlgoKind, kagreeAlgoKind; - - assert(relation); - - if (!token->class_id || strncmp(AUTH_HANDSHAKE_REPLY_TOKEN_ID, token->class_id, strlen(AUTH_HANDSHAKE_REPLY_TOKEN_ID)) != 0) + DDS_Security_ValidationResult_t result; + const DDS_Security_BinaryProperty_t *prop = DDS_Security_DataHolder_find_binary_property (token, name); + if (prop == NULL) { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "process_handshake: HandshakeMessageToken incorrect class_id: %s (expected %s)", token->class_id ? token->class_id : "NULL", AUTH_HANDSHAKE_REPLY_TOKEN_ID); - goto err_inv_class_id; + result = set_exception (ex, "process_handshake: HandshakeMessageToken property %s missing", name); + (void) result; + return NULL; } - - /* Check presents of mandatory properties: c.id, c.perm, c.pdata, c.dsign_algo, c.kagree_algo, challenge1, dh2, challenge2, signature */ - c_id = DDS_Security_DataHolder_find_binary_property(token, "c.id"); - if (!c_id || (c_id->value._length == 0) || (c_id->value._buffer == NULL)) + else if (prop->value._length > INT_MAX) { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "process_handshake: HandshakeMessageToken property c.id missing"); - goto err_no_c_id; + result = set_exception (ex, "process_handshake: HandshakeMessageToken property %s has unsupported size (%"PRIu32" bytes)", name, prop->value._length); + (void) result; + return NULL; } - - /* Verify Identity Certificate */ - if (load_X509_certificate_from_data((char *)c_id->value._buffer, (int)c_id->value._length, &identityCert, ex) != DDS_SECURITY_VALIDATION_OK) - goto err_identity_cert_load; - - { - DDS_Security_ValidationResult_t result = DDS_SECURITY_VALIDATION_OK; - if (trusted_ca_list->length == 0) - result = verify_certificate(identityCert, relation->localIdentity->identityCA, ex); - else - { - DDS_Security_Exception_clean(ex); - for (unsigned i = 0; i < trusted_ca_list->length; ++i) - { - DDS_Security_Exception_reset(ex); - result = verify_certificate(identityCert, trusted_ca_list->buffer[i], ex); - if (result == DDS_SECURITY_VALIDATION_OK) - break; - } - } - if (result != DDS_SECURITY_VALIDATION_OK) - goto err_inv_identity_cert; - } - - if (check_certificate_expiry(identityCert, ex) != DDS_SECURITY_VALIDATION_OK) - goto err_inv_identity_cert; - - if (!(c_perm = DDS_Security_DataHolder_find_binary_property(token, "c.perm"))) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "process_handshake: HandshakeMessageToken property c.perm missing"); - goto err_no_c_perm; - } - if (c_perm->value._length > 0) - { - ddsrt_free(relation->remoteIdentity->permissionsDocument); - relation->remoteIdentity->permissionsDocument = string_from_data(c_perm->value._buffer, c_perm->value._length); - } - - if (!(c_pdata = DDS_Security_DataHolder_find_binary_property(token, "c.pdata"))) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "process_handshake: HandshakeMessageToken property c.pdata missing"); - goto err_no_c_pdata; - } - if (validate_pdata(&c_pdata->value, identityCert, ex) != DDS_SECURITY_VALIDATION_OK) - goto err_inv_pdata; - - if (!(c_dsign_algo = DDS_Security_DataHolder_find_binary_property(token, "c.dsign_algo"))) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "process_handshake: HandshakeMessageToken property c.dsign_algo missing"); - goto err_no_c_dsign_algo; - } - - if ((dsignAlgoKind = get_dsign_algo_from_string((const char *)c_dsign_algo->value._buffer)) == AUTH_ALGO_KIND_UNKNOWN) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "process_handshake: HandshakeMessageToken property c.dsign_algo not supported"); - goto err_no_c_dsign_algo; - } - - if (!(c_kagree_algo = DDS_Security_DataHolder_find_binary_property(token, "c.kagree_algo"))) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "process_handshake: HandshakeMessageToken property c.kagree_algo missing"); - goto err_no_c_kagree_algo; - } - - if ((kagreeAlgoKind = get_kagree_algo_from_string((const char *)c_kagree_algo->value._buffer)) == AUTH_ALGO_KIND_UNKNOWN) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "process_handshake: HandshakeMessageToken property c.kagree_algo not support"); - goto err_no_c_kagree_algo; - } - - /* dh1 is optional */ - dh1 = DDS_Security_DataHolder_find_binary_property(token, "dh1"); - DDSRT_UNUSED_ARG(dh1); - - dh2 = DDS_Security_DataHolder_find_binary_property(token, "dh2"); - if (!dh2 || dh2->value._length == 0 || dh2->value._buffer == NULL) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "process_handshake: HandshakeMessageToken property dh2 missing"); - goto err_no_dh; - } - - if ((hash_c1 = DDS_Security_DataHolder_find_binary_property(token, "hash_c1"))) - { - if (hash_c1->value._length != sizeof(HashValue_t) || memcmp(hash_c1->value._buffer, handshake->hash_c1, sizeof(HashValue_t)) != 0) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "begin_handshake_reply: HandshakeMessageToken property hash_c1 invalid"); - goto err_inv_hash_c1; - } - } - - { - const DDS_Security_BinaryProperty_t *binary_properties[] = { c_id, c_perm, c_pdata, c_dsign_algo, c_kagree_algo }; - (void)compute_hash_value(&handshake->hash_c2[0], binary_properties, 5, NULL); - } - - if ((hash_c2 = DDS_Security_DataHolder_find_binary_property(token, "hash_c2"))) - { - if (hash_c2->value._length != sizeof(HashValue_t) || memcmp(hash_c2->value._buffer, handshake->hash_c2, sizeof(HashValue_t)) != 0) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "begin_handshake_reply: HandshakeMessageToken property hash_c2 invalid"); - goto err_inv_hash_c2; - } - } - - signature = DDS_Security_DataHolder_find_binary_property(token, "signature"); - if (!signature || signature->value._length == 0 || signature->value._buffer == NULL) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "process_handshake: HandshakeMessageToken property signature missing"); - goto err_no_signature; - } - - *pdhkey = NULL; - if (dh_oct_to_public_key(pdhkey, kagreeAlgoKind, dh2->value._buffer, dh2->value._length, ex) != DDS_SECURITY_VALIDATION_OK) - goto err_inv_dh; - - if (!(challenge1 = DDS_Security_DataHolder_find_binary_property(token, "challenge1"))) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "process_handshake: HandshakeMessageToken property challenge1 missing"); - goto err_no_challenge; - } - if (challenge1->value._length != sizeof(AuthenticationChallenge) || challenge1->value._buffer == NULL) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "process_handshake: HandshakeMessageToken property challenge1 invalid"); - goto err_no_challenge; - } - - if (!(challenge2 = DDS_Security_DataHolder_find_binary_property(token, "challenge2"))) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "process_handshake: HandshakeMessageToken property challenge2 missing"); - goto err_no_challenge; - } - if (challenge2->value._length != sizeof(AuthenticationChallenge) || challenge2->value._buffer == NULL) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "process_handshake: HandshakeMessageToken property challenge2 invalid"); - goto err_no_challenge; - } - - /* When validate_remote_identity was provided with a remote_auth_request_token then the future_challenge in the remote identity was set and the challenge2 - property of the handshake_reply_token should be the same as the future_challenge stored in the remote identity. */ - if (relation->rchallenge) - { - if (memcmp(relation->rchallenge->value, challenge2->value._buffer, sizeof(AuthenticationChallenge)) != 0) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "process_handshake: HandshakeMessageToken property challenge2 does not match future_challenge"); - goto err_no_challenge; - } - } - else - { - if (challenge2->value._length == sizeof(relation->rchallenge->value)) - { - relation->rchallenge = ddsrt_malloc(sizeof(AuthenticationChallenge)); - memcpy(relation->rchallenge, challenge2->value._buffer, challenge2->value._length); - } - else - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "process_handshake: HandshakeMessageToken property challenge2 invalid (incorrect size)"); - goto err_no_challenge; - } - } - - if (relation->lchallenge) - { - if (memcmp(relation->lchallenge->value, challenge1->value._buffer, sizeof(AuthenticationChallenge)) != 0) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "process_handshake: HandshakeMessageToken property challenge1 does not match future_challenge"); - goto err_no_challenge; - } - } - else - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "process_handshake: No future challenge exists for this token"); - goto err_no_challenge; - } - - /* TODO: check if an identity certificate was already associated with the remote identity and when that is the case both should be the same */ - if (relation->remoteIdentity->identityCert) - X509_free(relation->remoteIdentity->identityCert); - relation->remoteIdentity->identityCert = identityCert; - relation->remoteIdentity->dsignAlgoKind = dsignAlgoKind; - relation->remoteIdentity->kagreeAlgoKind = kagreeAlgoKind; - - if ((public_key = X509_get_pubkey(relation->remoteIdentity->identityCert))) - { - DDS_Security_BinaryProperty_t *hash_c1_val = hash_value_to_binary_property("hash_c1", handshake->hash_c1); - DDS_Security_BinaryProperty_t *hash_c2_val = hash_value_to_binary_property("hash_c2", handshake->hash_c2); - const DDS_Security_BinaryProperty_t *properties[HANDSHAKE_SIGNATURE_CONTENT_SIZE] = { hash_c2_val, challenge2, dh2, challenge1, dh1, hash_c1_val }; - DDS_Security_ValidationResult_t result = validate_signature(public_key, properties, HANDSHAKE_SIGNATURE_CONTENT_SIZE, signature->value._buffer, signature->value._length, ex); - EVP_PKEY_free(public_key); - DDS_Security_BinaryProperty_free(hash_c1_val); - DDS_Security_BinaryProperty_free(hash_c2_val); - if (result != DDS_SECURITY_VALIDATION_OK) - goto err_inv_signature; - } - else - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "X509_get_pubkey failed"); - goto err_inv_signature; - } - - DDS_Security_OctetSeq_copy(&relation->remoteIdentity->pdata, &c_pdata->value); - return DDS_SECURITY_VALIDATION_OK; - -err_inv_signature: -err_no_challenge: -err_inv_dh: - EVP_PKEY_free(*pdhkey); -err_no_signature: -err_inv_hash_c2: -err_inv_hash_c1: -err_no_dh: -err_no_c_kagree_algo: -err_no_c_dsign_algo: -err_inv_pdata: -err_no_c_pdata: -err_no_c_perm: -err_inv_identity_cert: - X509_free(identityCert); -err_identity_cert_load: -err_no_c_id: -err_inv_class_id: - return DDS_SECURITY_VALIDATION_FAILED; + return prop; } -static DDS_Security_ValidationResult_t validate_handshake_final_token(const DDS_Security_HandshakeMessageToken *token, HandshakeInfo *handshake, DDS_Security_SecurityException *ex) +static const DDS_Security_BinaryProperty_t *find_required_nonempty_binprop (const DDS_Security_HandshakeMessageToken *token, const char *name, DDS_Security_SecurityException *ex) { - IdentityRelation *relation = handshake->relation; - const DDS_Security_BinaryProperty_t *dh1, *dh2, *hash_c1, *hash_c2, *challenge1, *challenge2, *signature; - EVP_PKEY *public_key; - - assert(relation); - - if (!token->class_id || strncmp(AUTH_HANDSHAKE_FINAL_TOKEN_ID, token->class_id, strlen(AUTH_HANDSHAKE_FINAL_TOKEN_ID)) != 0) + DDS_Security_ValidationResult_t result; + const DDS_Security_BinaryProperty_t *prop = find_required_binprop (token, name, ex); + if (prop != NULL && (prop->value._length == 0 || prop->value._buffer == NULL)) { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "process_handshake: HandshakeMessageToken incorrect class_id: %s (expected %s)", token->class_id ? token->class_id : "NULL", AUTH_HANDSHAKE_FINAL_TOKEN_ID); - goto failed; + // FIXME: _buffer == NULL check must go, that should've been guaranteed before + result = set_exception (ex, "process_handshake: HandshakeMessageToken property %s is empty", name); + (void) result; + return NULL; } + return prop; +} - /* Check presents of mandatory properties: challenge1, challenge2, signature */ - - /* dh1 and dh2 are optional */ - dh1 = DDS_Security_DataHolder_find_binary_property(token, "dh1"); - DDSRT_UNUSED_ARG(dh1); - dh2 = DDS_Security_DataHolder_find_binary_property(token, "dh2"); - DDSRT_UNUSED_ARG(dh2); - - /* hash_c1 is optional */ - if ((hash_c1 = DDS_Security_DataHolder_find_binary_property(token, "hash_c1"))) +static const DDS_Security_BinaryProperty_t *find_required_binprop_exactsize (const DDS_Security_HandshakeMessageToken *token, const char *name, size_t size, DDS_Security_SecurityException *ex) +{ + DDS_Security_ValidationResult_t result; + const DDS_Security_BinaryProperty_t *prop = find_required_binprop (token, name, ex); + if (prop != NULL && prop->value._length != size) { - if (hash_c1->value._length != sizeof(HashValue_t) || memcmp(hash_c1->value._buffer, handshake->hash_c1, sizeof(HashValue_t)) != 0) + result = set_exception (ex, "process_handshake: HandshakeMessageToken property %s has wrong size (%"PRIu32" while expecting %"PRIuSIZE")", name, prop->value._length, size); + (void) result; + return NULL; + } + return prop; +} + +static X509 *load_X509_certificate_from_binprop (const DDS_Security_BinaryProperty_t *prop, X509 *own_ca, const X509Seq *trusted_ca_list, DDS_Security_SecurityException *ex) +{ + X509 *cert; + + if (load_X509_certificate_from_data ((char *) prop->value._buffer, (int) prop->value._length, &cert, ex) != DDS_SECURITY_VALIDATION_OK) + return NULL; + + DDS_Security_ValidationResult_t result = DDS_SECURITY_VALIDATION_FAILED; + if (trusted_ca_list->length == 0) + result = verify_certificate (cert, own_ca, ex); + else + { + DDS_Security_Exception_clean (ex); + for (unsigned i = 0; i < trusted_ca_list->length; ++i) { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "begin_handshake_reply: HandshakeMessageToken property hash_c1 invalid"); - goto failed; + DDS_Security_Exception_reset (ex); + if ((result = verify_certificate (cert, trusted_ca_list->buffer[i], ex)) == DDS_SECURITY_VALIDATION_OK) + break; } } - - /* hash_c2 is optional */ - if ((hash_c2 = DDS_Security_DataHolder_find_binary_property(token, "hash_c2"))) + if (result != DDS_SECURITY_VALIDATION_OK || check_certificate_expiry (cert, ex) != DDS_SECURITY_VALIDATION_OK) { - if (hash_c2->value._length != sizeof(HashValue_t) || memcmp(hash_c2->value._buffer, handshake->hash_c2, sizeof(HashValue_t)) != 0) + X509_free (cert); + return NULL; + } + return cert; +} + +static DDS_Security_ValidationResult_t validate_handshake_token_impl (const DDS_Security_HandshakeMessageToken *token, enum handshake_token_type token_type, + HandshakeInfo *handshake, EVP_PKEY **pdhkey_reply, X509Seq *trusted_ca_list, EVP_PKEY **pdhkey_req, X509 **identityCert, DDS_Security_SecurityException *ex) +{ + IdentityRelation * const relation = handshake->relation; + const DDS_Security_BinaryProperty_t *c_pdata = NULL; + AuthenticationAlgoKind_t dsignAlgoKind = AUTH_ALGO_KIND_UNKNOWN, kagreeAlgoKind = AUTH_ALGO_KIND_UNKNOWN; + const DDS_Security_BinaryProperty_t *dh1 = NULL, *dh2 = NULL; + const DDS_Security_BinaryProperty_t *hash_c1 = NULL, *hash_c2 = NULL; + const DDS_Security_BinaryProperty_t *challenge1 = NULL, *challenge2 = NULL; + const DDS_Security_BinaryProperty_t *signature = NULL; + const char *token_class_id = NULL; + + assert (relation); + assert (pdhkey_req == NULL || *pdhkey_req == NULL); + assert (pdhkey_reply == NULL || *pdhkey_reply == NULL); + assert (pdhkey_req == NULL || token_type == HS_TOKEN_REQ); + assert (pdhkey_reply == NULL || token_type == HS_TOKEN_REPLY); + + switch (token_type) + { + case HS_TOKEN_REQ: token_class_id = AUTH_HANDSHAKE_REQUEST_TOKEN_ID; break; + case HS_TOKEN_REPLY: token_class_id = AUTH_HANDSHAKE_REPLY_TOKEN_ID; break; + case HS_TOKEN_FINAL: token_class_id = AUTH_HANDSHAKE_FINAL_TOKEN_ID; break; + } + assert (token_class_id); + + if (!token->class_id || strncmp (token_class_id, token->class_id, strlen (token_class_id)) != 0) + return set_exception (ex, "process_handshake: HandshakeMessageToken incorrect class_id: %s (expected %s)", token->class_id ? token->class_id : "NULL", token_class_id); + + if (token_type == HS_TOKEN_REQ || token_type == HS_TOKEN_REPLY) + { + const DDS_Security_BinaryProperty_t *c_id, *c_perm, *c_dsign_algo, *c_kagree_algo; + + if ((c_id = find_required_nonempty_binprop (token, "c.id", ex)) == NULL) + return DDS_SECURITY_VALIDATION_FAILED; + if ((*identityCert = load_X509_certificate_from_binprop (c_id, relation->localIdentity->identityCA, trusted_ca_list, ex)) == NULL) + return DDS_SECURITY_VALIDATION_FAILED; + + if ((c_perm = find_required_binprop (token, "c.perm", ex)) == NULL) + return DDS_SECURITY_VALIDATION_FAILED; + if (c_perm->value._length > 0) { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "begin_handshake_reply: HandshakeMessageToken property hash_c2 invalid"); - goto failed; + ddsrt_free (relation->remoteIdentity->permissionsDocument); + relation->remoteIdentity->permissionsDocument = string_from_data (c_perm->value._buffer, c_perm->value._length); } + + if ((c_pdata = find_required_binprop (token, "c.pdata", ex)) == NULL) + return DDS_SECURITY_VALIDATION_FAILED; + if (validate_pdata (&c_pdata->value, *identityCert, ex) != DDS_SECURITY_VALIDATION_OK) + return DDS_SECURITY_VALIDATION_FAILED; + + if ((c_dsign_algo = find_required_nonempty_binprop (token, "c.dsign_algo", ex)) == NULL) + return DDS_SECURITY_VALIDATION_FAILED; + if ((dsignAlgoKind = get_dsign_algo_from_string ((const char *) c_dsign_algo->value._buffer)) == AUTH_ALGO_KIND_UNKNOWN) + return set_exception (ex, "process_handshake: HandshakeMessageToken property c.dsign_algo not supported"); + + if ((c_kagree_algo = find_required_nonempty_binprop (token, "c.kagree_algo", ex)) == NULL) + return DDS_SECURITY_VALIDATION_FAILED; + if ((kagreeAlgoKind = get_kagree_algo_from_string ((const char *) c_kagree_algo->value._buffer)) == AUTH_ALGO_KIND_UNKNOWN) + return set_exception (ex, "process_handshake: HandshakeMessageToken property c.kagree_algo not supported"); + + /* calculate the hash value and set in handshake hash_c1 (req) or hash_c2 (reply) */ + const DDS_Security_BinaryProperty_t *binary_properties[] = { c_id, c_perm, c_pdata, c_dsign_algo, c_kagree_algo }; + (void) compute_hash_value ((token_type == HS_TOKEN_REQ) ? handshake->hash_c1 : handshake->hash_c2, binary_properties, 5, NULL); } - if (!(challenge1 = DDS_Security_DataHolder_find_binary_property(token, "challenge1"))) + if ((dh1 = find_required_nonempty_binprop (token, "dh1", ex)) == NULL) + return DDS_SECURITY_VALIDATION_FAILED; + if ((challenge1 = find_required_binprop_exactsize (token, "challenge1", sizeof (AuthenticationChallenge), ex)) == NULL) + return DDS_SECURITY_VALIDATION_FAILED; + if (token_type == HS_TOKEN_REQ) { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "process_handshake: HandshakeMessageToken property challenge1 missing"); - goto failed; - } - if (challenge1->value._length != sizeof(AuthenticationChallenge) || challenge1->value._buffer == NULL) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "process_handshake: HandshakeMessageToken property challenge1 invalid"); - goto failed; + if (dh_oct_to_public_key (pdhkey_req, kagreeAlgoKind, dh1->value._buffer, dh1->value._length, ex) != DDS_SECURITY_VALIDATION_OK) + return DDS_SECURITY_VALIDATION_FAILED; } - if (!(challenge2 = DDS_Security_DataHolder_find_binary_property(token, "challenge2"))) + if (token_type == HS_TOKEN_REPLY || token_type == HS_TOKEN_FINAL) { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "process_handshake: HandshakeMessageToken property challenge2 missing"); - goto failed; + if ((dh2 = find_required_nonempty_binprop (token, "dh2", ex)) == NULL) + return DDS_SECURITY_VALIDATION_FAILED; + if ((challenge2 = find_required_binprop_exactsize (token, "challenge2", sizeof (AuthenticationChallenge), ex)) == NULL) + return DDS_SECURITY_VALIDATION_FAILED; + if ((signature = find_required_nonempty_binprop (token, "signature", ex)) == NULL) + return DDS_SECURITY_VALIDATION_FAILED; } - if (challenge2->value._length != sizeof(AuthenticationChallenge) || challenge2->value._buffer == NULL) + if (token_type == HS_TOKEN_REPLY) { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "process_handshake: HandshakeMessageToken property challenge2 invalid"); - goto failed; + if (dh_oct_to_public_key (pdhkey_reply, kagreeAlgoKind, dh2->value._buffer, dh2->value._length, ex) != DDS_SECURITY_VALIDATION_OK) + return DDS_SECURITY_VALIDATION_FAILED; } - /* When validate_remote_identity was provided with a remote_auth_request_token then the future_challenge in the remote identity was set and the challenge1 - property of the handshake_reply_token should be the same as the future_challenge stored in the remote identity. */ + /* When validate_remote_identity was provided with a remote_auth_request_token then the future_challenge in + the remote identity was set and the challenge(1|2) property of the handshake_(request|reply|final)_token + should be the same as the future_challenge stored in the remote identity. */ + const DDS_Security_BinaryProperty_t *rc = (token_type == HS_TOKEN_REPLY) ? challenge2 : challenge1; if (relation->rchallenge) { - if (memcmp(relation->rchallenge->value, challenge1->value._buffer, sizeof(AuthenticationChallenge)) != 0) + if (memcmp (relation->rchallenge->value, rc->value._buffer, sizeof (AuthenticationChallenge)) != 0) + return set_exception (ex, "process_handshake: HandshakeMessageToken property challenge%d does not match future_challenge", (token_type == HS_TOKEN_REPLY) ? 2 : 1); + } + else if (token_type != HS_TOKEN_FINAL) + { + relation->rchallenge = ddsrt_memdup (rc->value._buffer, sizeof (AuthenticationChallenge)); + } + + /* From DDS Security spec: inclusion of the hash_c1 property is optional. Its only purpose is to + facilitate troubleshoot interoperability problems. */ + if ((hash_c1 = DDS_Security_DataHolder_find_binary_property (token, "hash_c1"))) + { + /* hash_c1 should be set during req or reply token validation */ + assert (handshake->hash_c1 != NULL); + if (hash_c1->value._length != sizeof (HashValue_t) || memcmp (hash_c1->value._buffer, handshake->hash_c1, sizeof (HashValue_t)) != 0) + return set_exception (ex, "process_handshake: HandshakeMessageToken property hash_c1 invalid"); + } + + if (token_type == HS_TOKEN_REPLY || token_type == HS_TOKEN_FINAL) + { + /* hash_c2 should be set during reply token validation */ + assert (handshake->hash_c2 != NULL); + + /* From DDS Security spec: inclusion of the hash_c2 property is optional. Its only purpose is to + facilitate troubleshoot interoperability problems. */ + if ((hash_c2 = DDS_Security_DataHolder_find_binary_property (token, "hash_c2"))) { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "process_handshake: HandshakeMessageToken property challenge1 does not match future_challenge"); - goto failed; + if (hash_c2->value._length != sizeof (HashValue_t) || memcmp (hash_c2->value._buffer, handshake->hash_c2, sizeof (HashValue_t)) != 0) + return set_exception (ex, "process_handshake: HandshakeMessageToken property hash_c2 invalid"); } - } - else - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "process_handshake: No challenge exists to check challenge1 in the token."); - goto failed; + if (relation->lchallenge == NULL) + return set_exception (ex, "process_handshake: No future challenge exists for this token"); + const DDS_Security_BinaryProperty_t *lc = (token_type == HS_TOKEN_REPLY) ? challenge1 : challenge2; + if (memcmp (relation->lchallenge->value, lc->value._buffer, sizeof (AuthenticationChallenge)) != 0) + return set_exception (ex, "process_handshake: HandshakeMessageToken property challenge1 does not match future_challenge"); } - if (relation->lchallenge) + if (token_type == HS_TOKEN_REQ) { - if (memcmp(relation->lchallenge->value, challenge2->value._buffer, sizeof(AuthenticationChallenge)) != 0) - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "process_handshake: HandshakeMessageToken property challenge2 does not match future_challenge"); - goto failed; - } - } - else - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "process_handshake: No challenge exists to check challenge2 in the token."); - goto failed; + if (!EVP_PKEY_up_ref (*pdhkey_req)) + return set_exception (ex, "process_handshake: failed to increment refcount of DH key"); + handshake->rdh = *pdhkey_req; } - if (!(signature = DDS_Security_DataHolder_find_binary_property(token, "signature"))) + if (token_type == HS_TOKEN_REQ || token_type == HS_TOKEN_REPLY) { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "process_handshake: HandshakeMessageToken property signature missing"); - goto failed; + assert (*identityCert != NULL); + assert (dsignAlgoKind != AUTH_ALGO_KIND_UNKNOWN); + assert (kagreeAlgoKind != AUTH_ALGO_KIND_UNKNOWN); + assert (c_pdata != NULL); + + /* TODO: check if an identity certificate was already associated with the remote identity and when that is the case both should be the same */ + if (relation->remoteIdentity->identityCert) + X509_free (relation->remoteIdentity->identityCert); + if (!X509_up_ref (*identityCert)) + return set_exception (ex, "process_handshake: failed to increment refcount of identity certificate"); + relation->remoteIdentity->identityCert = *identityCert; + relation->remoteIdentity->dsignAlgoKind = dsignAlgoKind; + relation->remoteIdentity->kagreeAlgoKind = kagreeAlgoKind; + DDS_Security_OctetSeq_copy (&relation->remoteIdentity->pdata, &c_pdata->value); } - /* Validate signature */ - if ((public_key = X509_get_pubkey(relation->remoteIdentity->identityCert))) + if (token_type == HS_TOKEN_REPLY || token_type == HS_TOKEN_FINAL) { - DDS_Security_BinaryProperty_t *hash_c1_val = hash_value_to_binary_property("hash_c1", handshake->hash_c1); - DDS_Security_BinaryProperty_t *hash_c2_val = hash_value_to_binary_property("hash_c2", handshake->hash_c2); - const DDS_Security_BinaryProperty_t *properties[HANDSHAKE_SIGNATURE_CONTENT_SIZE] = { hash_c1_val, challenge1, dh1, challenge2, dh2, hash_c2_val }; - DDS_Security_ValidationResult_t result = validate_signature(public_key, properties, HANDSHAKE_SIGNATURE_CONTENT_SIZE, signature->value._buffer, signature->value._length, ex); - EVP_PKEY_free(public_key); - DDS_Security_BinaryProperty_free(hash_c1_val); - DDS_Security_BinaryProperty_free(hash_c2_val); + EVP_PKEY *public_key; + if ((public_key = X509_get_pubkey (relation->remoteIdentity->identityCert)) == NULL) + return set_exception (ex, "X509_get_pubkey failed"); + + DDS_Security_BinaryProperty_t hash_c1_val = { + .name = "hash_c1", .value = { ._length = sizeof (handshake->hash_c1), ._buffer = handshake->hash_c1 } + }; + DDS_Security_BinaryProperty_t hash_c2_val = { + .name = "hash_c2", .value = { ._length = sizeof (handshake->hash_c2), ._buffer = handshake->hash_c2 } + }; + const DDS_Security_BinaryProperty_t **properties; + if (token_type == HS_TOKEN_REPLY) + properties = (const DDS_Security_BinaryProperty_t *[]) { &hash_c2_val, challenge2, dh2, challenge1, dh1, &hash_c1_val }; + else + properties = (const DDS_Security_BinaryProperty_t *[]) { &hash_c1_val, challenge1, dh1, challenge2, dh2, &hash_c2_val }; + const DDS_Security_ValidationResult_t result = validate_signature (public_key, properties, 6, signature->value._buffer, signature->value._length, ex); + EVP_PKEY_free (public_key); if (result != DDS_SECURITY_VALIDATION_OK) - goto failed; + return result; } - else - { - DDS_Security_Exception_set(ex, DDS_AUTH_PLUGIN_CONTEXT, DDS_SECURITY_ERR_UNDEFINED_CODE, DDS_SECURITY_VALIDATION_FAILED, "X509_get_pubkey failed"); - goto failed; - } - return DDS_SECURITY_VALIDATION_OK; -failed: - return DDS_SECURITY_VALIDATION_FAILED; + return DDS_SECURITY_VALIDATION_OK; +} + +static DDS_Security_ValidationResult_t validate_handshake_token(const DDS_Security_HandshakeMessageToken *token, enum handshake_token_type token_type, HandshakeInfo *handshake, + EVP_PKEY **pdhkey_reply, X509Seq *trusted_ca_list, DDS_Security_SecurityException *ex) +{ + X509 *identityCert = NULL; + EVP_PKEY *pdhkey_req = NULL; + + const DDS_Security_ValidationResult_t ret = validate_handshake_token_impl (token, token_type, handshake, pdhkey_reply, trusted_ca_list, + (token_type == HS_TOKEN_REQ) ? &pdhkey_req : NULL, &identityCert, ex); + + if (ret != DDS_SECURITY_VALIDATION_OK) + { + IdentityRelation *relation = handshake->relation; + if (relation->remoteIdentity->identityCert) + { + X509_free (relation->remoteIdentity->identityCert); + relation->remoteIdentity->identityCert = NULL; + } + if (pdhkey_reply && *pdhkey_reply) + EVP_PKEY_free (*pdhkey_reply); + } + + if (pdhkey_req) + EVP_PKEY_free (pdhkey_req); + if (identityCert) + { + /* free id cert also when validation succeeded, because the refcount was increased + when assigning this cert to relation->remoteIdentity */ + X509_free (identityCert); + } + return ret; } DDS_Security_ValidationResult_t begin_handshake_reply(dds_security_authentication *instance, DDS_Security_HandshakeHandle *handshake_handle, @@ -1892,7 +1644,7 @@ DDS_Security_ValidationResult_t begin_handshake_reply(dds_security_authenticatio assert(relation); } - if (validate_handshake_request_token(handshake_message_in, handshake, &(impl->trustedCAList), ex) != DDS_SECURITY_VALIDATION_OK) + if (validate_handshake_token(handshake_message_in, HS_TOKEN_REQ, handshake, NULL, &(impl->trustedCAList), ex) != DDS_SECURITY_VALIDATION_OK) goto err_inv_token; if (get_certificate_contents(localIdent->identityCert, &certData, &certDataSize, ex) != DDS_SECURITY_VALIDATION_OK) goto err_alloc_cid; @@ -1937,7 +1689,7 @@ DDS_Security_ValidationResult_t begin_handshake_reply(dds_security_authenticatio DDS_Security_BinaryProperty_set_by_string(c_dsign_algo, "c.dsign_algo", get_dsign_algo(localIdent->dsignAlgoKind)); DDS_Security_BinaryProperty_set_by_string(c_kagree_algo, "c.kagree_algo", get_kagree_algo(remoteIdent->kagreeAlgoKind)); - /* Calculate the hash_c2 */ + /* Todo: including hash_c2 is optional (conform spec); add a configuration option to leave it out */ { DDS_Security_BinaryPropertySeq bseq = { ._length = 5, ._buffer = tokens }; get_hash_binary_property_seq(&bseq, handshake->hash_c2); @@ -2099,7 +1851,7 @@ DDS_Security_ValidationResult_t process_handshake(dds_security_authentication *i case CREATEDREQUEST: /* The source of the handshake_handle is a begin_handshake_request function. So, handshake_message_in is from a remote begin_handshake_reply function */ /* Verify Message Token contents according to Spec 9.3.2.5.2 (Reply Message) */ - if (validate_handshake_reply_token(handshake_message_in, handshake, &dhkeyRemote, &(impl->trustedCAList), ex) != DDS_SECURITY_VALIDATION_OK) + if (validate_handshake_token(handshake_message_in, HS_TOKEN_REPLY, handshake, &dhkeyRemote, &(impl->trustedCAList), ex) != DDS_SECURITY_VALIDATION_OK) goto err_inv_token; handshake->rdh = dhkeyRemote; @@ -2168,7 +1920,7 @@ DDS_Security_ValidationResult_t process_handshake(dds_security_authentication *i case CREATEDREPLY: /* The source of the handshake_handle is a begin_handshake_reply function So, handshake_message_in is from a remote process_handshake function */ /* Verify Message Token contents according to Spec 9.3.2.5.3 (Final Message) */ - if (validate_handshake_final_token(handshake_message_in, handshake, ex) != DDS_SECURITY_VALIDATION_OK) + if (validate_handshake_token(handshake_message_in, HS_TOKEN_FINAL, handshake, NULL, NULL, ex) != DDS_SECURITY_VALIDATION_OK) goto err_inv_token; challenge2_ref_for_shared_secret = (DDS_Security_octet *)(handshake->relation->lchallenge); challenge1_ref_for_shared_secret = (DDS_Security_octet *)(handshake->relation->rchallenge); diff --git a/src/security/core/include/dds/security/core/dds_security_utils.h b/src/security/core/include/dds/security/core/dds_security_utils.h index c9cbf0e..c61829a 100644 --- a/src/security/core/include/dds/security/core/dds_security_utils.h +++ b/src/security/core/include/dds/security/core/dds_security_utils.h @@ -14,6 +14,7 @@ #define DSCMN_SECURITY_UTILS_H_ #include +#include #include #include #include "dds/export.h" @@ -261,6 +262,15 @@ DDS_EXPORT void DDS_Security_HandleSeq_deinit( DDS_Security_HandleSeq *seq); +DDS_EXPORT void +DDS_Security_Exception_vset( + DDS_Security_SecurityException *ex, + const char *context, + int code, + int minor_code, + const char *fmt, + va_list ap); + DDS_EXPORT void DDS_Security_Exception_set( DDS_Security_SecurityException *ex, @@ -268,7 +278,7 @@ DDS_Security_Exception_set( int code, int minor_code, const char *fmt, - ...); + ...); #ifdef DDSI_INCLUDE_SSL diff --git a/src/security/core/src/dds_security_utils.c b/src/security/core/src/dds_security_utils.c index 3c04c60..723a383 100644 --- a/src/security/core/src/dds_security_utils.c +++ b/src/security/core/src/dds_security_utils.c @@ -760,50 +760,49 @@ DDS_Security_HandleSeq_deinit( DDS_Security_HandleSeq_freebuf(seq); } - - - -void -DDS_Security_Exception_set( - DDS_Security_SecurityException *ex, - const char *context, - int code, - int minor_code, - const char *fmt, - ...) +void DDS_Security_Exception_vset (DDS_Security_SecurityException *ex, const char *context, int code, int minor_code, const char *fmt, va_list args1) { - int32_t ret; - size_t len; - char buf[1] = { '\0' }; - char *str = NULL; - va_list args1, args2; + int32_t ret; + size_t len; + char buf[1] = { '\0' }; + char *str = NULL; + va_list args2; - assert(context); - assert(fmt); - assert(ex); - DDSRT_UNUSED_ARG( context ); + assert(context); + assert(fmt); + assert(ex); + DDSRT_UNUSED_ARG( context ); - va_start(args1, fmt); - va_copy(args2, args1); + va_copy(args2, args1); - if ((ret = vsnprintf(buf, sizeof(buf), fmt, args1)) >= 0) { - len = (size_t)ret; /* +1 for null byte */ - if ((str = ddsrt_malloc(len + 1)) == NULL) { - assert(false); - } else if ((ret = vsnprintf(str, len + 1, fmt, args2)) >= 0) { - assert((size_t) ret == len); - } else { - ddsrt_free(str); - str = NULL; - } + if ((ret = vsnprintf(buf, sizeof(buf), fmt, args1)) >= 0) { + len = (size_t)ret; /* +1 for null byte */ + if ((str = ddsrt_malloc(len + 1)) == NULL) { + assert(false); + } else if ((ret = vsnprintf(str, len + 1, fmt, args2)) >= 0) { + assert((size_t) ret == len); + } else { + ddsrt_free(str); + str = NULL; } + } - va_end(args1); - va_end(args2); + va_end(args1); - ex->message = str; - ex->code = code; - ex->minor_code = minor_code; + ex->message = str; + ex->code = code; + ex->minor_code = minor_code; +} + +void DDS_Security_Exception_set (DDS_Security_SecurityException *ex, const char *context, int code, int minor_code, const char *fmt, ...) +{ + va_list args1; + assert(context); + assert(fmt); + assert(ex); + va_start(args1, fmt); + DDS_Security_Exception_vset (ex, context, code, minor_code, fmt, args1); + va_end(args1); } #ifdef DDSI_INCLUDE_SSL