diff --git a/iothub/device/src/Authentication/HsmAuthentication/HttpClientHelper.cs b/iothub/device/src/Authentication/HsmAuthentication/HttpClientHelper.cs index 38feb1f6aa..6ee8d6f7ec 100644 --- a/iothub/device/src/Authentication/HsmAuthentication/HttpClientHelper.cs +++ b/iothub/device/src/Authentication/HsmAuthentication/HttpClientHelper.cs @@ -34,7 +34,7 @@ internal static HttpClient GetHttpClient(Uri providerUri) throw new InvalidOperationException("ProviderUri scheme is not supported"); } - internal static string GetBaseUrl(Uri providerUri) + internal static string GetBaseUri(Uri providerUri) { return providerUri.Scheme.Equals(UnixScheme, StringComparison.OrdinalIgnoreCase) ? $"{HttpScheme}://{providerUri.Segments.Last()}" diff --git a/iothub/device/src/Authentication/HsmAuthentication/HttpHsmSignatureProvider.cs b/iothub/device/src/Authentication/HsmAuthentication/HttpHsmSignatureProvider.cs index 25574150aa..bd33fc438a 100644 --- a/iothub/device/src/Authentication/HsmAuthentication/HttpHsmSignatureProvider.cs +++ b/iothub/device/src/Authentication/HsmAuthentication/HttpHsmSignatureProvider.cs @@ -46,7 +46,7 @@ public async Task SignAsync(string moduleId, string generationId, string { var hsmHttpClient = new HttpHsmClient(httpClient) { - BaseUrl = HttpClientHelper.GetBaseUrl(_providerUri) + BaseUrl = HttpClientHelper.GetBaseUri(_providerUri) }; SignResponse response = await SignWithRetryAsync( diff --git a/iothub/device/src/Authentication/HsmAuthentication/Transport/HttpUdsMessageHandler.cs b/iothub/device/src/Authentication/HsmAuthentication/Transport/HttpUdsMessageHandler.cs index 511cbe4fc8..3a8a988a55 100644 --- a/iothub/device/src/Authentication/HsmAuthentication/Transport/HttpUdsMessageHandler.cs +++ b/iothub/device/src/Authentication/HsmAuthentication/Transport/HttpUdsMessageHandler.cs @@ -52,7 +52,7 @@ private async Task GetConnectedSocketAsync() // around unix sockets in the BCL. For older versions of the framework we will continue to use the existing class since it works // fine. For netcore 2.1 and greater as well as .NET 5.0 and greater the native framework version can be an alternatve. - var endpoint = new Microsoft.Azure.Devices.Client.HsmAuthentication.Transport.UnixDomainSocketEndPoint(_providerUri.LocalPath); + var endpoint = new UnixDomainSocketEndPoint(_providerUri.LocalPath); await socket.ConnectAsync(endpoint).ConfigureAwait(false); return socket; } diff --git a/iothub/device/src/Authentication/HsmAuthentication/Transport/UnixDomainSocketEndPoint.cs b/iothub/device/src/Authentication/HsmAuthentication/Transport/UnixDomainSocketEndPoint.cs index 8095e4acb7..5347d406fa 100644 --- a/iothub/device/src/Authentication/HsmAuthentication/Transport/UnixDomainSocketEndPoint.cs +++ b/iothub/device/src/Authentication/HsmAuthentication/Transport/UnixDomainSocketEndPoint.cs @@ -5,14 +5,23 @@ using System; using System.Text; using System.Net; +using System.Net.Sockets; +using System.Diagnostics; namespace Microsoft.Azure.Devices.Client.HsmAuthentication.Transport { internal sealed class UnixDomainSocketEndPoint : EndPoint { - private const int NativePathLength = 91; // sockaddr_un.sun_path at http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_un.h.html, -1 for terminator + private const AddressFamily EndPointAddressFamily = AddressFamily.Unix; + private static readonly Encoding s_pathEncoding = Encoding.UTF8; + private const int NativePathOffset = 2; // = offset of(struct sockaddr_un, sun_path). It's the same on Linux and OSX + + private const int NativePathLength = 91; // sockaddr_un.sun_path at http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_un.h.html, -1 for terminator + + private const int NativeAddressSize = NativePathOffset + NativePathLength; + private readonly string _path; private readonly byte[] _encodedPath; @@ -27,5 +36,70 @@ internal UnixDomainSocketEndPoint(string path) throw new ArgumentOutOfRangeException(nameof(path), path); } } + + internal UnixDomainSocketEndPoint(SocketAddress socketAddress) + { + if (socketAddress == null) + { + throw new ArgumentNullException(nameof(socketAddress)); + } + + if (socketAddress.Family != EndPointAddressFamily + || socketAddress.Size > NativeAddressSize) + { + throw new ArgumentOutOfRangeException(nameof(socketAddress)); + } + + if (socketAddress.Size > NativePathOffset) + { + _encodedPath = new byte[socketAddress.Size - NativePathOffset]; + for (int i = 0; i < _encodedPath.Length; i++) + { + _encodedPath[i] = socketAddress[NativePathOffset + i]; + } + + _path = s_pathEncoding.GetString(_encodedPath, 0, _encodedPath.Length); + } + else + { + _encodedPath = Array.Empty(); + _path = string.Empty; + } + } + + /// + /// Do not remove. Even though there are no references, it will be called by System.Net.HttpClient. + /// + public override SocketAddress Serialize() + { + var result = new SocketAddress(AddressFamily.Unix, NativeAddressSize); + Debug.Assert(_encodedPath.Length + NativePathOffset <= result.Size, "Expected path to fit in address"); + + for (int index = 0; index < _encodedPath.Length; index++) + { + result[NativePathOffset + index] = _encodedPath[index]; + } + result[NativePathOffset + _encodedPath.Length] = 0; // path must be null-terminated + + return result; + } + + + /// + /// Do not remove. Even though there are no references, it will be called by System.Net.HttpClient. + /// + public override EndPoint Create(SocketAddress socketAddress) => new UnixDomainSocketEndPoint(socketAddress); + + + /// + /// Do not remove. Even though there are no references, it will be called by System.Net.HttpClient. + /// + public override AddressFamily AddressFamily => EndPointAddressFamily; + + + /// + /// Do not remove. Even though there are no references, it will be called by System.Net.HttpClient. + /// + public override string ToString() => _path; } } diff --git a/iothub/device/src/Edge/EdgeModuleClientHelper.cs b/iothub/device/src/Edge/EdgeModuleClientHelper.cs index 422b2050e2..58b828d9e6 100644 --- a/iothub/device/src/Edge/EdgeModuleClientHelper.cs +++ b/iothub/device/src/Edge/EdgeModuleClientHelper.cs @@ -18,33 +18,36 @@ namespace Microsoft.Azure.Devices.Client /// internal static class EdgeModuleClientHelper { - private const string IotEdgedUriVariableName = "IOTEDGE_WORKLOADURI"; + private const string EdgeHsmApiVersion = "2018-06-28"; + + private const string IotEdgeWorkloadUriVariableName = "IOTEDGE_WORKLOADURI"; private const string IotHubHostNameVariableName = "IOTEDGE_IOTHUBHOSTNAME"; - private const string GatewayHostnameVariableName = "IOTEDGE_GATEWAYHOSTNAME"; + private const string GatewayHostNameVariableName = "IOTEDGE_GATEWAYHOSTNAME"; private const string DeviceIdVariableName = "IOTEDGE_DEVICEID"; private const string ModuleIdVariableName = "IOTEDGE_MODULEID"; private const string ModuleGenerationIdVariableName = "IOTEDGE_MODULEGENERATIONID"; private const string AuthSchemeVariableName = "IOTEDGE_AUTHSCHEME"; private const string SasTokenAuthScheme = "SasToken"; - private const string EdgehubConnectionstringVariableName = "EdgeHubConnectionString"; - private const string IothubConnectionstringVariableName = "IotHubConnectionString"; + private const string EdgeHubConnectionStringVariableName = "EdgeHubConnectionString"; + private const string IotHubConnectionStringVariableName = "IotHubConnectionString"; private const string EdgeCaCertificateFileVariableName = "EdgeModuleCACertificateFile"; internal static IotHubConnectionCredentials CreateIotHubConnectionCredentialsFromEnvironment() { IDictionary envVariables = Environment.GetEnvironmentVariables(); - string connectionString = GetValueFromEnvironment(envVariables, EdgehubConnectionstringVariableName) - ?? GetValueFromEnvironment(envVariables, IothubConnectionstringVariableName); + string connectionString = GetValueFromEnvironment(envVariables, EdgeHubConnectionStringVariableName) + ?? GetValueFromEnvironment(envVariables, IotHubConnectionStringVariableName); - // First try to create from connection string and if env variable for connection string is not found try to create from edgedUri + // First try to create from connection string and if env variable for connection string is not found try + // to create from IOTEDGE_WORKLOADURI. if (!string.IsNullOrWhiteSpace(connectionString)) { return new IotHubConnectionCredentials(connectionString); } - string edgedUri = GetValueFromEnvironment(envVariables, IotEdgedUriVariableName) - ?? throw new InvalidOperationException($"Environment variable {IotEdgedUriVariableName} is required."); + string edgeWorkloadUri = GetValueFromEnvironment(envVariables, IotEdgeWorkloadUriVariableName) + ?? throw new InvalidOperationException($"Environment variable {IotEdgeWorkloadUriVariableName} is required."); string deviceId = GetValueFromEnvironment(envVariables, DeviceIdVariableName) ?? throw new InvalidOperationException($"Environment variable {DeviceIdVariableName} is required."); string moduleId = GetValueFromEnvironment(envVariables, ModuleIdVariableName) @@ -55,14 +58,14 @@ internal static IotHubConnectionCredentials CreateIotHubConnectionCredentialsFro ?? throw new InvalidOperationException($"Environment variable {AuthSchemeVariableName} is required."); string generationId = GetValueFromEnvironment(envVariables, ModuleGenerationIdVariableName) ?? throw new InvalidOperationException($"Environment variable {ModuleGenerationIdVariableName} is required."); - string gateway = GetValueFromEnvironment(envVariables, GatewayHostnameVariableName); + string gateway = GetValueFromEnvironment(envVariables, GatewayHostNameVariableName); if (!StringComparer.OrdinalIgnoreCase.Equals(authScheme, SasTokenAuthScheme)) { throw new InvalidOperationException($"Unsupported authentication scheme. Supported scheme is {SasTokenAuthScheme}."); } - ISignatureProvider signatureProvider = new HttpHsmSignatureProvider(edgedUri, ClientApiVersionHelper.ApiVersionLatest); + ISignatureProvider signatureProvider = new HttpHsmSignatureProvider(edgeWorkloadUri, EdgeHsmApiVersion); // TODO: environment variables need to be added to accept SasTokenTimeToLive and SasTokenRenewalBuffer. // These values can then be passed on to EdgeModuleAuthenticationWithHsm (internal class). @@ -79,7 +82,9 @@ internal static IotHubConnectionCredentials CreateIotHubConnectionCredentialsFro return new IotHubConnectionCredentials(authMethod, hostName, gateway); } - internal static async Task CreateCertificateValidatorFromEnvironmentAsync(ITrustBundleProvider trustBundleProvider, IotHubClientOptions options) + internal static async Task CreateCertificateValidatorFromEnvironmentAsync( + ITrustBundleProvider trustBundleProvider, + IotHubClientOptions options) { Debug.Assert(options != null); @@ -87,8 +92,8 @@ internal static async Task CreateCertificateValidatorFrom IDictionary envVariables = Environment.GetEnvironmentVariables(); - string connectionString = GetValueFromEnvironment(envVariables, EdgehubConnectionstringVariableName) - ?? GetValueFromEnvironment(envVariables, IothubConnectionstringVariableName); + string connectionString = GetValueFromEnvironment(envVariables, EdgeHubConnectionStringVariableName) + ?? GetValueFromEnvironment(envVariables, IotHubConnectionStringVariableName); // First try to create from connection string and if env variable for connection string is not found try to create from edgedUri if (!string.IsNullOrWhiteSpace(connectionString)) @@ -107,15 +112,18 @@ internal static async Task CreateCertificateValidatorFrom return certificateValidator; } - string edgedUri = GetValueFromEnvironment(envVariables, IotEdgedUriVariableName) ?? throw new InvalidOperationException($"Environment variable {IotEdgedUriVariableName} is required."); - string gateway = GetValueFromEnvironment(envVariables, GatewayHostnameVariableName); + string edgeWorkloadUri = GetValueFromEnvironment(envVariables, IotEdgeWorkloadUriVariableName) + ?? throw new InvalidOperationException($"Environment variable {IotEdgeWorkloadUriVariableName} is required."); + string gateway = GetValueFromEnvironment(envVariables, GatewayHostNameVariableName); if (Logging.IsEnabled) Logging.Info("EdgeModuleClientFactory setupTrustBundle from service"); if (!string.IsNullOrEmpty(gateway)) { - IList certs = await trustBundleProvider.GetTrustBundleAsync(new Uri(edgedUri), ClientApiVersionHelper.ApiVersionLatest).ConfigureAwait(false); + IList certs = await trustBundleProvider + .GetTrustBundleAsync(new Uri(edgeWorkloadUri), EdgeHsmApiVersion) + .ConfigureAwait(false); certificateValidator = CreateCertificateValidator(certs, options); } diff --git a/iothub/device/src/Edge/TrustBundleProvider.cs b/iothub/device/src/Edge/TrustBundleProvider.cs index 1483df12cc..61eafa5c75 100644 --- a/iothub/device/src/Edge/TrustBundleProvider.cs +++ b/iothub/device/src/Edge/TrustBundleProvider.cs @@ -24,7 +24,7 @@ public async Task> GetTrustBundleAsync(Uri providerUri, using HttpClient httpClient = HttpClientHelper.GetHttpClient(providerUri); var hsmHttpClient = new HttpHsmClient(httpClient) { - BaseUrl = HttpClientHelper.GetBaseUrl(providerUri) + BaseUrl = HttpClientHelper.GetBaseUri(providerUri) }; TrustBundleResponse response = await GetTrustBundleWithRetryAsync(hsmHttpClient, apiVersion).ConfigureAwait(false);