Beispiel #1
0
        public BindingDialog(IServiceProvider serviceProvider, Binding binding, Site site)
            : base(serviceProvider)
        {
            InitializeComponent();
            Binding = binding;
            _site   = site;
            Text    = Binding == null ? "Create Site Binding" : "Edit Site Binding";
            DialogHelper.LoadAddresses(cbAddress);
            txtPort.Text         = "80";
            cbType.SelectedIndex = 0;
            if (!site.Server.SupportsSni)
            {
                cbSniRequired.Enabled = false;
            }

            if (Binding == null)
            {
                txtHost.Text = site.Server.Mode == WorkingMode.IisExpress ? "localhost" : string.Empty;
                return;
            }

            cbType.Text    = Binding.Protocol;
            cbType.Enabled = Binding == null;
            cbAddress.Text = Binding.EndPoint.Address.AddressToCombo();
            txtPort.Text   = Binding.EndPoint.Port.ToString();
            txtHost.Text   = Binding.Host.HostToDisplay();
            if (site.Server.SupportsSni)
            {
                cbSniRequired.Checked = Binding.GetIsSni();
            }
        }
Beispiel #2
0
        public static string CleanUpSni(this Binding binding)
        {
#if !IIS
            if (!binding.GetIsSni())
            {
                return(string.Empty);
            }
#endif
            try
            {
                var hash = binding.CertificateHash == null ? string.Empty : Hex.ToHexString(binding.CertificateHash);
                // remove sni mapping
                using (var process = new Process())
                {
                    var start = process.StartInfo;
                    start.Verb            = "runas";
                    start.UseShellExecute = true;
                    start.FileName        = "cmd";
                    start.Arguments       =
                        $"/c \"\"{CertificateInstallerLocator.FileName}\" /h:\"{hash}\" /s:{binding.CertificateStoreName}\" /i:{AppIdIisExpress} /o:{binding.EndPoint.Port} /x:{binding.Host}";
                    start.CreateNoWindow = true;
                    start.WindowStyle    = ProcessWindowStyle.Hidden;
                    process.Start();
                    process.WaitForExit();

                    if (process.ExitCode != 0)
                    {
                        return("Remove SNI certificate failed: access is denied");
                    }

                    return(string.Empty);
                }
            }
            catch (Win32Exception ex)
            {
                // elevation is cancelled.
                if (ex.NativeErrorCode != NativeMethods.ErrorCancelled)
                {
                    RollbarLocator.RollbarInstance.Error(ex, new Dictionary <string, object> {
                        { "native", ex.NativeErrorCode }
                    });
                    return($"Remove SNI certificate failed: unknown (native {ex.NativeErrorCode})");
                }

                return("Remove SNI certificate failed: operation is cancelled");
            }
            catch (NullReferenceException ex)
            {
                RollbarLocator.RollbarInstance.Error(ex, new Dictionary <string, object> {
                    { "binding", binding.ToString() }, { "endpointNull", binding.EndPoint == null }
                });
                return($"Remove SNI certificate failed: unknown ({ex.Message})");
            }
            catch (Exception ex)
            {
                RollbarLocator.RollbarInstance.Error(ex);
                return($"Remove SNI certificate failed: unknown ({ex.Message})");
            }
        }
Beispiel #3
0
        internal static void Reinitialize(this Binding original, Binding binding)
        {
            if (original.GetIsSni() && (!binding.GetIsSni() || original.Host != binding.Host))
            {
                original.CleanUpSni();
            }

            original.BindingInformation   = binding.BindingInformation;
            original.Protocol             = binding.Protocol;
            original.CertificateHash      = binding.CertificateHash;
            original.CertificateStoreName = binding.CertificateStoreName;
            original.SslFlags             = binding.SslFlags;
            binding.Delete();
        }
