From 4d44baa8172cfa1f09e10ebe87d38007a4d614cc Mon Sep 17 00:00:00 2001 From: Olov Karlsson Date: Wed, 22 Oct 2014 21:34:14 +0200 Subject: [PATCH] First commit of SNI and CCS support in the IIS80 Provider. Only small changes to IIS70 to allow inheritance. --- WebsitePanel/Database/update_db.sql | 42 ++ .../Microsoft.Web.Administration.dll | Bin 0 -> 143360 bytes .../Common/ConfigurationModuleService.cs | 2 +- .../SSL/SSLModuleService.cs | 2 +- .../WebsitePanel.Providers.Web.IIs80/IIs80.cs | 132 ++++- .../SSL/SSLModuleService80.cs | 508 ++++++++++++++++++ .../WebsitePanel.Providers.Web.IIs80.csproj | 17 +- .../ProviderControls/IIS70_Settings.ascx | 34 ++ .../ProviderControls/IIS70_Settings.ascx.cs | 19 + .../IIS70_Settings.ascx.designer.cs | 109 +++- .../WebsitePanel/WebSitesEditSite.ascx.cs | 23 +- 11 files changed, 841 insertions(+), 47 deletions(-) create mode 100644 WebsitePanel/Lib/References/Microsoft/Windows2012/Microsoft.Web.Administration.dll create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.Web.IIs80/SSL/SSLModuleService80.cs diff --git a/WebsitePanel/Database/update_db.sql b/WebsitePanel/Database/update_db.sql index fe2f171b..98849b0c 100644 --- a/WebsitePanel/Database/update_db.sql +++ b/WebsitePanel/Database/update_db.sql @@ -5374,4 +5374,46 @@ AS RETURN @Result END +GO + + + +-- IIS80 Provider update for SNI and CCS support +-- Add default serviceproperties for all existing IIS80 Services (if any). These properties are used as markers in the IIS70 Controls in WebPortal to know the version of the IIS Provider +declare c cursor read_only for +select ServiceID from Services where ProviderID in(select ProviderID from Providers where ProviderName='IIS80') + +declare @ServiceID int + +open c + +fetch next from c +into @ServiceID + +while @@FETCH_STATUS = 0 +begin + if not exists(select null from ServiceProperties where ServiceID = @ServiceID and PropertyName = 'sslccscommonpassword') + insert into ServiceProperties(ServiceID, PropertyName, PropertyValue) + values(@ServiceID, 'sslccscommonpassword', '') + + if not exists(select null from ServiceProperties where ServiceID = @ServiceID and PropertyName = 'sslccsuncpath') + insert into ServiceProperties(ServiceID, PropertyName, PropertyValue) + values(@ServiceID, 'sslccsuncpath', '') + + if not exists(select null from ServiceProperties where ServiceID = @ServiceID and PropertyName = 'ssluseccs') + insert into ServiceProperties(ServiceID, PropertyName, PropertyValue) + values(@ServiceID, 'ssluseccs', 'False') + + if not exists(select null from ServiceProperties where ServiceID = @ServiceID and PropertyName = 'ssluseccs') + insert into ServiceProperties(ServiceID, PropertyName, PropertyValue) + values(@ServiceID, 'sslusesni', 'False') + + fetch next from c + into @ServiceID +end + +close c + +deallocate c + GO \ No newline at end of file diff --git a/WebsitePanel/Lib/References/Microsoft/Windows2012/Microsoft.Web.Administration.dll b/WebsitePanel/Lib/References/Microsoft/Windows2012/Microsoft.Web.Administration.dll new file mode 100644 index 0000000000000000000000000000000000000000..7415f557f3efdcf7c1e59bdd713175ac7d35c63e GIT binary patch literal 143360 zcmeFa37k~L)jnK(Z};tfY|2VE~iw{e8>tbCjxk zPMtb+>eQ)Ib?a7DAG_=lWh$lo`1kqeN=_c%h}J2J~8o2=9nLlptMv%jGROO_(>oDg@u|

4ScDA{~u{!1K+Fh$9Gdh1=Q}xm3rkQpDGZn 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 U98%zr0*4ehq`)Bs4yM5W1q`7vTL1t6 literal 0 HcmV?d00001 diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIS70/Common/ConfigurationModuleService.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIS70/Common/ConfigurationModuleService.cs index c522d736..a83ca798 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIS70/Common/ConfigurationModuleService.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIS70/Common/ConfigurationModuleService.cs @@ -43,7 +43,7 @@ namespace WebsitePanel.Providers.Web.Iis.Common /// We'll use it in the future to implement management of web farm with shared configuration enabled /// /// - internal ServerManager GetServerManager() + protected internal ServerManager GetServerManager() { return new ServerManager(); } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIS70/SSL/SSLModuleService.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIS70/SSL/SSLModuleService.cs index e08dd492..91d60a3e 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIS70/SSL/SSLModuleService.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIS70/SSL/SSLModuleService.cs @@ -40,7 +40,7 @@ using WebsitePanel.Providers.Web.Iis.WebObjects; namespace WebsitePanel.Providers.Web.Iis { - internal sealed class SSLModuleService : ConfigurationModuleService + public class SSLModuleService : ConfigurationModuleService { public void GenerateCsr(SSLCertificate cert) { diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIs80/IIs80.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIs80/IIs80.cs index 07717566..9b77a7bf 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIs80/IIs80.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIs80/IIs80.cs @@ -26,18 +26,84 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +using Microsoft.Web.Administration; using Microsoft.Win32; using System; using System.Collections.Generic; -using System.Linq; -using System.Text; +using WebsitePanel.Providers.Common; +using WebsitePanel.Providers.Web.Iis; namespace WebsitePanel.Providers.Web { - public class IIs80 : IIs70, IWebServer + public class IIs80 : IIs70 { - public IIs80() : base() + private SslFlags SSLFlags { + get + { + return (UseSni ? SslFlags.Sni : SslFlags.None) | (UseCcs ? SslFlags.CentralCertStore : SslFlags.None); + } + } + + public string CCSUncPath { + get { return ProviderSettings["SSLCCSUNCPath"]; } + } + + public string CCSCommonPassword { + get { return ProviderSettings["SSLCCSCommonPassword"]; } + } + + public bool UseSni { + get + { + try + { + return Convert.ToBoolean(ProviderSettings["SSLUseSNI"]); + } + catch + { + return false; + } + } + } + + public bool UseCcs { + get + { + try + { + return Convert.ToBoolean(ProviderSettings["SSLUseCCS"]); + } + catch + { + return false; + } + } + } + + public override SettingPair[] GetProviderDefaultSettings() + { + var allSettings = new List(); + allSettings.AddRange(base.GetProviderDefaultSettings()); + + // Add these to get som default values in. These are also used a marker in the IIS70_Settings.ascx.cs to know that it is the IIS80 provider that is used + allSettings.Add(new SettingPair("SSLUseCCS", false.ToString())); + allSettings.Add(new SettingPair("SSLUseSNI", false.ToString())); + allSettings.Add(new SettingPair("SSLCCSUNCPath", "")); + allSettings.Add(new SettingPair("SSLCCSCommonPassword", "")); + + return allSettings.ToArray(); + } + + public override string[] Install() + { + var messages = new List(); + + messages.AddRange(base.Install()); + + // TODO: Setup ccs + + return messages.ToArray(); } public override bool IsIISInstalled() @@ -58,5 +124,63 @@ namespace WebsitePanel.Providers.Web { return IsIISInstalled(); } + + public override bool CheckCertificate(WebSite webSite) + { + var sslObjectService = new SSLModuleService80(SSLFlags, CCSUncPath, CCSCommonPassword); + + return sslObjectService.CheckCertificate(webSite); + } + + public override ResultObject DeleteCertificate(SSLCertificate certificate, WebSite website) + { + var sslObjectService = new SSLModuleService80(SSLFlags, CCSUncPath, CCSCommonPassword); + + return sslObjectService.DeleteCertificate(certificate, website); + } + + public override SSLCertificate installPFX(byte[] certificate, string password, WebSite website) + { + var sslObjectService = new SSLModuleService80(SSLFlags, CCSUncPath, CCSCommonPassword); + + return sslObjectService.InstallPfx(certificate, password, website); + } + + public override SSLCertificate ImportCertificate(WebSite website) + { + var sslObjectService = new SSLModuleService80(SSLFlags, CCSUncPath, CCSCommonPassword); + + return sslObjectService.ImportCertificate(website); + } + + public override byte[] exportCertificate(string serialNumber, string password) + { + var sslObjectService = new SSLModuleService80(SSLFlags, CCSUncPath, CCSCommonPassword); + + return sslObjectService.ExportPfx(serialNumber, password); + } + + public override SSLCertificate generateCSR(SSLCertificate certificate) + { + var sslObjectService = new SSLModuleService80(SSLFlags, CCSUncPath, CCSCommonPassword); + + sslObjectService.GenerateCsr(certificate); + + return certificate; + } + + public override List getServerCertificates() + { + var sslObjectService = new SSLModuleService80(SSLFlags, CCSUncPath, CCSCommonPassword); + + return sslObjectService.GetServerCertificates(); + } + + public override SSLCertificate installCertificate(SSLCertificate certificate, WebSite website) + { + var sslObjectService = new SSLModuleService80(SSLFlags, CCSUncPath, CCSCommonPassword); + + return sslObjectService.InstallCertificate(certificate, website); + } } } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIs80/SSL/SSLModuleService80.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIs80/SSL/SSLModuleService80.cs new file mode 100644 index 00000000..01baf828 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIs80/SSL/SSLModuleService80.cs @@ -0,0 +1,508 @@ +using System; +using System.Globalization; +using System.IO; +using CertEnrollInterop; +using WebsitePanel.Providers.Common; +using WebsitePanel.Server.Utils; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography.X509Certificates; +using Microsoft.Web.Administration; + +namespace WebsitePanel.Providers.Web.Iis +{ + public class SSLModuleService80 : SSLModuleService + { + private const string CertificateStoreName = "WebHosting"; + + public bool UseSNI { get; private set; } + public bool UseCCS { get; private set; } + public string CCSUncPath { get; private set; } + public string CCSCommonPassword { get; private set; } + + public SSLModuleService80(SslFlags sslFlags, string ccsUncPath, string ccsCommonPassword) + { + UseSNI = sslFlags.HasFlag(SslFlags.Sni); + UseCCS = sslFlags.HasFlag(SslFlags.CentralCertStore); + CCSUncPath = ccsUncPath; + CCSCommonPassword = ccsCommonPassword; + } + + public new SSLCertificate InstallCertificate(SSLCertificate cert, WebSite website) + { + try + { + var response = Activator.CreateInstance(Type.GetTypeFromProgID("X509Enrollment.CX509Enrollment", true)) as CX509Enrollment; + if (response == null) + { + throw new Exception("Cannot create instance of X509Enrollment.CX509Enrollment"); + } + + response.Initialize(X509CertificateEnrollmentContext.ContextMachine); + response.InstallResponse( + InstallResponseRestrictionFlags.AllowUntrustedRoot, + cert.Certificate, EncodingType.XCN_CRYPT_STRING_BASE64HEADER, + null + ); + + // At this point, certificate has been installed into "Personal" store + // We need to move it into "WebHosting" store + // Get certificate + var servercert = GetServerCertificates(StoreName.My.ToString()).Single(c => c.FriendlyName == cert.FriendlyName); + if (UseCCS) + { + // Delete existing certificate, if any. This is needed to install a new binding + if (CheckCertificate(website)) + { + DeleteCertificate(GetCurrentSiteCertificate(website), website); + } + } + + // Get certificate data - the one we just added to "Personal" store + var storeMy = new X509Store(StoreName.My, StoreLocation.LocalMachine); + storeMy.Open(OpenFlags.MaxAllowed); + X509CertificateCollection existCerts2 = storeMy.Certificates.Find(X509FindType.FindBySerialNumber, servercert.SerialNumber, false); + var certData = existCerts2[0].Export(X509ContentType.Pfx); + storeMy.Close(); + + if (UseCCS) + { + // Revert to InstallPfx to install new certificate - this also adds binding + InstallPfx(certData, string.Empty, website); + } + else + { + // Add new certificate to "WebHosting" store + var store = new X509Store(CertificateStoreName, StoreLocation.LocalMachine); + var x509Cert = new X509Certificate2(certData); + store.Open(OpenFlags.ReadWrite); + store.Add(x509Cert); + store.Close(); + } + + // Remove certificate from "Personal" store + storeMy.Open(OpenFlags.MaxAllowed); + X509CertificateCollection existCerts = storeMy.Certificates.Find(X509FindType.FindBySerialNumber, servercert.SerialNumber, false); + storeMy.Remove((X509Certificate2)existCerts[0]); + storeMy.Close(); + // Fill object with certificate data + cert.SerialNumber = servercert.SerialNumber; + cert.ValidFrom = servercert.ValidFrom; + cert.ExpiryDate = servercert.ExpiryDate; + cert.Hash = servercert.Hash; + cert.DistinguishedName = servercert.DistinguishedName; + + if (!UseCCS) + { + if (CheckCertificate(website)) + { + DeleteCertificate(GetCurrentSiteCertificate(website), website); + } + + AddBinding(cert, website); + } + } + catch (Exception ex) + { + Log.WriteError("Error adding SSL certificate", ex); + cert.Success = false; + } + + return cert; + } + + public new List GetServerCertificates() + { + // Use Web Hosting store - new for IIS 8.0 + return GetServerCertificates(CertificateStoreName); + } + + public new SSLCertificate ImportCertificate(WebSite website) + { + SSLCertificate certificate; + + try + { + certificate = GetCurrentSiteCertificate(website); + } + catch (Exception ex) + { + certificate = new SSLCertificate + { + Success = false, + Certificate = ex.ToString() + }; + } + + return certificate; + } + + public new SSLCertificate InstallPfx(byte[] certificate, string password, WebSite website) + { + SSLCertificate newcert, oldcert = null; + + // Ensure we perform operations safely and preserve the original state during all manipulations, save the oldcert if one is used + if (CheckCertificate(website)) + { + oldcert = GetCurrentSiteCertificate(website); + } + + X509Certificate2 x509Cert; + var store = new X509Store(CertificateStoreName, StoreLocation.LocalMachine); + + if (UseCCS) + { + // We need to use this constructor or we won't be able to export this certificate + x509Cert = new X509Certificate2(certificate, password, X509KeyStorageFlags.Exportable); + + var certData = x509Cert.Export(X509ContentType.Pfx); + var convertedCert = new X509Certificate2(certData, string.Empty, X509KeyStorageFlags.Exportable); + + // Attempts to move certificate to CCS UNC path + try + { + // Create a stream out of that new certificate + certData = convertedCert.Export(X509ContentType.Pfx, CCSCommonPassword); + + // Open UNC path and set path to certificate subject + var filename = (CCSUncPath.EndsWith("/") ? CCSUncPath: CCSUncPath + "/") + x509Cert.GetNameInfo(X509NameType.SimpleName, false) + ".pfx"; + var writer = new BinaryWriter(File.Open(filename, FileMode.Create)); + writer.Write(certData); + writer.Flush(); + writer.Close(); + // Certificated saved + } + catch (Exception ex) + { + // Log error + Log.WriteError("SSLModuleService could not save certificate to Centralized Certificate Store", ex); + // Re-throw + throw; + } + } + else + { + x509Cert = new X509Certificate2(certificate, password); + + // Step 1: Register X.509 certificate in the store + // Trying to keep X.509 store open as less as possible + try + { + store.Open(OpenFlags.ReadWrite); + + store.Add(x509Cert); + } + catch (Exception ex) + { + Log.WriteError(String.Format("SSLModuleService could not import PFX into X509Store('{0}', '{1}')", store.Name, store.Location), ex); + // Re-throw error + throw; + } + finally + { + store.Close(); + } + } + + // Step 2: Instantiate a copy of new X.509 certificate + try + { + store.Open(OpenFlags.ReadWrite); + newcert = GetSSLCertificateFromX509Certificate2(x509Cert); + } + catch (Exception ex) + { + if (!UseCCS) + { + // Rollback X.509 store changes + store.Remove(x509Cert); + } + // Log error + Log.WriteError("SSLModuleService could not instantiate a copy of new X.509 certificate. All previous changes have been rolled back.", ex); + // Re-throw + throw; + } + finally + { + store.Close(); + } + + if (!UseCCS) + { + // Step 3: Remove old certificate from the web site if any + try + { + store.Open(OpenFlags.ReadWrite); + // Check if certificate already exists, remove it. + if (oldcert != null) + DeleteCertificate(oldcert, website); + } + catch (Exception ex) + { + // Rollback X.509 store changes + store.Remove(x509Cert); + // Log the error + Log.WriteError( + String.Format("SSLModuleService could not remove existing certificate from '{0}' web site. All changes have been rolled back.", website.Name), ex); + // Re-throw + throw; + } + finally + { + store.Close(); + } + } + + // Step 4: Register new certificate with HTTPS binding on the web site + try + { + //if (!UseCCS) + //{ + // store.Open(OpenFlags.ReadWrite); + //} + + AddBinding(newcert, website); + } + catch (Exception ex) + { + if (!UseCCS) + { + // Install old certificate back if any + store.Open(OpenFlags.ReadWrite); + if (oldcert != null) + InstallCertificate(oldcert, website); + // Rollback X.509 store changes + store.Remove(x509Cert); + store.Close(); + } + // Log the error + Log.WriteError( + String.Format("SSLModuleService could not add new X.509 certificate to '{0}' web site. All changes have been rolled back.", website.Name), ex); + // Re-throw + throw; + } + + return newcert; + } + + public new byte[] ExportPfx(string serialNumber, string password) + { + if (UseCCS) + { + // This is not a good way to do it + // Find cert by somehow perhaps first looking in the database? There vi kan lookup the serialnumber and find the hostname needed to create the path to the cert in CCS and then we can load the certdata into a cert and do a export with new password. + // Another solution would be to look through all SSL-bindings on all sites until we found the site with the binding that has this serialNumber. But serialNumber is not good enough, we need hash that is unique and present in bindingInfo + // A third solution is to iterate over all files in CCS, load them into memory and find the one with the correct serialNumber, but that cannot be good if there are thousands of files... + foreach (var file in Directory.GetFiles(CCSUncPath)) + { + var fileStream = File.OpenRead(file); + + // Read certificate data from file + var certData = new byte[fileStream.Length]; + fileStream.Read(certData, 0, (int) fileStream.Length); + var convertedCert = new X509Certificate2(certData, CCSCommonPassword, X509KeyStorageFlags.Exportable); + + fileStream.Close(); + + if (convertedCert.SerialNumber == serialNumber) + { + return convertedCert.Export(X509ContentType.Pfx, password); + } + } + } + + var store = new X509Store(CertificateStoreName, StoreLocation.LocalMachine); + store.Open(OpenFlags.ReadOnly); + var cert = store.Certificates.Find(X509FindType.FindBySerialNumber, serialNumber, false)[0]; + var exported = cert.Export(X509ContentType.Pfx, password); + return exported; + } + + + public new void AddBinding(SSLCertificate certificate, WebSite website) + { + using (var srvman = GetServerManager()) + { + var store = new X509Store(CertificateStoreName, StoreLocation.LocalMachine); + store.Open(OpenFlags.ReadOnly); + + // Look for dedicated ip + var dedicatedIp = SiteHasBindingWithDedicatedIp(srvman, website); + + var bindingInformation = string.Format("{0}:443:{1}", website.SiteIPAddress, dedicatedIp ? "" : certificate.Hostname); + + Binding siteBinding = UseCCS ? + srvman.Sites[website.SiteId].Bindings.Add(bindingInformation, "https") : + srvman.Sites[website.SiteId].Bindings.Add(bindingInformation, certificate.Hash, store.Name); + + if (UseSNI) + { + siteBinding.SslFlags |= SslFlags.Sni; + } + if (UseCCS) + { + siteBinding.SslFlags |= SslFlags.CentralCertStore; + } + + store.Close(); + + srvman.CommitChanges(); + } + } + + public new ResultObject DeleteCertificate(SSLCertificate certificate, WebSite website) + { + var result = new ResultObject() { IsSuccess = true }; + + if (certificate == null) + { + return result; + } + + try + { + // Regardless of the CCS setting on the server, we try to find and remove the certificate from both CCS and WebHosting Store. + // This is because we don't know how this was set when the certificate was added + + if (!string.IsNullOrWhiteSpace(CCSUncPath) && Directory.Exists(CCSUncPath)) + { + // This is where it will be if CCS is used + var path = GetCCSPath(certificate.Hostname); + if (File.Exists(path)) + { + File.Delete(path); + } + } + + // Now delete all certs with the same serialnumber in WebHosting Store + var store = new X509Store(CertificateStoreName, StoreLocation.LocalMachine); + store.Open(OpenFlags.MaxAllowed); + + var certs = store.Certificates.Find(X509FindType.FindBySerialNumber, certificate.SerialNumber, false); + foreach (var cert in certs) + { + store.Remove(cert); + } + + store.Close(); + + // Remove binding from site + if (CheckCertificate(website)) + { + RemoveBinding(certificate, website); + } + } + catch (Exception ex) + { + Log.WriteError(String.Format("Unable to delete certificate for website {0}", website.Name), ex); + result.IsSuccess = false; + result.AddError("", ex); + } + + return result; + } + + public new SSLCertificate GetCurrentSiteCertificate(WebSite website) + { + using (var srvman = GetServerManager()) + { + var site = srvman.Sites[website.SiteId]; + var sslBinding = site.Bindings.First(b => b.Protocol == "https"); + + X509Certificate2 cert = null; + + // If the certificate is in the central store + if (((SslFlags)Enum.Parse(typeof(SslFlags), sslBinding["sslFlags"].ToString())).HasFlag(SslFlags.CentralCertStore)) + { + // Let's try to match binding host and certificate filename + var path = GetCCSPath(sslBinding.Host); + if (File.Exists(path)) + { + var fileStream = File.OpenRead(path); + + // Read certificate data from file + var certData = new byte[fileStream.Length]; + fileStream.Read(certData, 0, (int) fileStream.Length); + cert = new X509Certificate2(certData, CCSCommonPassword); + fileStream.Close(); + } + } + else + { + var currentHash = sslBinding.CertificateHash; + var store = new X509Store(CertificateStoreName, StoreLocation.LocalMachine); + store.Open(OpenFlags.ReadOnly); + + cert = store.Certificates.Cast().Single(c => Convert.ToBase64String(c.GetCertHash()) == Convert.ToBase64String(currentHash)); + + store.Close(); + } + + return GetSSLCertificateFromX509Certificate2(cert); + } + } + + private static List GetServerCertificates(string certificateStoreName) + { + var store = new X509Store(certificateStoreName, StoreLocation.LocalMachine); + + List certificates; + + try + { + store.Open(OpenFlags.ReadOnly); + certificates = store.Certificates.Cast().Select(GetSSLCertificateFromX509Certificate2).ToList(); + } + catch (Exception ex) + { + Log.WriteError( + String.Format("SSLModuleService is unable to get certificates from X509Store('{0}', '{1}') and complete GetServerCertificates call", store.Name, store.Location), ex); + // Re-throw exception + throw; + } + finally + { + store.Close(); + } + + return certificates; + } + + private string GetCCSPath(string bindingName) + { + return (CCSUncPath.EndsWith("/") ? CCSUncPath : CCSUncPath + "/") + bindingName + ".pfx"; + } + + private static SSLCertificate GetSSLCertificateFromX509Certificate2(X509Certificate2 cert) + { + var certificate = new SSLCertificate + { + Hostname = cert.GetNameInfo(X509NameType.SimpleName, false), + FriendlyName = cert.FriendlyName, + CSRLength = Convert.ToInt32(cert.PublicKey.Key.KeySize.ToString(CultureInfo.InvariantCulture)), + Installed = true, + DistinguishedName = cert.Subject, + Hash = cert.GetCertHash(), + SerialNumber = cert.SerialNumber, + ExpiryDate = DateTime.Parse(cert.GetExpirationDateString()), + ValidFrom = DateTime.Parse(cert.GetEffectiveDateString()), + Success = true + }; + + return certificate; + } + + private static bool SiteHasBindingWithDedicatedIp(ServerManager srvman, WebSite website) + { + try + { + var bindings = srvman.Sites[website.SiteId].Bindings; + return bindings.Any(b => string.IsNullOrEmpty(b.Host) && b.BindingInformation.Split(':')[1] != "*"); + } + catch + { + return false; + } + } + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIs80/WebsitePanel.Providers.Web.IIs80.csproj b/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIs80/WebsitePanel.Providers.Web.IIs80.csproj index 8e83d0c4..dd28ae8c 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIs80/WebsitePanel.Providers.Web.IIs80.csproj +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIs80/WebsitePanel.Providers.Web.IIs80.csproj @@ -9,8 +9,9 @@ Properties WebsitePanel.Providers.Web WebsitePanel.Providers.Web.IIs80 - v3.5 + v4.0 512 + true @@ -32,11 +33,7 @@ False - ..\..\Lib\References\Microsoft\Microsoft.Web.Administration.dll - - - False - ..\..\Lib\References\Microsoft\Microsoft.Web.Management.dll + ..\..\Lib\References\Microsoft\Windows2012\Microsoft.Web.Administration.dll @@ -51,6 +48,9 @@ + + Code + @@ -65,7 +65,12 @@ {1b9dce85-c664-49fc-b6e1-86c63cab88d1} WebsitePanel.Providers.Web.IIs70 + + {e91e52f3-9555-4d00-b577-2b1dbdd87ca7} + WebsitePanel.Server.Utils + +