Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Net;
using System.Net.Security;
using System.Runtime.InteropServices;
using System.Security.Authentication;
Expand Down Expand Up @@ -135,7 +136,8 @@ internal static extern int SslSetIoCallbacks(
private static extern int AppleCryptoNative_SslIsHostnameMatch(
SafeSslHandle handle,
SafeCreateHandle cfHostname,
SafeCFDateHandle cfValidTime);
SafeCFDateHandle cfValidTime,
out int pOSStatus);

[DllImport(Interop.Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_SslShutdown")]
internal static extern int SslShutdown(SafeSslHandle sslHandle);
Expand Down Expand Up @@ -371,7 +373,7 @@ internal static byte[] SslGetAlpnSelected(SafeSslHandle ssl)
}
}

public static bool SslCheckHostnameMatch(SafeSslHandle handle, string hostName, DateTime notBefore)
public static bool SslCheckHostnameMatch(SafeSslHandle handle, string hostName, DateTime notBefore, out int osStatus)
{
int result;
// The IdnMapping converts Unicode input into the IDNA punycode sequence.
Expand All @@ -388,7 +390,7 @@ public static bool SslCheckHostnameMatch(SafeSslHandle handle, string hostName,
using (SafeCFDateHandle cfNotBefore = CoreFoundation.CFDateCreate(notBefore))
using (SafeCreateHandle cfHostname = CoreFoundation.CFStringCreateWithCString(matchName))
{
result = AppleCryptoNative_SslIsHostnameMatch(handle, cfHostname, cfNotBefore);
result = AppleCryptoNative_SslIsHostnameMatch(handle, cfHostname, cfNotBefore, out osStatus);
}

switch (result)
Expand All @@ -398,6 +400,8 @@ public static bool SslCheckHostnameMatch(SafeSslHandle handle, string hostName,
case 1:
return true;
default:
if (NetEventSource.IsEnabled)
NetEventSource.Error(null, $"AppleCryptoNative_SslIsHostnameMatch returned '{result}' for '{hostName}'");
Debug.Fail($"AppleCryptoNative_SslIsHostnameMatch returned {result}");
throw new SslException();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -354,10 +354,14 @@ PAL_TlsIo AppleCryptoNative_SslRead(SSLContextRef sslContext, uint8_t* buf, uint
return OSStatusToPAL_TlsIo(status);
}

int32_t AppleCryptoNative_SslIsHostnameMatch(SSLContextRef sslContext, CFStringRef cfHostname, CFDateRef notBefore)
int32_t AppleCryptoNative_SslIsHostnameMatch(SSLContextRef sslContext, CFStringRef cfHostname, CFDateRef notBefore, int32_t* pOSStatus)
{
if (sslContext == NULL || notBefore == NULL)
if (pOSStatus != NULL)
*pOSStatus = noErr;

if (sslContext == NULL || notBefore == NULL || pOSStatus == NULL)
return -1;

if (cfHostname == NULL)
return -2;

Expand All @@ -380,6 +384,7 @@ int32_t AppleCryptoNative_SslIsHostnameMatch(SSLContextRef sslContext, CFStringR
if (osStatus != noErr)
{
CFRelease(certs);
*pOSStatus = osStatus;
return -5;
}

Expand Down Expand Up @@ -430,9 +435,40 @@ int32_t AppleCryptoNative_SslIsHostnameMatch(SSLContextRef sslContext, CFStringR
osStatus = SecTrustEvaluate(trust, &trustResult);
#pragma clang diagnostic pop

if (osStatus == noErr && trustResult != kSecTrustResultUnspecified && trustResult != kSecTrustResultProceed)
{
// If evaluation succeeded but result is not trusted try to get details.
CFDictionaryRef detailsAndStuff = SecTrustCopyResult(trust);

if (detailsAndStuff != NULL)
{
CFArrayRef details = CFDictionaryGetValue(detailsAndStuff, CFSTR("TrustResultDetails"));

if (details != NULL && CFArrayGetCount(details) > 0)
{
CFArrayRef statusCodes = CFDictionaryGetValue(CFArrayGetValueAtIndex(details,0), CFSTR("StatusCodes"));

OSStatus status = 0;
// look for first failure to keep it simple. Normally, there will be exactly one.
for (int i = 0; i < CFArrayGetCount(statusCodes); i++)
{
CFNumberGetValue(CFArrayGetValueAtIndex(statusCodes, i), kCFNumberSInt32Type, &status);
if (status != noErr)
{
*pOSStatus = status;
break;
}
}
}

CFRelease(detailsAndStuff);
}
}

if (osStatus != noErr)
{
ret = -7;
*pOSStatus = osStatus;
}
else if (trustResult == kSecTrustResultUnspecified || trustResult == kSecTrustResultProceed)
{
Expand All @@ -447,6 +483,10 @@ int32_t AppleCryptoNative_SslIsHostnameMatch(SSLContextRef sslContext, CFStringR
ret = -8;
}
}
else
{
*pOSStatus = osStatus;
}

if (trust != NULL)
CFRelease(trust);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ the certificate being expired (or not yet valid).
Returns 1 on match, 0 on mismatch, any other value indicates an invalid state.
*/
DLLEXPORT int32_t
AppleCryptoNative_SslIsHostnameMatch(SSLContextRef sslContext, CFStringRef cfHostname, CFDateRef notBefore);
AppleCryptoNative_SslIsHostnameMatch(SSLContextRef sslContext, CFStringRef cfHostname, CFDateRef notBefore, int32_t* pOSStatus);

/*
Generate a TLS Close alert to terminate the session.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,12 @@ internal static SslPolicyErrors VerifyCertificateProperties(
{
SafeDeleteSslContext sslContext = (SafeDeleteSslContext)securityContext;

if (!Interop.AppleCrypto.SslCheckHostnameMatch(sslContext.SslContext, hostName, remoteCertificate.NotBefore))
if (!Interop.AppleCrypto.SslCheckHostnameMatch(sslContext.SslContext, hostName, remoteCertificate.NotBefore, out int osStatus))
{
errors |= SslPolicyErrors.RemoteCertificateNameMismatch;

if (NetEventSource.IsEnabled)
NetEventSource.Error(sslContext, $"Cert name validation for '{hostName}' failed with status '{osStatus}'");
}
}
}
Expand Down