Beispiel #4
0
        public static string CleanUpSni(this Binding binding)
        {
#if !IIS
            if (!binding.GetIsSni())
            {
                return(string.Empty);
            }
#endif
            try
            {
                // remove sni mapping
                using (var process = new Process())
                {
                    var start = process.StartInfo;
                    start.Verb      = "runas";
                    start.FileName  = "cmd";
                    start.Arguments = string.Format(
                        "/c \"\"{2}\" /h:\"{0}\" /s:{1}\" /i:{3} /o:{4} /x:{5}",
                        Hex.ToHexString(binding.CertificateHash),
                        binding.CertificateStoreName,
                        Path.Combine(Environment.CurrentDirectory, "certificateinstaller.exe"),
                        AppIdIisExpress,
                        binding.EndPoint.Port,
                        binding.Host);
                    start.CreateNoWindow = true;
                    start.WindowStyle    = ProcessWindowStyle.Hidden;
                    process.Start();
                    process.WaitForExit();

                    if (process.ExitCode != 0)
                    {
                        return("Remove SNI certificate failed: access is denied");
                    }

                    return(string.Empty);
                }
            }
            catch (Exception)
            {
                // elevation is cancelled.
                return("Remove SNI certificate failed: operation is cancelled");
            }
        }
Beispiel #5
0
        internal static string FixCertificateMapping(this Binding binding, X509Certificate2 certificate2)
        {
            if (binding.Protocol == "http")
            {
                return(string.Empty);
            }

            if (binding.Parent.Parent.Server.SupportsSni)
            {
                if (binding.GetIsSni())
                {
                    if (certificate2.GetNameInfo(X509NameType.DnsName, false) != binding.Host)
                    {
                        return("SNI mode requires host name matches common name of the certificate");
                    }

                    // handle SNI
                    var sni = NativeMethods.QuerySslSniInfo(new Tuple <string, int>(binding.Host,
                                                                                    binding.EndPoint.Port));
                    if (sni == null)
                    {
                        try
                        {
                            // register mapping
                            using (var process = new Process())
                            {
                                var start = process.StartInfo;
                                start.Verb      = "runas";
                                start.FileName  = "cmd";
                                start.Arguments =
                                    $"/c \"\"{Path.Combine(Environment.CurrentDirectory, "certificateinstaller.exe")}\" /h:\"{Hex.ToHexString(binding.CertificateHash)}\" /s:{binding.CertificateStoreName}\" /i:{AppIdIisExpress} /a:{binding.EndPoint.Address} /o:{binding.EndPoint.Port} /x:{binding.Host}";
                                start.CreateNoWindow = true;
                                start.WindowStyle    = ProcessWindowStyle.Hidden;
                                process.Start();
                                process.WaitForExit();

                                if (process.ExitCode != 0)
                                {
                                    return("Register new certificate failed: access is denied");
                                }

                                return(string.Empty);
                            }
                        }
                        catch (Win32Exception ex)
                        {
                            // elevation is cancelled.
                            if (ex.NativeErrorCode != NativeMethods.ErrorCancelled)
                            {
                                Rollbar.Report(ex, ErrorLevel.Error, new Dictionary <string, object> {
                                    { "native", ex.NativeErrorCode }
                                });
                                return($"Register new certificate failed: unknown (native {ex.NativeErrorCode})");
                            }

                            return("Register new certificate failed: operation is cancelled");
                        }
                        catch (Exception ex)
                        {
                            Rollbar.Report(ex, ErrorLevel.Error);
                            return($"Register new certificate failed: unknown ({ex.Message})");
                        }
                    }

                    if (!sni.Hash.SequenceEqual(binding.CertificateHash))
                    {
                        // TODO: fix the error message.
                        var result =
                            MessageBox.Show(
                                "At least one other site is using the same HTTPS binding and the binding is configured with a different certificate. Are you sure that you want to reuse this HTTPS binding and reassign the other site or sites to use the new certificate?",
                                "TODO",
                                MessageBoxButtons.YesNo,
                                MessageBoxIcon.Question,
                                MessageBoxDefaultButton.Button1);
                        if (result != DialogResult.Yes)
                        {
                            return
                                ("Certificate hash does not match. Please use the certificate that matches HTTPS binding");
                        }

                        try
                        {
                            // register mapping
                            using (var process = new Process())
                            {
                                var start = process.StartInfo;
                                start.Verb      = "runas";
                                start.FileName  = "cmd";
                                start.Arguments =
                                    $"/c \"\"{Path.Combine(Environment.CurrentDirectory, "certificateinstaller.exe")}\" /h:\"{Hex.ToHexString(binding.CertificateHash)}\" /s:{binding.CertificateStoreName}\" /i:{AppIdIisExpress} /a:{binding.EndPoint.Address} /o:{binding.EndPoint.Port} /x:{binding.Host}";
                                start.CreateNoWindow = true;
                                start.WindowStyle    = ProcessWindowStyle.Hidden;
                                process.Start();
                                process.WaitForExit();

                                if (process.ExitCode != 0)
                                {
                                    return("Register new certificate failed: access is denied");
                                }

                                return(string.Empty);
                            }
                        }
                        catch (Win32Exception ex)
                        {
                            // elevation is cancelled.
                            if (ex.NativeErrorCode != NativeMethods.ErrorCancelled)
                            {
                                Rollbar.Report(ex, ErrorLevel.Error, new Dictionary <string, object> {
                                    { "native", ex.NativeErrorCode }
                                });
                                return($"Register new certificate failed: unknown (native {ex.NativeErrorCode})");
                            }

                            return("Register new certificate failed: operation is cancelled");
                        }
                        catch (Exception ex)
                        {
                            Rollbar.Report(ex, ErrorLevel.Error);
                            return($"Register new certificate failed: unknown ({ex.Message})");
                        }
                    }

                    if (!string.Equals(sni.StoreName, binding.CertificateStoreName, StringComparison.OrdinalIgnoreCase))
                    {
                        // TODO: can this happen?
                        return
                            ("Certificate store name does not match. Please use the certificate that matches HTTPS binding");
                    }

                    return(string.Empty);
                }
            }

            // handle IP based
            var certificate = NativeMethods.QuerySslCertificateInfo(binding.EndPoint);

            if (certificate == null)
            {
                try
                {
                    // register mapping
                    using (var process = new Process())
                    {
                        var start = process.StartInfo;
                        start.Verb      = "runas";
                        start.FileName  = "cmd";
                        start.Arguments =
                            $"/c \"\"{Path.Combine(Environment.CurrentDirectory, "certificateinstaller.exe")}\" /h:\"{Hex.ToHexString(binding.CertificateHash)}\" /s:{binding.CertificateStoreName}\" /i:{AppIdIisExpress} /a:{binding.EndPoint.Address} /o:{binding.EndPoint.Port}";
                        start.CreateNoWindow = true;
                        start.WindowStyle    = ProcessWindowStyle.Hidden;
                        process.Start();
                        process.WaitForExit();

                        if (process.ExitCode != 0)
                        {
                            return("Register new certificate failed: access is denied");
                        }

                        return(string.Empty);
                    }
                }
                catch (Win32Exception ex)
                {
                    // elevation is cancelled.
                    if (ex.NativeErrorCode != NativeMethods.ErrorCancelled)
                    {
                        Rollbar.Report(ex, ErrorLevel.Error, new Dictionary <string, object> {
                            { "native", ex.NativeErrorCode }
                        });
                        return($"Register new certificate failed: unknown (native {ex.NativeErrorCode})");
                    }

                    return("Register new certificate failed: operation is cancelled");
                }
                catch (Exception ex)
                {
                    Rollbar.Report(ex, ErrorLevel.Error);
                    return($"Register new certificate failed: unknown ({ex.Message})");
                }
            }

            if (!certificate.Hash.SequenceEqual(binding.CertificateHash))
            {
                var result =
                    MessageBox.Show(
                        "At least one other site is using the same HTTPS binding and the binding is configured with a different certificate. Are you sure that you want to reuse this HTTPS binding and reassign the other site or sites to use the new certificate?",
                        "TODO",
                        MessageBoxButtons.YesNo,
                        MessageBoxIcon.Question,
                        MessageBoxDefaultButton.Button1);
                if (result != DialogResult.Yes)
                {
                    return("Certificate hash does not match. Please use the certificate that matches HTTPS binding");
                }

                try
                {
                    // register mapping
                    using (var process = new Process())
                    {
                        var start = process.StartInfo;
                        start.Verb      = "runas";
                        start.FileName  = "cmd";
                        start.Arguments =
                            $"/c \"\"{Path.Combine(Environment.CurrentDirectory, "certificateinstaller.exe")}\" /h:\"{Hex.ToHexString(binding.CertificateHash)}\" /s:{binding.CertificateStoreName}\" /i:{AppIdIisExpress} /a:{binding.EndPoint.Address} /o:{binding.EndPoint.Port}";
                        start.CreateNoWindow = true;
                        start.WindowStyle    = ProcessWindowStyle.Hidden;
                        process.Start();
                        process.WaitForExit();

                        if (process.ExitCode != 0)
                        {
                            return("Register new certificate failed: access is denied");
                        }

                        return(string.Empty);
                    }
                }
                catch (Win32Exception ex)
                {
                    // elevation is cancelled.
                    if (ex.NativeErrorCode != NativeMethods.ErrorCancelled)
                    {
                        Rollbar.Report(ex, ErrorLevel.Error, new Dictionary <string, object> {
                            { "native", ex.NativeErrorCode }
                        });
                        return($"Register new certificate failed: unknown (native {ex.NativeErrorCode})");
                    }

                    return("Register new certificate failed: operation is cancelled");
                }
                catch (Exception ex)
                {
                    Rollbar.Report(ex, ErrorLevel.Error);
                    return($"Register new certificate failed: unknown ({ex.Message})");
                }
            }

            if (!string.Equals(certificate.StoreName, binding.CertificateStoreName, StringComparison.OrdinalIgnoreCase))
            {
                // TODO: can this happen?
                return
                    ("Certificate store name does not match. Please use the certificate that matches HTTPS binding");
            }

            return(string.Empty);
        }
        public BindingDialog(IServiceProvider serviceProvider, Binding binding1, Site site)
            : base(serviceProvider)
        {
            InitializeComponent();
            Binding = binding1;
            Text    = Binding == null ? "Create Site Binding" : "Edit Site Binding";
            DialogHelper.LoadAddresses(cbAddress);
            txtPort.Text         = "80";
            cbType.SelectedIndex = 0;
            if (!site.Server.SupportsSni)
            {
                cbSniRequired.Enabled = false;
            }

            if (Binding == null)
            {
                txtHost.Text = site.Server.Mode == WorkingMode.IisExpress ? "localhost" : string.Empty;
            }
            else
            {
                cbType.Text    = Binding.Protocol;
                cbType.Enabled = Binding == null;
                cbAddress.Text = Binding.EndPoint.Address.AddressToCombo();
                txtPort.Text   = Binding.EndPoint.Port.ToString();
                txtHost.Text   = Binding.Host.HostToDisplay();
                if (site.Server.SupportsSni)
                {
                    cbSniRequired.Checked = Binding.GetIsSni();
                }
            }

            var container = new CompositeDisposable();

            FormClosed += (sender, args) => container.Dispose();

            container.Add(
                Observable.FromEventPattern <EventArgs>(btnOK, "Click")
                .ObserveOn(System.Threading.SynchronizationContext.Current)
                .Subscribe(evt =>
            {
                IPAddress address;
                try
                {
                    address = cbAddress.Text.ComboToAddress();
                }
                catch (Exception)
                {
                    MessageBox.Show("The specified IP address is invalid. Specify a valid IP address.", Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    return;
                }

                int port;
                try
                {
                    port = int.Parse(txtPort.Text);
                }
                catch (Exception)
                {
                    MessageBox.Show("The server port number must be a positive integer between 1 and 65535", Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    return;
                }

                if (port < 1 || port > 65535)
                {
                    MessageBox.Show("The server port number must be a positive integer between 1 and 65535", Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    return;
                }

                var invalid = "\"/\\[]:|<>+=;,?*$%#@{}^`".ToCharArray();
                foreach (var ch in invalid)
                {
                    if (txtHost.Text.Contains(ch))
                    {
                        MessageBox.Show("The specified host name is incorrect. The host name must use a valid host name format and cannot contain the following characters: \"/\\[]:|<>+=;,?*$%#@{}^`. Example: www.contoso.com.", Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                        return;
                    }
                }

                if (site.Server.Mode == WorkingMode.IisExpress)
                {
                    if (txtHost.Text != "localhost")
                    {
                        MessageBox.Show(
                            "The specific host name is not recommended for IIS Express. The host name should be localhost.",
                            Text,
                            MessageBoxButtons.OK,
                            MessageBoxIcon.Warning);
                    }
                }

                var certificate = cbCertificates.SelectedItem as CertificateInfo;
                var host        = txtHost.Text.DisplayToHost();
                var binding     = new Binding(
                    cbType.Text,
                    $"{address.AddressToDisplay()}:{port}:{host.HostToDisplay()}",
                    cbType.Text == "https" ? certificate?.Certificate.GetCertHash() : new byte[0],
                    cbType.Text == "https" ? certificate?.Store : null,
                    cbSniRequired.Checked ? SslFlags.Sni : SslFlags.None,
                    site.Bindings);
                var matched = site.Parent.FindDuplicate(binding, site, Binding);
                if (matched == true)
                {
                    var result = ShowMessage(
                        $"The binding '{binding}' is assigned to another site. If you assign the same binding to this site, you will only be able to start one of the sites. Are you sure that you want to add this duplicate binding?",
                        MessageBoxButtons.YesNo,
                        MessageBoxIcon.Question,
                        MessageBoxDefaultButton.Button1);
                    if (result != DialogResult.Yes)
                    {
                        return;
                    }
                }

                if (matched == null)
                {
                    ShowMessage(
                        "The specific port is being used by a different binding.",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Warning,
                        MessageBoxDefaultButton.Button1);
                    return;
                }

                var conflicts = binding.DetectConflicts();
                if (conflicts)
                {
                    var result = ShowMessage(
                        $"This binding is already being used. If you continue you might overwrite the existing certificate for this IP Address:Port or Host Name:Port combination. Do you want to use this binding anyway?",
                        MessageBoxButtons.YesNo,
                        MessageBoxIcon.Question,
                        MessageBoxDefaultButton.Button1);
                    if (result != DialogResult.Yes)
                    {
                        return;
                    }
                }

                if (Binding == null)
                {
                    Binding = binding;
                }
                else
                {
                    Binding.Reinitialize(binding);
                }

                if (site.Server.Mode == WorkingMode.IisExpress)
                {
                    var result = Binding.FixCertificateMapping(certificate?.Certificate);
                    if (!string.IsNullOrEmpty(result))
                    {
                        MessageBox.Show($"The binding '{Binding}' is invalid: {result}", Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
                        return;
                    }
                }

                DialogResult = DialogResult.OK;
            }));

            container.Add(
                Observable.FromEventPattern <EventArgs>(cbType, "SelectedIndexChanged")
                .Merge(Observable.FromEventPattern <EventArgs>(this, "Load"))
                .ObserveOn(System.Threading.SynchronizationContext.Current)
                .Subscribe(evt =>
            {
                txtPort.Text            = cbType.Text == "http" ? "80" : "443";
                txtCertificates.Visible = cbType.SelectedIndex == 1;
                cbSniRequired.Visible   = cbType.SelectedIndex == 1;
                cbCertificates.Visible  = cbType.SelectedIndex == 1;
                btnSelect.Visible       = cbType.SelectedIndex == 1;
                btnView.Visible         = cbType.SelectedIndex == 1;
            }));

            var certificatesSelected = Observable.FromEventPattern <EventArgs>(cbCertificates, "SelectedIndexChanged");

            container.Add(
                certificatesSelected
                .ObserveOn(System.Threading.SynchronizationContext.Current)
                .Subscribe(evt =>
            {
                btnView.Enabled = cbCertificates.SelectedIndex > 0;
            }));

            container.Add(
                Observable.FromEventPattern <EventArgs>(cbAddress, "TextChanged")
                .Merge(Observable.FromEventPattern <EventArgs>(txtPort, "TextChanged"))
                .Merge(certificatesSelected)
                .Sample(TimeSpan.FromSeconds(1))
                .ObserveOn(System.Threading.SynchronizationContext.Current)
                .Subscribe(evt =>
            {
                if (Helper.IsRunningOnMono())
                {
                    return;
                }

                var toElevate = BindingUtility.Verify(cbType.Text, cbAddress.Text, txtPort.Text, cbCertificates.SelectedItem as CertificateInfo);
                btnOK.Enabled = toElevate != null;
                if (!toElevate.HasValue || !toElevate.Value)
                {
                    JexusManager.NativeMethods.RemoveShieldFromButton(btnOK);
                }
                else
                {
                    JexusManager.NativeMethods.TryAddShieldToButton(btnOK);
                }
            }));

            container.Add(
                Observable.FromEventPattern <EventArgs>(btnView, "Click")
                .ObserveOn(System.Threading.SynchronizationContext.Current)
                .Subscribe(evt =>
            {
                DialogHelper.DisplayCertificate(((CertificateInfo)cbCertificates.SelectedItem).Certificate, Handle);
            }));

            container.Add(
                Observable.FromEventPattern <EventArgs>(btnSelect, "Click")
                .ObserveOn(System.Threading.SynchronizationContext.Current)
                .Subscribe(evt =>
            {
                // TODO:
            }));
        }