Fixed custom root certificate validation on Linux

This commit is contained in:
Thorsten Sommer 2026-07-15 14:31:43 +02:00
parent 8ef6cda901
commit 8ba78e3263
Signed by untrusted user who does not match committer: tsommer
GPG Key ID: 371BBA77A02C0108
2 changed files with 41 additions and 4 deletions

View File

@ -359,10 +359,19 @@ public static class ExternalHttpClientTimeout
if (sslPolicyErrors is SslPolicyErrors.None)
return true;
if (sslPolicyErrors is not SslPolicyErrors.RemoteCertificateChainErrors || certificate is null)
return false;
var host = ReadRequestHost(request);
if (certificate is null)
{
LOGGER.Value.LogError($"Rejected external HTTPS certificate for '{HostForLog(host)}' because the TLS stack did not provide a server certificate. TLS policy errors: {sslPolicyErrors}.");
return false;
}
if (sslPolicyErrors is not SslPolicyErrors.RemoteCertificateChainErrors)
{
LOGGER.Value.LogError($"Rejected external HTTPS certificate for '{HostForLog(host)}' because custom root certificates can only resolve certificate chain trust errors. TLS policy errors: {sslPolicyErrors}.");
return false;
}
if (trustPolicy is ExternalHttpTrustPolicy.SYSTEM_TRUST_ONLY)
{
LOGGER.Value.LogError($"Rejected external HTTPS certificate for '{HostForLog(host)}' because this request requires system trust only. Configured custom root certificates are not allowed for this request.");
@ -383,6 +392,10 @@ public static class ExternalHttpClientTimeout
customChain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
customChain.ChainPolicy.CustomTrustStore.AddRange(customRootCertificateCache.Certificates);
customChain.ChainPolicy.ApplicationPolicy.Add(new Oid(TLS_SERVER_AUTHENTICATION_EKU_OID));
// Match the .NET 9 HttpClient default used for the initial system-trust validation.
// Hostname, signature, validity, EKU, and root trust checks remain enabled.
customChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
if (originalChain is not null)
{
@ -398,6 +411,8 @@ public static class ExternalHttpClientTimeout
var isValid = customChain.Build(serverCertificate);
if (isValid)
LogCustomRootCertificateAccepted(request);
else
LogCustomRootCertificateValidationFailure(request, sslPolicyErrors, customChain);
return isValid;
}
@ -459,6 +474,27 @@ public static class ExternalHttpClientTimeout
LOGGER.Value.LogWarning($"Accepted an external HTTPS certificate for '{host}' using configured custom root certificates.");
}
private static void LogCustomRootCertificateValidationFailure(HttpRequestMessage request, SslPolicyErrors sslPolicyErrors, X509Chain chain)
{
var chainStatuses = FormatChainStatusesForLog(chain.ChainStatus);
var elementStatuses = chain.ChainElements
.Cast<X509ChainElement>()
.Select((element, index) => $"element {index}: {FormatChainStatusesForLog(element.ChainElementStatus)}")
.ToList();
var host = ReadRequestHost(request);
LOGGER.Value.LogError($"Rejected external HTTPS certificate for '{HostForLog(host)}' after validation with configured custom root certificates. TLS policy errors: {sslPolicyErrors}. Chain statuses: {chainStatuses}. Chain element statuses: {string.Join("; ", elementStatuses)}");
}
private static string FormatChainStatusesForLog(IEnumerable<X509ChainStatus> statuses)
{
var formattedStatuses = statuses
.Select(status => $"{status.Status} ({status.StatusInformation.Trim()})")
.ToList();
return formattedStatuses.Count == 0
? "none"
: string.Join(", ", formattedStatuses);
}
private static string ReadRequestHost(HttpRequestMessage request)
{
var host = request.RequestUri?.IdnHost;
@ -484,4 +520,4 @@ public static class ExternalHttpClientTimeout
string CacheKey,
X509Certificate2Collection Certificates,
ExternalHttpCustomRootCertificateState State);
}
}

View File

@ -5,6 +5,7 @@
- Improved the "My Tasks Assistant": you can now provide one or more documents in addition to text or use documents alone when asking to identify tasks.
- Improved update guidance for Flatpak installations and added an enterprise option that lets organizations manage updates entirely through their IT department.
- Fixed an issue that could leave AI Studio unresponsive after waking the computer from sleep. Yes, we know this was an annoying bug, and we apologize for the inconvenience.
- Fixed connections to internal HTTPS services and enterprise configuration servers that use organization-provided root certificates on Linux.
- Fixed enterprise configuration plugins from Windows-created ZIP files may not load correctly on Linux when the ZIP contained plugin files inside a folder.
- Fixed voice recording and transcription on Linux.
- Fixed being able to switch document analysis policies while an analysis or media transcription was still in progress.