Commit 4d9b158b authored by Jason Williams's avatar Jason Williams Committed by GitHub
Browse files

Merge pull request #157 from WilliamsJason/master

Updates the library for .NET to not accept untrusted roots.
parents 90ad3a52 470af055
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@
        <Button x:Name="rebootDevice" Content="Reboot" Grid.Column="0" HorizontalAlignment="Left" Margin="114,123,0,0" VerticalAlignment="Top" Width="93" Click="RebootDevice_Click" IsEnabled="False" Height="32"/>
        <Button x:Name="shutdownDevice" Content="Shutdown" Grid.Column="0" HorizontalAlignment="Left" Margin="212,123,0,0" VerticalAlignment="Top" Width="93" Click="ShutdownDevice_Click" IsEnabled="False" Height="32"/>

        <TextBlock x:Name="UnsecureCertWarning" Grid.Column="0" HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Top" FontWeight="Bold" FontSize="16" Margin="12,231,0,0" Height="60" Text="Note: The UWP Sample App does not currently support certificate validation for HTTPS communication" />

        <Border Grid.Column="0" BorderBrush="Black" BorderThickness="1" Height="1" Margin="15,160,15,0" VerticalAlignment="Top"/>

        <Button x:Name="getIPConfig" Content="Get IP Config" Grid.Column="0" HorizontalAlignment="Left" Margin="12,166,0,0" VerticalAlignment="Top" Width="144" Click="GetIPConfig_Click" IsEnabled="False"/>
+3 −1
Original line number Diff line number Diff line
@@ -107,6 +107,8 @@ namespace SampleWdpClient.UniversalWindows
                            }
                        };

                        // TODO: Support proper certificate validation instead of blindly trusting this cert (Issue #154/#145).
                        await portal.GetRootDeviceCertificate(true);
                        await portal.Connect();

                        this.MarshalUpdateCommandOutput(sb.ToString());
+50 −2
Original line number Diff line number Diff line
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
@@ -28,6 +30,11 @@ namespace SampleWdpClient
        /// </summary>
        private DevicePortal portal;

        /// <summary>
        /// An SSL thumbprint that we'll accept.
        /// </summary>
        private string thumbprint;

        /// <summary>
        /// The main page constructor.
        /// </summary>
@@ -76,6 +83,9 @@ namespace SampleWdpClient
                    this.username.Text,
                    this.password.Password));

            // Add additional handling for untrusted certs.
            portal.UnvalidatedCert += DoCertValidation;

            StringBuilder sb = new StringBuilder();
            Task connectTask = new Task(
                async () =>
@@ -117,7 +127,8 @@ namespace SampleWdpClient
                    this.MarshalEnableConnectionControls(true);
                });

            connectTask.Start();        }
            connectTask.Start();
        }

        /// <summary>
        /// Enables or disables the Connect button based on the current state of the
@@ -452,5 +463,42 @@ namespace SampleWdpClient
        {
            this.EnableConnectButton();
        }

        /// <summary>
        /// Validate the server certificate
        /// </summary>
        /// <param name="sender">The sender object</param>
        /// <param name="certificate">The server's certificate</param>
        /// <param name="chain">The cert chain</param>
        /// <param name="sslPolicyErrors">Policy Errors</param>
        /// <returns>whether the cert passes validation</returns>
        private bool DoCertValidation(DevicePortal sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            X509Certificate2 cert = new X509Certificate2(certificate);

            // If we have previously said to accept this cert, don't prompt again for this session.
            if (!string.IsNullOrEmpty(this.thumbprint) && this.thumbprint.Equals(cert.Thumbprint, StringComparison.OrdinalIgnoreCase))
            {
                return true;
            }

            // We could alternatively ask the user if they wanted to always trust
            // this device and we could persist the thumbprint in some way (registry, database, filesystem, etc).
            MessageBoxResult result = MessageBox.Show(string.Format(
                                "Do you want to accept the following certificate?\n\nThumbprint:\n  {0}\nIssuer:\n  {1}", 
                                cert.Thumbprint,
                                cert.Issuer),
                            "Untrusted Certificate Detected", 
                            MessageBoxButton.YesNo,
                            MessageBoxImage.Question,
                            MessageBoxResult.No);

            if (result == MessageBoxResult.Yes)
            {
                thumbprint = cert.Thumbprint;
                return true;
            }
            return false;
        }
    }
}
+70 −0
Original line number Diff line number Diff line
//----------------------------------------------------------------------------------------------
// <copyright file="CredManager.cs" company="Microsoft Corporation">
//     Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using Windows.Security.Credentials;

namespace XboxWdpDriver
{
    /// <summary>
    /// Manages storing and retrieving WDP creds on Windows 8 and above.
    /// </summary>
    public static class CredManager
    {
        /// <summary>
        /// Retrieves any stored credentials for this target.
        /// </summary>
        /// <param name="target">The target the credentials are stored for.</param>
        /// <param name="userName">The stored username.</param>
        /// <param name="password">The stored password.</param>
        public static void RetrieveStoredCreds(string target, ref string userName, ref string password)
        {
            try
            {
                PasswordVault vault = new PasswordVault();
                // Set the first stored cred as our network creds.
                IReadOnlyList<PasswordCredential> creds = vault.FindAllByResource(target);
                if (creds != null && creds.Count > 0)
                {
                    creds[0].RetrievePassword();
                    userName = creds[0].UserName;
                    password = creds[0].Password;
                }
            }
            catch (Exception)
            {
                // Do nothing. No credentials were stored. If they are needed, REST calls will fail with Unauthorized.
            }
        }

