312 lines
11 KiB
C#
Raw Normal View History

//
// SSPIWrapper.cs
//
// Author:
// Martin Baulig <martin.baulig@xamarin.com>
//
// Copyright (c) 2015 Xamarin Inc. (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if MONO_FEATURE_NEW_TLS && SECURITY_DEP
#if MONO_SECURITY_ALIAS
extern alias MonoSecurity;
#endif
#if MONO_X509_ALIAS
extern alias PrebuiltSystem;
#endif
#if MONO_SECURITY_ALIAS
using MX = MonoSecurity::Mono.Security.X509;
using MonoSecurity::Mono.Security.Interface;
#else
using MX = Mono.Security.X509;
using Mono.Security.Interface;
#endif
#if MONO_X509_ALIAS
using XX509CertificateCollection = PrebuiltSystem::System.Security.Cryptography.X509Certificates.X509CertificateCollection;
#else
using XX509CertificateCollection = System.Security.Cryptography.X509Certificates.X509CertificateCollection;
#endif
using System.Runtime.InteropServices;
using System.Security.Authentication.ExtendedProtection;
using System.Security.Cryptography.X509Certificates;
using MNS = Mono.Net.Security;
namespace System.Net.Security
{
internal class SSPIInterface
{
public IMonoTlsContext Context {
get;
private set;
}
public IMonoTlsEventSink EventSink {
get;
private set;
}
public SSPIInterface (IMonoTlsContext context, IMonoTlsEventSink eventSink)
{
Context = context;
EventSink = eventSink;
}
}
internal static class GlobalSSPI
{
internal static SSPIInterface Create (string hostname, bool serverMode, SchProtocols protocolFlags, X509Certificate serverCertificate, XX509CertificateCollection clientCertificates,
bool remoteCertRequired, bool checkCertName, bool checkCertRevocationStatus, EncryptionPolicy encryptionPolicy,
LocalCertSelectionCallback certSelectionDelegate, RemoteCertValidationCallback remoteValidationCallback, SSPIConfiguration userConfig)
{
if (userConfig.Settings != null && remoteValidationCallback != null)
throw new InvalidOperationException ();
var context = userConfig.Provider.CreateTlsContext (
hostname, serverMode, (TlsProtocols)protocolFlags, serverCertificate, clientCertificates,
remoteCertRequired, checkCertName, checkCertRevocationStatus,
(MonoEncryptionPolicy)encryptionPolicy, userConfig.Settings);
return new SSPIInterface (context, userConfig.EventSink);
}
}
/*
* SSPIWrapper _is a _class that provides a managed implementation of the equivalent
* _class _in Microsofts .NET Framework.
*
* The SSPIWrapper class is used by the TLS/SSL stack to implement both the
* protocol handshake as well as the encryption and decryption of messages.
*
* Microsoft's implementation of this class is merely a P/Invoke wrapper
* around the native SSPI APIs on Windows. This implementation instead,
* provides a managed implementation that uses the cross platform Mono.Security
* to provide the equivalent functionality.
*
* Ideally, this should be abstracted with a different name, and decouple
* the naming from the SSPIWrapper name, but this allows Mono to reuse
* the .NET code with minimal changes.
*
* The "internal" methods here are the API that is consumed by the class
* libraries.
*/
internal static class SSPIWrapper
{
static void SetCredentials (SSPIInterface secModule, SafeFreeCredentials credentials)
{
if (credentials != null && !credentials.IsInvalid) {
if (!secModule.Context.HasCredentials && credentials.Certificate != null) {
var cert = new X509Certificate2 (credentials.Certificate.RawData);
secModule.Context.SetCertificate (cert, credentials.Certificate.PrivateKey);
}
bool success = true;
credentials.DangerousAddRef (ref success);
}
}
/*
* @safecontext is null on the first use, but it will become non-null for invocations
* where the connection is being re-negotiated.
*
*/
internal static int AcceptSecurityContext (SSPIInterface secModule, ref SafeFreeCredentials credentials, ref SafeDeleteContext safeContext, ContextFlags inFlags, Endianness endianness, SecurityBuffer inputBuffer, SecurityBuffer outputBuffer, ref ContextFlags outFlags)
{
if (endianness != Endianness.Native)
throw new NotSupportedException ();
if (safeContext == null) {
if (credentials == null || credentials.IsInvalid)
return (int)SecurityStatus.CredentialsNeeded;
secModule.Context.Initialize (secModule.EventSink);
safeContext = new SafeDeleteContext (secModule.Context);
}
SetCredentials (secModule, credentials);
var incoming = GetInputBuffer (inputBuffer);
IBufferOffsetSize outgoing;
var retval = (int)safeContext.Context.GenerateNextToken (incoming, out outgoing);
UpdateOutput (outgoing, outputBuffer);
return retval;
}
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)
{
if (inputBuffer != null)
throw new InvalidOperationException ();
if (safeContext == null) {
secModule.Context.Initialize (secModule.EventSink);
safeContext = new SafeDeleteContext (secModule.Context);
}
return InitializeSecurityContext (secModule, credentials, ref safeContext, targetName, inFlags, endianness, null, outputBuffer, ref outFlags);
}
internal static int InitializeSecurityContext (SSPIInterface secModule, SafeFreeCredentials credentials, ref SafeDeleteContext safeContext, string targetName, ContextFlags inFlags, Endianness endianness, SecurityBuffer[] inputBuffers, SecurityBuffer outputBuffer, ref ContextFlags outFlags)
{
if (endianness != Endianness.Native)
throw new NotSupportedException ();
SetCredentials (secModule, credentials);
SecurityBuffer inputBuffer = null;
if (inputBuffers != null) {
if (inputBuffers.Length != 2 || inputBuffers [1].type != BufferType.Empty)
throw new NotSupportedException ();
inputBuffer = inputBuffers [0];
}
var incoming = GetInputBuffer (inputBuffer);
IBufferOffsetSize outgoing = null;
var retval = (int)safeContext.Context.GenerateNextToken (incoming, out outgoing);
UpdateOutput (outgoing, outputBuffer);
return retval;
}
internal static int EncryptMessage (SSPIInterface secModule, SafeDeleteContext safeContext, SecurityBuffer securityBuffer, uint sequenceNumber)
{
var incoming = GetInputBuffer (securityBuffer);
var retval = (int)safeContext.Context.EncryptMessage (ref incoming);
UpdateOutput (incoming, securityBuffer);
return retval;
}
internal static int DecryptMessage (SSPIInterface secModule, SafeDeleteContext safeContext, SecurityBuffer securityBuffer, uint sequenceNumber)
{
var incoming = GetInputBuffer (securityBuffer);
var retval = (int)safeContext.Context.DecryptMessage (ref incoming);
UpdateOutput (incoming, securityBuffer);
return retval;
}
internal static byte[] CreateShutdownMessage (SSPIInterface secModule, SafeDeleteContext safeContext)
{
return safeContext.Context.CreateCloseNotify ();
}
internal static byte[] CreateHelloRequestMessage (SSPIInterface secModule, SafeDeleteContext safeContext)
{
return safeContext.Context.CreateHelloRequest ();
}
internal static bool IsClosed (SSPIInterface secModule, SafeDeleteContext safeContext)
{
return safeContext.Context.ReceivedCloseNotify;
}
internal static SafeFreeCredentials AcquireCredentialsHandle (SSPIInterface SecModule, string package, CredentialUse intent, SecureCredential scc)
{
return new SafeFreeCredentials (scc);
}
public static ChannelBinding QueryContextChannelBinding (SSPIInterface SecModule, SafeDeleteContext securityContext, ContextAttribute contextAttribute)
{
return null;
}
internal static X509Certificate2 GetRemoteCertificate (SafeDeleteContext safeContext, out X509Certificate2Collection remoteCertificateStore)
{
XX509CertificateCollection monoCollection;
if (safeContext == null || safeContext.IsInvalid) {
remoteCertificateStore = null;
return null;
}
var monoCert = safeContext.Context.GetRemoteCertificate (out monoCollection);
if (monoCert == null) {
remoteCertificateStore = null;
return null;
}
remoteCertificateStore = new X509Certificate2Collection ();
foreach (var cert in monoCollection) {
remoteCertificateStore.Add (cert);
}
return (X509Certificate2)monoCert;
}
internal static bool CheckRemoteCertificate (SafeDeleteContext safeContext)
{
return safeContext.Context.VerifyRemoteCertificate ();
}
internal static MonoTlsConnectionInfo GetMonoConnectionInfo (SSPIInterface SecModule, SafeDeleteContext securityContext)
{
return securityContext.Context.GetConnectionInfo ();
}
internal static SslConnectionInfo GetConnectionInfo (SSPIInterface SecModule, SafeDeleteContext securityContext)
{
var info = securityContext.Context.GetConnectionInfo ();
if (info == null)
return null;
return new SslConnectionInfo ((int)info.ProtocolVersion);
}
class InputBuffer : IBufferOffsetSize
{
public byte[] Buffer {
get;
private set;
}
public int Offset {
get;
private set;
}
public int Size {
get;
private set;
}
public InputBuffer (byte[] buffer, int offset, int size)
{
Buffer = buffer;
Offset = offset;
Size = size;
}
}
static IBufferOffsetSize GetInputBuffer (SecurityBuffer incoming)
{
return incoming != null ? new InputBuffer (incoming.token, incoming.offset, incoming.size) : null;
}
static void UpdateOutput (IBufferOffsetSize buffer, SecurityBuffer outputBuffer)
{
if (buffer != null) {
outputBuffer.token = buffer.Buffer;
outputBuffer.offset = buffer.Offset;
outputBuffer.size = buffer.Size;
outputBuffer.type = BufferType.Token;
} else {
outputBuffer.token = null;
outputBuffer.size = outputBuffer.offset = 0;
outputBuffer.type = BufferType.Empty;
}
}
}
}
#endif