From 7bcc862c451c19efd73f9a23ac83c96cbc3779f5 Mon Sep 17 00:00:00 2001 From: Paul Medynski <31868385+paulmedynski@users.noreply.github.com> Date: Fri, 19 Jun 2026 12:39:29 -0300 Subject: [PATCH] Sign CI Package pipeline assemblies and tests; verify SqlClient in Abstractions Bare-minimum changes to produce and test signed assemblies in the CI Package pipeline, plus the Abstractions strong-name verification of the SqlClient assembly. - Add the download-assembly-signing-key pipeline step and thread SigningKeyPath / TestSigningKeyPath through the CI Package build, pack, and test jobs so the SqlClient package and its tests are produced and run signed. - Thread isInternalBuild through the SqlClient and Abstractions package stages. - In SqlAuthenticationProvider (Abstractions), verify the loaded Microsoft.Data.SqlClient assembly's public key token when STRONG_NAME_SIGNING is defined, with defense-in-depth post-load verification on .NET Core. - Add conditional signed InternalsVisibleTo and test-assembly signing for the Abstractions project and its tests. --- build.proj | 89 +++++++++++++++---- .../ci/package/sqlclient-package.yml | 12 +-- .../steps/download-assembly-signing-key.yml | 39 ++++++++ .../templates/jobs/ci-build-nugets-job.yml | 13 +++ .../templates/jobs/ci-run-tests-job.yml | 18 ++++ .../templates/stages/ci-run-tests-stage.yml | 7 ++ .../templates/steps/ci-project-build-step.yml | 9 ++ .../templates/steps/run-all-tests-step.yml | 48 ++++++++++ eng/pipelines/dotnet-sqlclient-ci-core.yml | 9 ++ ...qlclient-ci-package-reference-pipeline.yml | 1 + ...qlclient-ci-project-reference-pipeline.yml | 1 + .../jobs/pack-abstractions-package-ci-job.yml | 60 +++++++------ .../jobs/test-abstractions-package-ci-job.yml | 43 +++++++-- .../jobs/validate-signed-package-job.yml | 4 +- .../onebranch/steps/build-buildproj-step.yml | 10 +-- .../build-abstractions-package-ci-stage.yml | 12 +++ .../build-sqlclient-package-ci-stage.yml | 6 ++ .../Abstractions/src/Abstractions.csproj | 17 ++++ .../src/SqlAuthenticationProvider.Internal.cs | 65 +++++++++++--- .../test/Abstractions.Test.csproj | 16 +++- 20 files changed, 405 insertions(+), 74 deletions(-) create mode 100644 eng/pipelines/common/steps/download-assembly-signing-key.yml diff --git a/build.proj b/build.proj index 4ff29a08df..9875098aa0 100644 --- a/build.proj +++ b/build.proj @@ -452,6 +452,8 @@ "$(DotnetPath)dotnet" build "$(SqlClientNotSupportedProjectPath)" + + -p:Configuration=$(Configuration) -p:GenApiPath="@(GenApiArtifactPath->'%(FullPath)')" $(SigningKeyPathArgument) @@ -483,6 +485,8 @@ "$(DotnetPath)dotnet" build $(SqlClientRefProjectPath) + + -p:Configuration=$(Configuration) $(SigningKeyPathArgument) @@ -491,7 +495,7 @@ $(BuildSuffixArgument) $(PackageVersionSqlClientArgument) - + $(ReferenceTypeArgument) $(PackageVersionAbstractionsArgument) $(PackageVersionSqlServerArgument) @@ -512,6 +516,8 @@ "$(DotnetPath)dotnet" build $(SqlClientProjectPath) + + -p:Configuration=$(Configuration) -p:TargetOs=Unix $(SigningKeyPathArgument) @@ -543,11 +549,13 @@ "$(DotnetPath)dotnet" build $(SqlClientProjectPath) + + -p:Configuration=$(Configuration) -p:TargetOs=Windows_NT $(SigningKeyPathArgument) - + $(BuildNumberArgument) $(BuildSuffixArgument) $(PackageVersionSqlClientArgument) @@ -586,6 +594,8 @@ "$(DotnetPath)dotnet" pack "$(SqlClientProjectPath)" + + -p:Configuration=$(Configuration) $(PackBuildArgument) $(SigningKeyPathArgument) @@ -601,7 +611,7 @@ $(PackageVersionLoggingArgument) $(PackageVersionSqlServerArgument) - + -p:PackageOutputPath="$(SqlClientPackageArtifactRoot)" @@ -635,7 +645,12 @@ "$(DotnetPath)dotnet" test "$(SqlClientFunctionalTestProjectPath)" + + -p:Configuration=$(Configuration) + $(SigningKeyPathArgument) + + $(TestBlameArgument) $(TestCodeCoverageArgument) $(TestFiltersArgument) @@ -680,7 +695,12 @@ "$(DotnetPath)dotnet" test "$(SqlClientManualTestProjectPath)" + + -p:Configuration=$(Configuration) + $(SigningKeyPathArgument) + + $(TestBlameArgument) $(TestCodeCoverageArgument) $(ManualTestFiltersArgument) @@ -710,17 +730,24 @@ "$(DotnetPath)dotnet" test "$(SqlClientUnitTestProjectPath)" + + -p:Configuration=$(Configuration) + $(SigningKeyPathArgument) + $(TestSigningKeyPathArgument) + + $(TestBlameArgument) $(TestCodeCoverageArgument) $(TestFiltersArgument) $(TestFrameworkArgument) + --results-directory "$(TestResultsFolderPath)" + --logger:"trx;LogFilePrefix=$(LogFilePrefix)" + + $(ReferenceTypeArgument) - $(TestSigningKeyPathArgument) $(PackageVersionSqlClientArgument) $(PackageVersionSqlServerArgument) - --results-directory "$(TestResultsFolderPath)" - --logger:"trx;LogFilePrefix=$(LogFilePrefix)" @@ -746,15 +773,17 @@ "$(DotnetPath)dotnet" build "$(AkvProviderProjectPath)" + + -p:Configuration=$(Configuration) $(SigningKeyPathArgument) - + $(BuildNumberArgument) $(BuildSuffixArgument) $(PackageVersionAkvProviderArgument) - + $(ReferenceTypeArgument) $(PackageVersionAbstractionsArgument) $(PackageVersionLoggingArgument) @@ -774,6 +803,8 @@ "$(DotnetPath)dotnet" pack "$(AkvProviderProjectPath)" + + -p:Configuration=$(Configuration) $(PackBuildArgument) $(SigningKeyPathArgument) @@ -827,6 +858,8 @@ "$(DotnetPath)dotnet" build "$(AbstractionsProjectPath)" + + -p:Configuration=$(Configuration) $(SigningKeyPathArgument) @@ -852,6 +885,8 @@ "$(DotnetPath)dotnet" pack "$(AbstractionsProjectPath)" + + -p:Configuration=$(Configuration) $(PackBuildArgument) $(SigningKeyPathArgument) @@ -887,22 +922,28 @@ - AbstractionsTests-$(OS) $(LogFilePrefix)-$(TestFramework) "$(DotnetPath)dotnet" test "$(AbstractionsTestProjectPath)" + + -p:Configuration=$(Configuration) + $(SigningKeyPathArgument) + $(TestSigningKeyPathArgument) + + $(TestBlameArgument) $(TestCodeCoverageArgument) $(TestFiltersArgument) $(TestFrameworkArgument) --results-directory "$(TestResultsFolderPath)" --logger:"trx;LogFilePrefix=$(LogFilePrefix)" + + + $(ReferenceTypeArgument) + $(PackageVersionLoggingArgument) $([System.Text.RegularExpressions.Regex]::Replace($(DotnetCommand), "\s+", " ")) @@ -929,6 +970,8 @@ "$(DotnetPath)dotnet" build "$(AzureProjectPath)" + + -p:Configuration=$(Configuration) $(SigningKeyPathArgument) @@ -937,7 +980,7 @@ $(BuildSuffixArgument) $(PackageVersionAzureArgument) - + $(ReferenceTypeArgument) $(PackageVersionLoggingArgument) @@ -954,6 +997,8 @@ "$(DotnetPath)dotnet" pack "$(AzureProjectPath)" + + -p:Configuration=$(Configuration) $(PackBuildArgument) $(SigningKeyPathArgument) @@ -994,7 +1039,13 @@ "$(DotnetPath)dotnet" test "$(AzureTestProjectPath)" + + -p:Configuration=$(Configuration) + $(SigningKeyPathArgument) + $(TestSigningKeyPathArgument) + + $(TestBlameArgument) $(TestCodeCoverageArgument) $(TestFiltersArgument) @@ -1002,7 +1053,7 @@ --results-directory "$(TestResultsFolderPath)" --logger:"trx;LogFilePrefix=$(LogFilePrefix)" - + $(ReferenceTypeArgument) $(PackageVersionAbstractionsArgument) $(PackageVersionLoggingArgument) @@ -1030,6 +1081,8 @@ "$(DotnetPath)dotnet" build $(LoggingProjectPath) + + -p:Configuration=$(Configuration) $(SigningKeyPathArgument) @@ -1051,6 +1104,8 @@ "$(DotnetPath)dotnet" pack $(LoggingProjectPath) + + -p:Configuration=$(Configuration) $(PackBuildArgument) $(SigningKeyPathArgument) @@ -1092,10 +1147,12 @@ "$(DotnetPath)dotnet" build $(SqlServerProjectPath) + + -p:Configuration=$(Configuration) $(SigningKeyPathArgument) - + $(BuildNumberArgument) $(BuildSuffixArgument) $(PackageVersionSqlServerArgument) @@ -1113,6 +1170,8 @@ "$(DotnetPath)dotnet" pack $(SqlServerProjectPath) + + -p:Configuration=$(Configuration) $(PackBuildArgument) $(SigningKeyPathArgument) diff --git a/eng/pipelines/ci/package/sqlclient-package.yml b/eng/pipelines/ci/package/sqlclient-package.yml index e194e2abef..72c65b364e 100644 --- a/eng/pipelines/ci/package/sqlclient-package.yml +++ b/eng/pipelines/ci/package/sqlclient-package.yml @@ -89,10 +89,10 @@ variables: value: ${{ eq(variables['System.TeamProject'], 'ADO.Net') }} # Signing key argument passed to build.proj. On internal builds this references the secure file - # downloaded by DownloadSecureFile@1; on public builds it expands to empty. + # downloaded by download-assembly-signing-key.yml; on public builds it expands to empty. - name: signingKeyArg ${{ if eq(variables.isInternalBuild, true) }}: - value: '-p:SigningKeyPath="$(keyFile.secureFilePath)"' + value: '-p:SigningKeyPath="$(driverKeyFile.secureFilePath)"' ${{ else }}: value: '' @@ -133,13 +133,9 @@ jobs: Write-Host 'Done.' displayName: Clean packages/ directory - # On internal builds, download the strong-name signing key. + # On internal builds, download the assembly signing key. - ${{ if eq(variables.isInternalBuild, true) }}: - - task: DownloadSecureFile@1 - displayName: Download Signing Key - inputs: - secureFile: netfxKeypair.snk - name: keyFile + - template: /eng/pipelines/common/steps/download-assembly-signing-key.yml@self # Run the Pack target via build.proj. - task: DotNetCoreCLI@2 diff --git a/eng/pipelines/common/steps/download-assembly-signing-key.yml b/eng/pipelines/common/steps/download-assembly-signing-key.yml new file mode 100644 index 0000000000..b7c415f74d --- /dev/null +++ b/eng/pipelines/common/steps/download-assembly-signing-key.yml @@ -0,0 +1,39 @@ +################################################################################ +# Licensed to the .NET Foundation under one or more agreements. The .NET +# Foundation licenses this file to you under the MIT license. See the LICENSE +# file in the project root for more information. +################################################################################ + +# Downloads a signing key from ADO secure files. +# +# When isTest is false, downloads the driver signing key and exports it as 'driverKeyFile'. When +# isTest is true, downloads the test signing key and exports it as 'testKeyFile'. +# +# Downstream steps reference the path via: +# +# $(driverKeyFile.secureFilePath) or +# $(testKeyFile.secureFilePath) + +parameters: + + # When false, download the driver signing key. + # When true, download the test signing key. + - name: isTest + type: boolean + default: false + +steps: + + - ${{ if eq(parameters.isTest, false) }}: + - task: DownloadSecureFile@1 + displayName: Download Driver Signing Key + inputs: + secureFile: netfxKeypair.snk + name: driverKeyFile + + - ${{ if eq(parameters.isTest, true) }}: + - task: DownloadSecureFile@1 + displayName: Download Test Signing Key + inputs: + secureFile: sqlclient-test-key.snk + name: testKeyFile diff --git a/eng/pipelines/common/templates/jobs/ci-build-nugets-job.yml b/eng/pipelines/common/templates/jobs/ci-build-nugets-job.yml index aa6a1c50c5..50d017f459 100644 --- a/eng/pipelines/common/templates/jobs/ci-build-nugets-job.yml +++ b/eng/pipelines/common/templates/jobs/ci-build-nugets-job.yml @@ -87,6 +87,11 @@ parameters: type: string default: SqlServer.Artifacts + # True when building on the internal ADO.Net project. + - name: isInternalBuild + type: boolean + default: false + jobs: - job: build_mds_akv_packages_job displayName: Build MDS & AKV Packages @@ -137,6 +142,10 @@ jobs: # Restore dotnet CLI tools (e.g. pwsh, apicompat) before building. - template: /eng/pipelines/common/steps/restore-dotnet-tools.yml@self + # Download the assembly signing key for internal Package-mode builds. + - ${{ if and(eq(parameters.isInternalBuild, true), ne(parameters.referenceType, 'Project')) }}: + - template: /eng/pipelines/common/steps/download-assembly-signing-key.yml@self + # When we're performing a Debug build, we still want to try _compiling_ the # code in Release mode to ensure downstream pipelines don't encounter # compilation errors. We won't use the Release artifacts for anything else @@ -159,6 +168,8 @@ jobs: abstractionsPackageVersion: ${{parameters.abstractionsPackageVersion}} loggingPackageVersion: ${{ parameters.loggingPackageVersion }} sqlServerPackageVersion: ${{ parameters.sqlServerPackageVersion }} + ${{ if and(eq(parameters.isInternalBuild, true), ne(parameters.referenceType, 'Project')) }}: + signingKeyPath: $(driverKeyFile.secureFilePath) - task: DotNetCoreCLI@2 displayName: 'Create MDS NuGet Package' @@ -206,6 +217,8 @@ jobs: mdsPackageVersion: ${{ parameters.mdsPackageVersion }} akvPackageVersion: ${{ parameters.akvPackageVersion }} sqlServerPackageVersion: ${{ parameters.sqlServerPackageVersion }} + ${{ if and(eq(parameters.isInternalBuild, true), ne(parameters.referenceType, 'Project')) }}: + signingKeyPath: $(driverKeyFile.secureFilePath) - task: DotNetCoreCLI@2 displayName: 'Create AKV Provider NuGet Package' diff --git a/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml b/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml index 1b1ea7a2e7..1bbaef213a 100644 --- a/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml +++ b/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml @@ -160,6 +160,11 @@ parameters: - name: saPassword type: string + # True when building on the internal ADO.Net project. + - name: isInternalBuild + type: boolean + default: false + jobs: - job: ${{ format('{0}', coalesce(parameters.jobDisplayName, parameters.image, 'unknown_image')) }} @@ -223,6 +228,13 @@ jobs: # Restore dotnet CLI tools (e.g. pwsh, apicompat) before building. - template: /eng/pipelines/common/steps/restore-dotnet-tools.yml@self + # Download the assembly signing keys for internal Package-mode builds. + - ${{ if and(eq(parameters.isInternalBuild, true), ne(parameters.referenceType, 'Project')) }}: + - template: /eng/pipelines/common/steps/download-assembly-signing-key.yml@self + - template: /eng/pipelines/common/steps/download-assembly-signing-key.yml@self + parameters: + isTest: true + - ${{ if ne(parameters.prebuildSteps, '') }}: - ${{ parameters.prebuildSteps }} # extra steps to run before the build like downloading sni and the required configuration @@ -378,6 +390,9 @@ jobs: loggingPackageVersion: ${{ parameters.loggingPackageVersion }} mdsPackageVersion: ${{ parameters.mdsPackageVersion }} sqlServerPackageVersion: ${{ parameters.sqlServerPackageVersion }} + ${{ if and(eq(parameters.isInternalBuild, true), ne(parameters.referenceType, 'Project')) }}: + signingKeyPath: $(driverKeyFile.secureFilePath) + testSigningKeyPath: $(testKeyFile.secureFilePath) - ${{ if and(eq(parameters.enableX86Test, true), eq(parameters.operatingSystem, 'Windows')) }}: - template: /eng/pipelines/common/templates/steps/run-all-tests-step.yml@self @@ -394,6 +409,9 @@ jobs: loggingPackageVersion: ${{ parameters.loggingPackageVersion }} mdsPackageVersion: ${{ parameters.mdsPackageVersion }} sqlServerPackageVersion: ${{ parameters.sqlServerPackageVersion }} + ${{ if and(eq(parameters.isInternalBuild, true), ne(parameters.referenceType, 'Project')) }}: + signingKeyPath: $(driverKeyFile.secureFilePath) + testSigningKeyPath: $(testKeyFile.secureFilePath) - template: /eng/pipelines/common/templates/steps/publish-test-results-step.yml@self parameters: diff --git a/eng/pipelines/common/templates/stages/ci-run-tests-stage.yml b/eng/pipelines/common/templates/stages/ci-run-tests-stage.yml index 86ddc0ca45..86c86f7aec 100644 --- a/eng/pipelines/common/templates/stages/ci-run-tests-stage.yml +++ b/eng/pipelines/common/templates/stages/ci-run-tests-stage.yml @@ -88,6 +88,11 @@ parameters: - name: testJobTimeout type: number + # True when building on the internal ADO.Net project. + - name: isInternalBuild + type: boolean + default: false + stages: - ${{ each config in parameters.testConfigurations }}: - ${{ each image in config.value.images }}: @@ -124,6 +129,7 @@ stages: loggingPackageVersion: ${{ parameters.loggingPackageVersion }} mdsArtifactsName: ${{ parameters.mdsArtifactsName }} mdsPackageVersion: ${{ parameters.mdsPackageVersion }} + isInternalBuild: ${{ parameters.isInternalBuild }} sqlServerArtifactsName: ${{ parameters.sqlServerArtifactsName }} sqlServerPackageVersion: ${{ parameters.sqlServerPackageVersion }} prebuildSteps: ${{ parameters.prebuildSteps }} @@ -162,6 +168,7 @@ stages: loggingPackageVersion: ${{ parameters.loggingPackageVersion }} mdsArtifactsName: ${{ parameters.mdsArtifactsName }} mdsPackageVersion: ${{ parameters.mdsPackageVersion }} + isInternalBuild: ${{ parameters.isInternalBuild }} sqlServerArtifactsName: ${{ parameters.sqlServerArtifactsName }} sqlServerPackageVersion: ${{ parameters.sqlServerPackageVersion }} prebuildSteps: ${{ parameters.prebuildSteps }} diff --git a/eng/pipelines/common/templates/steps/ci-project-build-step.yml b/eng/pipelines/common/templates/steps/ci-project-build-step.yml index e5fa6d4424..a4b980f46e 100644 --- a/eng/pipelines/common/templates/steps/ci-project-build-step.yml +++ b/eng/pipelines/common/templates/steps/ci-project-build-step.yml @@ -73,6 +73,13 @@ parameters: type: string default: $(sqlServerPackageVersion) + # Path to the assembly signing key file. When non-empty, passed as -p:SigningKeyPath="" to the + # build. The calling job is responsible for downloading the key (via + # download-assembly-signing-key.yml). + - name: signingKeyPath + type: string + default: '' + steps: # Build MDS - ${{ if or(eq(parameters.build, 'MDS'), eq(parameters.build, 'all'), eq(parameters.build, 'allNoDocs')) }}: @@ -91,6 +98,7 @@ steps: -p:PackageVersionLogging=${{ parameters.loggingPackageVersion }} -p:PackageVersionSqlClient=${{ parameters.mdsPackageVersion }} -p:PackageVersionSqlServer=${{ parameters.sqlServerPackageVersion }} + -p:SigningKeyPath="${{ parameters.signingKeyPath }}" # Build AKV Provider - ${{ if or(eq(parameters.build, 'AkvProvider'), eq(parameters.build, 'all'), eq(parameters.build, 'allNoDocs')) }}: @@ -110,3 +118,4 @@ steps: -p:PackageVersionLogging=${{ parameters.loggingPackageVersion }} -p:PackageVersionSqlClient=${{ parameters.mdsPackageVersion }} -p:PackageVersionSqlServer=${{ parameters.sqlServerPackageVersion }} + -p:SigningKeyPath="${{ parameters.signingKeyPath }}" diff --git a/eng/pipelines/common/templates/steps/run-all-tests-step.yml b/eng/pipelines/common/templates/steps/run-all-tests-step.yml index e41c7d22b2..02c22087b4 100644 --- a/eng/pipelines/common/templates/steps/run-all-tests-step.yml +++ b/eng/pipelines/common/templates/steps/run-all-tests-step.yml @@ -72,6 +72,18 @@ parameters: type: number default: 2 + # Path to the assembly signing key file. When non-empty, passed to build.proj so that the + # test-filter logic can include category=signed tests. + - name: signingKeyPath + type: string + default: '' + + # Path to the test assembly signing key file. When non-empty, passed to build.proj so that test + # assemblies are signed and can satisfy InternalsVisibleTo grants from signed source assemblies. + - name: testSigningKeyPath + type: string + default: '' + steps: - ${{ if parameters.debug }}: - powershell: 'dotnet sdk check' @@ -94,6 +106,8 @@ steps: -p:PackageVersionSqlClient=${{ parameters.mdsPackageVersion }} -p:PackageVersionSqlServer=${{ parameters.sqlServerPackageVersion }} -p:TestResultsFolderPath=TestResults + -p:SigningKeyPath="${{ parameters.signingKeyPath }}" + -p:TestSigningKeyPath="${{ parameters.testSigningKeyPath }}" ${{ else }}: # x86 arguments: >- -t:TestSqlClientUnit @@ -104,6 +118,8 @@ steps: -p:PackageVersionSqlServer=${{ parameters.sqlServerPackageVersion }} -p:DotnetPath=${{ parameters.dotnetx86RootPath }} -p:TestResultsFolderPath=TestResults + -p:SigningKeyPath="${{ parameters.signingKeyPath }}" + -p:TestSigningKeyPath="${{ parameters.testSigningKeyPath }}" - task: DotNetCoreCLI@2 displayName: 'Run Flaky Unit Tests ${{parameters.msbuildArchitecture }}' @@ -122,6 +138,8 @@ steps: -p:TestFilters="category=flaky" -p:TestResultsFolderPath=TestResults -p:TestCodeCoverage=false + -p:SigningKeyPath="${{ parameters.signingKeyPath }}" + -p:TestSigningKeyPath="${{ parameters.testSigningKeyPath }}" ${{ else }}: # x86 arguments: >- -t:TestSqlClientUnit @@ -134,6 +152,8 @@ steps: -p:TestFilters="category=flaky" -p:TestResultsFolderPath=TestResults -p:TestCodeCoverage=false + -p:SigningKeyPath="${{ parameters.signingKeyPath }}" + -p:TestSigningKeyPath="${{ parameters.testSigningKeyPath }}" continueOnError: true - task: DotNetCoreCLI@2 @@ -153,6 +173,8 @@ steps: -p:PackageVersionSqlClient=${{ parameters.mdsPackageVersion }} -p:PackageVersionSqlServer=${{ parameters.sqlServerPackageVersion }} -p:TestResultsFolderPath=TestResults + -p:SigningKeyPath="${{ parameters.signingKeyPath }}" + -p:TestSigningKeyPath="${{ parameters.testSigningKeyPath }}" ${{ else }}: # x86 arguments: >- -t:TestSqlClientFunctional @@ -165,6 +187,8 @@ steps: -p:PackageVersionSqlServer=${{ parameters.sqlServerPackageVersion }} -p:DotnetPath=${{ parameters.dotnetx86RootPath }} -p:TestResultsFolderPath=TestResults + -p:SigningKeyPath="${{ parameters.signingKeyPath }}" + -p:TestSigningKeyPath="${{ parameters.testSigningKeyPath }}" - task: DotNetCoreCLI@2 displayName: 'Run Flaky Functional Tests ${{parameters.msbuildArchitecture }}' @@ -185,6 +209,8 @@ steps: -p:TestFilters="category=flaky" -p:TestResultsFolderPath=TestResults -p:TestCodeCoverage=false + -p:SigningKeyPath="${{ parameters.signingKeyPath }}" + -p:TestSigningKeyPath="${{ parameters.testSigningKeyPath }}" ${{ else }}: # x86 arguments: >- -t:TestSqlClientFunctional @@ -199,6 +225,8 @@ steps: -p:TestFilters="category=flaky" -p:TestResultsFolderPath=TestResults -p:TestCodeCoverage=false + -p:SigningKeyPath="${{ parameters.signingKeyPath }}" + -p:TestSigningKeyPath="${{ parameters.testSigningKeyPath }}" continueOnError: true - task: DotNetCoreCLI@2 @@ -219,6 +247,8 @@ steps: -p:PackageVersionSqlClient=${{ parameters.mdsPackageVersion }} -p:PackageVersionSqlServer=${{ parameters.sqlServerPackageVersion }} -p:TestResultsFolderPath=TestResults + -p:SigningKeyPath="${{ parameters.signingKeyPath }}" + -p:TestSigningKeyPath="${{ parameters.testSigningKeyPath }}" ${{ else }}: # x86 arguments: >- -t:TestSqlClientManual @@ -232,6 +262,8 @@ steps: -p:PackageVersionSqlServer=${{ parameters.sqlServerPackageVersion }} -p:DotnetPath=${{ parameters.dotnetx86RootPath }} -p:TestResultsFolderPath=TestResults + -p:SigningKeyPath="${{ parameters.signingKeyPath }}" + -p:TestSigningKeyPath="${{ parameters.testSigningKeyPath }}" retryCountOnTaskFailure: ${{parameters.retryCountOnManualTests }} - task: DotNetCoreCLI@2 @@ -254,6 +286,8 @@ steps: -p:TestFilters="category=flaky" -p:TestResultsFolderPath=TestResults -p:TestCodeCoverage=false + -p:SigningKeyPath="${{ parameters.signingKeyPath }}" + -p:TestSigningKeyPath="${{ parameters.testSigningKeyPath }}" ${{ else }}: # x86 arguments: >- -t:TestSqlClientManual @@ -269,6 +303,8 @@ steps: -p:TestFilters="category=flaky" -p:TestResultsFolderPath=TestResults -p:TestCodeCoverage=false + -p:SigningKeyPath="${{ parameters.signingKeyPath }}" + -p:TestSigningKeyPath="${{ parameters.testSigningKeyPath }}" continueOnError: true - ${{ else }}: # Linux or macOS @@ -286,6 +322,8 @@ steps: -p:PackageVersionSqlClient=${{ parameters.mdsPackageVersion }} -p:PackageVersionSqlServer=${{ parameters.sqlServerPackageVersion }} -p:TestResultsFolderPath=TestResults + -p:SigningKeyPath="${{ parameters.signingKeyPath }}" + -p:TestSigningKeyPath="${{ parameters.testSigningKeyPath }}" - task: DotNetCoreCLI@2 displayName: 'Run Flaky Unit Tests' @@ -303,6 +341,8 @@ steps: -p:TestFilters="category=flaky" -p:TestResultsFolderPath=TestResults -p:TestCodeCoverage=false + -p:SigningKeyPath="${{ parameters.signingKeyPath }}" + -p:TestSigningKeyPath="${{ parameters.testSigningKeyPath }}" continueOnError: true - task: DotNetCoreCLI@2 @@ -321,6 +361,8 @@ steps: -p:PackageVersionSqlClient=${{ parameters.mdsPackageVersion }} -p:PackageVersionSqlServer=${{ parameters.sqlServerPackageVersion }} -p:TestResultsFolderPath=TestResults + -p:SigningKeyPath="${{ parameters.signingKeyPath }}" + -p:TestSigningKeyPath="${{ parameters.testSigningKeyPath }}" - task: DotNetCoreCLI@2 displayName: 'Run Flaky Functional Tests' @@ -340,6 +382,8 @@ steps: -p:TestFilters="category=flaky" -p:TestResultsFolderPath=TestResults -p:TestCodeCoverage=false + -p:SigningKeyPath="${{ parameters.signingKeyPath }}" + -p:TestSigningKeyPath="${{ parameters.testSigningKeyPath }}" continueOnError: true - task: DotNetCoreCLI@2 displayName: 'Run Manual Tests' @@ -358,6 +402,8 @@ steps: -p:PackageVersionSqlClient=${{ parameters.mdsPackageVersion }} -p:PackageVersionSqlServer=${{ parameters.sqlServerPackageVersion }} -p:TestResultsFolderPath=TestResults + -p:SigningKeyPath="${{ parameters.signingKeyPath }}" + -p:TestSigningKeyPath="${{ parameters.testSigningKeyPath }}" retryCountOnTaskFailure: ${{parameters.retryCountOnManualTests }} - task: DotNetCoreCLI@2 @@ -379,4 +425,6 @@ steps: -p:TestFilters="category=flaky" -p:TestResultsFolderPath=TestResults -p:TestCodeCoverage=false + -p:SigningKeyPath="${{ parameters.signingKeyPath }}" + -p:TestSigningKeyPath="${{ parameters.testSigningKeyPath }}" continueOnError: true diff --git a/eng/pipelines/dotnet-sqlclient-ci-core.yml b/eng/pipelines/dotnet-sqlclient-ci-core.yml index 9ee7c7132d..5361407e0d 100644 --- a/eng/pipelines/dotnet-sqlclient-ci-core.yml +++ b/eng/pipelines/dotnet-sqlclient-ci-core.yml @@ -104,6 +104,12 @@ parameters: - detailed - diagnostic + # True when building on the internal ADO.Net project. Internal builds may perform additional or + # different steps, such as assembly signing. + - name: isInternalBuild + type: boolean + default: false + variables: - template: /eng/pipelines/libraries/ci-build-variables.yml@self @@ -167,6 +173,7 @@ stages: loggingArtifactsName: $(loggingArtifactsName) loggingPackageVersion: $(loggingPackageVersion) referenceType: ${{ parameters.referenceType }} + isInternalBuild: ${{ parameters.isInternalBuild }} # When building Abstractions via packages, we must depend on the Logging # package. ${{ if eq(parameters.referenceType, 'Package') }}: @@ -188,6 +195,7 @@ stages: mdsPackageVersion: $(mdsPackageVersion) akvPackageVersion: $(akvPackageVersion) referenceType: ${{ parameters.referenceType }} + isInternalBuild: ${{ parameters.isInternalBuild }} sqlServerArtifactsName: $(sqlServerArtifactsName) sqlServerPackageVersion: $(sqlServerPackageVersion) SNIVersion: ${{ parameters.SNIVersion }} @@ -251,6 +259,7 @@ stages: loggingPackageVersion: $(loggingPackageVersion) mdsArtifactsName: $(mdsArtifactsName) mdsPackageVersion: $(mdsPackageVersion) + isInternalBuild: ${{ parameters.isInternalBuild }} sqlServerArtifactsName: $(sqlServerArtifactsName) sqlServerPackageVersion: $(sqlServerPackageVersion) testJobTimeout: ${{ parameters.testJobTimeout }} diff --git a/eng/pipelines/dotnet-sqlclient-ci-package-reference-pipeline.yml b/eng/pipelines/dotnet-sqlclient-ci-package-reference-pipeline.yml index 98cac1bcdd..c510dc2c6b 100644 --- a/eng/pipelines/dotnet-sqlclient-ci-package-reference-pipeline.yml +++ b/eng/pipelines/dotnet-sqlclient-ci-package-reference-pipeline.yml @@ -174,3 +174,4 @@ extends: testJobTimeout: ${{ parameters.testJobTimeout }} testSets: ${{ parameters.testSets }} useManagedSNI: ${{ parameters.useManagedSNI }} + isInternalBuild: ${{ eq(variables['System.TeamProject'], 'ADO.Net') }} diff --git a/eng/pipelines/dotnet-sqlclient-ci-project-reference-pipeline.yml b/eng/pipelines/dotnet-sqlclient-ci-project-reference-pipeline.yml index 7068c9693c..29f2660f0f 100644 --- a/eng/pipelines/dotnet-sqlclient-ci-project-reference-pipeline.yml +++ b/eng/pipelines/dotnet-sqlclient-ci-project-reference-pipeline.yml @@ -174,3 +174,4 @@ extends: testJobTimeout: ${{ parameters.testJobTimeout }} testSets: ${{ parameters.testSets }} useManagedSNI: ${{ parameters.useManagedSNI }} + isInternalBuild: ${{ eq(variables['System.TeamProject'], 'ADO.Net') }} diff --git a/eng/pipelines/jobs/pack-abstractions-package-ci-job.yml b/eng/pipelines/jobs/pack-abstractions-package-ci-job.yml index d077aa2115..37b16769b4 100644 --- a/eng/pipelines/jobs/pack-abstractions-package-ci-job.yml +++ b/eng/pipelines/jobs/pack-abstractions-package-ci-job.yml @@ -77,6 +77,11 @@ parameters: # Reference sibling packages as C# projects. - Project + # True when building on the internal ADO.Net project. + - name: isInternalBuild + type: boolean + default: false + jobs: - job: pack_abstractions_package_job @@ -119,6 +124,23 @@ jobs: - name: Configuration value: '' + # Build properties passed to dotnet pack. Composed from a base set plus + # optional suffixes for Package-mode dependencies and assembly signing. + - name: baseBuildProperties + value: AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }};AbstractionsAssemblyFileVersion=${{ parameters.abstractionsAssemblyFileVersion }} + + # NOTE: We use compile-time ${{ if }} branches rather than concatenating + # separate variables (e.g. "$(base);$(optional);$(signing)") because + # when the optional variables are empty the semicolons remain, producing + # a trailing ";;" that MSBuild rejects with MSB1005. + - name: buildProperties + ${{ if and(eq(parameters.referenceType, 'Package'), eq(parameters.isInternalBuild, true)) }}: + value: $(baseBuildProperties);ReferenceType=Package;LoggingPackageVersion=${{ parameters.loggingPackageVersion }};SigningKeyPath=$(driverKeyFile.secureFilePath) + ${{ elseif eq(parameters.referenceType, 'Package') }}: + value: $(baseBuildProperties);ReferenceType=Package;LoggingPackageVersion=${{ parameters.loggingPackageVersion }} + ${{ else }}: + value: $(baseBuildProperties) + steps: # Emit environment variables if debug is enabled. @@ -140,32 +162,20 @@ jobs: parameters: debug: ${{ parameters.debug }} + # Download the assembly signing key for internal Package-mode builds. + - ${{ if and(eq(parameters.isInternalBuild, true), ne(parameters.referenceType, 'Project')) }}: + - template: /eng/pipelines/common/steps/download-assembly-signing-key.yml@self + # Create the NuGet packages. - # - # When referenceType is Package, we must pass ReferenceType and the - # dependency version so that Directory.Packages.props applies version - # ranges to sibling package dependencies. - - ${{ if eq(parameters.referenceType, 'Package') }}: - - task: DotNetCoreCLI@2 - displayName: Create NuGet Package - inputs: - command: pack - packagesToPack: $(project) - configurationToPack: ${{ parameters.buildConfiguration }} - packDirectory: $(dotnetPackagesDir) - verbosityToPack: ${{ parameters.dotnetVerbosity }} - buildProperties: AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }};AbstractionsAssemblyFileVersion=${{ parameters.abstractionsAssemblyFileVersion }};ReferenceType=Package;LoggingPackageVersion=${{ parameters.loggingPackageVersion }} - - - ${{ else }}: - - task: DotNetCoreCLI@2 - displayName: Create NuGet Package - inputs: - command: pack - packagesToPack: $(project) - configurationToPack: ${{ parameters.buildConfiguration }} - packDirectory: $(dotnetPackagesDir) - verbosityToPack: ${{ parameters.dotnetVerbosity }} - buildProperties: AbstractionsPackageVersion=${{ parameters.abstractionsPackageVersion }};AbstractionsAssemblyFileVersion=${{ parameters.abstractionsAssemblyFileVersion }} + - task: DotNetCoreCLI@2 + displayName: Create NuGet Package + inputs: + command: pack + packagesToPack: $(project) + configurationToPack: ${{ parameters.buildConfiguration }} + packDirectory: $(dotnetPackagesDir) + verbosityToPack: ${{ parameters.dotnetVerbosity }} + buildProperties: $(buildProperties) # Publish the NuGet packages as a named pipeline artifact. - task: PublishPipelineArtifact@1 diff --git a/eng/pipelines/jobs/test-abstractions-package-ci-job.yml b/eng/pipelines/jobs/test-abstractions-package-ci-job.yml index 83366b52a9..ceb176274a 100644 --- a/eng/pipelines/jobs/test-abstractions-package-ci-job.yml +++ b/eng/pipelines/jobs/test-abstractions-package-ci-job.yml @@ -61,6 +61,20 @@ parameters: - name: poolName type: string + # True when building on the internal ADO.Net project. When set, assemblies + # are signed with the driver key and tests are signed with the test key. + - name: isInternalBuild + type: boolean + default: false + + # The C# project reference type to use when building. + - name: referenceType + type: string + default: Project + values: + - Package + - Project + # The pool VM image to use. - name: vmImage type: string @@ -89,11 +103,21 @@ jobs: value: src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/Abstractions.Test.csproj # dotnet CLI arguments for build/test/pack commands - - name: buildArguments + - name: dotnetBuildOpts value: >- -p:Configuration=${{ parameters.buildConfiguration }} --verbosity ${{ parameters.dotnetVerbosity }} + # Signing arguments — only set for internal Package-mode builds. + - ${{ if and(eq(parameters.isInternalBuild, true), ne(parameters.referenceType, 'Project')) }}: + - name: signingArguments + value: >- + -p:SigningKeyPath=$(driverKeyFile.secureFilePath) + -p:TestSigningKeyPath=$(testKeyFile.secureFilePath) + - ${{ else }}: + - name: signingArguments + value: '' + # Explicitly unset the $PLATFORM environment variable that is set by the # 'ADO Build properties' Library in the ADO SqlClientDrivers public project. # This is defined with a non-standard Platform of 'AnyCPU', and will fail @@ -121,6 +145,13 @@ jobs: - pwsh: 'Get-ChildItem Env: | Sort-Object Name' displayName: '[Debug] Print Environment Variables' + # Download the assembly signing keys for internal Package-mode builds. + - ${{ if and(eq(parameters.isInternalBuild, true), ne(parameters.referenceType, 'Project')) }}: + - template: /eng/pipelines/common/steps/download-assembly-signing-key.yml@self + - template: /eng/pipelines/common/steps/download-assembly-signing-key.yml@self + parameters: + isTest: true + # Install the .NET SDK and Runtimes. - template: /eng/pipelines/common/steps/install-dotnet.yml@self parameters: @@ -136,7 +167,7 @@ jobs: inputs: command: build projects: $(project) - arguments: $(buildArguments) + arguments: $(dotnetBuildOpts) $(signingArguments) # Run the tests for each .NET runtime. - ${{ each runtime in parameters.netRuntimes }}: @@ -146,7 +177,7 @@ jobs: command: test projects: $(project) arguments: >- - $(buildArguments) + $(dotnetBuildOpts) --no-build -f ${{ runtime }} --filter "category != failing & category != flaky & category != interactive" @@ -157,7 +188,7 @@ jobs: command: test projects: $(project) arguments: >- - $(buildArguments) + $(dotnetBuildOpts) --no-build -f ${{ runtime }} --filter "category = flaky" @@ -170,7 +201,7 @@ jobs: command: test projects: $(project) arguments: >- - $(buildArguments) + $(dotnetBuildOpts) --no-build -f ${{ runtime }} --filter "category != failing & category != flaky & category != interactive" @@ -181,7 +212,7 @@ jobs: command: test projects: $(project) arguments: >- - $(buildArguments) + $(dotnetBuildOpts) --no-build -f ${{ runtime }} --filter "category = flaky" diff --git a/eng/pipelines/onebranch/jobs/validate-signed-package-job.yml b/eng/pipelines/onebranch/jobs/validate-signed-package-job.yml index afa5aa5918..0769e2093c 100644 --- a/eng/pipelines/onebranch/jobs/validate-signed-package-job.yml +++ b/eng/pipelines/onebranch/jobs/validate-signed-package-job.yml @@ -157,8 +157,8 @@ jobs: $nugetPackageInstallPath = "${{ variables.nugetPackageInstallPath }}" echo "nugetPackageInstallPath= $nugetPackageInstallPath" - # Verify strong name signing ##################################### - echo "> 1. Verifying strong name signing of DLLs ..." + # Verify strong-name signing ################################### + echo "> 1. Verifying strong-name signing of DLLs ..." # @TODO: This path seems brittle to VS upgrades, can we make it more flexible? $snPath = "C:\Program Files (x86)\Microsoft SDKs\Windows\*\bin\NETFX 4.8.1 Tools\sn.exe" diff --git a/eng/pipelines/onebranch/steps/build-buildproj-step.yml b/eng/pipelines/onebranch/steps/build-buildproj-step.yml index 2a5730c848..4b18be369b 100644 --- a/eng/pipelines/onebranch/steps/build-buildproj-step.yml +++ b/eng/pipelines/onebranch/steps/build-buildproj-step.yml @@ -44,12 +44,8 @@ parameters: type: string steps: - # Download the strong name signing key from secure file storage - - task: DownloadSecureFile@1 - displayName: 'Download Signing Key' - inputs: - secureFile: 'netfxKeypair.snk' - name: keyFile + # Download the assembly signing key from secure file storage. + - template: /eng/pipelines/common/steps/download-assembly-signing-key.yml@self - task: DotNetCoreCLI@2 displayName: 'build.proj - Build${{ parameters.packageShortName }}' @@ -61,7 +57,7 @@ steps: -p:Configuration=${{ parameters.buildConfiguration }} -p:ReferenceType=Package -p:SkipDependencyPack=true - -p:SigningKeyPath="$(keyFile.secureFilePath)" + -p:SigningKeyPath="$(driverKeyFile.secureFilePath)" -p:BuildNumber="$(Build.BuildNumber)" -p:PackageVersion${{ parameters.packageShortName }}="${{ parameters.packageVersion }}" ${{ parameters.dependencyArguments }} diff --git a/eng/pipelines/stages/build-abstractions-package-ci-stage.yml b/eng/pipelines/stages/build-abstractions-package-ci-stage.yml index 8579b79caf..82a4efd6a4 100644 --- a/eng/pipelines/stages/build-abstractions-package-ci-stage.yml +++ b/eng/pipelines/stages/build-abstractions-package-ci-stage.yml @@ -91,6 +91,11 @@ parameters: # Reference sibling packages as C# projects. - Project + # True when building on the internal ADO.Net project. + - name: isInternalBuild + type: boolean + default: false + stages: - stage: build_abstractions_package_stage @@ -112,10 +117,12 @@ stages: debug: ${{ parameters.debug }} displayNamePrefix: Linux dotnetVerbosity: ${{ parameters.dotnetVerbosity }} + isInternalBuild: ${{ parameters.isInternalBuild }} jobNameSuffix: linux netFrameworkRuntimes: [] netRuntimes: [net8.0, net9.0, net10.0] poolName: Azure Pipelines + referenceType: ${{ parameters.referenceType }} vmImage: ubuntu-latest # ------------------------------------------------------------------------ @@ -127,10 +134,12 @@ stages: debug: ${{ parameters.debug }} displayNamePrefix: Win dotnetVerbosity: ${{ parameters.dotnetVerbosity }} + isInternalBuild: ${{ parameters.isInternalBuild }} jobNameSuffix: windows netFrameworkRuntimes: [net462] netRuntimes: [net8.0, net9.0, net10.0] poolName: Azure Pipelines + referenceType: ${{ parameters.referenceType }} vmImage: windows-latest # ------------------------------------------------------------------------ @@ -142,10 +151,12 @@ stages: debug: ${{ parameters.debug }} displayNamePrefix: macOS dotnetVerbosity: ${{ parameters.dotnetVerbosity }} + isInternalBuild: ${{ parameters.isInternalBuild }} jobNameSuffix: macos netFrameworkRuntimes: [] netRuntimes: [net8.0, net9.0, net10.0] poolName: Azure Pipelines + referenceType: ${{ parameters.referenceType }} vmImage: macos-latest # ------------------------------------------------------------------------ @@ -168,3 +179,4 @@ stages: loggingArtifactsName: ${{ parameters.loggingArtifactsName }} loggingPackageVersion: ${{ parameters.loggingPackageVersion }} referenceType: ${{ parameters.referenceType }} + isInternalBuild: ${{ parameters.isInternalBuild }} diff --git a/eng/pipelines/stages/build-sqlclient-package-ci-stage.yml b/eng/pipelines/stages/build-sqlclient-package-ci-stage.yml index 858f1c8500..aa62f2122e 100644 --- a/eng/pipelines/stages/build-sqlclient-package-ci-stage.yml +++ b/eng/pipelines/stages/build-sqlclient-package-ci-stage.yml @@ -85,6 +85,11 @@ parameters: type: string default: '' + # True when building on the internal ADO.Net project. + - name: isInternalBuild + type: boolean + default: false + stages: - stage: build_sqlclient_package_stage @@ -109,6 +114,7 @@ stages: akvPackageVersion: ${{ parameters.akvPackageVersion }} sqlServerArtifactsName: ${{ parameters.sqlServerArtifactsName }} sqlServerPackageVersion: ${{ parameters.sqlServerPackageVersion }} + isInternalBuild: ${{ parameters.isInternalBuild }} ${{ if ne(parameters.SNIVersion, '') }}: prebuildSteps: - template: /eng/pipelines/common/templates/steps/override-sni-version.yml@self diff --git a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Abstractions.csproj b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Abstractions.csproj index 0a9d29aa1e..0957180637 100644 --- a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Abstractions.csproj +++ b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/Abstractions.csproj @@ -34,10 +34,22 @@ + + + + + + + $(RepoRoot)artifacts/ @@ -79,6 +91,11 @@ Condition="'$(ReferenceType)' == 'Package'" /> + + + + + diff --git a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/SqlAuthenticationProvider.Internal.cs b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/SqlAuthenticationProvider.Internal.cs index 5007384465..693206261e 100644 --- a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/SqlAuthenticationProvider.Internal.cs +++ b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/src/SqlAuthenticationProvider.Internal.cs @@ -40,30 +40,75 @@ static Internal() { const string assemblyName = "Microsoft.Data.SqlClient"; - // If the MDS package is present, load its + // If the SqlClient assembly is present, load its // SqlAuthenticationProviderManager class and get/set methods. try { - // Try to load the MDS assembly. + // Try to load the SqlClient assembly. + + #if STRONG_NAME_SIGNING + + // The expected public key token of the SqlClient assembly, used to avoid invoking + // APIs from imposter assemblies. This is the same token used by all assemblies in + // this repository when strong-name signed. + byte[] expectedPublicKeyToken = + [ 0x23, 0xec, 0x7f, 0xc2, 0xd6, 0xea, 0xa4, 0xa5 ]; + + // When strong-name signing is enabled, build a fully-qualified AssemblyName that + // includes the expected public key token. + Log($"Attempting to load SqlClient assembly={assemblyName} with " + + "expected public key token=" + + BitConverter.ToString(expectedPublicKeyToken).Replace("-", "")); + + var qualifiedName = new AssemblyName(assemblyName); + qualifiedName.SetPublicKeyToken(expectedPublicKeyToken); + + // The .NET Framework runtime enforces the token during binding, causing Load() to + // throw if it doesn't match. The .NET (Core) runtime ignores the token, so we + // verify it ourselves below. + var assembly = Assembly.Load(qualifiedName); + + // Defense-in-depth: verify the public key token after loading. This is necessary + // on .NET Core where the runtime does not enforce the token. It is harmless on .NET + // Framework. + if (assembly is not null) + { + byte[]? actualToken = assembly.GetName().GetPublicKeyToken(); + + if (actualToken is null || + !actualToken.AsSpan().SequenceEqual(expectedPublicKeyToken)) + { + Log($"SqlClient assembly={assembly.GetName()} has an " + + "unexpected public key token; " + + "Get/SetProvider() will not function"); + return; + } + } + + #else + + // Strong-name signing is disabled, so we cannot verify the public key token. + Log($"Loading SqlClient assembly={assemblyName} without strong-name identity " + + "verification; ensure this assembly is from a trusted source"); + var assembly = Assembly.Load(assemblyName); + #endif + if (assembly is null) { - Log($"MDS assembly={assemblyName} not found; " + + Log($"SqlClient assembly={assemblyName} not found; " + "Get/SetProvider() will not function"); return; } - // TODO(https://sqlclientdrivers.visualstudio.com/ADO.Net/_workitems/edit/39845): - // Verify the assembly is signed by us? - // Look for the manager class. const string className = "Microsoft.Data.SqlClient.SqlAuthenticationProviderManager"; Type? manager = assembly.GetType(className); if (manager is null) { - Log($"MDS auth manager manager class={className} not found; " + + Log($"SqlClient auth manager class={className} not found; " + "Get/SetProvider() will not function"); return; } @@ -75,7 +120,7 @@ static Internal() if (_getProvider is null) { - Log($"MDS GetProvider() method not found; " + + Log($"SqlClient GetProvider() method not found; " + "GetProvider() will not function"); } @@ -85,7 +130,7 @@ static Internal() if (_setProvider is null) { - Log($"MDS SetProvider() method not found; " + + Log($"SqlClient SetProvider() method not found; " + "SetProvider() will not function"); } } @@ -97,7 +142,7 @@ or BadImageFormatException or FileLoadException or FileNotFoundException) { - Log($"MDS assembly={assemblyName} not found or not usable; " + + Log($"SqlClient assembly={assemblyName} not found or not usable; " + $"Get/SetProvider() will not function: {ex} "); } // Any other exceptions are fatal. diff --git a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/Abstractions.Test.csproj b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/Abstractions.Test.csproj index 427a7aaf9f..8a80c24a51 100644 --- a/src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/Abstractions.Test.csproj +++ b/src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/Abstractions.Test.csproj @@ -2,12 +2,26 @@ Microsoft.Data.SqlClient.Extensions.Abstractions.Test - net462;net8.0;net9.0;net10.0 + net8.0;net9.0;net10.0 + + + $(TargetFrameworks);net462 false true + + + + true + $(TestSigningKeyPath) + + enable