5 // Martin Baulig <martin.baulig@xamarin.com>
7 // Copyright (c) 2015 Xamarin Inc. (http://www.xamarin.com)
9 // Permission is hereby granted, free of charge, to any person obtaining a copy
10 // of this software and associated documentation files (the "Software"), to deal
11 // in the Software without restriction, including without limitation the rights
12 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 // copies of the Software, and to permit persons to whom the Software is
14 // furnished to do so, subject to the following conditions:
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 #if MONO_FEATURE_NEW_TLS && SECURITY_DEP
28 #if MONO_SECURITY_ALIAS
29 extern alias MonoSecurity;
32 extern alias PrebuiltSystem;
35 #if MONO_SECURITY_ALIAS
36 using MX = MonoSecurity::Mono.Security.X509;
37 using MonoSecurity::Mono.Security.Interface;
39 using MX = Mono.Security.X509;
40 using Mono.Security.Interface;
43 using XX509CertificateCollection = PrebuiltSystem::System.Security.Cryptography.X509Certificates.X509CertificateCollection;
45 using XX509CertificateCollection = System.Security.Cryptography.X509Certificates.X509CertificateCollection;
48 using System.Runtime.InteropServices;
49 using System.Security.Authentication.ExtendedProtection;
50 using System.Security.Cryptography.X509Certificates;
51 using MNS = Mono.Net.Security;
53 namespace System.Net.Security
55 internal class SSPIInterface
57 public IMonoTlsContext Context {
62 public IMonoTlsEventSink EventSink {
67 public SSPIInterface (IMonoTlsContext context, IMonoTlsEventSink eventSink)
70 EventSink = eventSink;
74 internal static class GlobalSSPI
76 internal static SSPIInterface Create (string hostname, bool serverMode, SchProtocols protocolFlags, X509Certificate serverCertificate, XX509CertificateCollection clientCertificates,
77 bool remoteCertRequired, bool checkCertName, bool checkCertRevocationStatus, EncryptionPolicy encryptionPolicy,
78 LocalCertSelectionCallback certSelectionDelegate, RemoteCertValidationCallback remoteValidationCallback, SSPIConfiguration userConfig)
80 if (userConfig.Settings != null && remoteValidationCallback != null)
81 throw new InvalidOperationException ();
82 var context = userConfig.Provider.CreateTlsContext (
83 hostname, serverMode, (TlsProtocols)protocolFlags, serverCertificate, clientCertificates,
84 remoteCertRequired, checkCertName, checkCertRevocationStatus,
85 (MonoEncryptionPolicy)encryptionPolicy, userConfig.Settings);
86 return new SSPIInterface (context, userConfig.EventSink);
91 * SSPIWrapper _is a _class that provides a managed implementation of the equivalent
92 * _class _in Microsofts .NET Framework.
94 * The SSPIWrapper class is used by the TLS/SSL stack to implement both the
95 * protocol handshake as well as the encryption and decryption of messages.
97 * Microsoft's implementation of this class is merely a P/Invoke wrapper
98 * around the native SSPI APIs on Windows. This implementation instead,
99 * provides a managed implementation that uses the cross platform Mono.Security
100 * to provide the equivalent functionality.
102 * Ideally, this should be abstracted with a different name, and decouple
103 * the naming from the SSPIWrapper name, but this allows Mono to reuse
104 * the .NET code with minimal changes.
106 * The "internal" methods here are the API that is consumed by the class
109 internal static class SSPIWrapper
111 static void SetCredentials (SSPIInterface secModule, SafeFreeCredentials credentials)
113 if (credentials != null && !credentials.IsInvalid) {
114 if (!secModule.Context.HasCredentials && credentials.Certificate != null) {
115 var cert = new X509Certificate2 (credentials.Certificate.RawData);
116 secModule.Context.SetCertificate (cert, credentials.Certificate.PrivateKey);
119 credentials.DangerousAddRef (ref success);
124 * @safecontext is null on the first use, but it will become non-null for invocations
125 * where the connection is being re-negotiated.
128 internal static int AcceptSecurityContext (SSPIInterface secModule, ref SafeFreeCredentials credentials, ref SafeDeleteContext safeContext, ContextFlags inFlags, Endianness endianness, SecurityBuffer inputBuffer, SecurityBuffer outputBuffer, ref ContextFlags outFlags)
130 if (endianness != Endianness.Native)
131 throw new NotSupportedException ();
133 if (safeContext == null) {
134 if (credentials == null || credentials.IsInvalid)
135 return (int)SecurityStatus.CredentialsNeeded;
137 secModule.Context.Initialize (secModule.EventSink);
138 safeContext = new SafeDeleteContext (secModule.Context);
141 SetCredentials (secModule, credentials);
143 var incoming = GetInputBuffer (inputBuffer);
144 IBufferOffsetSize outgoing;
146 var retval = (int)safeContext.Context.GenerateNextToken (incoming, out outgoing);
147 UpdateOutput (outgoing, outputBuffer);
151 internal static int InitializeSecurityContext (SSPIInterface secModule, ref SafeFreeCredentials credentials, ref SafeDeleteContext safeContext, string targetName, ContextFlags inFlags, Endianness endianness, SecurityBuffer inputBuffer, SecurityBuffer outputBuffer, ref ContextFlags outFlags)
153 if (inputBuffer != null)
154 throw new InvalidOperationException ();
156 if (safeContext == null) {
157 secModule.Context.Initialize (secModule.EventSink);
158 safeContext = new SafeDeleteContext (secModule.Context);
161 return InitializeSecurityContext (secModule, credentials, ref safeContext, targetName, inFlags, endianness, null, outputBuffer, ref outFlags);
164 internal static int InitializeSecurityContext (SSPIInterface secModule, SafeFreeCredentials credentials, ref SafeDeleteContext safeContext, string targetName, ContextFlags inFlags, Endianness endianness, SecurityBuffer[] inputBuffers, SecurityBuffer outputBuffer, ref ContextFlags outFlags)
166 if (endianness != Endianness.Native)
167 throw new NotSupportedException ();
169 SetCredentials (secModule, credentials);
171 SecurityBuffer inputBuffer = null;
172 if (inputBuffers != null) {
173 if (inputBuffers.Length != 2 || inputBuffers [1].type != BufferType.Empty)
174 throw new NotSupportedException ();
175 inputBuffer = inputBuffers [0];
178 var incoming = GetInputBuffer (inputBuffer);
179 IBufferOffsetSize outgoing = null;
181 var retval = (int)safeContext.Context.GenerateNextToken (incoming, out outgoing);
182 UpdateOutput (outgoing, outputBuffer);
186 internal static int EncryptMessage (SSPIInterface secModule, SafeDeleteContext safeContext, SecurityBuffer securityBuffer, uint sequenceNumber)
188 var incoming = GetInputBuffer (securityBuffer);
189 var retval = (int)safeContext.Context.EncryptMessage (ref incoming);
190 UpdateOutput (incoming, securityBuffer);
194 internal static int DecryptMessage (SSPIInterface secModule, SafeDeleteContext safeContext, SecurityBuffer securityBuffer, uint sequenceNumber)
196 var incoming = GetInputBuffer (securityBuffer);
197 var retval = (int)safeContext.Context.DecryptMessage (ref incoming);
198 UpdateOutput (incoming, securityBuffer);
202 internal static byte[] CreateShutdownMessage (SSPIInterface secModule, SafeDeleteContext safeContext)
204 return safeContext.Context.CreateCloseNotify ();
207 internal static byte[] CreateHelloRequestMessage (SSPIInterface secModule, SafeDeleteContext safeContext)
209 return safeContext.Context.CreateHelloRequest ();
212 internal static bool IsClosed (SSPIInterface secModule, SafeDeleteContext safeContext)
214 return safeContext.Context.ReceivedCloseNotify;
217 internal static SafeFreeCredentials AcquireCredentialsHandle (SSPIInterface SecModule, string package, CredentialUse intent, SecureCredential scc)
219 return new SafeFreeCredentials (scc);
222 public static ChannelBinding QueryContextChannelBinding (SSPIInterface SecModule, SafeDeleteContext securityContext, ContextAttribute contextAttribute)
227 internal static X509Certificate2 GetRemoteCertificate (SafeDeleteContext safeContext, out X509Certificate2Collection remoteCertificateStore)
229 XX509CertificateCollection monoCollection;
230 if (safeContext == null || safeContext.IsInvalid) {
231 remoteCertificateStore = null;
234 var monoCert = safeContext.Context.GetRemoteCertificate (out monoCollection);
235 if (monoCert == null) {
236 remoteCertificateStore = null;
240 remoteCertificateStore = new X509Certificate2Collection ();
241 foreach (var cert in monoCollection) {
242 remoteCertificateStore.Add (cert);
244 return (X509Certificate2)monoCert;
247 internal static bool CheckRemoteCertificate (SafeDeleteContext safeContext)
249 return safeContext.Context.VerifyRemoteCertificate ();
252 internal static MonoTlsConnectionInfo GetMonoConnectionInfo (SSPIInterface SecModule, SafeDeleteContext securityContext)
254 return securityContext.Context.GetConnectionInfo ();
257 internal static SslConnectionInfo GetConnectionInfo (SSPIInterface SecModule, SafeDeleteContext securityContext)
259 var info = securityContext.Context.GetConnectionInfo ();
263 return new SslConnectionInfo ((int)info.ProtocolVersion);
266 class InputBuffer : IBufferOffsetSize
268 public byte[] Buffer {
283 public InputBuffer (byte[] buffer, int offset, int size)
291 static IBufferOffsetSize GetInputBuffer (SecurityBuffer incoming)
293 return incoming != null ? new InputBuffer (incoming.token, incoming.offset, incoming.size) : null;
296 static void UpdateOutput (IBufferOffsetSize buffer, SecurityBuffer outputBuffer)
298 if (buffer != null) {
299 outputBuffer.token = buffer.Buffer;
300 outputBuffer.offset = buffer.Offset;
301 outputBuffer.size = buffer.Size;
302 outputBuffer.type = BufferType.Token;
304 outputBuffer.token = null;
305 outputBuffer.size = outputBuffer.offset = 0;
306 outputBuffer.type = BufferType.Empty;