diff --git a/CMakeLists.txt b/CMakeLists.txt index c0e34a14..9a9a078c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,6 +96,7 @@ option(ENABLE_DNSSEC "Enable support for SSHFP retrieval using DNSSEC for SSH (r option(ENABLE_PAM "Detect and use PAM" ON) option(ENABLE_COMMON_TARGETS "Define common custom target names such as 'doc' or 'uninstall', may cause conflicts when using add_subdirectory() to build this project" ON) option(ENABLE_IP_FREEBIND "Enable the IP_FREEBIND/IPV6_FREEBIND options on the listening TCP socket" OFF) +option(ENABLE_COMPLY_WITH_ORAN_WG11 "Comply with the O-RAN WG11 security spec (e.g. enforce the clientAuth Extended Key Usage and the digitalSignature Key Usage on client certificates)" OFF) option(BUILD_SHARED_LIBS "By default, shared libs are enabled. Turn off for a static build." ON) set(READ_INACTIVE_TIMEOUT 20 CACHE STRING "Maximum number of seconds waiting for new data once some data have arrived") set(READ_ACTIVE_TIMEOUT 300 CACHE STRING "Maximum number of seconds for receiving a full message") @@ -181,6 +182,11 @@ if(ENABLE_DNSSEC AND NOT ENABLE_SSH_TLS) set(ENABLE_DNSSEC OFF) endif() +if(ENABLE_COMPLY_WITH_ORAN_WG11 AND NOT ENABLE_SSH_TLS) + message(WARNING "O-RAN WG11 compliance cannot be used without TLS support.") + set(ENABLE_COMPLY_WITH_ORAN_WG11 OFF) +endif() + if(ENABLE_VALGRIND_TESTS) find_program(VALGRIND_FOUND valgrind) if(NOT VALGRIND_FOUND) @@ -309,6 +315,11 @@ if(ENABLE_SSH_TLS) # set compiler flag set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNC_ENABLED_SSH_TLS") + + # comply with the O-RAN WG11 security spec (e.g. client cert Extended Key Usage and Key Usage checks) + if(ENABLE_COMPLY_WITH_ORAN_WG11) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNC_COMPLY_WITH_ORAN_WG11") + endif() endif() # dependencies - libval diff --git a/README.md b/README.md index 16dceeaa..6308b753 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,19 @@ interaction. Enable it with the following command. $ cmake -DENABLE_DNSSEC=ON .. ``` +### O-RAN WG11 Compliance + +By default, **libnetconf2** implements only the base YANG and NETCONF +specifications. The O-RAN WG11 security specification (section 6.9.3) imposes +additional requirements on the client certificate used for TLS authentication, +namely that its end-entity certificate must carry the `clientAuth` Extended Key +Usage and the `digitalSignature` Key Usage. This behavior is not part of the +base specification, so it is disabled by default and can be enabled with the +following command (requires TLS support). +``` +$ cmake -DENABLE_COMPLY_WITH_ORAN_WG11=ON .. +``` + ### Build Modes There are two build modes: diff --git a/src/session_mbedtls.c b/src/session_mbedtls.c index 81af476e..035b3305 100644 --- a/src/session_mbedtls.c +++ b/src/session_mbedtls.c @@ -715,6 +715,49 @@ nc_server_tls_prepend_issuer_chain(mbedtls_x509_crt *cert, mbedtls_x509_crt **ch return -1; } +#ifdef NC_COMPLY_WITH_ORAN_WG11 +/** + * @brief Check that a client certificate complies with the O-RAN WG11 6.9.3 key usage requirements. + * + * The end-entity (client) certificate MUST carry the clientAuth Extended Key Usage and a Key Usage + * extension with the digitalSignature bit set (the client signs the CertificateVerify handshake message). + * + * @param[in] session Session for logging. + * @param[in] cert Client (leaf) certificate to check. + * @param[in,out] flags Verification flags, set on failure. + * @return 0 if the certificate complies, non-zero otherwise. + */ +static int +nc_server_tls_check_oran_wg11_usage(struct nc_session *session, mbedtls_x509_crt *cert, uint32_t *flags) +{ + const mbedtls_x509_sequence *usage; + int has_client_auth = 0; + + /* the client certificate MUST carry the clientAuth Extended Key Usage */ + for (usage = &cert->ext_key_usage; usage && usage->buf.p; usage = usage->next) { + if (!MBEDTLS_OID_CMP(MBEDTLS_OID_CLIENT_AUTH, &usage->buf)) { + has_client_auth = 1; + break; + } + } + if (!has_client_auth) { + ERR(session, "Cert verify: fail (client certificate lacks the required clientAuth Extended Key Usage)."); + *flags |= MBEDTLS_X509_BADCERT_EXT_KEY_USAGE; + return 1; + } + + /* the client certificate MUST also carry a Key Usage extension with the digitalSignature bit set */ + if (!mbedtls_x509_crt_has_ext_type(cert, MBEDTLS_X509_EXT_KEY_USAGE) || + mbedtls_x509_crt_check_key_usage(cert, MBEDTLS_X509_KU_DIGITAL_SIGNATURE)) { + ERR(session, "Cert verify: fail (client certificate lacks the required digitalSignature Key Usage)."); + *flags |= MBEDTLS_X509_BADCERT_KEY_USAGE; + return 1; + } + + return 0; +} +#endif /* NC_COMPLY_WITH_ORAN_WG11 */ + /** * @brief Verify a certificate. * @@ -789,6 +832,13 @@ nc_server_tls_verify_cb(void *cb_data, mbedtls_x509_crt *cert, int depth, uint32 } else if (!ret) { /* success */ if ((depth == 0) && (!data->session->opts.server.client_cert)) { +#ifdef NC_COMPLY_WITH_ORAN_WG11 + /* O-RAN WG11 6.9.3: enforce the clientAuth Extended Key Usage and digitalSignature Key Usage */ + if (nc_server_tls_check_oran_wg11_usage(data->session, cert, flags)) { + return 0; + } +#endif /* NC_COMPLY_WITH_ORAN_WG11 */ + /* copy the client cert */ data->session->opts.server.client_cert = nc_tls_cert_dup(cert); if (!data->session->opts.server.client_cert) { diff --git a/src/session_openssl.c b/src/session_openssl.c index b21ea994..65de6d98 100644 --- a/src/session_openssl.c +++ b/src/session_openssl.c @@ -379,6 +379,56 @@ nc_server_tls_set_tls_versions_wrap(void *tls_cfg, enum nc_tls_version min, enum return 0; } +#ifdef NC_COMPLY_WITH_ORAN_WG11 +/** + * @brief Check that a client certificate complies with the O-RAN WG11 6.9.3 key usage requirements. + * + * The end-entity (client) certificate MUST carry the clientAuth Extended Key Usage and a Key Usage + * extension with the digitalSignature bit set (the client signs the CertificateVerify handshake message). + * + * @param[in] session Session for logging. + * @param[in] cert Client (leaf) certificate to check. + * @return 0 if the certificate complies, non-zero otherwise. + */ +static int +nc_server_tls_check_oran_wg11_usage(struct nc_session *session, X509 *cert) +{ + EXTENDED_KEY_USAGE *eku; + ASN1_BIT_STRING *ku; + int has_client_auth = 0, has_digital_signature = 0, i; + + /* the client certificate MUST carry the clientAuth Extended Key Usage */ + eku = X509_get_ext_d2i(cert, NID_ext_key_usage, NULL, NULL); + if (eku) { + for (i = 0; i < sk_ASN1_OBJECT_num(eku); ++i) { + if (OBJ_obj2nid(sk_ASN1_OBJECT_value(eku, i)) == NID_client_auth) { + has_client_auth = 1; + break; + } + } + EXTENDED_KEY_USAGE_free(eku); + } + if (!has_client_auth) { + ERR(session, "Cert verify: fail (client certificate lacks the required clientAuth Extended Key Usage)."); + return 1; + } + + /* the client certificate MUST also carry a Key Usage extension with the digitalSignature bit set */ + ku = X509_get_ext_d2i(cert, NID_key_usage, NULL, NULL); + if (ku) { + /* bit 0 of the Key Usage bit string is digitalSignature */ + has_digital_signature = ASN1_BIT_STRING_get_bit(ku, 0); + ASN1_BIT_STRING_free(ku); + } + if (!has_digital_signature) { + ERR(session, "Cert verify: fail (client certificate lacks the required digitalSignature Key Usage)."); + return 1; + } + + return 0; +} +#endif /* NC_COMPLY_WITH_ORAN_WG11 */ + /** * @brief Verify a certificate. * @@ -463,6 +513,13 @@ nc_server_tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) } else if (!ret) { /* success */ if ((depth == 0) && (!data->session->opts.server.client_cert)) { +#ifdef NC_COMPLY_WITH_ORAN_WG11 + /* O-RAN WG11 6.9.3: enforce the clientAuth Extended Key Usage and digitalSignature Key Usage */ + if (nc_server_tls_check_oran_wg11_usage(data->session, cert)) { + return 0; + } +#endif /* NC_COMPLY_WITH_ORAN_WG11 */ + /* copy the client cert */ data->session->opts.server.client_cert = X509_dup(cert); NC_CHECK_ERRMEM_RET(!data->session->opts.server.client_cert, 0);