|
//------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------------------------
namespace Microsoft.Tools.ServiceModel.WsatConfig
{
using System;
using System.Text;
using System.Security.Cryptography.X509Certificates;
using System.Security.Principal;
using System.Globalization;
using System.Diagnostics;
using System.Security;
using System.Security.Permissions;
using System.Security.Cryptography;
using System.Security.AccessControl;
using Microsoft.Win32;
// In theory we could abstract this class out and provide concrete implementation for config from different
// sources, in practice we didn't do that for historical code reasons
class WsatConfiguration
{
const string TransactionBridgeRegistryValue = "{BFFECCA7-4069-49F9-B5AB-7CCBB078ED91}";
const string TransactionBridge30RegistryValue = "{EEC5DCCA-05DC-4B46-8AF7-2881C1635AEA}";
internal const bool DefaultNetworkSupport = false;
internal const uint DefaultHttpsPort = 443;
internal const SourceLevels DefaultTraceLevel = SourceLevels.Warning;
internal const bool DefaultActivityPropagation = false;
internal const bool DefaultActivityTracing = false;
internal const uint DefaultDefaultTimeout = 60;
internal const uint DefaultMaxTimeout = 3600;
internal const bool DefaultTracingPii = false;
internal string[] DefaultX509GlobalAcl = { string.Empty };
internal string[] DefaultKerberosGlobalAcl = { @"NT AUTHORITY\Authenticated Users" };
const X509Certificate2 DefaultX509CertificateIdentity = null;
WsatConfiguration previousConfig;
FirewallWrapper firewallWrapper;
string machineName;
string virtualServer;
ConfigurationProvider wsatConfigProvider;
ConfigurationProvider msdtcConfigProvider;
bool isClusterRemoteNode = false;
uint httpsPort = DefaultHttpsPort;
uint defaultTimeout = DefaultDefaultTimeout;
uint maxTimeout = DefaultMaxTimeout;
SourceLevels diagnosticTraceLevel = DefaultTraceLevel;
bool activityPropagation = DefaultActivityPropagation;
bool activityTracing = DefaultActivityTracing;
bool tracePii = DefaultTracingPii;
bool transactionBridgeEnabled = false;
bool transactionBridge30Enabled = false;
X509Certificate2 certificate = DefaultX509CertificateIdentity;
string[] allowedCertificates = null;
string[] kerberosGlobalAcl = null;
bool minimalWrite = false;
string[] clusterNodes = null;
SafeHResource hClusterDtcResource;
[SecurityCritical]
internal WsatConfiguration(string machineName, string virtualServer, WsatConfiguration previousConfig, bool minimalWrite)
{
this.MachineName = machineName;
this.minimalWrite = minimalWrite;
this.firewallWrapper = new FirewallWrapper();
this.previousConfig = previousConfig;
this.virtualServer = virtualServer;
if (previousConfig == null)
{
this.allowedCertificates = DefaultX509GlobalAcl;
this.kerberosGlobalAcl = DefaultKerberosGlobalAcl;
}
else
{
CopyConfigurationData(previousConfig, this);
}
if (MsdtcClusterUtils.IsClusterServer(MachineName))
{
this.hClusterDtcResource = MsdtcClusterUtils.GetTransactionManagerClusterResource(VirtualServer, out clusterNodes);
if (hClusterDtcResource == null || hClusterDtcResource.IsInvalid)
{
if (!string.IsNullOrEmpty(VirtualServer))
{
throw new WsatAdminException(WsatAdminErrorCode.CANNOT_FIND_CLUSTER_VIRTUAL_SERVER, SR.GetString(SR.ErrorCanNotFindVirtualServer));
}
}
}
InitializeConfigurationProvider();
}
void CopyConfigurationData(WsatConfiguration src, WsatConfiguration dest)
{
dest.TransactionBridgeEnabled = src.TransactionBridgeEnabled;
dest.TransactionBridge30Enabled = src.TransactionBridge30Enabled;
dest.HttpsPort = src.HttpsPort;
dest.X509Certificate = src.X509Certificate;
dest.KerberosGlobalAcl = src.KerberosGlobalAcl;
dest.X509GlobalAcl = src.X509GlobalAcl;
dest.DefaultTimeout = src.DefaultTimeout;
dest.MaxTimeout = src.MaxTimeout;
dest.TraceLevel = src.TraceLevel;
dest.ActivityPropagation = src.ActivityPropagation;
dest.ActivityTracing = src.ActivityTracing;
dest.TracePii = src.TracePii;
dest.MachineName = src.MachineName;
dest.IsClusterRemoteNode = src.IsClusterRemoteNode;
dest.VirtualServer = src.VirtualServer;
}
internal MsdtcWrapper GetMsdtcWrapper()
{
return MsdtcWrapper.GetWrapper(MachineName, VirtualServer, this.msdtcConfigProvider);
}
// A cluster node can spawn remote processes to setup other nodes.
// Is this a node of cluster, but not the originating one?
internal bool IsClusterRemoteNode
{
get { return this.isClusterRemoteNode; }
set { this.isClusterRemoteNode = value; }
}
internal bool IsClustered
{
get
{
return (hClusterDtcResource != null && !hClusterDtcResource.IsInvalid);
}
}
internal string MachineName
{
get { return string.IsNullOrEmpty(this.machineName) ? string.Empty : machineName; }
set { this.machineName = value; }
}
internal string VirtualServer
{
get { return this.virtualServer; }
set { this.virtualServer = value; }
}
internal bool TransactionBridgeEnabled
{
get { return this.transactionBridgeEnabled; }
set { this.transactionBridgeEnabled = value; }
}
internal bool TransactionBridge30Enabled
{
get { return this.transactionBridge30Enabled; }
set { this.transactionBridge30Enabled = value; }
}
internal uint HttpsPort
{
get { return this.httpsPort; }
set { this.httpsPort = value; }
}
internal SourceLevels TraceLevel
{
get { return this.diagnosticTraceLevel; }
set { this.diagnosticTraceLevel = value; }
}
internal uint DefaultTimeout
{
get { return this.defaultTimeout; }
set { this.defaultTimeout = value; }
}
internal uint MaxTimeout
{
get { return this.maxTimeout; }
set { this.maxTimeout = value; }
}
internal bool ActivityTracing
{
get { return this.activityTracing; }
set { this.activityTracing = value; }
}
internal bool ActivityPropagation
{
get { return this.activityPropagation; }
set { this.activityPropagation = value; }
}
internal bool TracePii
{
get { return this.tracePii; }
set { this.tracePii = value; }
}
internal string[] X509GlobalAcl
{
get { return this.allowedCertificates; }
set
{
if (value == null)
{
this.allowedCertificates = new string[] { };
}
else
{
this.allowedCertificates = value;
}
}
}
internal string[] KerberosGlobalAcl
{
get { return kerberosGlobalAcl; }
set
{
if (value == null)
{
this.kerberosGlobalAcl = new string[] { };
}
else
{
this.kerberosGlobalAcl = value;
}
}
}
internal X509Certificate2 X509Certificate
{
get { return this.certificate; }
set { this.certificate = value; }
}
internal void ValidateThrow()
{
if (this.TransactionBridgeEnabled)
{
// rule: WS-AT network support requires MSDTC network transaction to be enabled
// GetNetworkTransactionAccess fails if current remote cluster node does not take ownership
if (!IsClusterRemoteNode)
{
MsdtcWrapper wrapper = this.GetMsdtcWrapper();
if (!wrapper.GetNetworkTransactionAccess())
{
throw new WsatAdminException(WsatAdminErrorCode.MSDTC_NETWORK_ACCESS_DISABLED,
SR.GetString(SR.ErrorMsdtcNetworkAccessDisabled));
}
}
// rule: HTTPS port must be in range 1-65535
if (this.HttpsPort < 1)
{
throw new WsatAdminException(WsatAdminErrorCode.INVALID_HTTPS_PORT,
SR.GetString(SR.ErrorHttpsPortRange));
}
// rule: local endpoint certificate must be specified and valid
ValidateIdentityCertificateThrow(this.X509Certificate, !Utilities.IsLocalMachineName(MachineName));
// rule: default timeout should be in range 1-3600
if (this.DefaultTimeout < 1 || this.DefaultTimeout > 3600)
{
throw new WsatAdminException(WsatAdminErrorCode.INVALID_DEFTIMEOUT_ARGUMENT,
SR.GetString(SR.ErrorDefaultTimeoutRange));
}
// rule: max timeout be in range 0-3600
if (this.MaxTimeout > 3600)
{
throw new WsatAdminException(WsatAdminErrorCode.INVALID_MAXTIMEOUT_ARGUMENT,
SR.GetString(SR.ErrorMaximumTimeoutRange));
}
}
}
void InitializeConfigurationProvider()
{
if (IsClustered)
{
this.msdtcConfigProvider = new ClusterRegistryConfigurationProvider(this.hClusterDtcResource, GetClusterMstdcRegistryKey());
this.wsatConfigProvider = new ClusterRegistryConfigurationProvider(this.hClusterDtcResource, WsatKeys.WsatClusterRegKey);
}
else
{
this.msdtcConfigProvider = new RegistryConfigurationProvider(RegistryHive.LocalMachine, WsatKeys.MsdtcRegKey, MachineName);
this.wsatConfigProvider = new RegistryConfigurationProvider(RegistryHive.LocalMachine, WsatKeys.WsatRegKey, MachineName);
}
}
//
// LH: Cluster\Resources\GUID_OF_DTC\MSDTCPrivate\MSDTC
// W2k3: Cluster\Resources\GUID_OF_DTC\SOME_GUID\, here SOME_GUID is the default value of GUID_OF_DTC\DataPointer\
//
string GetClusterMstdcRegistryKey()
{
Debug.Assert(IsClustered);
if (Utilities.OSMajor > 5)
{
return WsatKeys.MsdtcClusterRegKey_OS6;
}
ClusterRegistryConfigurationProvider clusterReg = new ClusterRegistryConfigurationProvider(this.hClusterDtcResource, WsatKeys.MsdtcClusterDataPointerRegKey_OS5);
using (clusterReg)
{
//the default value
string subKey = clusterReg.ReadString(string.Empty, string.Empty);
if (!string.IsNullOrEmpty(subKey))
{
return subKey;
}
}
RegistryExceptionHelper registryExceptionHelper = new RegistryExceptionHelper(WsatKeys.MsdtcClusterDataPointerRegKey_OS5);
throw registryExceptionHelper.CreateRegistryAccessException(null);
}
[SecurityCritical]
internal void LoadFromRegistry()
{
string value = msdtcConfigProvider.ReadString(WsatKeys.TransactionBridgeRegKey, null);
TransactionBridgeEnabled = Utilities.SafeCompare(value, TransactionBridgeRegistryValue);
TransactionBridge30Enabled = Utilities.SafeCompare(value, TransactionBridge30RegistryValue);
HttpsPort = wsatConfigProvider.ReadUInt32(WsatKeys.RegistryEntryHttpsPort, DefaultHttpsPort);
X509Certificate = CertificateManager.GetCertificateFromThumbprint(
wsatConfigProvider.ReadString(WsatKeys.RegistryEntryX509CertificateIdentity, string.Empty),
MachineName);
KerberosGlobalAcl = wsatConfigProvider.ReadMultiString(WsatKeys.RegistryEntryKerberosGlobalAcl, DefaultKerberosGlobalAcl);
X509GlobalAcl = wsatConfigProvider.ReadMultiString(WsatKeys.RegistryEntryX509GlobalAcl, DefaultX509GlobalAcl);
TraceLevel = (SourceLevels)wsatConfigProvider.ReadUInt32(WsatKeys.RegistryEntryTraceLevel, (uint)DefaultTraceLevel);
#pragma warning disable 429
ActivityTracing = wsatConfigProvider.ReadUInt32(WsatKeys.RegistryEntryActivityTracing, (DefaultActivityTracing ? 1 : 0)) != 0;
ActivityPropagation = wsatConfigProvider.ReadUInt32(WsatKeys.RegistryEntryPropagateActivity, (DefaultActivityPropagation ? 1 : 0)) != 0;
TracePii = wsatConfigProvider.ReadUInt32(WsatKeys.RegistryEntryTracingPii, (DefaultTracingPii ? 1 : 0)) != 0;
#pragma warning restore 429
DefaultTimeout = wsatConfigProvider.ReadUInt32(WsatKeys.RegistryEntryDefTimeout, DefaultDefaultTimeout);
MaxTimeout = wsatConfigProvider.ReadUInt32(WsatKeys.RegistryEntryMaxTimeout, DefaultMaxTimeout);
}
internal bool IsLocalMachine
{
get { return !IsClustered && Utilities.IsLocalMachineName(this.MachineName); }
}
// The code should align with the ValidateIdentityCertificate implementation in
// src\TransactionBridge\Microsoft\Transactions\Wsat\protocol\Configuration.cs
internal static void ValidateIdentityCertificateThrow(X509Certificate2 cert, bool remoteCert)
{
// I wish we had system-defined constants for these. We don't.
const string KeyUsage = "2.5.29.15";
const string EnhancedKeyUsage = "2.5.29.37";
const string ClientAuthentication = "1.3.6.1.5.5.7.3.2";
const string ServerAuthentication = "1.3.6.1.5.5.7.3.1";
X509Certificate2 identity = cert;
// 0) The certificate should be present
if (identity == null)
{
throw new WsatAdminException(WsatAdminErrorCode.INVALID_OR_MISSING_SSL_CERTIFICATE,
SR.GetString(SR.ErrorMissingSSLCert));
}
if (remoteCert)
{
return; // the following info is not accurate for remote cert, so we do not bother to check them
}
// 1) A certificate identity must have a private key
if (!identity.HasPrivateKey)
{
throw new WsatAdminException(WsatAdminErrorCode.INVALID_OR_MISSING_SSL_CERTIFICATE,
SR.GetString(SR.ErrorSSLCertHasNoPrivateKey));
}
// 2) A certificate identity must have an accessible private key
try
{
// Yes, this property throws on error...
AsymmetricAlgorithm privateKey = identity.PrivateKey;
}
catch (CryptographicException e)
{
throw new WsatAdminException(WsatAdminErrorCode.INVALID_OR_MISSING_SSL_CERTIFICATE,
SR.GetString(SR.ErrorSSLCertCanNotAccessPrivateKey), e);
}
// 3) If a "Key Usage" extension is present, it must allow "Key Encipherment"
// 4) If a "Key Usage" extension is present, it must allow "Digital Signature"
X509KeyUsageExtension keyUsage = (X509KeyUsageExtension)identity.Extensions[KeyUsage];
if (keyUsage != null)
{
const X509KeyUsageFlags required = X509KeyUsageFlags.KeyEncipherment |
X509KeyUsageFlags.DigitalSignature;
if ((keyUsage.KeyUsages & required) != required)
{
throw new WsatAdminException(WsatAdminErrorCode.INVALID_OR_MISSING_SSL_CERTIFICATE,
SR.GetString(SR.ErrorSSLCertDoesNotSupportKeyEnciphermentOrDsig));
}
}
X509EnhancedKeyUsageExtension enhancedKeyUsage = (X509EnhancedKeyUsageExtension)identity.Extensions[EnhancedKeyUsage];
if (enhancedKeyUsage != null)
{
// 5) If an "Enhanced Key Usage" extension is present, it must allow "Client Authentication"
if (enhancedKeyUsage.EnhancedKeyUsages[ClientAuthentication] == null)
{
throw new WsatAdminException(WsatAdminErrorCode.INVALID_OR_MISSING_SSL_CERTIFICATE,
SR.GetString(SR.ErrorSSLCertDoesNotSupportClientAuthentication));
}
// 6) If an "Enhanced Key Usage" extension is present, it must allow "Server Authentication"
if (enhancedKeyUsage.EnhancedKeyUsages[ServerAuthentication] == null)
{
throw new WsatAdminException(WsatAdminErrorCode.INVALID_OR_MISSING_SSL_CERTIFICATE,
SR.GetString(SR.ErrorSSLCertDoesNotSupportServerAuthentication)); }
}
}
void UpdateClusterNodesPorts(bool restart)
{
if (clusterNodes != null)
{
foreach (string node in clusterNodes)
{
if (Utilities.SafeCompare(node, Utilities.LocalHostName))
{
UpdatePorts();
UpdateCertificatePrivateKeyAccess();
}
else
{
//Explicitly not to restart DTC on remote cluster node, actually restart will be ignored anyway.
SaveRemote(node, false, true);
}
}
}
}
void RestartHelper(bool restart)
{
if (restart)
{
try
{
MsdtcWrapper msdtc = this.GetMsdtcWrapper();
msdtc.RestartDtcService();
}
catch (WsatAdminException)
{
throw;
}
#pragma warning suppress 56500
catch (Exception e)
{
if (Utilities.IsCriticalException(e))
{
throw;
}
throw new WsatAdminException(WsatAdminErrorCode.DTC_RESTART_ERROR, SR.GetString(SR.ErrorRestartMSDTC), e);
}
}
}
internal void Save(bool restart)
{
if (IsLocalMachine)
{
Utilities.Log("Save - LocalMachine");
// Single local machine:
// 1. Update local SSL binding
// 2. Update URL ACL
// 3. Update firewall port status
// 4. Update the endpoint cert's private key permission
// 5. Save to local registry
UpdatePorts();
UpdateCertificatePrivateKeyAccess();
SaveToRegistry();
RestartHelper(restart);
}
else if (IsClusterRemoteNode)
{
Utilities.Log("Save - Cluster Remote Node");
// Cluster remote node machine:
// DO NOT save to cluster registry, DO NOT restart DTC
// 1. Update SSL binding on the node
// 2. Update URL ACL on the node
// 3. Update firewall port status on the node
// 4. Update the endpoint cert's private key permission
UpdatePorts();
UpdateCertificatePrivateKeyAccess();
}
else if (IsClustered) // the orignating cluster node
{
Utilities.Log("Save - Cluster");
// Cluster originating node machine:
// 1. Update SSL binding on each node
// 2. Update URL ACL on each node
// 3. Update firewall port status on each remote node
// 4. Update the endpoint cert's private key permission on each remote node
// 5. Save to cluster registry
UpdateClusterNodesPorts(restart);
SaveToRegistry();
RestartHelper(restart);
}
else
{
Utilities.Log("Save - Remote");
// Remote machine:
// 1. Save to remote registry
// 2. Update SSL binding on remote machine
// 3. Update URL ACL on remote machine
// 4. Update firewall port status on remote machine
// 5. Update the endpoint cert's private key permission on remote machine
SaveRemote(restart, false);
}
CopyConfigurationData(this, previousConfig);
}
void SaveRemote(bool restart, bool clusterRemoteNode)
{
System.Diagnostics.Debug.Assert(!(IsLocalMachine || IsClustered));
SaveRemote(MachineName, restart, clusterRemoteNode);
}
void SaveRemote(string machineName, bool restart, bool clusterRemoteNode)
{
string portString = null;
string endpointCertString = null;
string accountsString = null;
string accountsCertsString = null;
string defaultTimeoutString = null;
string maxTimeoutString = null;
string traceLevelString = null;
string traceActivityString = null;
string tracePropString = null;
string tracePiiString = null;
// Performance is not a concern here so we just do plain string concatenation
string networkEnabledString = " -" + CommandLineOption.Network + ":" +
(this.TransactionBridgeEnabled ? CommandLineOption.Enable : CommandLineOption.Disable);
string virtualServerString = null;
if (!string.IsNullOrEmpty(VirtualServer))
{
virtualServerString = " -" + CommandLineOption.ClusterVirtualServer + ":" + "\"" + VirtualServer + "\"";
}
if (this.TransactionBridgeEnabled)
{
portString = " -" + CommandLineOption.Port + ":" + this.HttpsPort;
endpointCertString = this.X509Certificate == null ? "" : " -" + CommandLineOption.EndpointCert + ":" + this.X509Certificate.Thumbprint;
accountsString = " -" + CommandLineOption.Accounts + ":" + BuildAccountsArgument();
accountsCertsString = " -" + CommandLineOption.AccountsCerts + ":" + BuildAccountsCertsArgument();
defaultTimeoutString = " -" + CommandLineOption.DefaultTimeout + ":" + this.DefaultTimeout.ToString(CultureInfo.InvariantCulture);
traceLevelString = " -" + CommandLineOption.TraceLevel + ":" + ((uint)this.TraceLevel).ToString(CultureInfo.InvariantCulture);
traceActivityString = " -" + CommandLineOption.TraceActivity + ":" + (this.ActivityTracing ? CommandLineOption.Enable : CommandLineOption.Disable);
tracePropString = " -" + CommandLineOption.TraceProp + ":" + (this.ActivityPropagation ? CommandLineOption.Enable : CommandLineOption.Disable);
tracePiiString = " -" + CommandLineOption.TracePii + ":" + (this.TracePii ? CommandLineOption.Enable : CommandLineOption.Disable);
maxTimeoutString = " -" + CommandLineOption.MaxTimeout + ":" + this.MaxTimeout.ToString(CultureInfo.InvariantCulture);
}
string arguments = networkEnabledString + virtualServerString + portString + endpointCertString + accountsString +
accountsCertsString + defaultTimeoutString + maxTimeoutString +
traceLevelString + traceActivityString + tracePropString + tracePiiString;
if (clusterRemoteNode)
{
arguments += " -" + CommandLineOption.ClusterRemoteNode + ":" + CommandLineOption.Enable;
}
if (restart)
{
arguments += " -" + CommandLineOption.Restart;
}
Utilities.Log("Remote command arguments: " + arguments);
RemoteHelper remote = new RemoteHelper(machineName);
remote.ExecuteWsatProcess(arguments);
}
string BuildAccountsCertsArgument()
{
string result = string.Empty;
if (this.X509GlobalAcl != null && this.X509GlobalAcl.Length > 0)
{
result += "\"" + this.X509GlobalAcl[0] + "\"";
for (int i = 1; i < this.X509GlobalAcl.Length; ++i)
{
result += ",\"" + this.X509GlobalAcl[i] + "\"";
}
}
return result;
}
string BuildAccountsArgument()
{
string result = string.Empty;
if (this.KerberosGlobalAcl != null && this.KerberosGlobalAcl.Length > 0)
{
result += "\"" + this.KerberosGlobalAcl[0] + "\"";
for (int i = 1; i < this.KerberosGlobalAcl.Length; ++i)
{
result += ",\"" + this.KerberosGlobalAcl[i] + "\"";
}
}
return result;
}
void UpdateCertificatePrivateKeyAccess()
{
if (previousConfig == null)
{
AddCertificatePrivateKeyAccess(X509Certificate);
}
else if (X509Certificate != previousConfig.X509Certificate)
{
RemoveCertificatePrivateKeyAccess(previousConfig.X509Certificate);
AddCertificatePrivateKeyAccess(X509Certificate);
}
}
// This method could throw any exception, because RSACryptoServiceProvider ctor could do so
// We will escalate the exceptions to the callers who will be more sensible on how to deal with them
void CommitCryptoKeySecurity(CspKeyContainerInfo info, CryptoKeySecurity keySec)
{
CspParameters cspParams = new CspParameters(
info.ProviderType, info.ProviderName,
info.KeyContainerName);
cspParams.CryptoKeySecurity = keySec;
// Important flag, or the security setting will silently fail
cspParams.Flags = CspProviderFlags.UseMachineKeyStore;
// The RSACryptoServiceProvider ctor will automatically apply DACLs set in CSP's securtiy info
new RSACryptoServiceProvider(cspParams);
}
void RemoveCertificatePrivateKeyAccess(X509Certificate2 cert)
{
if (cert != null && cert.HasPrivateKey)
{
try
{
AsymmetricAlgorithm key = cert.PrivateKey;
// Only RSA provider is supported here
if (key is RSACryptoServiceProvider)
{
RSACryptoServiceProvider prov = key as RSACryptoServiceProvider;
CspKeyContainerInfo info = prov.CspKeyContainerInfo;
CryptoKeySecurity keySec = info.CryptoKeySecurity;
SecurityIdentifier ns = new SecurityIdentifier(WellKnownSidType.NetworkServiceSid, null);
AuthorizationRuleCollection rules = keySec.GetAccessRules(true, false, typeof(SecurityIdentifier));
foreach (AuthorizationRule rule in rules)
{
CryptoKeyAccessRule keyAccessRule = (CryptoKeyAccessRule)rule;
if (keyAccessRule.AccessControlType == AccessControlType.Allow &&
(int)(keyAccessRule.CryptoKeyRights & CryptoKeyRights.GenericRead) != 0)
{
SecurityIdentifier sid = keyAccessRule.IdentityReference as SecurityIdentifier;
if (ns.Equals(sid))
{
CryptoKeyAccessRule nsReadRule = new CryptoKeyAccessRule(ns,
CryptoKeyRights.GenericRead,
AccessControlType.Allow);
keySec.RemoveAccessRule(nsReadRule);
CommitCryptoKeySecurity(info, keySec);
break;
}
}
}
}
}
#pragma warning suppress 56500
catch (Exception e)
{
// CommitCryptoKeySecurity can actually throw any exception,
// so the safest way here is to catch a generic exception while throw on critical ones
if (Utilities.IsCriticalException(e))
{
throw;
}
throw new WsatAdminException(WsatAdminErrorCode.CANNOT_UPDATE_PRIVATE_KEY_PERM,
SR.GetString(SR.ErrorUpdateCertPrivateKeyPerm), e);
}
}
}
void AddCertificatePrivateKeyAccess(X509Certificate2 cert)
{
if (cert != null && cert.HasPrivateKey)
{
try
{
AsymmetricAlgorithm key = cert.PrivateKey;
// Only RSA provider is supported here
if (key is RSACryptoServiceProvider)
{
RSACryptoServiceProvider prov = key as RSACryptoServiceProvider;
CspKeyContainerInfo info = prov.CspKeyContainerInfo;
CryptoKeySecurity keySec = info.CryptoKeySecurity;
SecurityIdentifier ns = new SecurityIdentifier(WellKnownSidType.NetworkServiceSid, null);
// Just add a rule, exisitng settings will be merged
CryptoKeyAccessRule rule = new CryptoKeyAccessRule(ns,
CryptoKeyRights.GenericRead,
AccessControlType.Allow);
keySec.AddAccessRule(rule);
CommitCryptoKeySecurity(info, keySec);
}
}
#pragma warning suppress 56500
catch (Exception e)
{
// CommitCryptoKeySecurity can actually throw any exception,
// so the safest way here is to catch a generic exception while throw on critical ones
if (Utilities.IsCriticalException(e))
{
throw;
}
throw new WsatAdminException(WsatAdminErrorCode.CANNOT_UPDATE_PRIVATE_KEY_PERM,
SR.GetString(SR.ErrorUpdateCertPrivateKeyPerm), e);
}
}
}
void SaveToRegistry()
{
if (!this.minimalWrite || this.previousConfig == null || this.TransactionBridgeEnabled != this.previousConfig.TransactionBridgeEnabled || (this.previousConfig.TransactionBridge30Enabled && !this.TransactionBridgeEnabled) )
{
msdtcConfigProvider.WriteString(
WsatKeys.TransactionBridgeRegKey,
this.TransactionBridgeEnabled ? TransactionBridgeRegistryValue : string.Empty);
}
if (!this.minimalWrite || this.previousConfig == null || this.TraceLevel != this.previousConfig.TraceLevel)
{
wsatConfigProvider.WriteUInt32(WsatKeys.RegistryEntryTraceLevel, (uint)this.TraceLevel);
}
if (!this.minimalWrite || this.previousConfig == null || this.ActivityTracing != this.previousConfig.ActivityTracing)
{
wsatConfigProvider.WriteUInt32(WsatKeys.RegistryEntryActivityTracing, (this.ActivityTracing ? 1u : 0));
}
if (!this.minimalWrite || this.previousConfig == null || this.ActivityPropagation != this.previousConfig.ActivityPropagation)
{
wsatConfigProvider.WriteUInt32(WsatKeys.RegistryEntryPropagateActivity, (this.ActivityPropagation ? 1u : 0));
}
if (!this.minimalWrite || this.previousConfig == null || this.TracePii != this.previousConfig.TracePii)
{
wsatConfigProvider.WriteUInt32(WsatKeys.RegistryEntryTracingPii, (this.TracePii ? 1u : 0));
}
if (!this.minimalWrite || this.previousConfig == null || this.DefaultTimeout != this.previousConfig.DefaultTimeout)
{
wsatConfigProvider.WriteUInt32(WsatKeys.RegistryEntryDefTimeout, (uint)this.DefaultTimeout);
}
if (!this.minimalWrite || this.previousConfig == null || this.MaxTimeout != this.previousConfig.MaxTimeout)
{
wsatConfigProvider.WriteUInt32(WsatKeys.RegistryEntryMaxTimeout, (uint)this.MaxTimeout);
}
if (!this.minimalWrite || this.previousConfig == null || this.X509GlobalAcl != this.previousConfig.X509GlobalAcl)
{
wsatConfigProvider.WriteMultiString(WsatKeys.RegistryEntryX509GlobalAcl, this.X509GlobalAcl);
}
if (!this.minimalWrite || this.previousConfig == null || this.X509Certificate != this.previousConfig.X509Certificate)
{
wsatConfigProvider.WriteString(WsatKeys.RegistryEntryX509CertificateIdentity, (this.X509Certificate == null ? string.Empty : this.X509Certificate.Thumbprint));
}
if (!this.minimalWrite || this.previousConfig == null || this.HttpsPort != this.previousConfig.HttpsPort)
{
wsatConfigProvider.WriteUInt32(WsatKeys.RegistryEntryHttpsPort, this.HttpsPort);
}
if (!this.minimalWrite || this.previousConfig == null || this.KerberosGlobalAcl != this.previousConfig.KerberosGlobalAcl)
{
wsatConfigProvider.WriteMultiString(WsatKeys.RegistryEntryKerberosGlobalAcl, this.KerberosGlobalAcl);
}
if (IsClustered || IsLocalMachine)
{
wsatConfigProvider.AdjustRegKeyPermission();
}
}
void UpdateUrlAclReservation()
{
WsatServiceAddress wsatServiceAddress;
if (this.previousConfig != null)
{
wsatServiceAddress = new WsatServiceAddress(this.previousConfig.HttpsPort);
wsatServiceAddress.FreeWsatServiceAddress();
}
if (this.TransactionBridgeEnabled)
{
wsatServiceAddress = new WsatServiceAddress(this.HttpsPort);
wsatServiceAddress.ReserveWsatServiceAddress();
}
}
void UpdateSSLBinding()
{
WsatServiceCertificate wsatServiceCertificate;
if (this.previousConfig != null && this.previousConfig.X509Certificate != null)
{
wsatServiceCertificate = new WsatServiceCertificate(this.previousConfig.X509Certificate, previousConfig.HttpsPort);
wsatServiceCertificate.UnbindSSLCertificate();
}
if (this.TransactionBridgeEnabled && this.X509Certificate != null)
{
wsatServiceCertificate = new WsatServiceCertificate(this.X509Certificate, HttpsPort);
wsatServiceCertificate.BindSSLCertificate();
}
}
void UpdateFirewallPort()
{
FirewallWrapper firewallWrapper = new FirewallWrapper();
firewallWrapper.RemoveHttpsPort((int)this.previousConfig.HttpsPort);
if (this.TransactionBridgeEnabled)
{
firewallWrapper.AddHttpsPort((int)this.HttpsPort);
}
}
// update the ports for the SSL Binding, URL ACL Reservation and Firewall
void UpdatePorts()
{
UpdateFirewallPort();
UpdateUrlAclReservation();
UpdateSSLBinding();
}
#if WSAT_CMDLINE
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append(SR.GetString(SR.ConfigNetworkSupport, Utilities.GetEnabledStatusString(this.TransactionBridgeEnabled || this.TransactionBridge30Enabled)));
if (this.TransactionBridgeEnabled || this.TransactionBridge30Enabled)
{
sb.Append(SR.GetString(SR.ConfigHTTPSPort, this.HttpsPort));
sb.Append(SR.GetString(SR.ConfigIdentityCertificate,
this.X509Certificate == null ? SR.GetString(SR.ConfigNone) : this.X509Certificate.Thumbprint));
sb.Append(SR.GetString(SR.ConfigKerberosGACL));
if (this.KerberosGlobalAcl == null || this.KerberosGlobalAcl.Length < 1)
{
sb.AppendLine(SR.GetString(SR.ConfigNone));
}
else
{
int i = 0;
foreach (string ace in this.KerberosGlobalAcl)
{
if (i++ > 0)
{
sb.Append(SR.GetString(SR.ConfigACEPrefix));
}
sb.AppendLine(ace);
}
}
sb.Append(SR.GetString(SR.ConfigAcceptedCertificates));
if (this.X509GlobalAcl == null || this.X509GlobalAcl.Length < 1)
{
sb.AppendLine(SR.GetString(SR.ConfigNone));
}
else
{
int i = 0;
foreach (string cert in this.X509GlobalAcl)
{
if (i++ > 0)
{
sb.Append(SR.GetString(SR.ConfigAcceptedCertPrefix));
}
sb.AppendLine(cert);
}
}
sb.Append(SR.GetString(SR.ConfigDefaultTimeout, this.DefaultTimeout));
sb.Append(SR.GetString(SR.ConfigMaximumTimeout, this.MaxTimeout));
SourceLevels level = this.TraceLevel;
if (level != SourceLevels.All)
{
level = level & ~SourceLevels.ActivityTracing;
}
sb.Append(SR.GetString(SR.ConfigTraceLevel, level));
sb.Append(SR.GetString(SR.ConfigActivityTracing, Utilities.GetEnabledStatusString(this.ActivityTracing)));
sb.Append(SR.GetString(SR.ConfigActivityProp, Utilities.GetEnabledStatusString(this.ActivityPropagation)));
sb.Append(SR.GetString(SR.ConfigPiiTracing, Utilities.GetEnabledStatusString(this.TracePii)));
MsdtcWrapper msdtc = this.GetMsdtcWrapper();
if (!msdtc.GetNetworkTransactionAccess())
{
sb.Append(Environment.NewLine);
sb.Append(SR.GetString(SR.ConfigWarningNetworkDTCAccessIsDisabled));
}
}
else
{
// When network support is disabled, the only setting that still matters is the MaxTimeout
sb.Append(SR.GetString(SR.ConfigMaximumTimeoutWhenNetworkDisabled, this.MaxTimeout));
}
return sb.ToString();
}
#endif
}
}
|