Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Commit

Permalink
Initial implementation of X509Certificates, HttpClient, and SslStream…
Browse files Browse the repository at this point in the history
… for macOS (#16445)

Broken by this change:
* A lot of TLS CipherSuites have no metadata defined.
* macOS does not support version skipping in TLS.  So `Tls | Tls12` is an invalid choice.

In this change:
General:
* All OSStatus related exceptions now look up the error message.

X509Certificates:
* X509Certificate moves to using SecCertificateRef from OpenSSL's X509.
* X509 metadata comes from a managed reader after being loaded by Security.framework,
due to the significant amount of data that has no public export in Apple's libraries.
* Significant code was factored out to be shared by OpenSSL and Apple implementations for X500DistinguishedName and X509Certficate2Collection.Find.
* Loading a PFX (or, rather, the private keys from a PFX) via Apple's platform
requires importing into a Keychain, and a Keychain requires a file on disk.
A temporary keychain is created during cert loading and erased when safe.
Like the perphemeral key load on Windows this can leak files due to
abnormal program termination.
* The X.509 My store for CurrentUser and LocalMachine are the default (user) and System keychains.
* The X.509 Root store is an interpretation of the Apple SecTrustSettings data.
* The X.509 Disallowed store hasn't been implemented yet, but should be a very small change.
* Other X.509 stores cannot be created due to keychain complexity.

HttpClient:
* Initialization no longer wakes up OpenSSL

SslStream:
* New implementation based on Apple SecureTransport.
* Currently has support for SNI (for AuthenticateAsClient)
  • Loading branch information
bartonjs committed Mar 1, 2017
1 parent 7bce72c commit 3b19899
Show file tree
Hide file tree
Showing 105 changed files with 9,391 additions and 1,210 deletions.
55 changes: 55 additions & 0 deletions src/Common/src/Interop/OSX/Interop.CoreFoundation.CFArray.cs
@@ -0,0 +1,55 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Runtime.InteropServices;

using Microsoft.Win32.SafeHandles;

// Declared as signed long, which has sizeof(void*) on OSX.
using CFIndex=System.IntPtr;

internal static partial class Interop
{
internal static partial class CoreFoundation
{
[DllImport(Libraries.CoreFoundationLibrary, EntryPoint = "CFArrayGetCount")]
private static extern CFIndex _CFArrayGetCount(SafeCFArrayHandle cfArray);

// Follows the "Get" version of the "Create" rule, so needs to return an IntPtr to
// prevent CFRelease from being called on the SafeHandle close.
[DllImport(Libraries.CoreFoundationLibrary, EntryPoint = "CFArrayGetValueAtIndex")]
private static extern IntPtr CFArrayGetValueAtIndex(SafeCFArrayHandle cfArray, CFIndex index);

internal static long CFArrayGetCount(SafeCFArrayHandle cfArray)
{
return _CFArrayGetCount(cfArray).ToInt64();
}

internal static IntPtr CFArrayGetValueAtIndex(SafeCFArrayHandle cfArray, int index)
{
return CFArrayGetValueAtIndex(cfArray, new CFIndex(index));
}
}
}

namespace Microsoft.Win32.SafeHandles
{
internal sealed class SafeCFArrayHandle : SafeHandle
{
internal SafeCFArrayHandle()
: base(IntPtr.Zero, ownsHandle: true)
{
}

protected override bool ReleaseHandle()
{
Interop.CoreFoundation.CFRelease(handle);
SetHandle(IntPtr.Zero);
return true;
}

public override bool IsInvalid => handle == IntPtr.Zero;
}
}
5 changes: 5 additions & 0 deletions src/Common/src/Interop/OSX/Interop.CoreFoundation.CFData.cs
Expand Up @@ -58,6 +58,11 @@ internal SafeCFDataHandle()
{
}

internal SafeCFDataHandle(IntPtr handle, bool ownsHandle)
: base(handle, ownsHandle)
{
}

protected override bool ReleaseHandle()
{
Interop.CoreFoundation.CFRelease(handle);
Expand Down
66 changes: 66 additions & 0 deletions src/Common/src/Interop/OSX/Interop.CoreFoundation.CFDate.cs
@@ -0,0 +1,66 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

using Microsoft.Win32.SafeHandles;

using CFAbsoluteTime=System.Double;

internal static partial class Interop
{
internal static partial class CoreFoundation
{
// https://developer.apple.com/reference/corefoundation/cfabsolutetime
private static readonly DateTime s_cfDateEpoch = new DateTime(2001, 1, 1, 0, 0, 0, DateTimeKind.Utc);

[DllImport(Libraries.CoreFoundationLibrary)]
private static extern SafeCFDateHandle CFDateCreate(IntPtr zero, CFAbsoluteTime at);

internal static SafeCFDateHandle CFDateCreate(DateTime date)
{
Debug.Assert(
date.Kind != DateTimeKind.Unspecified,
"DateTimeKind.Unspecified should be specified to Local or UTC by the caller");

// UTC stays unchanged, Local is changed.
// Unspecified gets treated as Local (which may or may not be desired).
DateTime utcDate = date.ToUniversalTime();

double epochDeltaSeconds = (utcDate - s_cfDateEpoch).TotalSeconds;

SafeCFDateHandle cfDate = CFDateCreate(IntPtr.Zero, epochDeltaSeconds);

if (cfDate.IsInvalid)
{
cfDate.Dispose();
throw new OutOfMemoryException();
}

return cfDate;
}
}
}

namespace Microsoft.Win32.SafeHandles
{
internal sealed class SafeCFDateHandle : SafeHandle
{
internal SafeCFDateHandle()
: base(IntPtr.Zero, ownsHandle: true)
{
}

protected override bool ReleaseHandle()
{
Interop.CoreFoundation.CFRelease(handle);
SetHandle(IntPtr.Zero);
return true;
}

public override bool IsInvalid => handle == IntPtr.Zero;
}
}
27 changes: 26 additions & 1 deletion src/Common/src/Interop/OSX/Interop.CoreFoundation.cs
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

using Microsoft.Win32.SafeHandles;
Expand Down Expand Up @@ -50,7 +51,21 @@ private enum CFStringBuiltInEncodings : uint
IntPtr allocator,
string str,
CFStringBuiltInEncodings encoding);


/// <summary>
/// Creates a CFStringRef from a 8-bit String object. Follows the "Create Rule" where if you create it, you delete it.
/// </summary>
/// <param name="allocator">Should be IntPtr.Zero</param>
/// <param name="str">The string to get a CFStringRef for</param>
/// <param name="encoding">The encoding of the str variable. This should be UTF 8 for OS X</param>
/// <returns>Returns a pointer to a CFString on success; otherwise, returns IntPtr.Zero</returns>
/// <remarks>For *nix systems, the CLR maps ANSI to UTF-8, so be explicit about that</remarks>
[DllImport(Interop.Libraries.CoreFoundationLibrary, CharSet = CharSet.Ansi)]
private static extern SafeCreateHandle CFStringCreateWithCString(
IntPtr allocator,
IntPtr str,
CFStringBuiltInEncodings encoding);

/// <summary>
/// Creates a CFStringRef from a 8-bit String object. Follows the "Create Rule" where if you create it, you delete it.
/// </summary>
Expand All @@ -61,6 +76,16 @@ internal static SafeCreateHandle CFStringCreateWithCString(string str)
return CFStringCreateWithCString(IntPtr.Zero, str, CFStringBuiltInEncodings.kCFStringEncodingUTF8);
}

/// <summary>
/// Creates a CFStringRef from a 8-bit String object. Follows the "Create Rule" where if you create it, you delete it.
/// </summary>
/// <param name="utf8str">The string to get a CFStringRef for</param>
/// <returns>Returns a valid SafeCreateHandle to a CFString on success; otherwise, returns an invalid SafeCreateHandle</returns>
internal static SafeCreateHandle CFStringCreateWithCString(IntPtr utf8str)
{
return CFStringCreateWithCString(IntPtr.Zero, utf8str, CFStringBuiltInEncodings.kCFStringEncodingUTF8);
}

/// <summary>
/// Creates a pointer to an unmanaged CFArray containing the input values. Follows the "Create Rule" where if you create it, you delete it.
/// </summary>
Expand Down
Expand Up @@ -13,7 +13,6 @@ internal static partial class AppleCrypto
{
internal const string CCCryptorStatus = "CCCryptorStatus";
internal const string CCRNGStatus = "CCRNGStatus";
internal const string OSStatus = "OSStatus";

internal static Exception CreateExceptionForCCError(int errorCode, string errorType)
{
Expand Down

0 comments on commit 3b19899

Please sign in to comment.