private void CreateStreamAndStartWatcher() { Debug.Assert(_eventStream.IsInvalid); Debug.Assert(_watcherRunLoop == IntPtr.Zero); Debug.Assert(_callback == null); // Make sure we only do this if there is a valid directory if (String.IsNullOrEmpty(_directory) == false) { _fullDirectory = System.IO.Path.GetFullPath(_directory); // Get the path to watch and verify we created the CFStringRef SafeCreateHandle path = Interop.CoreFoundation.CFStringCreateWithCString(_fullDirectory); if (path.IsInvalid) { throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, true); } // Take the CFStringRef and put it into an array to pass to the EventStream SafeCreateHandle arrPaths = Interop.CoreFoundation.CFArrayCreate(new CFStringRef[1] { path.DangerousGetHandle() }, 1); if (arrPaths.IsInvalid) { path.Dispose(); throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, true); } // Create the callback for the EventStream _callback = new Interop.EventStream.FSEventStreamCallback(FileSystemEventCallback); // Make sure the OS file buffer(s) are fully flushed so we don't get events from cached I/O Interop.libc.sync(); // Create the event stream for the path and tell the stream to watch for file system events. _eventStream = Interop.EventStream.FSEventStreamCreate( _callback, arrPaths, Interop.EventStream.kFSEventStreamEventIdSinceNow, 0.0f, EventStreamFlags); if (_eventStream.IsInvalid) { arrPaths.Dispose(); path.Dispose(); throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, true); } // Create and start our watcher thread then wait for the thread to initialize and start // the RunLoop. We wait for that to prevent this function from returning before the RunLoop // has a chance to start so that any callers won't race with the background thread's initialization // and calling Stop, which would attempt to stop a RunLoop that hasn't started yet. _watcherThread = new Thread(new ThreadStart(WatchForFileSystemEventsThreadStart)); _watcherThread.Start(); _runLoopStartedEvent.WaitOne(); } }
internal void Start() { // Get the path to watch and verify we created the CFStringRef SafeCreateHandle path = Interop.CoreFoundation.CFStringCreateWithCString(_fullDirectory); if (path.IsInvalid) { throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, true); } // Take the CFStringRef and put it into an array to pass to the EventStream SafeCreateHandle arrPaths = Interop.CoreFoundation.CFArrayCreate(new CFStringRef[1] { path.DangerousGetHandle() }, 1); if (arrPaths.IsInvalid) { path.Dispose(); throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, true); } // Create the callback for the EventStream if it wasn't previously created for this instance. if (_callback == null) { _callback = new Interop.EventStream.FSEventStreamCallback(FileSystemEventCallback); } // Make sure the OS file buffer(s) are fully flushed so we don't get events from cached I/O Interop.Sys.Sync(); // Create the event stream for the path and tell the stream to watch for file system events. _eventStream = Interop.EventStream.FSEventStreamCreate( _callback, arrPaths, Interop.EventStream.kFSEventStreamEventIdSinceNow, 0.0f, EventStreamFlags); if (_eventStream.IsInvalid) { arrPaths.Dispose(); path.Dispose(); throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, true); } // Create and start our watcher thread then wait for the thread to initialize and start // the RunLoop. We wait for that to prevent this function from returning before the RunLoop // has a chance to start so that any callers won't race with the background thread's initialization // and calling Stop, which would attempt to stop a RunLoop that hasn't started yet. var runLoopStarted = new ManualResetEventSlim(); new Thread(WatchForFileSystemEventsThreadStart) { IsBackground = true }.Start(runLoopStarted); runLoopStarted.Wait(); }
private static void RunLoopThreadStart() { Debug.Assert(s_runLoop == IntPtr.Zero); s_runLoop = Interop.RunLoop.CFRunLoopGetCurrent(); Interop.RunLoop.CFRunLoopAddSource( s_runLoop, s_runLoopSource.DangerousGetHandle(), Interop.RunLoop.kCFRunLoopDefaultMode.DangerousGetHandle()); s_runLoopStartedEvent.Set(); Interop.RunLoop.CFRunLoopRun(); Interop.RunLoop.CFRunLoopRemoveSource( s_runLoop, s_runLoopSource.DangerousGetHandle(), Interop.RunLoop.kCFRunLoopDefaultMode.DangerousGetHandle()); s_runLoop = IntPtr.Zero; s_runLoopSource.Dispose(); s_runLoopSource = null; s_dynamicStoreRef.Dispose(); s_dynamicStoreRef = null; s_runLoopEndedEvent.Set(); }
internal static byte[] X509ExportPfx(IntPtr[] certHandles, SafePasswordHandle exportPassword) { SafeCreateHandle cfPassphrase = s_emptyExportString; bool releasePassword = false; try { if (!exportPassword.IsInvalid) { exportPassword.DangerousAddRef(ref releasePassword); IntPtr passwordHandle = exportPassword.DangerousGetHandle(); if (passwordHandle != IntPtr.Zero) { cfPassphrase = CoreFoundation.CFStringCreateWithCString(passwordHandle); } } return(X509Export(X509ContentType.Pkcs12, cfPassphrase, certHandles)); } finally { if (releasePassword) { exportPassword.DangerousRelease(); } if (cfPassphrase != s_emptyExportString) { cfPassphrase.Dispose(); } } }
internal static SafeSecCertificateHandle X509ImportCertificate( ReadOnlySpan <byte> bytes, X509ContentType contentType, SafePasswordHandle importPassword, out SafeSecIdentityHandle identityHandle) { SafeCreateHandle?cfPassphrase = null; bool releasePassword = false; try { if (!importPassword.IsInvalid) { importPassword.DangerousAddRef(ref releasePassword); cfPassphrase = CoreFoundation.CFStringCreateFromSpan(importPassword.DangerousGetSpan()); } return(X509ImportCertificate( bytes, contentType, cfPassphrase, out identityHandle)); } finally { if (releasePassword) { importPassword.DangerousRelease(); } cfPassphrase?.Dispose(); } }
internal static unsafe void SslCtxSetAlpnProtos(SafeSslHandle ctx, List <SslApplicationProtocol> protocols) { SafeCreateHandle cfProtocolsRefs = null; SafeCreateHandle[] cfProtocolsArrayRef = null; try { if (protocols.Count == 1 && protocols[0] == SslApplicationProtocol.Http2) { cfProtocolsRefs = s_cfAlpnHttp211Protocols; } else if (protocols.Count == 1 && protocols[0] == SslApplicationProtocol.Http11) { cfProtocolsRefs = s_cfAlpnHttp11Protocols; } else if (protocols.Count == 2 && protocols[0] == SslApplicationProtocol.Http2 && protocols[1] == SslApplicationProtocol.Http11) { cfProtocolsRefs = s_cfAlpnHttp211Protocols; } else { // we did not match common case. This is more expensive path allocating Core Foundation objects. cfProtocolsArrayRef = new SafeCreateHandle[protocols.Count]; IntPtr[] protocolsPtr = new System.IntPtr[protocols.Count]; for (int i = 0; i < protocols.Count; i++) { cfProtocolsArrayRef[i] = CoreFoundation.CFStringCreateWithCString(protocols[i].ToString()); protocolsPtr[i] = cfProtocolsArrayRef[i].DangerousGetHandle(); } cfProtocolsRefs = CoreFoundation.CFArrayCreate(protocolsPtr, (UIntPtr)protocols.Count); } int osStatus; int result = SSLSetALPNProtocols(ctx, cfProtocolsRefs, out osStatus); if (result != 1) { throw CreateExceptionForOSStatus(osStatus); } } finally { if (cfProtocolsArrayRef != null) { for (int i = 0; i < cfProtocolsArrayRef.Length; i++) { cfProtocolsArrayRef[i]?.Dispose(); } cfProtocolsRefs?.Dispose(); } } }
internal static SafeCFArrayHandle X509ImportCollection( ReadOnlySpan <byte> bytes, X509ContentType contentType, SafePasswordHandle importPassword) { SafeCreateHandle cfPassphrase = s_emptyExportString; bool releasePassword = false; SafeCFArrayHandle collectionHandle; int osStatus; try { if (!importPassword.IsInvalid) { importPassword.DangerousAddRef(ref releasePassword); IntPtr passwordHandle = importPassword.DangerousGetHandle(); if (passwordHandle != IntPtr.Zero) { cfPassphrase = CoreFoundation.CFStringCreateWithCString(passwordHandle); } } osStatus = AppleCryptoNative_X509ImportCollection( ref MemoryMarshal.GetReference(bytes), bytes.Length, contentType, cfPassphrase, out collectionHandle); if (osStatus == 0) { return(collectionHandle); } } finally { if (releasePassword) { importPassword.DangerousRelease(); } if (cfPassphrase != s_emptyExportString) { cfPassphrase.Dispose(); } } collectionHandle.Dispose(); throw CreateExceptionForOSStatus(osStatus); }
internal static byte[] SecKeyExport( SafeSecKeyRefHandle key, bool exportPrivate, string password) { SafeCreateHandle exportPassword = exportPrivate ? CoreFoundation.CFStringCreateWithCString(password) : s_nullExportString; int ret; SafeCFDataHandle cfData; int osStatus; try { ret = AppleCryptoNative_SecKeyExport( key, exportPrivate ? 1 : 0, exportPassword, out cfData, out osStatus); } finally { if (exportPassword != s_nullExportString) { exportPassword.Dispose(); } } byte[] exportedData; using (cfData) { if (ret == 0) { throw CreateExceptionForOSStatus(osStatus); } if (ret != 1) { Debug.Fail($"AppleCryptoNative_SecKeyExport returned {ret}"); throw new CryptographicException(); } exportedData = CoreFoundation.CFGetData(cfData); } return(exportedData); }
internal static SafeCFDataHandle SecKeyExportData( SafeSecKeyRefHandle?key, bool exportPrivate, ReadOnlySpan <char> password) { SafeCreateHandle exportPassword = exportPrivate ? CoreFoundation.CFStringCreateFromSpan(password) : s_nullExportString; int ret; SafeCFDataHandle cfData; int osStatus; try { ret = AppleCryptoNative_SecKeyExport( key, exportPrivate ? 1 : 0, exportPassword, out cfData, out osStatus); } finally { if (exportPassword != s_nullExportString) { exportPassword.Dispose(); } } if (ret == 1) { return(cfData); } cfData.Dispose(); if (ret == 0) { throw CreateExceptionForOSStatus(osStatus); } Debug.Fail($"AppleCryptoNative_SecKeyExport returned {ret}"); throw new CryptographicException(); }
private static unsafe void CreateAndStartRunLoop() { Debug.Assert(s_dynamicStoreRef == null); var storeContext = new Interop.SystemConfiguration.SCDynamicStoreContext(); using (SafeCreateHandle storeName = Interop.CoreFoundation.CFStringCreateWithCString("NetworkAddressChange.OSX")) { s_dynamicStoreRef = Interop.SystemConfiguration.SCDynamicStoreCreate( storeName.DangerousGetHandle(), s_storeCallback, &storeContext); } // Notification key string parts. We want to match notification keys // for any kind of IP address change, addition, or removal. using (SafeCreateHandle dynamicStoreDomainStateString = Interop.CoreFoundation.CFStringCreateWithCString("State:")) using (SafeCreateHandle compAnyRegexString = Interop.CoreFoundation.CFStringCreateWithCString("[^/]+")) using (SafeCreateHandle entNetIpv4String = Interop.CoreFoundation.CFStringCreateWithCString("IPv4")) using (SafeCreateHandle entNetIpv6String = Interop.CoreFoundation.CFStringCreateWithCString("IPv6")) { if (dynamicStoreDomainStateString.IsInvalid || compAnyRegexString.IsInvalid || entNetIpv4String.IsInvalid || entNetIpv6String.IsInvalid) { s_dynamicStoreRef.Dispose(); s_dynamicStoreRef = null; throw new NetworkInformationException(SR.net_PInvokeError); } using (SafeCreateHandle ipv4Pattern = Interop.SystemConfiguration.SCDynamicStoreKeyCreateNetworkServiceEntity( dynamicStoreDomainStateString.DangerousGetHandle(), compAnyRegexString.DangerousGetHandle(), entNetIpv4String.DangerousGetHandle())) using (SafeCreateHandle ipv6Pattern = Interop.SystemConfiguration.SCDynamicStoreKeyCreateNetworkServiceEntity( dynamicStoreDomainStateString.DangerousGetHandle(), compAnyRegexString.DangerousGetHandle(), entNetIpv6String.DangerousGetHandle())) using (SafeCreateHandle patterns = Interop.CoreFoundation.CFArrayCreate( new CFStringRef[2] { ipv4Pattern.DangerousGetHandle(), ipv6Pattern.DangerousGetHandle() }, (UIntPtr)2)) { // Try to register our pattern strings with the dynamic store instance. if (patterns.IsInvalid || !Interop.SystemConfiguration.SCDynamicStoreSetNotificationKeys( s_dynamicStoreRef.DangerousGetHandle(), IntPtr.Zero, patterns.DangerousGetHandle())) { s_dynamicStoreRef.Dispose(); s_dynamicStoreRef = null; throw new NetworkInformationException(SR.net_PInvokeError); } // Create a "RunLoopSource" that can be added to our listener thread's RunLoop. s_runLoopSource = Interop.SystemConfiguration.SCDynamicStoreCreateRunLoopSource( s_dynamicStoreRef.DangerousGetHandle(), IntPtr.Zero); } } s_runLoopThread = new Thread(RunLoopThreadStart); s_runLoopThread.Start(); s_runLoopStartedEvent.WaitOne(); // Wait for the new thread to finish initialization. }
internal unsafe void Start() { // Make sure _fullPath doesn't contain a link or alias // since the OS will give back the actual, non link'd or alias'd paths _fullDirectory = Interop.Sys.RealPath(_fullDirectory); if (_fullDirectory == null) { throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, true); } Debug.Assert(string.IsNullOrEmpty(_fullDirectory) == false, "Watch directory is null or empty"); // Normalize the _fullDirectory path to have a trailing slash if (_fullDirectory[_fullDirectory.Length - 1] != '/') { _fullDirectory += "/"; } // Get the path to watch and verify we created the CFStringRef SafeCreateHandle path = Interop.CoreFoundation.CFStringCreateWithCString(_fullDirectory); if (path.IsInvalid) { throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, true); } // Take the CFStringRef and put it into an array to pass to the EventStream SafeCreateHandle arrPaths = Interop.CoreFoundation.CFArrayCreate(new CFStringRef[1] { path.DangerousGetHandle() }, (UIntPtr)1); if (arrPaths.IsInvalid) { path.Dispose(); throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, true); } // Create the callback for the EventStream if it wasn't previously created for this instance. if (_callback == null) { _callback = new Interop.EventStream.FSEventStreamCallback(FileSystemEventCallback); } _context = ExecutionContext.Capture(); // Make sure the OS file buffer(s) are fully flushed so we don't get events from cached I/O Interop.Sys.Sync(); // Create the event stream for the path and tell the stream to watch for file system events. _eventStream = Interop.EventStream.FSEventStreamCreate( _callback, arrPaths, Interop.EventStream.kFSEventStreamEventIdSinceNow, 0.0f, EventStreamFlags); if (_eventStream.IsInvalid) { arrPaths.Dispose(); path.Dispose(); throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, true); } StaticWatcherRunLoopManager.ScheduleEventStream(_eventStream); bool started = Interop.EventStream.FSEventStreamStart(_eventStream); if (!started) { // Try to get the Watcher to raise the error event; if we can't do that, just silently exit since the watcher is gone anyway FileSystemWatcher watcher; if (_weakWatcher.TryGetTarget(out watcher)) { // An error occurred while trying to start the run loop so fail out watcher.OnError(new ErrorEventArgs(new IOException(SR.EventStream_FailedToStart, Marshal.GetLastWin32Error()))); } } }
internal void OpenTrustHandle( ICertificatePal leafCert, X509Certificate2Collection?extraStore, X509RevocationMode revocationMode, X509Certificate2Collection customTrustStore, X509ChainTrustMode trustMode) { _revocationMode = revocationMode; SafeCreateHandle policiesArray = PreparePoliciesArray(revocationMode != X509RevocationMode.NoCheck); SafeCreateHandle certsArray = PrepareCertsArray(leafCert, extraStore, customTrustStore, trustMode); int osStatus; SafeX509ChainHandle chain; int ret = Interop.AppleCrypto.AppleCryptoNative_X509ChainCreate( certsArray, policiesArray, out chain, out osStatus); if (ret == 1) { if (trustMode == X509ChainTrustMode.CustomRootTrust) { SafeCreateHandle customCertsArray = s_emptyArray; if (customTrustStore != null && customTrustStore.Count > 0) { customCertsArray = PrepareCustomCertsArray(customTrustStore); } try { int error = Interop.AppleCrypto.X509ChainSetTrustAnchorCertificates(chain, customCertsArray); if (error != 0) { throw Interop.AppleCrypto.CreateExceptionForOSStatus(error); } } finally { if (customCertsArray != s_emptyArray) { customCertsArray.Dispose(); } } } _chainHandle = chain; return; } chain.Dispose(); if (ret == 0) { throw Interop.AppleCrypto.CreateExceptionForOSStatus(osStatus); } Debug.Fail($"AppleCryptoNative_X509ChainCreate returned unexpected return value {ret}"); throw new CryptographicException(); }
internal static SafeCFArrayHandle X509ImportCollection( byte[] bytes, X509ContentType contentType, SafePasswordHandle importPassword, SafeKeychainHandle keychain, bool exportable) { SafeCreateHandle cfPassphrase = s_nullExportString; bool releasePassword = false; int ret; SafeCFArrayHandle collectionHandle; int osStatus; try { if (!importPassword.IsInvalid) { importPassword.DangerousAddRef(ref releasePassword); IntPtr passwordHandle = importPassword.DangerousGetHandle(); if (passwordHandle != IntPtr.Zero) { cfPassphrase = CoreFoundation.CFStringCreateWithCString(passwordHandle); } } ret = AppleCryptoNative_X509ImportCollection( bytes, bytes.Length, contentType, cfPassphrase, keychain, exportable ? 1 : 0, out collectionHandle, out osStatus); if (ret == 1) { return(collectionHandle); } } finally { if (releasePassword) { importPassword.DangerousRelease(); } if (cfPassphrase != s_nullExportString) { cfPassphrase.Dispose(); } } collectionHandle.Dispose(); const int SeeOSStatus = 0; const int ImportReturnedEmpty = -2; const int ImportReturnedNull = -3; switch (ret) { case SeeOSStatus: throw CreateExceptionForOSStatus(osStatus); case ImportReturnedNull: case ImportReturnedEmpty: throw new CryptographicException(); default: Debug.Fail($"Unexpected return value {ret}"); throw new CryptographicException(); } }
internal static DerSequenceReader SecKeyExport( SafeSecKeyRefHandle key, bool exportPrivate) { // Apple requires all private keys to be exported encrypted, but since we're trying to export // as parsed structures we will need to decrypt it for the user. const string ExportPassword = "******"; SafeCreateHandle exportPassword = exportPrivate ? CoreFoundation.CFStringCreateWithCString(ExportPassword) : s_nullExportString; int ret; SafeCFDataHandle cfData; int osStatus; try { ret = AppleCryptoNative_SecKeyExport( key, exportPrivate ? 1 : 0, exportPassword, out cfData, out osStatus); } finally { if (exportPassword != s_nullExportString) { exportPassword.Dispose(); } } byte[] exportedData; using (cfData) { if (ret == 0) { throw CreateExceptionForOSStatus(osStatus); } if (ret != 1) { Debug.Fail($"AppleCryptoNative_SecKeyExport returned {ret}"); throw new CryptographicException(); } exportedData = CoreFoundation.CFGetData(cfData); } DerSequenceReader reader = new DerSequenceReader(exportedData); if (!exportPrivate) { return(reader); } byte tag = reader.PeekTag(); // PKCS#8 defines two structures, PrivateKeyInfo, which starts with an integer, // and EncryptedPrivateKey, which starts with an encryption algorithm (DER sequence). if (tag == (byte)DerSequenceReader.DerTag.Integer) { return(reader); } const byte ConstructedSequence = DerSequenceReader.ConstructedFlag | (byte)DerSequenceReader.DerTag.Sequence; if (tag == ConstructedSequence) { return(ReadEncryptedPkcs8Blob(ExportPassword, reader)); } Debug.Fail($"Data was neither PrivateKey or EncryptedPrivateKey: {tag:X2}"); throw new CryptographicException(); }
internal unsafe void Start() { // Make sure _fullPath doesn't contain a link or alias // since the OS will give back the actual, non link'd or alias'd paths _fullDirectory = Interop.Sys.RealPath(_fullDirectory); if (_fullDirectory == null) { throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, true); } Debug.Assert(string.IsNullOrEmpty(_fullDirectory) == false, "Watch directory is null or empty"); // Normalize the _fullDirectory path to have a trailing slash if (_fullDirectory[_fullDirectory.Length - 1] != '/') { _fullDirectory += "/"; } // Get the path to watch and verify we created the CFStringRef SafeCreateHandle path = Interop.CoreFoundation.CFStringCreateWithCString(_fullDirectory); if (path.IsInvalid) { throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, true); } // Take the CFStringRef and put it into an array to pass to the EventStream SafeCreateHandle arrPaths = Interop.CoreFoundation.CFArrayCreate(new CFStringRef[1] { path.DangerousGetHandle() }, (UIntPtr)1); if (arrPaths.IsInvalid) { path.Dispose(); throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, true); } // Create the callback for the EventStream if it wasn't previously created for this instance. if (_callback == null) { _callback = new Interop.EventStream.FSEventStreamCallback(FileSystemEventCallback); } // Make sure the OS file buffer(s) are fully flushed so we don't get events from cached I/O Interop.Sys.Sync(); // Create the event stream for the path and tell the stream to watch for file system events. _eventStream = Interop.EventStream.FSEventStreamCreate( _callback, arrPaths, Interop.EventStream.kFSEventStreamEventIdSinceNow, 0.0f, EventStreamFlags); if (_eventStream.IsInvalid) { arrPaths.Dispose(); path.Dispose(); throw Interop.GetExceptionForIoErrno(Interop.Sys.GetLastErrorInfo(), _fullDirectory, true); } // Create and start our watcher thread then wait for the thread to initialize and start // the RunLoop. We wait for that to prevent this function from returning before the RunLoop // has a chance to start so that any callers won't race with the background thread's initialization // and calling Stop, which would attempt to stop a RunLoop that hasn't started yet. var runLoopStarted = new ManualResetEventSlim(); new Thread(WatchForFileSystemEventsThreadStart) { IsBackground = true }.Start(runLoopStarted); runLoopStarted.Wait(); }