From 39dd1abcc6f8868e2acc0b267fbae606400e9ce8 Mon Sep 17 00:00:00 2001 From: Heath Stewart Date: Thu, 8 Sep 2022 17:06:36 -0700 Subject: [PATCH 1/4] Verify challenge resource matches request domain --- .../CHANGELOG.md | 7 +++ ....KeyVault.Administration.netstandard2.0.cs | 1 + ...re.Security.KeyVault.Administration.csproj | 2 +- .../src/KeyVaultAccessControlClient.cs | 6 +- .../KeyVaultAdministrationClientOptions.cs | 6 ++ .../src/KeyVaultBackupClient.cs | 6 +- ...ChallengeBasedAuthenticationPolicyTests.cs | 62 ++++++++++++++++--- .../CHANGELOG.md | 7 +++ ...ty.KeyVault.Certificates.netstandard2.0.cs | 1 + ...zure.Security.KeyVault.Certificates.csproj | 2 +- .../src/CertificateClient.cs | 4 +- .../src/CertificateClientOptions.cs | 6 ++ .../Azure.Security.KeyVault.Keys/CHANGELOG.md | 7 +++ ...e.Security.KeyVault.Keys.netstandard2.0.cs | 2 + .../src/Azure.Security.KeyVault.Keys.csproj | 2 +- .../src/Cryptography/CryptographyClient.cs | 2 + .../Cryptography/CryptographyClientOptions.cs | 6 ++ .../src/Cryptography/KeyResolver.cs | 4 +- .../Cryptography/RemoteCryptographyClient.cs | 2 +- .../src/KeyClient.cs | 4 +- .../src/KeyClientOptions.cs | 6 ++ .../CHANGELOG.md | 7 +++ ...ecurity.KeyVault.Secrets.netstandard2.0.cs | 1 + .../Azure.Security.KeyVault.Secrets.csproj | 2 +- .../src/SecretClient.cs | 4 +- .../src/SecretClientOptions.cs | 6 ++ .../src/ChallengeBasedAuthenticationPolicy.cs | 22 ++++++- 27 files changed, 162 insertions(+), 25 deletions(-) diff --git a/sdk/keyvault/Azure.Security.KeyVault.Administration/CHANGELOG.md b/sdk/keyvault/Azure.Security.KeyVault.Administration/CHANGELOG.md index b3c21e6834b7..56d2079bc6fe 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Administration/CHANGELOG.md +++ b/sdk/keyvault/Azure.Security.KeyVault.Administration/CHANGELOG.md @@ -1,5 +1,12 @@ # Release History +## 4.2.0 (2022-09-13) + +### Breaking Changes + +- Verify the challenge resource matches the vault domain. + This should affect few customers who can set `KeyVaultAdministrationClientOptions.VerifyChallengeResource` to `false` to disable. + ## 4.1.0 (2022-03-24) Changes from both the last release and the last beta include: diff --git a/sdk/keyvault/Azure.Security.KeyVault.Administration/api/Azure.Security.KeyVault.Administration.netstandard2.0.cs b/sdk/keyvault/Azure.Security.KeyVault.Administration/api/Azure.Security.KeyVault.Administration.netstandard2.0.cs index d6c76260777a..d03d4e886b13 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Administration/api/Azure.Security.KeyVault.Administration.netstandard2.0.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Administration/api/Azure.Security.KeyVault.Administration.netstandard2.0.cs @@ -39,6 +39,7 @@ public KeyVaultAccessControlClient(System.Uri vaultUri, Azure.Core.TokenCredenti public partial class KeyVaultAdministrationClientOptions : Azure.Core.ClientOptions { public KeyVaultAdministrationClientOptions(Azure.Security.KeyVault.Administration.KeyVaultAdministrationClientOptions.ServiceVersion version = Azure.Security.KeyVault.Administration.KeyVaultAdministrationClientOptions.ServiceVersion.V7_3) { } + public bool VerifyChallengeResource { get { throw null; } set { } } public Azure.Security.KeyVault.Administration.KeyVaultAdministrationClientOptions.ServiceVersion Version { get { throw null; } } public enum ServiceVersion { diff --git a/sdk/keyvault/Azure.Security.KeyVault.Administration/src/Azure.Security.KeyVault.Administration.csproj b/sdk/keyvault/Azure.Security.KeyVault.Administration/src/Azure.Security.KeyVault.Administration.csproj index 329aefd89b74..0d911b2501f5 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Administration/src/Azure.Security.KeyVault.Administration.csproj +++ b/sdk/keyvault/Azure.Security.KeyVault.Administration/src/Azure.Security.KeyVault.Administration.csproj @@ -3,7 +3,7 @@ This is the Microsoft Azure Key Vault Administration client library Microsoft Azure.Security.KeyVault.Administration client library - 4.1.0 + 4.2.0 4.0.0 Microsoft Azure Key Vault Administration;$(PackageCommonTags) diff --git a/sdk/keyvault/Azure.Security.KeyVault.Administration/src/KeyVaultAccessControlClient.cs b/sdk/keyvault/Azure.Security.KeyVault.Administration/src/KeyVaultAccessControlClient.cs index da9682685a30..d34917d6aa5f 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Administration/src/KeyVaultAccessControlClient.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Administration/src/KeyVaultAccessControlClient.cs @@ -36,7 +36,7 @@ protected KeyVaultAccessControlClient() /// /// Initializes a new instance of the class for the specified vault. /// - /// A to the vault on which the client operates. Appears as "DNS Name" in the Azure portal. + /// A to the vault on which the client operates. Appears as "DNS Name" in the Azure portal. You should validate that this URI references a valid Key Vault or Managed HSM resource. See https://aka.ms/azsdk/blog/vault-uri for details. /// A used to authenticate requests to the vault, such as DefaultAzureCredential. /// or is null. public KeyVaultAccessControlClient(Uri vaultUri, TokenCredential credential) @@ -47,7 +47,7 @@ public KeyVaultAccessControlClient(Uri vaultUri, TokenCredential credential) /// /// Initializes a new instance of the class for the specified vault. /// - /// A to the vault on which the client operates. Appears as "DNS Name" in the Azure portal. + /// A to the vault on which the client operates. Appears as "DNS Name" in the Azure portal. You should validate that this URI references a valid Key Vault or Managed HSM resource. See https://aka.ms/azsdk/blog/vault-uri for details. /// A used to authenticate requests to the vault, such as DefaultAzureCredential. /// that allow to configure the management of the request sent to Key Vault. /// or is null. @@ -62,7 +62,7 @@ public KeyVaultAccessControlClient(Uri vaultUri, TokenCredential credential, Key string apiVersion = options.GetVersionString(); HttpPipeline pipeline = HttpPipelineBuilder.Build(options, - new ChallengeBasedAuthenticationPolicy(credential)); + new ChallengeBasedAuthenticationPolicy(credential, options.VerifyChallengeResource)); _diagnostics = new ClientDiagnostics(options); _definitionsRestClient = new RoleDefinitionsRestClient(_diagnostics, pipeline, apiVersion); diff --git a/sdk/keyvault/Azure.Security.KeyVault.Administration/src/KeyVaultAdministrationClientOptions.cs b/sdk/keyvault/Azure.Security.KeyVault.Administration/src/KeyVaultAdministrationClientOptions.cs index 2c2336006d89..97aee7fc98d7 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Administration/src/KeyVaultAdministrationClientOptions.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Administration/src/KeyVaultAdministrationClientOptions.cs @@ -59,6 +59,12 @@ public KeyVaultAdministrationClientOptions(ServiceVersion version = LatestVersio this.ConfigureLogging(); } + /// + /// Gets or sets whether to verify the authentication challenge resource matches the Key Vault or Managed HSM domain. + /// The default is true. + /// + public bool VerifyChallengeResource { get; set; } = true; + internal string GetVersionString() { return Version switch diff --git a/sdk/keyvault/Azure.Security.KeyVault.Administration/src/KeyVaultBackupClient.cs b/sdk/keyvault/Azure.Security.KeyVault.Administration/src/KeyVaultBackupClient.cs index e5d94a2d0a7c..32c71bcaebf3 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Administration/src/KeyVaultBackupClient.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Administration/src/KeyVaultBackupClient.cs @@ -33,7 +33,7 @@ protected KeyVaultBackupClient() /// /// Initializes a new instance of the class for the specified vault. /// - /// A to the vault on which the client operates. Appears as "DNS Name" in the Azure portal. + /// A to the vault on which the client operates. Appears as "DNS Name" in the Azure portal. You should validate that this URI references a valid Key Vault or Managed HSM resource. See https://aka.ms/azsdk/blog/vault-uri for details. /// A used to authenticate requests to the vault, such as DefaultAzureCredential. /// or is null. public KeyVaultBackupClient(Uri vaultUri, TokenCredential credential) @@ -43,7 +43,7 @@ public KeyVaultBackupClient(Uri vaultUri, TokenCredential credential) /// /// Initializes a new instance of the class for the specified vault. /// - /// A to the vault on which the client operates. Appears as "DNS Name" in the Azure portal. + /// A to the vault on which the client operates. Appears as "DNS Name" in the Azure portal You should validate that this URI references a valid Key Vault or Managed HSM resource. See https://aka.ms/azsdk/blog/vault-uri for details.. /// A used to authenticate requests to the vault, such as DefaultAzureCredential. /// that allow to configure the management of the request sent to Key Vault. /// or is null. @@ -58,7 +58,7 @@ public KeyVaultBackupClient(Uri vaultUri, TokenCredential credential, KeyVaultAd string apiVersion = options.GetVersionString(); HttpPipeline pipeline = HttpPipelineBuilder.Build(options, - new ChallengeBasedAuthenticationPolicy(credential)); + new ChallengeBasedAuthenticationPolicy(credential, options.VerifyChallengeResource)); _diagnostics = new ClientDiagnostics(options); _restClient = new BackupRestoreRestClient(_diagnostics, pipeline, apiVersion); diff --git a/sdk/keyvault/Azure.Security.KeyVault.Administration/tests/ChallengeBasedAuthenticationPolicyTests.cs b/sdk/keyvault/Azure.Security.KeyVault.Administration/tests/ChallengeBasedAuthenticationPolicyTests.cs index d4b005d6dee4..f8b17ee4b74c 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Administration/tests/ChallengeBasedAuthenticationPolicyTests.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Administration/tests/ChallengeBasedAuthenticationPolicyTests.cs @@ -2,6 +2,8 @@ // Licensed under the MIT License. using System; +using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Azure.Core; @@ -10,39 +12,85 @@ namespace Azure.Security.KeyVault.Tests { + [NonParallelizable] public class ChallengeBasedAuthenticationPolicyTests : SyncAsyncPolicyTestBase { internal ChallengeBasedAuthenticationPolicy _policy; private const string KeyVaultChallenge = "Bearer authorization=\"https://login.microsoftonline.com/72f988bf-86f1-41af-91ab-2d7cd011db47\", resource=\"https://vault.azure.net\""; public ChallengeBasedAuthenticationPolicyTests(bool isAsync) : base(isAsync) { - _policy = new ChallengeBasedAuthenticationPolicy(new MockCredentialThrowsWithNoScopes()); + _policy = new ChallengeBasedAuthenticationPolicy(new MockCredentialThrowsWithNoScopes(), true); } - [Test] - [NonParallelizable] - public async Task ScopesAreInitializedFromCache() + [SetUp] + public void SetUp() { // Clear the cache to ensure the test starts with an empty cache. ChallengeBasedAuthenticationPolicy.ClearCache(); + } + [Test] + public async Task ScopesAreInitializedFromCache() + { var keyvaultChallengeResponse = new MockResponse(401); keyvaultChallengeResponse.AddHeader(new HttpHeader("WWW-Authenticate", KeyVaultChallenge)); MockTransport transport = CreateMockTransport(keyvaultChallengeResponse, new MockResponse(200)); - var response = await SendGetRequest(transport, _policy, uri: new Uri("https://example.com")); + Response response = await SendGetRequest(transport, _policy, uri: new Uri("https://myvault.vault.azure.net")); Assert.That(response.Status, Is.EqualTo(200)); // Construct a new policy so that we can get the Scopes from cache. - _policy = new ChallengeBasedAuthenticationPolicy(new MockCredentialThrowsWithNoScopes()); + _policy = new ChallengeBasedAuthenticationPolicy(new MockCredentialThrowsWithNoScopes(), true); transport = CreateMockTransport(keyvaultChallengeResponse, new MockResponse(200)); - response = await SendGetRequest(transport, _policy, uri: new Uri("https://example.com")); + response = await SendGetRequest(transport, _policy, uri: new Uri("https://myvault.vault.azure.net")); Assert.That(response.Status, Is.EqualTo(200)); } + [TestCaseSource(nameof(VerifyChallengeResourceData))] + public async Task VerifyChallengeResource(Uri uri, bool verify) + { + var keyvaultChallengeResponse = new MockResponse(401); + keyvaultChallengeResponse.AddHeader(new HttpHeader("WWW-Authenticate", KeyVaultChallenge)); + MockTransport transport = CreateMockTransport(keyvaultChallengeResponse, new MockResponse(200)); + + ChallengeBasedAuthenticationPolicy policy = new(new MockCredentialThrowsWithNoScopes(), verify); + + if (verify) + { + InvalidOperationException ex = Assert.ThrowsAsync(async () => await SendGetRequest(transport, policy, uri: uri)); + Assert.That(ex.Message, Is.EqualTo("The challenge resource 'vault.azure.net' does not match the requested domain.")); + } + else + { + Response response = await SendGetRequest(transport, policy, uri: uri); + Assert.That(response.Status, Is.EqualTo(200)); + } + } + + private static IEnumerable VerifyChallengeResourceData => new[] + { + "https://example.com", + "https://examplevault.azure.net", + "https://example.vault.azure.com", + }.Zip(new[] { false, true }, (uri, verify) => new object[] { new Uri(uri), verify }); + + [Test] + public void VerifyChallengeResourceInvalidUri() + { + var keyvaultChallengeResponse = new MockResponse(401); + keyvaultChallengeResponse.AddHeader(new HttpHeader("WWW-Authenticate", "Bearer authorization=\"https://login.microsoftonline.com/72f988bf-86f1-41af-91ab-2d7cd011db47\", resource=\"invalid-uri\"")); + MockTransport transport = CreateMockTransport(keyvaultChallengeResponse, new MockResponse(200)); + + ChallengeBasedAuthenticationPolicy policy = new(new MockCredentialThrowsWithNoScopes(), true); + Uri uri = new("https://example.com"); + + InvalidOperationException ex = Assert.ThrowsAsync(async () => await SendGetRequest(transport, policy, uri: uri)); + Assert.That(ex.Message, Is.EqualTo("The challenge contains invalid scope 'invalid-uri/.default'.")); + } + public class MockCredentialThrowsWithNoScopes : TokenCredential { public override ValueTask GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken) diff --git a/sdk/keyvault/Azure.Security.KeyVault.Certificates/CHANGELOG.md b/sdk/keyvault/Azure.Security.KeyVault.Certificates/CHANGELOG.md index 1a7357fc0529..122d038ba65e 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Certificates/CHANGELOG.md +++ b/sdk/keyvault/Azure.Security.KeyVault.Certificates/CHANGELOG.md @@ -1,5 +1,12 @@ # Release History +## 4.4.0 (2022-09-13) + +### Breaking Changes + +- Verify the challenge resource matches the vault domain. + This should affect few customers who can set `CertificateClientOptions.VerifyChallengeResource` to `false` to disable. + ## 4.3.0 (2022-03-24) Changes from both the last release and the last beta include: diff --git a/sdk/keyvault/Azure.Security.KeyVault.Certificates/api/Azure.Security.KeyVault.Certificates.netstandard2.0.cs b/sdk/keyvault/Azure.Security.KeyVault.Certificates/api/Azure.Security.KeyVault.Certificates.netstandard2.0.cs index 6a2be436387d..af01c4c671cf 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Certificates/api/Azure.Security.KeyVault.Certificates.netstandard2.0.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Certificates/api/Azure.Security.KeyVault.Certificates.netstandard2.0.cs @@ -74,6 +74,7 @@ public CertificateClient(System.Uri vaultUri, Azure.Core.TokenCredential credent public partial class CertificateClientOptions : Azure.Core.ClientOptions { public CertificateClientOptions(Azure.Security.KeyVault.Certificates.CertificateClientOptions.ServiceVersion version = Azure.Security.KeyVault.Certificates.CertificateClientOptions.ServiceVersion.V7_3) { } + public bool VerifyChallengeResource { get { throw null; } set { } } public Azure.Security.KeyVault.Certificates.CertificateClientOptions.ServiceVersion Version { get { throw null; } } public enum ServiceVersion { diff --git a/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/Azure.Security.KeyVault.Certificates.csproj b/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/Azure.Security.KeyVault.Certificates.csproj index a6ed89301601..57cfacc5737b 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/Azure.Security.KeyVault.Certificates.csproj +++ b/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/Azure.Security.KeyVault.Certificates.csproj @@ -3,7 +3,7 @@ This is the Microsoft Azure Key Vault Certificates client library Microsoft Azure.Security.KeyVault.Certificates client library - 4.3.0 + 4.4.0 4.2.0 Microsoft Azure Key Vault Certificates;$(PackageCommonTags) diff --git a/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/CertificateClient.cs b/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/CertificateClient.cs index 543813228424..0e63c8d25b4a 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/CertificateClient.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/CertificateClient.cs @@ -42,6 +42,7 @@ protected CertificateClient() /// /// A to the vault on which the client operates. Appears as "DNS Name" in the Azure portal. /// If you have a certificate , use to parse the and other information. + /// You should validate that this URI references a valid Key Vault resource. See https://aka.ms/azsdk/blog/vault-uri for details. /// /// A used to authenticate requests to the vault, such as DefaultAzureCredential. /// or is null. @@ -56,6 +57,7 @@ public CertificateClient(Uri vaultUri, TokenCredential credential) /// /// A to the vault on which the client operates. Appears as "DNS Name" in the Azure portal. /// If you have a certificate , use to parse the and other information. + /// You should validate that this URI references a valid Key Vault resource. See https://aka.ms/azsdk/blog/vault-uri for details. /// /// A used to authenticate requests to the vault, such as DefaultAzureCredential. /// that allow to configure the management of the request sent to Key Vault. @@ -67,7 +69,7 @@ public CertificateClient(Uri vaultUri, TokenCredential credential, CertificateCl options ??= new CertificateClientOptions(); - HttpPipeline pipeline = HttpPipelineBuilder.Build(options, new ChallengeBasedAuthenticationPolicy(credential)); + HttpPipeline pipeline = HttpPipelineBuilder.Build(options, new ChallengeBasedAuthenticationPolicy(credential, options.VerifyChallengeResource)); _pipeline = new KeyVaultPipeline(vaultUri, options.GetVersionString(), pipeline, new ClientDiagnostics(options)); } diff --git a/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/CertificateClientOptions.cs b/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/CertificateClientOptions.cs index b80b300ab24b..2276d446a894 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/CertificateClientOptions.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/CertificateClientOptions.cs @@ -69,6 +69,12 @@ public CertificateClientOptions(ServiceVersion version = LatestVersion) this.ConfigureLogging(); } + /// + /// Gets or sets whether to verify the authentication challenge resource matches the Key Vault or Managed HSM domain. + /// The default is true. + /// + public bool VerifyChallengeResource { get; set; } = true; + internal string GetVersionString() { return Version switch diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/CHANGELOG.md b/sdk/keyvault/Azure.Security.KeyVault.Keys/CHANGELOG.md index ea3f22acb93d..6462025d66c1 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/CHANGELOG.md +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/CHANGELOG.md @@ -1,5 +1,12 @@ # Release History +## 4.4.0 (2022-09-13) + +### Breaking Changes + +- Verify the challenge resource matches the vault domain. + This should affect few customers who can set `KeyClientOptions.VerifyChallengeResource` or `CryptographyClientOptions.VerifyChallengeResource` to `false` to disable. + ## 4.3.0 (2022-03-24) Changes from both the last release and the last beta include: diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/api/Azure.Security.KeyVault.Keys.netstandard2.0.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/api/Azure.Security.KeyVault.Keys.netstandard2.0.cs index 64bba60ea245..1bb74ba9be88 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/api/Azure.Security.KeyVault.Keys.netstandard2.0.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/api/Azure.Security.KeyVault.Keys.netstandard2.0.cs @@ -160,6 +160,7 @@ public KeyClient(System.Uri vaultUri, Azure.Core.TokenCredential credential, Azu public partial class KeyClientOptions : Azure.Core.ClientOptions { public KeyClientOptions(Azure.Security.KeyVault.Keys.KeyClientOptions.ServiceVersion version = Azure.Security.KeyVault.Keys.KeyClientOptions.ServiceVersion.V7_3) { } + public bool VerifyChallengeResource { get { throw null; } set { } } public Azure.Security.KeyVault.Keys.KeyClientOptions.ServiceVersion Version { get { throw null; } } public enum ServiceVersion { @@ -424,6 +425,7 @@ public CryptographyClient(System.Uri keyId, Azure.Core.TokenCredential credentia public partial class CryptographyClientOptions : Azure.Core.ClientOptions { public CryptographyClientOptions(Azure.Security.KeyVault.Keys.Cryptography.CryptographyClientOptions.ServiceVersion version = Azure.Security.KeyVault.Keys.Cryptography.CryptographyClientOptions.ServiceVersion.V7_3) { } + public bool VerifyChallengeResource { get { throw null; } set { } } public Azure.Security.KeyVault.Keys.Cryptography.CryptographyClientOptions.ServiceVersion Version { get { throw null; } } public enum ServiceVersion { diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Azure.Security.KeyVault.Keys.csproj b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Azure.Security.KeyVault.Keys.csproj index 9d061d7c46b3..8dce18aaace9 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Azure.Security.KeyVault.Keys.csproj +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Azure.Security.KeyVault.Keys.csproj @@ -3,7 +3,7 @@ This is the Microsoft Azure Key Vault Keys client library Microsoft Azure.Security.KeyVault.Keys client library - 4.3.0 + 4.4.0 4.2.0 Microsoft Azure Key Vault Keys;$(PackageCommonTags) diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/CryptographyClient.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/CryptographyClient.cs index fdf864a43351..cde697bb6a76 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/CryptographyClient.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/CryptographyClient.cs @@ -36,6 +36,7 @@ protected CryptographyClient() /// /// The key identifier of the which will be used for cryptographic operations. /// If you have a key , use to parse the and other information. + /// You should validate that this URI references a valid Key Vault or Managed HSM resource. See https://aka.ms/azsdk/blog/vault-uri for details. /// /// A used to authenticate requests to the vault, like DefaultAzureCredential. /// or is null. @@ -50,6 +51,7 @@ public CryptographyClient(Uri keyId, TokenCredential credential) /// /// The key identifier of the which will be used for cryptographic operations. /// If you have a key , use to parse the and other information. + /// You should validate that this URI references a valid Key Vault or Managed HSM resource. See https://aka.ms/azsdk/blog/vault-uri for details. /// /// A used to authenticate requests to the vault, like DefaultAzureCredential. /// the for local or remote operations on Key Vault. diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/CryptographyClientOptions.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/CryptographyClientOptions.cs index 9f190351d241..2fb0bf277247 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/CryptographyClientOptions.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/CryptographyClientOptions.cs @@ -69,6 +69,12 @@ public CryptographyClientOptions(ServiceVersion version = LatestVersion) this.ConfigureLogging(); } + /// + /// Gets or sets whether to verify the authentication challenge resource matches the Key Vault or Managed HSM domain. + /// The default is true. + /// + public bool VerifyChallengeResource { get; set; } = true; + internal string GetVersionString() { return Version switch diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/KeyResolver.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/KeyResolver.cs index cfcebf660a73..99a28d80ff07 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/KeyResolver.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/KeyResolver.cs @@ -56,7 +56,7 @@ public KeyResolver(TokenCredential credential, CryptographyClientOptions options _apiVersion = options.GetVersionString(); _pipeline = HttpPipelineBuilder.Build(options, - new ChallengeBasedAuthenticationPolicy(credential)); + new ChallengeBasedAuthenticationPolicy(credential, options.VerifyChallengeResource)); _clientDiagnostics = new ClientDiagnostics(options); } @@ -64,7 +64,7 @@ public KeyResolver(TokenCredential credential, CryptographyClientOptions options /// /// Retrieves a capable of performing cryptographic operations with the key represented by the specified . /// - /// The key identifier of the key used by the created . + /// The key identifier of the key used by the created . You should validate that this URI references a valid Key Vault or Managed HSM resource. See https://aka.ms/azsdk/blog/vault-uri for details. /// A controlling the request lifetime. /// A new capable of performing cryptographic operations with the key represented by the specified . /// is null. diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/RemoteCryptographyClient.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/RemoteCryptographyClient.cs index f40099f6a7a1..7ff36d8f9cfa 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/RemoteCryptographyClient.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/RemoteCryptographyClient.cs @@ -27,7 +27,7 @@ internal RemoteCryptographyClient(Uri keyId, TokenCredential credential, Cryptog string apiVersion = options.GetVersionString(); HttpPipeline pipeline = HttpPipelineBuilder.Build(options, - new ChallengeBasedAuthenticationPolicy(credential)); + new ChallengeBasedAuthenticationPolicy(credential, options.VerifyChallengeResource)); Pipeline = new KeyVaultPipeline(keyId, apiVersion, pipeline, new ClientDiagnostics(options)); } diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/KeyClient.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/KeyClient.cs index b421bed3ea38..33a502bb7a3d 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/KeyClient.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/KeyClient.cs @@ -39,6 +39,7 @@ protected KeyClient() /// /// A to the vault on which the client operates. Appears as "DNS Name" in the Azure portal. /// If you have a key , use to parse the and other information. + /// You should validate that this URI references a valid Key Vault or Managed HSM resource. See https://aka.ms/azsdk/blog/vault-uri for details. /// /// A used to authenticate requests to the vault, such as DefaultAzureCredential. /// or is null. @@ -53,6 +54,7 @@ public KeyClient(Uri vaultUri, TokenCredential credential) /// /// A to the vault on which the client operates. Appears as "DNS Name" in the Azure portal. /// If you have a key , use to parse the and other information. + /// You should validate that this URI references a valid Key Vault or Managed HSM resource. See https://aka.ms/azsdk/blog/vault-uri for details. /// /// A used to authenticate requests to the vault, such as DefaultAzureCredential. /// that allow to configure the management of the request sent to Key Vault. @@ -66,7 +68,7 @@ public KeyClient(Uri vaultUri, TokenCredential credential, KeyClientOptions opti string apiVersion = options.GetVersionString(); HttpPipeline pipeline = HttpPipelineBuilder.Build(options, - new ChallengeBasedAuthenticationPolicy(credential)); + new ChallengeBasedAuthenticationPolicy(credential, options.VerifyChallengeResource)); _clientDiagnostics = new ClientDiagnostics(options); _pipeline = new KeyVaultPipeline(vaultUri, apiVersion, pipeline, _clientDiagnostics); diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/KeyClientOptions.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/KeyClientOptions.cs index 7693b6cc2fb6..5d4a3e49cc9a 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/KeyClientOptions.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/KeyClientOptions.cs @@ -69,6 +69,12 @@ public KeyClientOptions(ServiceVersion version = LatestVersion) this.ConfigureLogging(); } + /// + /// Gets or sets whether to verify the authentication challenge resource matches the Key Vault or Managed HSM domain. + /// The default is true. + /// + public bool VerifyChallengeResource { get; set; } = true; + internal string GetVersionString() { return Version switch diff --git a/sdk/keyvault/Azure.Security.KeyVault.Secrets/CHANGELOG.md b/sdk/keyvault/Azure.Security.KeyVault.Secrets/CHANGELOG.md index a1e868bda942..f33a7d867faf 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Secrets/CHANGELOG.md +++ b/sdk/keyvault/Azure.Security.KeyVault.Secrets/CHANGELOG.md @@ -1,5 +1,12 @@ # Release History +## 4.4.0 (2022-09-13) + +### Breaking Changes + +- Verify the challenge resource matches the vault domain. + This should affect few customers who can set `SecretClientOptions.VerifyChallengeResource` to `false` to disable. + ## 4.3.0 (2022-03-24) Changes from both the last release and the last beta include: diff --git a/sdk/keyvault/Azure.Security.KeyVault.Secrets/api/Azure.Security.KeyVault.Secrets.netstandard2.0.cs b/sdk/keyvault/Azure.Security.KeyVault.Secrets/api/Azure.Security.KeyVault.Secrets.netstandard2.0.cs index 241d36ee3b3a..fcb382df3a0f 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Secrets/api/Azure.Security.KeyVault.Secrets.netstandard2.0.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Secrets/api/Azure.Security.KeyVault.Secrets.netstandard2.0.cs @@ -96,6 +96,7 @@ public SecretClient(System.Uri vaultUri, Azure.Core.TokenCredential credential, public partial class SecretClientOptions : Azure.Core.ClientOptions { public SecretClientOptions(Azure.Security.KeyVault.Secrets.SecretClientOptions.ServiceVersion version = Azure.Security.KeyVault.Secrets.SecretClientOptions.ServiceVersion.V7_3) { } + public bool VerifyChallengeResource { get { throw null; } set { } } public Azure.Security.KeyVault.Secrets.SecretClientOptions.ServiceVersion Version { get { throw null; } } public enum ServiceVersion { diff --git a/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/Azure.Security.KeyVault.Secrets.csproj b/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/Azure.Security.KeyVault.Secrets.csproj index 446de6ad22fc..17e0fd9f7758 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/Azure.Security.KeyVault.Secrets.csproj +++ b/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/Azure.Security.KeyVault.Secrets.csproj @@ -3,7 +3,7 @@ This is the Microsoft Azure Key Vault Secrets client library Microsoft Azure.Security.KeyVault.Secrets client library - 4.3.0 + 4.4.0 4.2.0 Microsoft Azure Key Vault Secrets;$(PackageCommonTags) diff --git a/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/SecretClient.cs b/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/SecretClient.cs index 88cc54903c65..ca9f2fbb9c57 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/SecretClient.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/SecretClient.cs @@ -34,6 +34,7 @@ protected SecretClient() /// /// A to the vault on which the client operates. Appears as "DNS Name" in the Azure portal. /// If you have a secret , use to parse the and other information. + /// You should validate that this URI references a valid Key Vault resource. See https://aka.ms/azsdk/blog/vault-uri for details. /// /// A used to authenticate requests to the vault, such as DefaultAzureCredential. /// or is null. @@ -48,6 +49,7 @@ public SecretClient(Uri vaultUri, TokenCredential credential) /// /// A to the vault on which the client operates. Appears as "DNS Name" in the Azure portal. /// If you have a secret , use to parse the and other information. + /// You should validate that this URI references a valid Key Vault resource. See https://aka.ms/azsdk/blog/vault-uri for details. /// /// A used to authenticate requests to the vault, such as DefaultAzureCredential. /// that allow to configure the management of the request sent to Key Vault. @@ -61,7 +63,7 @@ public SecretClient(Uri vaultUri, TokenCredential credential, SecretClientOption string apiVersion = options.GetVersionString(); HttpPipeline pipeline = HttpPipelineBuilder.Build(options, - new ChallengeBasedAuthenticationPolicy(credential)); + new ChallengeBasedAuthenticationPolicy(credential, options.VerifyChallengeResource)); _pipeline = new KeyVaultPipeline(vaultUri, apiVersion, pipeline, new ClientDiagnostics(options)); } diff --git a/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/SecretClientOptions.cs b/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/SecretClientOptions.cs index d48c09d66772..bfc912f79844 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/SecretClientOptions.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/SecretClientOptions.cs @@ -69,6 +69,12 @@ public SecretClientOptions(ServiceVersion version = LatestVersion) this.ConfigureLogging(); } + /// + /// Gets or sets whether to verify the authentication challenge resource matches the Key Vault or Managed HSM domain. + /// The default is true. + /// + public bool VerifyChallengeResource { get; set; } = true; + internal string GetVersionString() { return Version switch diff --git a/sdk/keyvault/Azure.Security.KeyVault.Shared/src/ChallengeBasedAuthenticationPolicy.cs b/sdk/keyvault/Azure.Security.KeyVault.Shared/src/ChallengeBasedAuthenticationPolicy.cs index 3bfdb385e8a8..4b3554d19b57 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Shared/src/ChallengeBasedAuthenticationPolicy.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Shared/src/ChallengeBasedAuthenticationPolicy.cs @@ -13,6 +13,7 @@ namespace Azure.Security.KeyVault internal class ChallengeBasedAuthenticationPolicy : BearerTokenAuthenticationPolicy { private const string KeyVaultStashedContentKey = "KeyVaultContent"; + private readonly bool _verifyChallengeResource; /// /// Challenges are cached using the Key Vault or Managed HSM endpoint URI authority as the key. @@ -20,8 +21,10 @@ internal class ChallengeBasedAuthenticationPolicy : BearerTokenAuthenticationPol private static readonly ConcurrentDictionary s_challengeCache = new(); private ChallengeParameters _challenge; - public ChallengeBasedAuthenticationPolicy(TokenCredential credential) : base(credential, Array.Empty()) - { } + public ChallengeBasedAuthenticationPolicy(TokenCredential credential, bool verifyChallengeResource) : base(credential, Array.Empty()) + { + _verifyChallengeResource = verifyChallengeResource; + } /// protected override ValueTask AuthorizeRequestAsync(HttpMessage message) @@ -77,6 +80,7 @@ private async ValueTask AuthorizeRequestInternal(HttpMessage message, bool async protected override ValueTask AuthorizeRequestOnChallengeAsync(HttpMessage message) => AuthorizeRequestOnChallengeAsyncInternal(message, true); + /// protected override bool AuthorizeRequestOnChallenge(HttpMessage message) => AuthorizeRequestOnChallengeAsyncInternal(message, false).EnsureCompleted(); @@ -107,6 +111,20 @@ private async ValueTask AuthorizeRequestOnChallengeAsyncInternal(HttpMessa } else { + // Verify the scope domain with leading "." matches the requested host domain. + if (_verifyChallengeResource) + { + if (!Uri.TryCreate(scope, UriKind.Absolute, out Uri scopeUri)) + { + throw new InvalidOperationException($"The challenge contains invalid scope '{scope}'."); + } + + if (!message.Request.Uri.Host.EndsWith($".{scopeUri.Host}", StringComparison.OrdinalIgnoreCase)) + { + throw new InvalidOperationException($"The challenge resource '{scopeUri.Host}' does not match the requested domain."); + } + } + string authorization = AuthorizationChallengeParser.GetChallengeParameterFromResponse(message.Response, "Bearer", "authorization"); if (authorization is null) { From 35843c57eacc6b1f450b4b0ec7581ef0ce7650e1 Mon Sep 17 00:00:00 2001 From: Azure SDK Bot <53356347+azure-sdk@users.noreply.github.com> Date: Mon, 11 Jul 2022 10:42:46 -0700 Subject: [PATCH 2/4] Use --no-cone in pipeline sparse checkout script (#29782) Co-authored-by: Ben Broderick Phillips --- eng/common/pipelines/templates/steps/sparse-checkout.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/eng/common/pipelines/templates/steps/sparse-checkout.yml b/eng/common/pipelines/templates/steps/sparse-checkout.yml index 69db80fc82de..09da800c30d1 100644 --- a/eng/common/pipelines/templates/steps/sparse-checkout.yml +++ b/eng/common/pipelines/templates/steps/sparse-checkout.yml @@ -44,8 +44,10 @@ steps: Write-Host "git sparse-checkout init" git sparse-checkout init - Write-Host "git sparse-checkout set '/*' '!/*/' '/eng'" - git sparse-checkout set '/*' '!/*/' '/eng' + # Set non-cone mode otherwise path filters will not work in git >= 2.37.0 + # See https://github.blog/2022-06-27-highlights-from-git-2-37/#tidbits + Write-Host "git sparse-checkout set --no-cone '/*' '!/*/' '/eng'" + git sparse-checkout set --no-cone '/*' '!/*/' '/eng' } # Prevent wildcard expansion in Invoke-Expression (e.g. for checkout path '/*') From 9ae9c8704d4f93ef040f4f66d1f2be487c95664c Mon Sep 17 00:00:00 2001 From: Heath Stewart Date: Fri, 9 Sep 2022 10:43:14 -0700 Subject: [PATCH 3/4] Fix sharelink sample --- sdk/keyvault/samples/sharelink/ManagedStorageRestClient.cs | 2 +- sdk/keyvault/samples/sharelink/ShareLink.csproj | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/sdk/keyvault/samples/sharelink/ManagedStorageRestClient.cs b/sdk/keyvault/samples/sharelink/ManagedStorageRestClient.cs index 41fb53def8fc..9149a4416c92 100644 --- a/sdk/keyvault/samples/sharelink/ManagedStorageRestClient.cs +++ b/sdk/keyvault/samples/sharelink/ManagedStorageRestClient.cs @@ -23,7 +23,7 @@ internal static ManagedStorageRestClient Create(Uri vaultUri, TokenCredential cr { HttpPipeline pipeline = HttpPipelineBuilder.Build( options ?? throw new ArgumentNullException(nameof(options)), - new ChallengeBasedAuthenticationPolicy(credential ?? throw new ArgumentNullException(nameof(credential)))); + new ChallengeBasedAuthenticationPolicy(credential ?? throw new ArgumentNullException(nameof(credential)), true)); ClientDiagnostics diagnostics = new ClientDiagnostics(options); return new ManagedStorageRestClient(diagnostics, pipeline, vaultUri.AbsoluteUri); diff --git a/sdk/keyvault/samples/sharelink/ShareLink.csproj b/sdk/keyvault/samples/sharelink/ShareLink.csproj index 8604dcd4b560..359bdff7f2e0 100644 --- a/sdk/keyvault/samples/sharelink/ShareLink.csproj +++ b/sdk/keyvault/samples/sharelink/ShareLink.csproj @@ -21,21 +21,20 @@ - - + + - From 8be96bf43793de39b58d57715abd96ce7c4059f2 Mon Sep 17 00:00:00 2001 From: Heath Stewart Date: Mon, 12 Sep 2022 13:40:31 -0700 Subject: [PATCH 4/4] Resolve test PR feedback --- .../tests/ChallengeBasedAuthenticationPolicyTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/keyvault/Azure.Security.KeyVault.Administration/tests/ChallengeBasedAuthenticationPolicyTests.cs b/sdk/keyvault/Azure.Security.KeyVault.Administration/tests/ChallengeBasedAuthenticationPolicyTests.cs index f8b17ee4b74c..47ce4be4f07e 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Administration/tests/ChallengeBasedAuthenticationPolicyTests.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Administration/tests/ChallengeBasedAuthenticationPolicyTests.cs @@ -43,7 +43,7 @@ public async Task ScopesAreInitializedFromCache() // Construct a new policy so that we can get the Scopes from cache. _policy = new ChallengeBasedAuthenticationPolicy(new MockCredentialThrowsWithNoScopes(), true); - transport = CreateMockTransport(keyvaultChallengeResponse, new MockResponse(200)); + transport = CreateMockTransport(new MockResponse(200)); response = await SendGetRequest(transport, _policy, uri: new Uri("https://myvault.vault.azure.net")); Assert.That(response.Status, Is.EqualTo(200));