Skip to content

Commit

Permalink
Run ShouldContinue on the main thread in Connect-Xenserver.cs
Browse files Browse the repository at this point in the history
In .NET Core, the ValidateServerCertificate call runs in a separate thread.
This causes an InvalidOperationException when calling ShouldContinue, which must be called within the same thread as ProcessRecord.

Signed-off-by: Danilo Del Busso <Danilo.Del.Busso@citrix.com>
  • Loading branch information
danilo-delbusso committed Sep 5, 2022
1 parent 9f65297 commit a2824ec
Showing 1 changed file with 91 additions and 14 deletions.
105 changes: 91 additions & 14 deletions ocaml/sdk-gen/powershell/autogen/src/Connect-XenServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@
using System.Collections.Generic;
using System.Management.Automation;
using System.Net;
using System.Net.Http;
using System.Net.Security;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;

using XenAPI;

namespace Citrix.XenServer.Commands
Expand Down Expand Up @@ -202,23 +202,61 @@ protected override void ProcessRecord()
{
ThrowTerminatingError(new ErrorRecord(f, "", ErrorCategory.InvalidArgument, Url[i])
{
ErrorDetails = new ErrorDetails(string.Format("The host you are trying to connect to is a slave. To make regular API calls, please connect to the master host (IP address: {0}).",
f.ErrorDescription[1]))
ErrorDetails = new ErrorDetails($"The host you are trying to connect to is a slave. To make regular API calls, please connect to the master host (IP address: {f.ErrorDescription[1]}).")
});
}
else
{
throw;
}
}
catch (WebException e)
{
if (e.InnerException is HttpRequestException requestException)
{
if (requestException.InnerException is CertificateChangeException certificateChangeException)
{
if (Force || ShouldContinue(string.Format(CERT_CHANGED, certificateChangeException.Fingerprint, certificateChangeException.OldFingerprint, certificateChangeException.Trusted), CERT_HAS_CHANGED_CAPTION))
{
AddCertificate(certificateChangeException.Hostname, certificateChangeException.Fingerprint);
i--;
continue;
}
else
{
ThrowTerminatingError(new ErrorRecord(certificateChangeException, "", ErrorCategory.AuthenticationError, Url[i])
{
ErrorDetails = new ErrorDetails($"New certificate fingerprint rejected. ({certificateChangeException.Fingerprint} - {certificateChangeException.Hostname}).")
});
}
}
else if (requestException.InnerException is CertificateNotFoundException certificateNotFoundException)
{
if (Force || ShouldContinue(string.Format(CERT_FOUND, certificateNotFoundException.Fingerprint, certificateNotFoundException.Trusted), CERT_FOUND_CAPTION))
{
AddCertificate(certificateNotFoundException.Hostname, certificateNotFoundException.Fingerprint);
i--;
continue;
}
else
{
ThrowTerminatingError(new ErrorRecord(e, "", ErrorCategory.AuthenticationError, Url[i])
{
ErrorDetails = new ErrorDetails($"Certificate fingerprint rejected. ({certificateNotFoundException.Fingerprint} - {certificateNotFoundException.Hostname}).")
});
}
}
}

throw;
}
}
else
{
session = new Session(Url[i], OpaqueRef[i]);
}

session.Tag = Creds;
session.opaque_ref = session.opaque_ref;
sessions[session.opaque_ref] = session;
newSessions[session.opaque_ref] = session;

Expand Down Expand Up @@ -255,6 +293,13 @@ protected override void ProcessRecord()

#endregion

private void AddCertificate(string hostname, string fingerprint)
{
var certificates = CommonCmdletFunctions.LoadCertificates();
certificates[hostname] = fingerprint;
CommonCmdletFunctions.SaveCertificates(certificates);
}

private readonly object certificateValidationLock = new object();

private bool ValidateServerCertificate(
Expand All @@ -279,26 +324,28 @@ private bool ValidateServerCertificate(
? CERT_TRUSTED : CERT_NOT_TRUSTED;

var certificates = CommonCmdletFunctions.LoadCertificates();
bool ok;
bool ok = false;

if (certificates.ContainsKey(hostname))
{
string fingerprint_old = certificates[hostname];
if (fingerprint_old == fingerprint)
return true;

ok = Force || ignoreChanged || ShouldContinue(string.Format(CERT_CHANGED, fingerprint, fingerprint_old, trusted), CERT_HAS_CHANGED_CAPTION);
ok = Force || ignoreChanged;
if (!ok)
{
throw new CertificateChangeException(fingerprint, fingerprint_old, trusted, hostname);
}
}
else
{
ok = Force || ignoreNew || ShouldContinue(string.Format(CERT_FOUND, fingerprint, trusted), CERT_FOUND_CAPTION);
}

if (ok)
{
certificates[hostname] = fingerprint;
CommonCmdletFunctions.SaveCertificates(certificates);
ok = Force || ignoreNew;
if (!ok)
{
throw new CertificateNotFoundException(fingerprint, trusted, hostname);
}
}
AddCertificate(hostname, fingerprint);
return ok;
}
}
Expand All @@ -316,4 +363,34 @@ private bool VerifyInAllStores(X509Certificate2 certificate2)
}
}
}

public class CertificateChangeException : Exception
{
public readonly string Fingerprint;
public readonly string OldFingerprint;
public readonly string Trusted;
public readonly string Hostname;

public CertificateChangeException(string fingerprint, string oldFingerprint, string trusted, string hostname)
{
Fingerprint = fingerprint;
OldFingerprint = oldFingerprint;
Trusted = trusted;
Hostname = hostname;
}
}

public class CertificateNotFoundException : Exception
{
public readonly string Fingerprint;
public readonly string Trusted;
public readonly string Hostname;

public CertificateNotFoundException(string fingerprint, string trusted, string hostname)
{
Fingerprint = fingerprint;
Trusted = trusted;
Hostname = hostname;
}
}
}

0 comments on commit a2824ec

Please sign in to comment.