Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
3.0.0
* Added Integration Tests to aid in future development and testing.
* Improved messaging in the event an Entry Parameter is missing (or does not meet the casing requirements)
* Fixed the SNI/SSL flag being returned during inventory, now returns extended SSL flags
* Fixed the SNI/SSL flag when binding the certificate to allow for extended SSL flags
* Added SSL Flag validation to make sure the bit flag is correct. These are the current SSL Flags:
* 0 No SNI
* 1 SNI Enabled
* 2 Non SNI binding which uses Central Certificate Store
* 3 SNI binding which uses Central Certificate Store
* 4 Use App ID (Requires AppID parameter to be set)
* 8 Use Central Certificate Store (Non SNI)
* 32 Central Certificate Store
* 64 Disable HTTP2
* 128 Disable OCSP Stapling

2.6.3
* Fixed re-enrollment or ODKG job when RDN Components contained escaped commas.
* Updated renewal job for IIS Certs to delete the old cert if not bound or used by other web sites.
Expand Down
52 changes: 46 additions & 6 deletions IISU/ImplementedStoreTypes/WinIIS/IISBindingInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@

namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore.IISU
{
[Flags]
public enum SslFlags
{
None = 0,
SslRequireSni = 1,
SslNegotiateCert = 2,
SslRequireCert = 4,
SslMapCert = 8,
CentralCertStore = 32,
DisableHTTP2 = 64,
DisableOCSPStapling = 128
}

public class IISBindingInfo
{
public string SiteName { get; set; }
Expand All @@ -42,12 +55,19 @@ public IISBindingInfo()

public IISBindingInfo(Dictionary<string, object> bindingInfo)
{
SiteName = bindingInfo["SiteName"].ToString();
Protocol = bindingInfo["Protocol"].ToString();
IPAddress = bindingInfo["IPAddress"].ToString();
Port = bindingInfo["Port"].ToString();
HostName = bindingInfo["HostName"]?.ToString();
SniFlag = MigrateSNIFlag(bindingInfo["SniFlag"].ToString());
try
{
SiteName = bindingInfo["SiteName"].ToString();
Protocol = bindingInfo["Protocol"].ToString();
IPAddress = bindingInfo["IPAddress"].ToString();
Port = bindingInfo["Port"].ToString();
HostName = bindingInfo["HostName"]?.ToString();
SniFlag = MigrateSNIFlag(bindingInfo["SniFlag"].ToString());
}
catch (KeyNotFoundException ex)
{
throw new ArgumentException($"An Entry Parameter was missing. Please check the Cert Store Type Definition, note that entry parameters are case sensitive. Message: {ex.Message}");
}
}

public static IISBindingInfo ParseAliaseBindingString(string alias)
Expand All @@ -69,6 +89,26 @@ public static IISBindingInfo ParseAliaseBindingString(string alias)
};
}

public (bool IsValid, string Message) ValidateSslFlags(int flags)
{
const int validBits = 1 | 2 | 4 | 8 | 32 | 64 | 128;

// Unknown bits
if ((flags & ~validBits) != 0)
return (false, $"SslFlags contains unknown bits: {flags}");

bool negotiate = (flags & (int)SslFlags.SslNegotiateCert) != 0;
bool require = (flags & (int)SslFlags.SslRequireCert) != 0;
bool mapCert = (flags & (int)SslFlags.SslMapCert) != 0;

if (negotiate && require)
return (false, "Cannot use both SslNegotiateCert (0x2) and SslRequireCert (0x4).");

if (mapCert && !(negotiate || require))
return (false, "SslMapCert (0x8) requires either SslNegotiateCert (0x2) or SslRequireCert (0x4).");

return (true, $"SslFlags value {flags} is valid.");
}

