diff --git a/CMakeLists.txt b/CMakeLists.txt index c0e34a14..4e400c65 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), non-default behavior" 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} -DCOMPLY_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..3b7854ef 100644 --- a/src/session_mbedtls.c +++ b/src/session_mbedtls.c @@ -789,6 +789,37 @@ 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 COMPLY_WITH_ORAN_WG11 + /* WG11 6.9.3 / O-RAN: the end-entity (client) certificate MUST carry + * the clientAuth Extended Key Usage, so explicitly reject any leaf + * certificate lacking the clientAuth EKU extension. */ + const mbedtls_x509_sequence *usage; + int has_client_auth = 0; + + 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(data->session, "Cert verify: fail (client certificate lacks the required clientAuth Extended Key Usage)."); + *flags |= MBEDTLS_X509_BADCERT_EXT_KEY_USAGE; + return 0; + } + + /* WG11 6.9.3 / O-RAN: the end-entity (client) certificate MUST also + * carry a Key Usage extension with the digitalSignature bit set (the + * client signs the CertificateVerify handshake message), so reject any + * leaf certificate missing the Key Usage extension or that bit. */ + 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(data->session, "Cert verify: fail (client certificate lacks the required digitalSignature Key Usage)."); + *flags |= MBEDTLS_X509_BADCERT_KEY_USAGE; + return 0; + } +#endif /* 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..5d93059d 100644 --- a/src/session_openssl.c +++ b/src/session_openssl.c @@ -463,6 +463,49 @@ 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 COMPLY_WITH_ORAN_WG11 + /* WG11 6.9.3 / O-RAN: the end-entity (client) certificate MUST carry + * the clientAuth Extended Key Usage, so explicitly reject any leaf + * certificate lacking the clientAuth EKU extension. */ + { + EXTENDED_KEY_USAGE *eku = X509_get_ext_d2i(cert, NID_ext_key_usage, NULL, NULL); + int has_client_auth = 0, i; + + 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(data->session, "Cert verify: fail (client certificate lacks the required clientAuth Extended Key Usage)."); + return 0; + } + } + + /* WG11 6.9.3 / O-RAN: the end-entity (client) certificate MUST also + * carry a Key Usage extension with the digitalSignature bit set (the + * client signs the CertificateVerify handshake message), so reject any + * leaf certificate missing the Key Usage extension or that bit. */ + { + ASN1_BIT_STRING *ku = X509_get_ext_d2i(cert, NID_key_usage, NULL, NULL); + int has_digital_signature = 0; + + 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(data->session, "Cert verify: fail (client certificate lacks the required digitalSignature Key Usage)."); + return 0; + } + } +#endif /* 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);