        /// <summary>
        /// Updates the stored credentials for the target.
        /// </summary>
        /// <param name="target">The target for which to update credentials.</param>
        /// <param name="userName">The new username.</param>
        /// <param name="password">The new password.</param>
        public static void UpdateStoredCreds(string target, string userName, string password)
        {
            PasswordVault vault = new PasswordVault();

            try
            {
                // Remove any existing stored creds for this address and add these ones.
                foreach (var cred in vault.FindAllByResource(target))
                {
                    vault.Remove(cred);
                }
            }
            catch (Exception)
            {
                // Do nothing. This is expected if no credentials have been previously stored
            }

            vault.Add(new PasswordCredential(target, userName, password));
        }
    }
}
+0 −216
Original line number Diff line number Diff line
//----------------------------------------------------------------------------------------------
// <copyright file="DevicePortalConnection.cs" company="Microsoft Corporation">
//     Licensed under the MIT License. See LICENSE.TXT in the project root license information.
// </copyright>
//----------------------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Text.RegularExpressions;
using Microsoft.Tools.WindowsDevicePortal;
using Windows.Security.Credentials;
using static Microsoft.Tools.WindowsDevicePortal.DevicePortal;

namespace XboxWdpDriver
{
    /// <summary>
    /// IDevicePortalConnection implementation for Xbox test project
    /// </summary>
    public class DevicePortalConnection : IDevicePortalConnection
    {
        /// <summary>
        /// Device Certificate
        /// </summary>
        private X509Certificate2 deviceCertificate = null;

        /// <summary>
        /// Initializes a new instance of the <see cref="DevicePortalConnection"/> class.
        /// </summary>
        /// <param name="address">The ip address or hostname of the device we are connecting to.</param>
        /// <param name="userName">The WDP username.</param>
        /// <param name="password">The WDP password.</param>
        public DevicePortalConnection(
            string address,
            string userName,
            string password)
        {
            this.Connection = new Uri(string.Format("https://{0}:11443", address));
            this.Credentials = new NetworkCredential(userName, password);

            PasswordVault vault = new PasswordVault();

            try
            {
                // Remove any existing stored creds for this address and add these ones.
                foreach (var cred in vault.FindAllByResource(address))
                {
                    vault.Remove(cred);
                }
            }
            catch (Exception)
            {
                // Do nothing. This is expected if no credentials have been previously stored
            }

            vault.Add(new PasswordCredential(address, userName, password));
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DevicePortalConnection"/> class.
        /// This version of the contructor can be used if WDP credentials are not provided,
        /// and should be used if they were previously persisted or are not needed.
        /// </summary>
        /// <param name="address">The ip address or hostname of the device we are connecting to.</param>
        public DevicePortalConnection(
            string address)
        {
            this.Connection = new Uri(string.Format("https://{0}:11443", address));

            try
            {
                PasswordVault vault = new PasswordVault();
                // Set the first stored cred as our network creds.
                IReadOnlyList<PasswordCredential> creds = vault.FindAllByResource(address);
                if (creds != null && creds.Count > 0)
                {
                    creds[0].RetrievePassword();
                    this.Credentials = new NetworkCredential(creds[0].UserName, creds[0].Password);
                }
            }
            catch (Exception)
            {
                // Do nothing. No credentials were stored. If they are needed, REST calls will fail with Unauthorized.
            }
        }

        /// <summary>
        /// Gets Connection property
        /// </summary>
        public Uri Connection
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets Web Socket Connection property
        /// </summary>
        public Uri WebSocketConnection
        {
            get
            {
                if (this.Connection == null)
                {
                    return null;
                }

                string absoluteUri = this.Connection.AbsoluteUri;

                if (absoluteUri.StartsWith("https", StringComparison.OrdinalIgnoreCase))
                {
                    return new Uri(Regex.Replace(absoluteUri, "https", "wss", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant));
                }
                else
                {
                    return new Uri(Regex.Replace(absoluteUri, "http", "ws", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant));
                }
            }
        }

        /// <summary>
        /// Gets Credentials property
        /// </summary>
        public NetworkCredential Credentials
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets or sets device family
        /// </summary>
        public string Family
        { 
            get; 
            set;
        }

        /// <summary>
        /// Gets or sets the device name
        /// </summary>
        public string Name
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets device OS Info
        /// </summary>
        public OperatingSystemInformation OsInfo
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets a value indicating whether or not we are allowing cert override which may specify a proxy instead of the web management service.
        /// </summary>
        public bool AllowCertOverride
        {
            get;
            set;
        }

        /// <summary>
        /// Returns certificate data
        /// </summary>
        /// <returns>certificate data</returns>
        public byte[] GetDeviceCertificateData()
        {
            return this.deviceCertificate.GetRawCertData();
        }

        /// <summary>
        /// Validates and sets the device certificate.
        /// </summary>
        /// <param name="certificate">The device's root certificate.</param>
        public void SetDeviceCertificate(X509Certificate2 certificate)
        {
            if (!this.AllowCertOverride)
            {
                if (!certificate.IssuerName.Name.Contains(DevicePortalCertificateIssuer))
                {
                    throw new DevicePortalException(
                        (HttpStatusCode)0,
                        "Invalid certificate issuer",
                        null,
                        "Failed to download device certificate");
                }
            }

            this.deviceCertificate = certificate;
        }

        /// <summary>
        /// Xbox will never update the connection.
        /// </summary>
        /// <param name="requiresHttps">https required</param>
        public void UpdateConnection(bool requiresHttps)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        ///  Xbox will never update the connection.
        /// </summary>
        /// <param name="ipConfig">IP info</param>
        /// <param name="requiresHttps">https required</param>
        public void UpdateConnection(IpConfiguration ipConfig, bool requiresHttps)
        {
            throw new NotImplementedException();
        }
    }
}
Loading