private string MigrateSNIFlag(string input)
{
Expand Down
27 changes: 25 additions & 2 deletions IISU/ImplementedStoreTypes/WinIIS/Management.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ namespace Keyfactor.Extensions.Orchestrator.WindowsCertStore.IISU
{
public class Management : WinCertJobTypeBase, IManagementJobExtension
{


public string ExtensionName => "WinIISUManagement";
private ILogger _logger;

Expand Down Expand Up @@ -92,6 +94,26 @@ public JobResult ProcessJob(ManagementJobConfiguration config)
bool includePortInSPN = (bool)jobProperties?.SpnPortFlag;
string alias = config.JobCertificate?.Alias?.Split(':').FirstOrDefault() ?? string.Empty; // Thumbprint is first part of the alias

// Assign the binding information
IISBindingInfo bindingInfo = new IISBindingInfo(config.JobProperties);

// Check if the Ssl flags are set correctly
if (bindingInfo.Protocol.ToLower() == "https" && string.IsNullOrEmpty(bindingInfo.SniFlag))
{
throw new ArgumentException("SniFlag must be set when using HTTPS protocol. Valid values are 0 (None), 1 (SNI Enabled), or 2 (IP Based).");
}
else if (bindingInfo.Protocol.ToLower() != "https")
{
bindingInfo.SniFlag = "0"; // Set to None if not using HTTPS
}

var (isValid, SslErrorMessage) = bindingInfo.ValidateSslFlags(int.Parse(bindingInfo.SniFlag));
if (!isValid)
{
throw new ArgumentException($"Invalid SSL Flag Combination: {SslErrorMessage}");
}


_psHelper = new(protocol, port, includePortInSPN, _clientMachineName, serverUserName, serverPassword);

_psHelper.Initialize();
Expand All @@ -102,6 +124,8 @@ public JobResult ProcessJob(ManagementJobConfiguration config)
{
case CertStoreOperationType.Add:
{
_logger.LogTrace($"Beginning the Adding of Certificate process.");

string certificateContents = config.JobCertificate.Contents;
string privateKeyPassword = config.JobCertificate.PrivateKeyPassword;
#pragma warning disable CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
Expand All @@ -111,7 +135,6 @@ public JobResult ProcessJob(ManagementJobConfiguration config)
// Add Certificate to Cert Store
try
{
IISBindingInfo bindingInfo = new IISBindingInfo(config.JobProperties);

OrchestratorJobStatusJobResult psResult = OrchestratorJobStatusJobResult.Unknown;
string failureMessage = "";
Expand Down Expand Up @@ -265,7 +288,7 @@ public JobResult ProcessJob(ManagementJobConfiguration config)
}
finally
{
_psHelper.Terminate();
if (_psHelper != null) _psHelper.Terminate();
_logger.MethodExit();
}
}
Expand Down
4 changes: 2 additions & 2 deletions IISU/JobConfigurationParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public static string ParseManagementJobConfiguration(ManagementJobConfiguration
try
{
// JobCertificate
managementParser.JobCertificateProperties.Thumbprint = config.JobCertificate.Thumbprint;
managementParser.JobCertificateProperties.Thumbprint = config.JobCertificate.Thumbprint ?? string.Empty;
managementParser.JobCertificateProperties.Contents = config.JobCertificate.Contents;
managementParser.JobCertificateProperties.Alias = config.JobCertificate.Alias;
managementParser.JobCertificateProperties.PrivateKeyPassword = "**********";
Expand Down Expand Up @@ -140,7 +140,7 @@ public static string ParseInventoryJobConfiguration(InventoryJobConfiguration co
inventoryParser.Capability = config.Capability;

// JobProperties
JobProperties jobProperties = JsonConvert.DeserializeObject<JobProperties>(config.CertificateStoreDetails.Properties, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate });
JobProperties jobProperties = JsonConvert.DeserializeObject<JobProperties>(config.CertificateStoreDetails?.Properties ?? "{}", new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Populate });
inventoryParser.JobConfigurationProperties = jobProperties;

// PreviousInventoryItem
Expand Down
17 changes: 12 additions & 5 deletions IISU/PSHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Remoting;
Expand Down Expand Up @@ -282,8 +283,9 @@ public void Terminate()
PS.Invoke();
CheckErrors();
}
catch (Exception)
catch (Exception ex)
{
_logger.LogDebug($"Error while removing PSSession: {ex.Message}");
}
}

Expand All @@ -294,18 +296,23 @@ public void Terminate()
File.Delete(tempKeyFilePath);
_logger.LogTrace($"Temporary KeyFilePath deleted: {tempKeyFilePath}");
}
catch (Exception)
catch (FileNotFoundException)
{
_logger.LogTrace($"Temporary KeyFilePath was not found: {tempKeyFilePath}");
}
catch (Exception ex)
{
_logger.LogError($"Error while deleting KeyFilePath.");
_logger.LogDebug($"Error while deleting KeyFilePath: {ex.Message}");
}
}

try
{
PS.Runspace.Close();
}
catch (Exception)
catch (Exception ex)
{
_logger.LogDebug($"Error while attempting to close the PowerShell Runspace: {ex.Message}");
}

PS.Dispose();
Expand Down Expand Up @@ -580,7 +587,7 @@ private string createPrivateKeyFile()
_logger.LogTrace($"Created temporary KeyFilePath: {tmpFile}, writing bytes.");

File.WriteAllText(tmpFile, formatPrivateKey(serverPassword));

if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
_logger.LogTrace($"Changing permissions on Windows temp file: {tmpFile}.");
Expand Down
Loading
Loading