diff --git a/.azure/azure-pipelines-int.ci.yml b/.azure/azure-pipelines-int.ci.yml index b06d3e2d83..4f95f056d0 100644 --- a/.azure/azure-pipelines-int.ci.yml +++ b/.azure/azure-pipelines-int.ci.yml @@ -141,6 +141,7 @@ stages: tls: schannel logProfile: Full.Light extraArgs: -Filter -*CryptTest.HpMaskChaCha20:CryptTest.WellKnownChaChaPoly:Alpn.ValidAlpnLengths + testCerts: true # # Build Verification Tests (Kernel Mode) @@ -169,6 +170,7 @@ stages: logProfile: Full.Light extraArgs: -Kernel -Filter -*Handshake/WithHandshakeArgs6.ConnectClientCertificate/*:Alpn.ValidAlpnLengths kernel: true + testCerts: true # # Package diff --git a/.azure/azure-pipelines.ci.yml b/.azure/azure-pipelines.ci.yml index 1667816c4a..58842a9e25 100644 --- a/.azure/azure-pipelines.ci.yml +++ b/.azure/azure-pipelines.ci.yml @@ -438,6 +438,7 @@ stages: logProfile: Full.Verbose extraArgs: -Kernel -Filter -*Handshake/WithHandshakeArgs6.ConnectClientCertificate/*:Alpn.ValidAlpnLengths kernel: true + testCerts: true # # Build Verification Tests @@ -457,45 +458,46 @@ stages: tls: schannel logProfile: Full.Verbose extraArgs: -Filter -*CryptTest.HpMaskChaCha20:CryptTest.WellKnownChaChaPoly:Alpn.ValidAlpnLengths + testCerts: true - template: ./templates/run-bvt.yml parameters: image: windows-latest platform: windows tls: mitls logProfile: Full.Light - extraArgs: -Filter -*Unreachable/0:CryptTest/CryptTest.Encryption/2:CryptTest.WellKnownChaChaPoly:*HandshakeParam* + extraArgs: -Filter -*Unreachable/0:CryptTest/CryptTest.Encryption/2:CryptTest.WellKnownChaChaPoly:*HandshakeParam*:CredValidation* - template: ./templates/run-bvt.yml parameters: image: windows-latest platform: windows tls: openssl - extraArgs: -Filter -*Unreachable/0:AppData/*:Misc/* + extraArgs: -Filter -*Unreachable/0:AppData/*:Misc/*:CredValidation* - template: ./templates/run-bvt.yml parameters: image: ubuntu-latest platform: linux tls: openssl - extraArgs: -Filter -*.Tcp* + extraArgs: -Filter -*.Tcp*:CredValidation* - template: ./templates/run-bvt.yml parameters: image: ubuntu-latest platform: linux tls: stub - extraArgs: -Filter -*TlsTest.CertificateError:TlsTest.ExtraCertificateValidation:*.Tcp*:*HandshakeParam*:Handshake/WithHandshakeArgs6.ConnectClientCertificate/*:*ResumeRejection* + extraArgs: -Filter -*TlsTest.CertificateError:TlsTest.ExtraCertificateValidation:*.Tcp*:*HandshakeParam*:Handshake/WithHandshakeArgs6.ConnectClientCertificate/*:*ResumeRejection*:CredValidation* - template: ./templates/run-bvt.yml parameters: image: macOS-10.15 platform: macos tls: openssl logProfile: None - extraArgs: -Filter -*.Tcp* + extraArgs: -Filter -*.Tcp*:CredValidation* - template: ./templates/run-bvt.yml parameters: image: ubuntu-latest platform: linux tls: openssl extraArtifactDir: '_SystemCrypto' - extraArgs: -Filter -*.Tcp* -ExtraArtifactDir SystemCrypto + extraArgs: -Filter -*.Tcp*:CredValidation* -ExtraArtifactDir SystemCrypto # # SpinQuic Tests diff --git a/.azure/templates/run-bvt.yml b/.azure/templates/run-bvt.yml index 71fd0b08e5..8a49efd8e6 100644 --- a/.azure/templates/run-bvt.yml +++ b/.azure/templates/run-bvt.yml @@ -11,6 +11,7 @@ parameters: codeCoverage: false logProfile: 'Basic.Light' extraArgs: '' + testCerts: false extraArtifactDir: '' jobs: @@ -41,7 +42,10 @@ jobs: inputs: pwsh: true filePath: scripts/prepare-machine.ps1 - arguments: -Configuration Test + ${{ if eq(parameters.testCerts, true) }}: + arguments: -Configuration Test -TestCertificates + ${{ if eq(parameters.testCerts, false) }}: + arguments: -Configuration Test - task: PowerShell@2 displayName: Run BVTs diff --git a/scripts/prepare-machine.ps1 b/scripts/prepare-machine.ps1 index ac72300b7c..e4cfb66080 100644 --- a/scripts/prepare-machine.ps1 +++ b/scripts/prepare-machine.ps1 @@ -19,6 +19,9 @@ on the provided configuration. .PARAMETER Kernel Indicates build is for kernel mode. +.PARAMETER TestCertificates + Generate test certificates. Only supported for Windows test configuration. + .EXAMPLE prepare-machine.ps1 -Configuration Build @@ -45,7 +48,10 @@ param ( [switch]$Kernel, [Parameter(Mandatory = $false)] - [switch]$FailOnError + [switch]$FailOnError, + + [Parameter(Mandatory = $false)] + [switch]$TestCertificates ) #Requires -RunAsAdministrator @@ -174,6 +180,68 @@ if ($IsWindows) { } if ($Configuration -eq "Test") { + if ($TestCertificates) { + # Install test certificates on windows + $PfxPassword = ConvertTo-SecureString -String "TestCert" -Force -AsPlainText + $NewRoot = $false + Write-Host "Searching for MsQuicTestRoot certificate..." + $RootCert = Get-ChildItem -path Cert:\LocalMachine\Root\* -Recurse | Where-Object {$_.Subject -eq "CN=MsQuicTestRoot"} + if (!$RootCert) { + Write-Host "MsQuicTestRoot not found! Creating new MsQuicTestRoot certificate..." + $RootCert = New-SelfSignedCertificate -Subject "CN=MsQuicTestRoot" -FriendlyName MsQuicTestRoot -KeyUsageProperty Sign -KeyUsage CertSign,DigitalSignature -CertStoreLocation cert:\CurrentUser\My -HashAlgorithm SHA256 -Provider "Microsoft Software Key Storage Provider" -KeyExportPolicy Exportable -KeyAlgorithm ECDSA_nistP521 -CurveExport CurveName -NotAfter(Get-Date).AddYears(5) -TextExtension @("2.5.29.19 = {text}ca=1&pathlength=0") -Type Custom + $TempRootPath = Join-Path $Env:TEMP "MsQuicTestRoot.cer" + Export-Certificate -Type CERT -Cert $RootCert -FilePath $TempRootPath + CertUtil.exe -addstore Root $TempRootPath + Remove-Item $TempRootPath + $NewRoot = $true + Write-Host "New MsQuicTestRoot certificate installed!" + } else { + Write-Host "Found existing MsQuicTestRoot certificate!" + } + Write-Host "Searching for MsQuicTestServer certificate..." + $ServerCert = Get-ChildItem -path Cert:\LocalMachine\My\* -Recurse | Where-Object {$_.Subject -eq "CN=MsQuicTestServer"} + if (!$ServerCert) { + Write-Host "MsQuicTestServer not found! Creating new MsQuicTestServer certificate..." + $ServerCert = New-SelfSignedCertificate -Subject "CN=MsQuicTestServer" -DnsName $env:computername,localhost,"127.0.0.1","::1" -FriendlyName MsQuicTestServer -KeyUsageProperty Sign -KeyUsage DigitalSignature -CertStoreLocation cert:\CurrentUser\My -HashAlgorithm SHA256 -Provider "Microsoft Software Key Storage Provider" -KeyExportPolicy Exportable -KeyAlgorithm ECDSA_nistP256 -CurveExport CurveName -NotAfter(Get-Date).AddYears(5) -TextExtension @("2.5.29.19 = {text}","2.5.29.37 = {text}1.3.6.1.5.5.7.3.1") -Signer $RootCert + $TempServerPath = Join-Path $Env:TEMP "MsQuicTestServerCert.pfx" + Export-PfxCertificate -Cert $ServerCert -Password $PfxPassword -FilePath $TempServerPath + Import-PfxCertificate -FilePath $TempServerPath -Password $PfxPassword -Exportable -CertStoreLocation Cert:\LocalMachine\My + Remove-Item $TempServerPath + Write-Host "New MsQuicTestServer certificate installed!" + } else { + Write-Host "Found existing MsQuicTestServer certificate!" + } + Write-Host "Searching for MsQuicTestExpiredServer certificate..." + $ExpiredServerCert = Get-ChildItem -path Cert:\LocalMachine\My\* -Recurse | Where-Object {$_.Subject -eq "CN=MsQuicTestExpiredServer"} + if (!$ExpiredServerCert) { + Write-Host "MsQuicTestExpiredServer not found! Creating new MsQuicTestExpiredServer certificate..." + $ExpiredServerCert = New-SelfSignedCertificate -Subject "CN=MsQuicTestExpiredServer" -DnsName $env:computername,localhost,"127.0.0.1","::1" -FriendlyName MsQuicTestExpiredServer -KeyUsageProperty Sign -KeyUsage DigitalSignature -CertStoreLocation cert:\CurrentUser\My -HashAlgorithm SHA256 -Provider "Microsoft Software Key Storage Provider" -KeyExportPolicy Exportable -KeyAlgorithm ECDSA_nistP256 -CurveExport CurveName -NotBefore (Get-Date).AddYears(-2) -NotAfter(Get-Date).AddYears(-1) -TextExtension @("2.5.29.19 = {text}","2.5.29.37 = {text}1.3.6.1.5.5.7.3.1") -Signer $RootCert + $TempExpiredServerPath = Join-Path $Env:TEMP "MsQuicTestExpiredServerCert.pfx" + Export-PfxCertificate -Cert $ExpiredServerCert -Password $PfxPassword -FilePath $TempExpiredServerPath + Import-PfxCertificate -FilePath $TempExpiredServerPath -Password $PfxPassword -Exportable -CertStoreLocation Cert:\LocalMachine\My + Remove-Item $TempExpiredServerPath + Write-Host "New MsQuicTestExpiredServer certificate installed!" + } else { + Write-Host "Found existing MsQuicTestExpiredServer certificate!" + } + Write-Host "Searching for MsQuicTestClient certificate..." + $ClientCert = Get-ChildItem -path Cert:\LocalMachine\My\* -Recurse | Where-Object {$_.Subject -eq "CN=MsQuicTestClient"} + if (!$ClientCert) { + Write-Host "MsQuicTestClient not found! Creating new MsQuicTestClient certificate..." + $ClientCert = New-SelfSignedCertificate -Subject "CN=MsQuicTestClient" -FriendlyName MsQuicTestClient -KeyUsageProperty Sign -KeyUsage DigitalSignature -CertStoreLocation cert:\CurrentUser\My -HashAlgorithm SHA256 -Provider "Microsoft Software Key Storage Provider" -KeyExportPolicy Exportable -KeyAlgorithm ECDSA_nistP256 -CurveExport CurveName -NotAfter(Get-Date).AddYears(5) -TextExtension @("2.5.29.19 = {text}","2.5.29.37 = {text}1.3.6.1.5.5.7.3.2") -Signer $RootCert + $TempClientPath = Join-Path $Env:TEMP "MsQuicTestClientCert.pfx" + Export-PfxCertificate -Cert $ClientCert -Password $PfxPassword -FilePath $TempClientPath + Import-PfxCertificate -FilePath $TempClientPath -Password $PfxPassword -Exportable -CertStoreLocation Cert:\LocalMachine\My + Remove-Item $TempClientPath + Write-Host "New MsQuicTestClient certificate installed!" + }else { + Write-Host "Found existing MsQuicTestClient certificate!" + } + if ($NewRoot) { + Write-Host "Deleting MsQuicTestRoot from MY store..." + Remove-Item $rootCert.PSPath + } + } # Install OpenCppCoverage on test machines if (!(Test-Path "C:\Program Files\OpenCppCoverage\OpenCppCoverage.exe")) { # Download the installer. diff --git a/src/core/connection.c b/src/core/connection.c index bfe3e6fdcf..de7e592c1e 100644 --- a/src/core/connection.c +++ b/src/core/connection.c @@ -1403,7 +1403,11 @@ QuicErrorCodeToStatus( case QUIC_ERROR_CRYPTO_USER_CANCELED: return QUIC_STATUS_USER_CANCELED; case QUIC_ERROR_CRYPTO_HANDSHAKE_FAILURE: return QUIC_STATUS_HANDSHAKE_FAILURE; case QUIC_ERROR_CRYPTO_NO_APPLICATION_PROTOCOL: return QUIC_STATUS_ALPN_NEG_FAILURE; - default: return QUIC_STATUS_INTERNAL_ERROR; + default: + if (IS_QUIC_CRYPTO_ERROR(ErrorCode)) { + return QUIC_STATUS_TLS_ALERT(ErrorCode); + } + return QUIC_STATUS_INTERNAL_ERROR; } } diff --git a/src/core/crypto.c b/src/core/crypto.c index 697ffe1a46..36fab03394 100644 --- a/src/core/crypto.c +++ b/src/core/crypto.c @@ -1297,7 +1297,7 @@ QuicCryptoProcessTlsCompletion( "[conn][%p] ERROR, %u, %s.", Connection, Crypto->TlsState.AlertCode, - "Received alert from TLS."); + "Received alert from TLS"); QuicConnTransportError( Connection, QUIC_ERROR_CRYPTO_ERROR(0xFF & Crypto->TlsState.AlertCode)); @@ -1829,7 +1829,11 @@ QuicCryptoProcessAppData( &DataLength, &Crypto->TlsState); if (Crypto->ResultFlags & CXPLAT_TLS_RESULT_ERROR) { - Status = QUIC_STATUS_INTERNAL_ERROR; + if (Crypto->TlsState.AlertCode != 0) { + Status = QUIC_STATUS_TLS_ALERT(Crypto->TlsState.AlertCode); + } else { + Status = QUIC_STATUS_INTERNAL_ERROR; + } goto Error; } diff --git a/src/core/frame.h b/src/core/frame.h index 7e73009a5c..7a034def01 100644 --- a/src/core/frame.h +++ b/src/core/frame.h @@ -84,6 +84,7 @@ // described in Section 4.8 of [QUIC-TLS]. // #define QUIC_ERROR_CRYPTO_ERROR(TlsAlertCode) ((QUIC_VAR_INT)(0x100 | (TlsAlertCode))) +#define IS_QUIC_CRYPTO_ERROR(QuicCryptoError) ((QuicCryptoError & 0x100) == 0x100) #define QUIC_ERROR_CRYPTO_USER_CANCELED QUIC_ERROR_CRYPTO_ERROR(22) // TLS error code for 'user_canceled' #define QUIC_ERROR_CRYPTO_HANDSHAKE_FAILURE QUIC_ERROR_CRYPTO_ERROR(40) // TLS error code for 'handshake_failure' diff --git a/src/inc/msquic_posix.h b/src/inc/msquic_posix.h index ea48997088..8a4ae15ab6 100644 --- a/src/inc/msquic_posix.h +++ b/src/inc/msquic_posix.h @@ -118,6 +118,8 @@ inline ENUMTYPE &operator ^= (ENUMTYPE &a, ENUMTYPE b) throw() { return (ENUMTYP #define ERROR_ALPN_NEG_FAILURE 20 + ERROR_BASE #define ERROR_STREAM_LIMIT_REACHED 21 + ERROR_BASE +#define TLS_ERROR_BASE 256 + ERROR_BASE + #define QUIC_STATUS_SUCCESS ((QUIC_STATUS)ERROR_SUCCESS) #define QUIC_STATUS_PENDING ((QUIC_STATUS)ERROR_NOT_READY) #define QUIC_STATUS_CONTINUE ((QUIC_STATUS)ERROR_CONTINUE) @@ -146,6 +148,12 @@ inline ENUMTYPE &operator ^= (ENUMTYPE &a, ENUMTYPE b) throw() { return (ENUMTYP #define QUIC_STATUS_ALPN_NEG_FAILURE ((QUIC_STATUS)ERROR_ALPN_NEG_FAILURE) #define QUIC_STATUS_STREAM_LIMIT_REACHED ((QUIC_STATUS)ERROR_STREAM_LIMIT_REACHED) +#define QUIC_STATUS_TLS_ALERT(Alert) ((QUIC_STATUS)(0xff & Alert) + TLS_ERROR_BASE) + +#define QUIC_STATUS_CLOSE_NOTIFY QUIC_STATUS_TLS_ALERT(0) // Close notify +#define QUIC_STATUS_BAD_CERTIFICATE QUIC_STATUS_TLS_ALERT(42) // Bad Certificate +#define QUIC_STATUS_EXPIRED_CERTIFICATE QUIC_STATUS_TLS_ALERT(45) // Expired Certificate + typedef unsigned char BOOLEAN; typedef struct in_addr IN_ADDR; typedef struct in6_addr IN6_ADDR; diff --git a/src/inc/msquic_winkernel.h b/src/inc/msquic_winkernel.h index 84054e9610..6cc4ae297e 100644 --- a/src/inc/msquic_winkernel.h +++ b/src/inc/msquic_winkernel.h @@ -76,6 +76,10 @@ typedef UINT64 uint64_t; #define STATUS_QUIC_STREAM_LIMIT_REACHED ((NTSTATUS)0xC0240008L) #endif +#ifndef QUIC_TLS_ALERT_NTSTATUS_PREFIX +#define QUIC_TLS_ALERT_NTSTATUS_PREFIX ((NTSTATUS)0xC0240100L) +#endif + #define QUIC_API NTAPI #define QUIC_STATUS NTSTATUS #define QUIC_FAILED(X) (!NT_SUCCESS(X)) @@ -104,6 +108,12 @@ typedef UINT64 uint64_t; #define QUIC_STATUS_ALPN_NEG_FAILURE STATUS_QUIC_ALPN_NEG_FAILURE #define QUIC_STATUS_STREAM_LIMIT_REACHED STATUS_QUIC_STREAM_LIMIT_REACHED +#define QUIC_STATUS_TLS_ALERT(Alert) (QUIC_TLS_ALERT_NTSTATUS_PREFIX | (0xff & Alert)) + +#define QUIC_STATUS_CLOSE_NOTIFY QUIC_STATUS_TLS_ALERT(0) // Close notify +#define QUIC_STATUS_BAD_CERTIFICATE QUIC_STATUS_TLS_ALERT(42) // Bad Certificate +#define QUIC_STATUS_EXPIRED_CERTIFICATE QUIC_STATUS_TLS_ALERT(45) // Expired Certificate + // // Swaps byte orders between host and network endianness. // diff --git a/src/inc/msquic_winuser.h b/src/inc/msquic_winuser.h index 0f6db9264a..42681cbfb3 100644 --- a/src/inc/msquic_winuser.h +++ b/src/inc/msquic_winuser.h @@ -69,6 +69,10 @@ #define ERROR_QUIC_STREAM_LIMIT_REACHED _HRESULT_TYPEDEF_(0x80410008L) #endif +#ifndef QUIC_TLS_ALERT_HRESULT_PREFIX +#define QUIC_TLS_ALERT_HRESULT_PREFIX _HRESULT_TYPEDEF_(0x80410100L) +#endif + #define QUIC_API __cdecl #define QUIC_MAIN_EXPORT __cdecl #define QUIC_STATUS HRESULT @@ -99,6 +103,12 @@ #define QUIC_STATUS_ALPN_NEG_FAILURE ERROR_QUIC_ALPN_NEG_FAILURE #define QUIC_STATUS_STREAM_LIMIT_REACHED ERROR_QUIC_STREAM_LIMIT_REACHED +#define QUIC_STATUS_TLS_ALERT(Alert) (QUIC_TLS_ALERT_HRESULT_PREFIX | (0xff & Alert)) + +#define QUIC_STATUS_CLOSE_NOTIFY QUIC_STATUS_TLS_ALERT(0) // Close notify +#define QUIC_STATUS_BAD_CERTIFICATE QUIC_STATUS_TLS_ALERT(42) // Bad Certificate +#define QUIC_STATUS_EXPIRED_CERTIFICATE QUIC_STATUS_TLS_ALERT(45) // Expired Certificate + // // Swaps byte orders between host and network endianness. // diff --git a/src/inc/quic_platform.h b/src/inc/quic_platform.h index 5f68da94fb..dafe90890a 100644 --- a/src/inc/quic_platform.h +++ b/src/inc/quic_platform.h @@ -332,12 +332,20 @@ extern "C" { #endif typedef struct QUIC_CREDENTIAL_CONFIG QUIC_CREDENTIAL_CONFIG; +typedef struct QUIC_CERTIFICATE_HASH QUIC_CERTIFICATE_HASH; +typedef struct QUIC_CERTIFICATE_HASH_STORE QUIC_CERTIFICATE_HASH_STORE; typedef enum CXPLAT_SELF_SIGN_CERT_TYPE { CXPLAT_SELF_SIGN_CERT_USER, CXPLAT_SELF_SIGN_CERT_MACHINE } CXPLAT_SELF_SIGN_CERT_TYPE; +typedef enum CXPLAT_TEST_CERT_TYPE { + CXPLAT_TEST_CERT_VALID_SERVER, + CXPLAT_TEST_CERT_VALID_CLIENT, + CXPLAT_TEST_CERT_EXPIRED_SERVER +} CXPLAT_TEST_CERT_TYPE; + _IRQL_requires_max_(PASSIVE_LEVEL) const QUIC_CREDENTIAL_CONFIG* CxPlatGetSelfSignedCert( @@ -345,12 +353,36 @@ CxPlatGetSelfSignedCert( _In_ BOOLEAN ClientCertificate ); +_Success_(return == TRUE) +BOOLEAN +CxPlatGetTestCertificate( + _In_ CXPLAT_TEST_CERT_TYPE Type, + _In_ CXPLAT_SELF_SIGN_CERT_TYPE StoreType, + _In_ uint32_t CredType, + _Out_ QUIC_CREDENTIAL_CONFIG* Params, + _When_(CredType == QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH, _Out_) + _When_(CredType != QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH, _Reserved_) + QUIC_CERTIFICATE_HASH* CertHash, + _When_(CredType == QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH_STORE, _Out_) + _When_(CredType != QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH_STORE, _Reserved_) + QUIC_CERTIFICATE_HASH_STORE* CertHashStore, + _When_(CredType == QUIC_CREDENTIAL_TYPE_NONE, _Out_z_bytecap_(100)) + _When_(CredType != QUIC_CREDENTIAL_TYPE_NONE, _Reserved_) + char Principal[100] + ); + _IRQL_requires_max_(PASSIVE_LEVEL) void CxPlatFreeSelfSignedCert( _In_ const QUIC_CREDENTIAL_CONFIG* CredConfig ); +_IRQL_requires_max_(PASSIVE_LEVEL) +void +CxPlatFreeTestCert( + _In_ QUIC_CREDENTIAL_CONFIG* Params + ); + #if defined(__cplusplus) } #endif diff --git a/src/inc/quic_tls.h b/src/inc/quic_tls.h index 19ee8288f5..e57dbf405f 100644 --- a/src/inc/quic_tls.h +++ b/src/inc/quic_tls.h @@ -44,6 +44,7 @@ typedef enum CXPLAT_TLS_ALERT_CODES { CXPLAT_TLS_ALERT_CODE_HANDSHAKE_FAILURE = 40, CXPLAT_TLS_ALERT_CODE_BAD_CERTIFICATE = 42, + CXPLAT_TLS_ALERT_CODE_CERTIFICATE_EXPIRED = 45, CXPLAT_TLS_ALERT_CODE_UNKNOWN_CA = 48, CXPLAT_TLS_ALERT_CODE_INTERNAL_ERROR = 80, CXPLAT_TLS_ALERT_CODE_USER_CANCELED = 90, diff --git a/src/manifest/clog.sidecar b/src/manifest/clog.sidecar index 8606c7a240..533cab9180 100644 --- a/src/manifest/clog.sidecar +++ b/src/manifest/clog.sidecar @@ -8975,6 +8975,13 @@ "splitArgs": [], "macroName": "QuicTraceLogInfo" }, + "CertFindCertificateFriendlyName": { + "ModuleProperites": {}, + "TraceString": "[test] No certificate found by FriendlyName", + "UniqueId": "CertFindCertificateFriendlyName", + "splitArgs": [], + "macroName": "QuicTraceLogWarning" + }, "CertWaitForCreationEvent": { "ModuleProperites": {}, "TraceString": "[test] WaitForSingleObject returned 0x%x, proceeding without caution... (GLE: 0x%x)", @@ -13294,6 +13301,10 @@ "UniquenessHash": "19b21f4a-a6e8-3b9b-c4da-e303c8f108b1", "TraceID": "WindowsUserUninitialized" }, + { + "UniquenessHash": "5fc489fc-c2f2-0e16-8c99-87acf91e235d", + "TraceID": "CertFindCertificateFriendlyName" + }, { "UniquenessHash": "b3f755ee-8d5e-ee68-77ce-d28d21c60214", "TraceID": "CertWaitForCreationEvent" diff --git a/src/platform/selfsign_capi.c b/src/platform/selfsign_capi.c index c2ad18dd95..8bd2a26034 100644 --- a/src/platform/selfsign_capi.c +++ b/src/platform/selfsign_capi.c @@ -26,6 +26,13 @@ #define CXPLAT_KEY_CONTAINER_NAME L"MsQuicSelfSignKey2" #define CXPLAT_KEY_SIZE 2048 +#define CXPLAT_TEST_CERT_VALID_SERVER_FRIENDLY_NAME L"MsQuicTestServer" +#define CXPLAT_TEST_CERT_VALID_CLIENT_FRIENDLY_NAME L"MsQuicTestClient" +#define CXPLAT_TEST_CERT_EXPIRED_SERVER_FRIENDLY_NAME L"MsQuicTestExpiredServer" +#define CXPLAT_TEST_CERT_VALID_SERVER_SUBJECT_NAME "MsQuicTestServer" +#define CXPLAT_TEST_CERT_VALID_CLIENT_SUBJECT_NAME "MsQuicTestClient" +#define CXPLAT_TEST_CERT_EXPIRED_SERVER_SUBJECT_NAME "MsQuicTestExpiredServer" + void CleanTestCertificatesFromStore(BOOLEAN UserStore) { @@ -823,6 +830,75 @@ FreeServerCertificate( CertFreeCertificateContext((PCCERT_CONTEXT)CertCtx); } +_Success_(return != NULL) +PCCERT_CONTEXT +FindCertificate( + _In_ HCERTSTORE CertStore, + _In_ BOOLEAN IncludeInvalid, + _In_z_ const wchar_t* SearchFriendlyName, + _Out_writes_all_(20) uint8_t* CertHash + ) +{ + PCCERT_CONTEXT Cert = NULL; + DWORD FriendlyNamePropId = CERT_FRIENDLY_NAME_PROP_ID; + + while (NULL != + (Cert = CertFindCertificateInStore( + CertStore, + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + 0, + CERT_FIND_PROPERTY, + &FriendlyNamePropId, + Cert))) { + + BYTE FriendlyName[200]; + DWORD NameSize = sizeof(FriendlyName); + +#pragma prefast(suppress:6054, "SAL doesn't track null terminator correctly") + if (!CertGetCertificateContextProperty(Cert, CERT_FRIENDLY_NAME_PROP_ID, FriendlyName, &NameSize) || + wcscmp( + (wchar_t*)FriendlyName, + SearchFriendlyName) != 0) { + continue; + } + + if (!IncludeInvalid) { + // + // Check if the certificate is valid. + // + FILETIME Now; + GetSystemTimeAsFileTime(&Now); + if (CertVerifyTimeValidity(&Now, Cert->pCertInfo) == 0) { + goto Done; + } + } else { + goto Done; + } + } +Done: + if (Cert != NULL) { + DWORD CertHashLength = 20; + if (!CertGetCertificateContextProperty( + Cert, + CERT_HASH_PROP_ID, + CertHash, + &CertHashLength)) { + QuicTraceEvent( + LibraryErrorStatus, + "[ lib] ERROR, %u, %s.", + GetLastError(), + "CertGetCertificateContextProperty failed"); + CertFreeCertificateContext(Cert); + Cert = NULL; + } + } else { + QuicTraceLogWarning( + CertFindCertificateFriendlyName, + "[test] No certificate found by FriendlyName"); + } + return Cert; +} + /* Find the first MsQuic test certificate that is valid, or create one. */ @@ -835,7 +911,6 @@ FindOrCreateCertificate( ) { PCCERT_CONTEXT Cert = NULL; - DWORD FriendlyNamePropId = CERT_FRIENDLY_NAME_PROP_ID; BOOLEAN First = FALSE; HANDLE Event = CreateEventW(NULL, TRUE, FALSE, CXPLAT_CERT_CREATION_EVENT_NAME); @@ -889,40 +964,16 @@ FindOrCreateCertificate( goto Done; } - while (NULL != - (Cert = CertFindCertificateInStore( - CertStore, - X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, - 0, - CERT_FIND_PROPERTY, - &FriendlyNamePropId, - Cert))) { - - BYTE FriendlyName[ - max( - sizeof(CXPLAT_CERTIFICATE_TEST_CLIENT_FRIENDLY_NAME), - sizeof(CXPLAT_CERTIFICATE_TEST_FRIENDLY_NAME))+ - sizeof(WCHAR)]; - DWORD NameSize = sizeof(FriendlyName); - -#pragma prefast(suppress:6054, "SAL doesn't track null terminator correctly") - if (!CertGetCertificateContextProperty(Cert, CERT_FRIENDLY_NAME_PROP_ID, FriendlyName, &NameSize) || - wcscmp( - (wchar_t*)FriendlyName, - IsClient ? - CXPLAT_CERTIFICATE_TEST_CLIENT_FRIENDLY_NAME : - CXPLAT_CERTIFICATE_TEST_FRIENDLY_NAME) != 0) { - continue; - } + Cert = FindCertificate( + CertStore, + FALSE, + IsClient ? + CXPLAT_CERTIFICATE_TEST_CLIENT_FRIENDLY_NAME : + CXPLAT_CERTIFICATE_TEST_FRIENDLY_NAME, + CertHash); - // - // Check if the certificate is valid. - // - FILETIME Now; - GetSystemTimeAsFileTime(&Now); - if (CertVerifyTimeValidity(&Now, Cert->pCertInfo) == 0) { - goto Done; - } + if (Cert != NULL) { + goto Done; } // @@ -946,8 +997,6 @@ FindOrCreateCertificate( CertFreeCertificateContext(Cert); Cert = NULL; } - -Done: if (Cert != NULL) { DWORD CertHashLength = 20; if (!CertGetCertificateContextProperty( @@ -964,6 +1013,8 @@ FindOrCreateCertificate( Cert = NULL; } } + +Done: if (CertStore != NULL) { CertCloseStore(CertStore, 0); } @@ -1003,6 +1054,166 @@ CxPlatGetSelfSignedCert( return Params; } +_Success_(return == TRUE) +BOOLEAN +CxPlatGetTestCertificate( + _In_ CXPLAT_TEST_CERT_TYPE Type, + _In_ CXPLAT_SELF_SIGN_CERT_TYPE StoreType, + _In_ uint32_t CredType, + _Out_ QUIC_CREDENTIAL_CONFIG* Params, + _When_(CredType == QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH, _Out_) + _When_(CredType != QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH, _Reserved_) + QUIC_CERTIFICATE_HASH* CertHash, + _When_(CredType == QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH_STORE, _Out_) + _When_(CredType != QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH_STORE, _Reserved_) + QUIC_CERTIFICATE_HASH_STORE* CertHashStore, + _When_(CredType == QUIC_CREDENTIAL_TYPE_NONE, _Out_z_bytecap_(100)) + _When_(CredType != QUIC_CREDENTIAL_TYPE_NONE, _Reserved_) + char Principal[100] + ) +{ + BOOLEAN Success = FALSE; + PCCERT_CONTEXT Cert = NULL; + const wchar_t* FriendlyName = NULL; + const char* SubjectName = NULL; + + switch (Type) { + case CXPLAT_TEST_CERT_VALID_SERVER: + FriendlyName = CXPLAT_TEST_CERT_VALID_SERVER_FRIENDLY_NAME; + SubjectName = CXPLAT_TEST_CERT_VALID_SERVER_SUBJECT_NAME; + break; + case CXPLAT_TEST_CERT_VALID_CLIENT: + FriendlyName = CXPLAT_TEST_CERT_VALID_CLIENT_FRIENDLY_NAME; + SubjectName = CXPLAT_TEST_CERT_VALID_CLIENT_SUBJECT_NAME; + break; + case CXPLAT_TEST_CERT_EXPIRED_SERVER: + FriendlyName = CXPLAT_TEST_CERT_EXPIRED_SERVER_FRIENDLY_NAME; + SubjectName = CXPLAT_TEST_CERT_EXPIRED_SERVER_SUBJECT_NAME; + break; + default: + QuicTraceEvent( + LibraryErrorStatus, + "[ lib] ERROR, %u, %s.", + Type, + "Unsupported Type passed to CxPlatGetTestCertificate"); + return FALSE; + } + + switch (CredType) { + case QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH: + if (CertHash == NULL) { + QuicTraceEvent( + LibraryErrorStatus, + "[ lib] ERROR, %u, %s.", + (unsigned int)QUIC_STATUS_INVALID_PARAMETER, + "NULL CertHash passed to CxPlatGetTestCertificate"); + return FALSE; + } + break; + case QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH_STORE: + if (CertHashStore == NULL) { + QuicTraceEvent( + LibraryErrorStatus, + "[ lib] ERROR, %u, %s.", + (unsigned int)QUIC_STATUS_INVALID_PARAMETER, + "NULL CertHashStore passed to CxPlatGetTestCertificate"); + return FALSE; + } + break; + case QUIC_CREDENTIAL_TYPE_NONE: + if (Principal == NULL) { + QuicTraceEvent( + LibraryErrorStatus, + "[ lib] ERROR, %u, %s.", + (unsigned int)QUIC_STATUS_INVALID_PARAMETER, + "NULL Principal passed to CxPlatGetTestCertificate"); + return FALSE; + } + break; + case QUIC_CREDENTIAL_TYPE_CERTIFICATE_CONTEXT: + break; + default: + QuicTraceEvent( + LibraryErrorStatus, + "[ lib] ERROR, %u, %s.", + CredType, + "Unsupported CredType passed to CxPlatGetTestCertificate"); + return FALSE; + } + + CxPlatZeroMemory(Params, sizeof(*Params)); + + HCERTSTORE CertStore = + CertOpenStore( + CERT_STORE_PROV_SYSTEM_A, + 0, + 0, + StoreType == CXPLAT_SELF_SIGN_CERT_USER ? + CERT_SYSTEM_STORE_CURRENT_USER : + CERT_SYSTEM_STORE_LOCAL_MACHINE, + "MY"); + if (CertStore == NULL) { + QuicTraceEvent( + LibraryErrorStatus, + "[ lib] ERROR, %u, %s.", + GetLastError(), + "CertOpenStore failed"); + goto Done; + } + uint8_t CertHashBytes[20]; + + Cert = FindCertificate( + CertStore, + TRUE, + FriendlyName, + CertHashBytes); + + if (Cert == NULL) { + goto Done; + } + + switch (CredType) { + case QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH: + Params->Type = QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH; + Params->CertificateHash = CertHash; + CxPlatCopyMemory(CertHash->ShaHash, CertHashBytes, sizeof(CertHash->ShaHash)); + break; + case QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH_STORE: + Params->Type = QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH_STORE; + Params->CertificateHashStore = CertHashStore; + CxPlatCopyMemory(CertHashStore->ShaHash, CertHashBytes, sizeof(CertHashStore->ShaHash)); + strncpy_s(CertHashStore->StoreName, sizeof(CertHashStore->StoreName), "MY", sizeof("MY")); + CertHashStore->Flags = + StoreType == CXPLAT_SELF_SIGN_CERT_USER ? + QUIC_CERTIFICATE_HASH_STORE_FLAG_NONE : + QUIC_CERTIFICATE_HASH_STORE_FLAG_MACHINE_STORE; + break; + case QUIC_CREDENTIAL_TYPE_NONE: + // + // Assume Principal in use here + // + Params->Type = QUIC_CREDENTIAL_TYPE_NONE; + Params->Principal = Principal; + strncpy_s(Principal, 100, SubjectName, 100); + break; + case QUIC_CREDENTIAL_TYPE_CERTIFICATE_CONTEXT: + Params->Type = QUIC_CREDENTIAL_TYPE_CERTIFICATE_CONTEXT; + Params->CertificateContext = (QUIC_CERTIFICATE*)Cert; + Cert = NULL; + break; + } + Success = TRUE; +Done: + if (Cert != NULL) { + CertFreeCertificateContext(Cert); + } + if (CertStore != NULL) { + CertCloseStore(CertStore, 0); + } + + return Success; +} + _IRQL_requires_max_(PASSIVE_LEVEL) void CxPlatFreeSelfSignedCert( @@ -1012,3 +1223,14 @@ CxPlatFreeSelfSignedCert( FreeServerCertificate(Params->CertificateContext); HeapFree(GetProcessHeap(), 0, (void*)Params); } + +_IRQL_requires_max_(PASSIVE_LEVEL) +void +CxPlatFreeTestCert( + _In_ QUIC_CREDENTIAL_CONFIG* Params + ) +{ + if (Params->Type == QUIC_CREDENTIAL_TYPE_CERTIFICATE_CONTEXT) { + CertFreeCertificateContext((PCCERT_CONTEXT)Params->CertificateContext); + } +} diff --git a/src/platform/selfsign_openssl.c b/src/platform/selfsign_openssl.c index 3f4ac53e63..ddb31b62db 100644 --- a/src/platform/selfsign_openssl.c +++ b/src/platform/selfsign_openssl.c @@ -426,6 +426,44 @@ CxPlatGetSelfSignedCert( return NULL; } +_Success_(return == TRUE) +BOOLEAN +CxPlatGetTestCertificate( + _In_ CXPLAT_TEST_CERT_TYPE Type, + _In_ CXPLAT_SELF_SIGN_CERT_TYPE StoreType, + _In_ uint32_t CredType, + _Out_ QUIC_CREDENTIAL_CONFIG* Params, + _When_(CredType == QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH, _Out_) + _When_(CredType != QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH, _Reserved_) + QUIC_CERTIFICATE_HASH* CertHash, + _When_(CredType == QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH_STORE, _Out_) + _When_(CredType != QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH_STORE, _Reserved_) + QUIC_CERTIFICATE_HASH_STORE* CertHashStore, + _When_(CredType == QUIC_CREDENTIAL_TYPE_NONE, _Out_z_bytecap_(100)) + _When_(CredType != QUIC_CREDENTIAL_TYPE_NONE, _Reserved_) + char Principal[100] + ) +{ + // Not yet supported + UNREFERENCED_PARAMETER(Type); + UNREFERENCED_PARAMETER(StoreType); + UNREFERENCED_PARAMETER(CredType); + UNREFERENCED_PARAMETER(Params); + UNREFERENCED_PARAMETER(CertHash); + UNREFERENCED_PARAMETER(CertHashStore); + UNREFERENCED_PARAMETER(Principal); + return FALSE; +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +void +CxPlatFreeTestCert( + _In_ QUIC_CREDENTIAL_CONFIG* Params + ) +{ + UNREFERENCED_PARAMETER(Params); +} + _IRQL_requires_max_(PASSIVE_LEVEL) void CxPlatFreeSelfSignedCert( diff --git a/src/platform/selfsign_stub.c b/src/platform/selfsign_stub.c index 4e2ba01dd9..fdcc0f4b48 100644 --- a/src/platform/selfsign_stub.c +++ b/src/platform/selfsign_stub.c @@ -34,6 +34,44 @@ CxPlatGetSelfSignedCert( return Params; } +_Success_(return == TRUE) +BOOLEAN +CxPlatGetTestCertificate( + _In_ CXPLAT_TEST_CERT_TYPE Type, + _In_ CXPLAT_SELF_SIGN_CERT_TYPE StoreType, + _In_ uint32_t CredType, + _Out_ QUIC_CREDENTIAL_CONFIG* Params, + _When_(CredType == QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH, _Out_) + _When_(CredType != QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH, _Reserved_) + QUIC_CERTIFICATE_HASH* CertHash, + _When_(CredType == QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH_STORE, _Out_) + _When_(CredType != QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH_STORE, _Reserved_) + QUIC_CERTIFICATE_HASH_STORE* CertHashStore, + _When_(CredType == QUIC_CREDENTIAL_TYPE_NONE, _Out_z_bytecap_(100)) + _When_(CredType != QUIC_CREDENTIAL_TYPE_NONE, _Reserved_) + char Principal[100] + ) +{ + // Not yet supported + UNREFERENCED_PARAMETER(Type); + UNREFERENCED_PARAMETER(StoreType); + UNREFERENCED_PARAMETER(CredType); + UNREFERENCED_PARAMETER(Params); + UNREFERENCED_PARAMETER(CertHash); + UNREFERENCED_PARAMETER(CertHashStore); + UNREFERENCED_PARAMETER(Principal); + return FALSE; +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +void +CxPlatFreeTestCert( + _In_ QUIC_CREDENTIAL_CONFIG* Params + ) +{ + UNREFERENCED_PARAMETER(Params); +} + _IRQL_requires_max_(PASSIVE_LEVEL) void CxPlatFreeSelfSignedCert( diff --git a/src/test/MsQuicTests.h b/src/test/MsQuicTests.h index 429a6cfa6c..b8dbb4b7ad 100644 --- a/src/test/MsQuicTests.h +++ b/src/test/MsQuicTests.h @@ -173,6 +173,16 @@ QuicTestConnectServerRejected( _In_ int Family ); +void +QuicTestConnectExpiredServerCertificate( + _In_ const QUIC_CREDENTIAL_CONFIG* Config + ); + +void +QuicTestConnectValidServerCertificate( + _In_ const QUIC_CREDENTIAL_CONFIG* Config + ); + // // Post Handshake Tests // @@ -728,4 +738,19 @@ typedef struct { #define IOCTL_QUIC_RUN_INVALID_ALPN_LENGTHS \ QUIC_CTL_CODE(58, METHOD_BUFFERED, FILE_WRITE_DATA) -#define QUIC_MAX_IOCTL_FUNC_CODE 58 +typedef struct { + QUIC_CREDENTIAL_CONFIG CredConfig; + union { + QUIC_CERTIFICATE_HASH CertHash; + QUIC_CERTIFICATE_HASH_STORE CertHashStore; + char PrincipalString[100]; + }; +} QUIC_RUN_CRED_VALIDATION; + +#define IOCTL_QUIC_RUN_EXPIRED_SERVER_CERT \ + QUIC_CTL_CODE(59, METHOD_BUFFERED, FILE_WRITE_DATA) + +#define IOCTL_QUIC_RUN_VALID_SERVER_CERT \ + QUIC_CTL_CODE(60, METHOD_BUFFERED, FILE_WRITE_DATA) + +#define QUIC_MAX_IOCTL_FUNC_CODE 60 diff --git a/src/test/bin/quic_gtest.cpp b/src/test/bin/quic_gtest.cpp index 011a78f53f..ba445cd327 100644 --- a/src/test/bin/quic_gtest.cpp +++ b/src/test/bin/quic_gtest.cpp @@ -623,6 +623,83 @@ TEST_P(WithHandshakeArgs6, ConnectClientCertificate) { } #endif +TEST(CredValidation, ConnectExpiredServerCertificate) { + QUIC_RUN_CRED_VALIDATION Params; + for (auto CredType : {QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH, QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH_STORE }) { + ASSERT_TRUE(CxPlatGetTestCertificate( + CXPLAT_TEST_CERT_EXPIRED_SERVER, + TestingKernelMode ? + CXPLAT_SELF_SIGN_CERT_MACHINE : + CXPLAT_SELF_SIGN_CERT_USER, + CredType, + &Params.CredConfig, + &Params.CertHash, + &Params.CertHashStore, + (char*)Params.PrincipalString)); + if (TestingKernelMode) { + ASSERT_TRUE(DriverClient.Run(IOCTL_QUIC_RUN_EXPIRED_SERVER_CERT, Params)); + } else { + QuicTestConnectExpiredServerCertificate(&Params.CredConfig); + } + CxPlatFreeTestCert((QUIC_CREDENTIAL_CONFIG*)&Params.CredConfig); + } + + + if (!TestingKernelMode) { + // + // Test cert context in user mode only. + // + ASSERT_TRUE(CxPlatGetTestCertificate( + CXPLAT_TEST_CERT_EXPIRED_SERVER, + CXPLAT_SELF_SIGN_CERT_USER, + QUIC_CREDENTIAL_TYPE_CERTIFICATE_CONTEXT, + &Params.CredConfig, + &Params.CertHash, + &Params.CertHashStore, + (char*)Params.PrincipalString)); + QuicTestConnectExpiredServerCertificate(&Params.CredConfig); + CxPlatFreeTestCert((QUIC_CREDENTIAL_CONFIG*)&Params.CredConfig); + } +} + +TEST(CredValidation, ConnectValidServerCertificate) { + QUIC_RUN_CRED_VALIDATION Params; + for (auto CredType : {QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH, QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH_STORE }) { + ASSERT_TRUE(CxPlatGetTestCertificate( + CXPLAT_TEST_CERT_VALID_SERVER, + TestingKernelMode ? + CXPLAT_SELF_SIGN_CERT_MACHINE : + CXPLAT_SELF_SIGN_CERT_USER, + CredType, + &Params.CredConfig, + &Params.CertHash, + &Params.CertHashStore, + (char*)Params.PrincipalString)); + if (TestingKernelMode) { + ASSERT_TRUE(DriverClient.Run(IOCTL_QUIC_RUN_VALID_SERVER_CERT, Params)); + } else { + QuicTestConnectValidServerCertificate(&Params.CredConfig); + } + CxPlatFreeTestCert((QUIC_CREDENTIAL_CONFIG*)&Params.CredConfig); + } + + if (!TestingKernelMode) { + // + // Test cert context in user mode only. + // + ASSERT_TRUE(CxPlatGetTestCertificate( + CXPLAT_TEST_CERT_VALID_SERVER, + CXPLAT_SELF_SIGN_CERT_USER, + QUIC_CREDENTIAL_TYPE_CERTIFICATE_CONTEXT, + &Params.CredConfig, + &Params.CertHash, + &Params.CertHashStore, + (char*)Params.PrincipalString)); + QuicTestConnectValidServerCertificate(&Params.CredConfig); + CxPlatFreeTestCert((QUIC_CREDENTIAL_CONFIG*)&Params.CredConfig); + } +} + #if QUIC_TEST_DATAPATH_HOOKS_ENABLED TEST_P(WithHandshakeArgs4, RandomLoss) { TestLoggerT Logger("QuicTestConnect-RandomLoss", GetParam()); diff --git a/src/test/bin/quic_gtest.h b/src/test/bin/quic_gtest.h index d685919bad..fc128fb531 100644 --- a/src/test/bin/quic_gtest.h +++ b/src/test/bin/quic_gtest.h @@ -19,6 +19,8 @@ #include "quic_gtest.h.clog.h" #endif +extern bool TestingKernelMode; + class WithBool : public testing::Test, public testing::WithParamInterface { }; diff --git a/src/test/bin/winkernel/control.cpp b/src/test/bin/winkernel/control.cpp index 551d11a69c..2b00a67b36 100644 --- a/src/test/bin/winkernel/control.cpp +++ b/src/test/bin/winkernel/control.cpp @@ -427,7 +427,9 @@ size_t QUIC_IOCTL_BUFFER_SIZES[] = 0, sizeof(QUIC_RUN_CONNECT_CLIENT_CERT), 0, - 0 + 0, + sizeof(QUIC_RUN_CRED_VALIDATION), + sizeof(QUIC_RUN_CRED_VALIDATION) }; CXPLAT_STATIC_ASSERT( @@ -452,6 +454,7 @@ typedef union { QUIC_RUN_CUSTOM_CERT_VALIDATION CustomCertValidationParams; QUIC_RUN_VERSION_NEGOTIATION_EXT VersionNegotiationExtParams; QUIC_RUN_CONNECT_CLIENT_CERT ConnectClientCertParams; + QUIC_RUN_CRED_VALIDATION CredValidationParams; } QUIC_IOCTL_PARAMS; @@ -921,6 +924,48 @@ QuicTestCtlEvtIoDeviceControl( QuicTestCtlRun(QuicTestInvalidAlpnLengths()); break; + case IOCTL_QUIC_RUN_EXPIRED_SERVER_CERT: + CXPLAT_FRE_ASSERT(Params != nullptr); + // + // Fix up pointers for kernel mode + // + switch (Params->CredValidationParams.CredConfig.Type) { + case QUIC_CREDENTIAL_TYPE_NONE: + Params->CredValidationParams.CredConfig.Principal = (const char*)Params->CredValidationParams.PrincipalString; + break; + case QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH: + Params->CredValidationParams.CredConfig.CertificateHash = &Params->CredValidationParams.CertHash; + break; + case QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH_STORE: + Params->CredValidationParams.CredConfig.CertificateHashStore = &Params->CredValidationParams.CertHashStore; + break; + } + QuicTestCtlRun( + QuicTestConnectExpiredServerCertificate( + &Params->CredValidationParams.CredConfig)); + break; + + case IOCTL_QUIC_RUN_VALID_SERVER_CERT: + CXPLAT_FRE_ASSERT(Params != nullptr); + // + // Fix up pointers for kernel mode + // + switch (Params->CredValidationParams.CredConfig.Type) { + case QUIC_CREDENTIAL_TYPE_NONE: + Params->CredValidationParams.CredConfig.Principal = (const char*)Params->CredValidationParams.PrincipalString; + break; + case QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH: + Params->CredValidationParams.CredConfig.CertificateHash = &Params->CredValidationParams.CertHash; + break; + case QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH_STORE: + Params->CredValidationParams.CredConfig.CertificateHashStore = &Params->CredValidationParams.CertHashStore; + break; + } + QuicTestCtlRun( + QuicTestConnectValidServerCertificate( + &Params->CredValidationParams.CredConfig)); + break; + default: Status = STATUS_NOT_IMPLEMENTED; break; diff --git a/src/test/lib/HandshakeTest.cpp b/src/test/lib/HandshakeTest.cpp index 403c1cabdc..8383d19c93 100644 --- a/src/test/lib/HandshakeTest.cpp +++ b/src/test/lib/HandshakeTest.cpp @@ -782,7 +782,7 @@ QuicTestCustomCertificateValidation( UniquePtr Server; ServerAcceptContext ServerAcceptCtx((TestConnection**)&Server); if (!AcceptCert) { - ServerAcceptCtx.ExpectedTransportCloseStatus = QUIC_STATUS_INTERNAL_ERROR; + ServerAcceptCtx.ExpectedTransportCloseStatus = QUIC_STATUS_BAD_CERTIFICATE; } Listener.Context = &ServerAcceptCtx; @@ -793,7 +793,7 @@ QuicTestCustomCertificateValidation( Client.SetExpectedCustomValidationResult(AcceptCert); Client.SetAsyncCustomValidationResult(AsyncValidation); if (!AcceptCert) { - Client.SetExpectedTransportCloseStatus(QUIC_STATUS_INTERNAL_ERROR); // Better error? + Client.SetExpectedTransportCloseStatus(QUIC_STATUS_BAD_CERTIFICATE); } TEST_QUIC_SUCCEEDED( @@ -2046,7 +2046,7 @@ QuicTestConnectClientCertificate( TestConnection Client(Registration); TEST_TRUE(Client.IsValid()); if (!UseClientCertificate) { - Client.SetExpectedTransportCloseStatus(QUIC_STATUS_INTERNAL_ERROR); + Client.SetExpectedTransportCloseStatus(QUIC_STATUS_CLOSE_NOTIFY); } TEST_QUIC_SUCCEEDED( @@ -2068,7 +2068,7 @@ QuicTestConnectClientCertificate( return; } } else { - Server->SetExpectedTransportCloseStatus(QUIC_STATUS_INTERNAL_ERROR); + Server->SetExpectedTransportCloseStatus(QUIC_STATUS_CLOSE_NOTIFY); } TEST_EQUAL(UseClientCertificate, Server->GetIsConnected()); } @@ -2171,3 +2171,131 @@ QuicTestValidAlpnLengths( } } } + +void +QuicTestConnectExpiredServerCertificate( + _In_ const QUIC_CREDENTIAL_CONFIG* Config + ) +{ + MsQuicRegistration Registration; + TEST_TRUE(Registration.IsValid()); + + MsQuicAlpn Alpn("MsQuicTest"); + + MsQuicSettings Settings; + Settings.SetIdleTimeoutMs(3000); + + MsQuicConfiguration ServerConfiguration(Registration, Alpn, Settings, *Config); + TEST_TRUE(ServerConfiguration.IsValid()); + + MsQuicCredentialConfig ClientCredConfig; + ClientCredConfig.Flags &= ~QUIC_CREDENTIAL_FLAG_NO_CERTIFICATE_VALIDATION; + MsQuicConfiguration ClientConfiguration(Registration, Alpn, Settings, ClientCredConfig); + TEST_TRUE(ClientConfiguration.IsValid()); + + QUIC_ADDRESS_FAMILY QuicAddrFamily = QUIC_ADDRESS_FAMILY_INET; + + { + TestListener Listener(Registration, ListenerAcceptConnection, ServerConfiguration); + TEST_TRUE(Listener.IsValid()); + QuicAddr ServerLocalAddr(QuicAddrFamily); + TEST_QUIC_SUCCEEDED(Listener.Start(Alpn, &ServerLocalAddr.SockAddr)); + + TEST_QUIC_SUCCEEDED(Listener.GetLocalAddr(ServerLocalAddr)); + + { + UniquePtr Server; + ServerAcceptContext ServerAcceptCtx((TestConnection**)&Server); + Listener.Context = &ServerAcceptCtx; + + { + TestConnection Client(Registration); + TEST_TRUE(Client.IsValid()); + Client.SetExpectedTransportCloseStatus(QUIC_STATUS_EXPIRED_CERTIFICATE); + + TEST_QUIC_SUCCEEDED( + Client.Start( + ClientConfiguration, + QuicAddrFamily, + QUIC_LOCALHOST_FOR_AF( + QuicAddrGetFamily(&ServerLocalAddr.SockAddr)), + ServerLocalAddr.GetPort())); + + if (!Client.WaitForConnectionComplete()) { + return; + } + TEST_EQUAL(false, Client.GetIsConnected()); + + TEST_NOT_EQUAL(nullptr, Server); + Server->SetExpectedTransportCloseStatus(QUIC_STATUS_EXPIRED_CERTIFICATE); + if (!Server->WaitForConnectionComplete()) { + return; + } + TEST_EQUAL(false, Server->GetIsConnected()); + } + } + } +} + +void +QuicTestConnectValidServerCertificate( + _In_ const QUIC_CREDENTIAL_CONFIG* Config + ) +{ + MsQuicRegistration Registration; + TEST_TRUE(Registration.IsValid()); + + MsQuicAlpn Alpn("MsQuicTest"); + + MsQuicSettings Settings; + Settings.SetIdleTimeoutMs(3000); + + MsQuicConfiguration ServerConfiguration(Registration, Alpn, Settings, *Config); + TEST_TRUE(ServerConfiguration.IsValid()); + + MsQuicCredentialConfig ClientCredConfig; + ClientCredConfig.Flags &= ~QUIC_CREDENTIAL_FLAG_NO_CERTIFICATE_VALIDATION; + MsQuicConfiguration ClientConfiguration(Registration, Alpn, Settings, ClientCredConfig); + TEST_TRUE(ClientConfiguration.IsValid()); + + QUIC_ADDRESS_FAMILY QuicAddrFamily = QUIC_ADDRESS_FAMILY_INET; + + { + TestListener Listener(Registration, ListenerAcceptConnection, ServerConfiguration); + TEST_TRUE(Listener.IsValid()); + QuicAddr ServerLocalAddr(QuicAddrFamily); + TEST_QUIC_SUCCEEDED(Listener.Start(Alpn, &ServerLocalAddr.SockAddr)); + + TEST_QUIC_SUCCEEDED(Listener.GetLocalAddr(ServerLocalAddr)); + + { + UniquePtr Server; + ServerAcceptContext ServerAcceptCtx((TestConnection**)&Server); + Listener.Context = &ServerAcceptCtx; + + { + TestConnection Client(Registration); + TEST_TRUE(Client.IsValid()); + + TEST_QUIC_SUCCEEDED( + Client.Start( + ClientConfiguration, + QuicAddrFamily, + QUIC_LOCALHOST_FOR_AF( + QuicAddrGetFamily(&ServerLocalAddr.SockAddr)), + ServerLocalAddr.GetPort())); + + if (!Client.WaitForConnectionComplete()) { + return; + } + TEST_EQUAL(true, Client.GetIsConnected()); + + TEST_NOT_EQUAL(nullptr, Server); + if (!Server->WaitForConnectionComplete()) { + return; + } + TEST_EQUAL(true, Server->GetIsConnected()); + } + } + } +} diff --git a/submodules/openssl b/submodules/openssl index 9b885b5de8..bbf3bea123 160000 --- a/submodules/openssl +++ b/submodules/openssl @@ -1 +1 @@ -Subproject commit 9b885b5de8dbf9367d9c5ffcbcd7d105d443f475 +Subproject commit bbf3bea123739178ff35d9d61cb7c2f944e0b5f0