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

Initial implementation of X509Certificates, HttpClient, and SslStream for macOS #15970

Closed
wants to merge 4 commits into from

Conversation

bartonjs
Copy link
Member

@bartonjs bartonjs commented Feb 8, 2017

Broken by this change:

  • X509Store has no implementation yet on macOS.
  • Exporting as a PFX is not yet implemented on macOS.
  • 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.

HttpClient:

  • Initialization no longer wakes up OpenSSL

SslStream:

  • New implementation based on Apple SecureTransport.
  • Currently has support for SNI (for AuthenticateAsClient)

@bartonjs
Copy link
Member Author

bartonjs commented Feb 8, 2017

This is a bit of a Work In Progress. But I'd gone on long enough that I wanted to get something out for review.

X509Certificates should fully pass tests. HttpClient will probably explode due to a TODO I left myself, and SslClient I'm trying to see what places the official tests are having trouble with in case my machine is behaving oddly.

My current remaining cleanup notes (aside from "implement deferred functionality"):

  • Do a full headers scrub (add/document the shim functions)
  • Remove all the printfs (they're all #defined away, but get rid of them
  • Remove ForceLoadPrivateKey (it should be vestigial, but something breaks when I do, need to investigate, since it means there's a problem somewhere else)
  • Curl VERIFYPEER/VERIFYHOST problem.
  • Fix comments that say REVIEW
  • ChainPal::Execute => "Handle this failure"
  • Two different TLS Protocol ID enums are in use. Oops

Part of what I'm looking for at this juncture is macro-comments; things that might take a day or two of restructuring to fix. Micro-comments are also quite welcome.

@karelz karelz added this to the 2.0.0 milestone Feb 9, 2017
@bartonjs
Copy link
Member Author

bartonjs commented Feb 9, 2017

@dotnet-bot Test Outerloop OSX Debug please

Copy link
Member

@steveharter steveharter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursory review done. Will do a detailed review when we pull things back

return -2;
}

//*pOSStatus = SecTrustSetNetworkFetchAllowed(chain, allowNetwork);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debugging?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, good catch.

}

CFDictionaryRef detailsAndStuff = SecTrustCopyResult(chain);
CFTypeRef detailsPtr = CFDictionaryGetValue(detailsAndStuff, CFSTR("TrustResultDetails"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No error checking against detailsAndStuff and detailsPtr here

CFRetain(details);
CFRelease(detailsAndStuff);

printf("Just the details:\n");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More debugging? (I'll stop mentioning that regarding printfs)

PAL_X509ChainCtlNotTimeValid = 0x00020000,
PAL_X509ChainCtlNotSignatureValid = 0x00040000,
PAL_X509ChainCtlNotValidForUsage = 0x00080000,
PAL_X509ChainOfflineRevocation = 0x01000000,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Went from 80000 to 1000000 (an extra 0)?

PAL_X509ChainNoIssuanceChainPolicy = 0x02000000,
PAL_X509ChainExplicitDistrust = 0x04000000,
PAL_X509ChainHasNotSupportedCriticalExtension = 0x08000000,
PAL_X509ChainHasWeakSignature = 0x00100000,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PAL_X509ChainHasWeakSignature out of order or need extra 0

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Values and ordering should be the same as they are in X509ChainStatusFlags.

{
// This is as likely as anything. No error conditions are defined for
// the OS function, and our shim only adds a NULL if isServer isn't a normalized bool.
throw new OutOfMemoryException();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like we can have something better.... Just raw Exception?


private unsafe int WriteToConnection(void* connection, byte* data, void** dataLength)
{
ulong toWrite = (ulong)*dataLength;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

verify connection == _toConnection?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

connection will be NULL since we don't give them one. (It's their callback's way of maintaining object notions; but we got that captured in the delegate)

X509ChainElementCollection elements = chain.ChainElements;

// We need to leave off the EE (first) and root (last) certificate from the intermediates.
X509Certificate2[] intermediateCerts = elements.Count < 3 ?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does <3 guarantee that we need to remove the first and last? Vs. 4, 5, etc.?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If 1, it's just the first cert.
If 2, it's a cert directly from a root.

In both 1 and 2 we've removed the items we care about by being Array.Empty. At 3 we have things left over after we remove up to two.

And, really, it's because we're about to do Count - 2 as an array size.

@@ -0,0 +1,363 @@
// Licensed to the .NET Foundation under one or more agreements.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't review. Refactoring of OpenSslCertificateFinder.cs ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep

@@ -2,12 +2,14 @@
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW I'm working on cleaning up the .props, test .csproj because most of our newer tests are not running due build changes and renaming of netstandard :(

… for macOS

Broken by this change:
* X509Store has no implementation yet on macOS.
* Exporting as a PFX is not yet implemented on macOS.
* 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.

HttpClient:
* Initialization no longer wakes up OpenSSL

SslStream:
* New implementation based on Apple SecureTransport.
* Currently has support for SNI (for AuthenticateAsClient)
Arg_InvalidHandle takes an argument name.
Cryptography_OpenInvalidHandle just describes the state of things.
@bartonjs
Copy link
Member Author

Okay, it seems that Sierra (10.12) fixed some bugs in SecTrustEvaluate. On El Capitan (10.11) it segfaults 50% of the time when the evaluation doesn't involve the SSL policy (the other 50% it reports a corrupt keychain when no keychain should be involved).

Hmm.

@bartonjs
Copy link
Member Author

Enough changed that I made a completely new PR (#16445).

To get an idea of the changes: bartonjs/corefx@apple_x509_part1...bartonjs:apple_x509_part2

@bartonjs bartonjs closed this Feb 24, 2017
@bartonjs bartonjs removed their assignment Mar 14, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
4 participants