Merge pull request #2807 from akoeplinger/gchandle
[mono.git] / mcs / class / System / ReferenceSources / SSPIWrapper.cs
1 //
2 // SSPIWrapper.cs
3 //
4 // Author:
5 //       Martin Baulig <martin.baulig@xamarin.com>
6 //
7 // Copyright (c) 2015 Xamarin Inc. (http://www.xamarin.com)
8 //
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:
15 //
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
18 //
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
25 // THE SOFTWARE.
26
27 #if MONO_FEATURE_NEW_TLS && SECURITY_DEP
28 #if MONO_SECURITY_ALIAS
29 extern alias MonoSecurity;
30 #endif
31 #if MONO_X509_ALIAS
32 extern alias PrebuiltSystem;
33 #endif
34
35 #if MONO_SECURITY_ALIAS
36 using MX = MonoSecurity::Mono.Security.X509;
37 using MonoSecurity::Mono.Security.Interface;
38 #else
39 using MX = Mono.Security.X509;
40 using Mono.Security.Interface;
41 #endif
42 #if MONO_X509_ALIAS
43 using XX509CertificateCollection = PrebuiltSystem::System.Security.Cryptography.X509Certificates.X509CertificateCollection;
44 #else
45 using XX509CertificateCollection = System.Security.Cryptography.X509Certificates.X509CertificateCollection;
46 #endif
47
48 using System.Runtime.InteropServices;
49 using System.Security.Authentication.ExtendedProtection;
50 using System.Security.Cryptography.X509Certificates;
51 using MNS = Mono.Net.Security;
52
53 namespace System.Net.Security
54 {
55         internal class SSPIInterface
56         {
57                 public IMonoTlsContext Context {
58                         get;
59                         private set;
60                 }
61
62                 public IMonoTlsEventSink EventSink {
63                         get;
64                         private set;
65                 }
66
67                 public SSPIInterface (IMonoTlsContext context, IMonoTlsEventSink eventSink)
68                 {
69                         Context = context;
70                         EventSink = eventSink;
71                 }
72         }
73
74         internal static class GlobalSSPI
75         {
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)
79                 {
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);
87                 }
88         }
89
90         /*
91          * SSPIWrapper _is a _class that provides a managed implementation of the equivalent
92          * _class _in Microsofts .NET Framework.   
93          * 
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.
96          * 
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.
101          * 
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.
105          * 
106          * The "internal" methods here are the API that is consumed by the class
107          * libraries.
108          */
109         internal static class SSPIWrapper
110         {
111                 static void SetCredentials (SSPIInterface secModule, SafeFreeCredentials credentials)
112                 {
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);
117                                 }
118                                 bool success = true;
119                                 credentials.DangerousAddRef (ref success);
120                         }
121                 }
122
123                 /*
124                  * @safecontext is null on the first use, but it will become non-null for invocations 
125                  * where the connection is being re-negotiated.
126                  * 
127                 */
128                 internal static int AcceptSecurityContext (SSPIInterface secModule, ref SafeFreeCredentials credentials, ref SafeDeleteContext safeContext, ContextFlags inFlags, Endianness endianness, SecurityBuffer inputBuffer, SecurityBuffer outputBuffer, ref ContextFlags outFlags)
129                 {
130                         if (endianness != Endianness.Native)
131                                 throw new NotSupportedException ();
132
133                         if (safeContext == null) {
134                                 if (credentials == null || credentials.IsInvalid)
135                                         return (int)SecurityStatus.CredentialsNeeded;
136
137                                 secModule.Context.Initialize (secModule.EventSink);
138                                 safeContext = new SafeDeleteContext (secModule.Context);
139                         }
140
141                         SetCredentials (secModule, credentials);
142
143                         var incoming = GetInputBuffer (inputBuffer);
144                         IBufferOffsetSize outgoing;
145
146                         var retval = (int)safeContext.Context.GenerateNextToken (incoming, out outgoing);
147                         UpdateOutput (outgoing, outputBuffer);
148                         return retval;
149                 }
150
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)
152                 {
153                         if (inputBuffer != null)
154                                 throw new InvalidOperationException ();
155
156                         if (safeContext == null) {
157                                 secModule.Context.Initialize (secModule.EventSink);
158                                 safeContext = new SafeDeleteContext (secModule.Context);
159                         }
160
161                         return InitializeSecurityContext (secModule, credentials, ref safeContext, targetName, inFlags, endianness, null, outputBuffer, ref outFlags);
162                 }
163
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)
165                 {
166                         if (endianness != Endianness.Native)
167                                 throw new NotSupportedException ();
168
169                         SetCredentials (secModule, credentials);
170
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];
176                         }
177
178                         var incoming = GetInputBuffer (inputBuffer);
179                         IBufferOffsetSize outgoing = null;
180
181                         var retval = (int)safeContext.Context.GenerateNextToken (incoming, out outgoing);
182                         UpdateOutput (outgoing, outputBuffer);
183                         return retval;
184                 }
185
186                 internal static int EncryptMessage (SSPIInterface secModule, SafeDeleteContext safeContext, SecurityBuffer securityBuffer, uint sequenceNumber)
187                 {
188                         var incoming = GetInputBuffer (securityBuffer);
189                         var retval = (int)safeContext.Context.EncryptMessage (ref incoming);
190                         UpdateOutput (incoming, securityBuffer);
191                         return retval;
192                 }
193
194                 internal static int DecryptMessage (SSPIInterface secModule, SafeDeleteContext safeContext, SecurityBuffer securityBuffer, uint sequenceNumber)
195                 {
196                         var incoming = GetInputBuffer (securityBuffer);
197                         var retval = (int)safeContext.Context.DecryptMessage (ref incoming);
198                         UpdateOutput (incoming, securityBuffer);
199                         return retval;
200                 }
201
202                 internal static byte[] CreateShutdownMessage (SSPIInterface secModule, SafeDeleteContext safeContext)
203                 {
204                         return safeContext.Context.CreateCloseNotify ();
205                 }
206
207                 internal static byte[] CreateHelloRequestMessage (SSPIInterface secModule, SafeDeleteContext safeContext)
208                 {
209                         return safeContext.Context.CreateHelloRequest ();
210                 }
211
212                 internal static bool IsClosed (SSPIInterface secModule, SafeDeleteContext safeContext)
213                 {
214                         return safeContext.Context.ReceivedCloseNotify;
215                 }
216
217                 internal static SafeFreeCredentials AcquireCredentialsHandle (SSPIInterface SecModule, string package, CredentialUse intent, SecureCredential scc)
218                 {
219                         return new SafeFreeCredentials (scc);
220                 }
221
222                 public static ChannelBinding QueryContextChannelBinding (SSPIInterface SecModule, SafeDeleteContext securityContext, ContextAttribute contextAttribute)
223                 {
224                         return null;
225                 }
226
227                 internal static X509Certificate2 GetRemoteCertificate (SafeDeleteContext safeContext, out X509Certificate2Collection remoteCertificateStore)
228                 {
229                         XX509CertificateCollection monoCollection;
230                         if (safeContext == null || safeContext.IsInvalid) {
231                                 remoteCertificateStore = null;
232                                 return null;
233                         }
234                         var monoCert = safeContext.Context.GetRemoteCertificate (out monoCollection);
235                         if (monoCert == null) {
236                                 remoteCertificateStore = null;
237                                 return null;
238                         }
239
240                         remoteCertificateStore = new X509Certificate2Collection ();
241                         foreach (var cert in monoCollection) {
242                                 remoteCertificateStore.Add (cert);
243                         }
244                         return (X509Certificate2)monoCert;
245                 }
246
247                 internal static bool CheckRemoteCertificate (SafeDeleteContext safeContext)
248                 {
249                         return safeContext.Context.VerifyRemoteCertificate ();
250                 }
251
252                 internal static MonoTlsConnectionInfo GetMonoConnectionInfo (SSPIInterface SecModule, SafeDeleteContext securityContext)
253                 {
254                         return securityContext.Context.GetConnectionInfo ();
255                 }
256
257                 internal static SslConnectionInfo GetConnectionInfo (SSPIInterface SecModule, SafeDeleteContext securityContext)
258                 {
259                         var info = securityContext.Context.GetConnectionInfo ();
260                         if (info == null)
261                                 return null;
262
263                         return new SslConnectionInfo ((int)info.ProtocolVersion);
264                 }
265
266                 class InputBuffer : IBufferOffsetSize
267                 {
268                         public byte[] Buffer {
269                                 get;
270                                 private set;
271                         }
272
273                         public int Offset {
274                                 get;
275                                 private set;
276                         }
277
278                         public int Size {
279                                 get;
280                                 private set;
281                         }
282
283                         public InputBuffer (byte[] buffer, int offset, int size)
284                         {
285                                 Buffer = buffer;
286                                 Offset = offset;
287                                 Size = size;
288                         }
289                 }
290
291                 static IBufferOffsetSize GetInputBuffer (SecurityBuffer incoming)
292                 {
293                         return incoming != null ? new InputBuffer (incoming.token, incoming.offset, incoming.size) : null;
294                 }
295
296                 static void UpdateOutput (IBufferOffsetSize buffer, SecurityBuffer outputBuffer)
297                 {
298                         if (buffer != null) {
299                                 outputBuffer.token = buffer.Buffer;
300                                 outputBuffer.offset = buffer.Offset;
301                                 outputBuffer.size = buffer.Size;
302                                 outputBuffer.type = BufferType.Token;
303                         } else {
304                                 outputBuffer.token = null;
305                                 outputBuffer.size = outputBuffer.offset = 0;
306                                 outputBuffer.type = BufferType.Empty;
307                         }
308                 }
309         }
310 }
311 #endif