2146 lines
68 KiB
C#
2146 lines
68 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
|
|
using System.Security.Cryptography.X509Certificates;
|
|
using System.Runtime.InteropServices;
|
|
using System.Net;
|
|
using System.IO;
|
|
using System.Net.Sockets;
|
|
using System.Diagnostics;
|
|
using System.Security;
|
|
|
|
namespace Intel.Management.Wsman
|
|
{
|
|
|
|
/// <summary>
|
|
/// Implements an SChannel Client
|
|
/// </summary>
|
|
public class SchannelClient : IDisposable
|
|
{
|
|
SECURITY_HANDLE _hCredHandle;//handle to acquired Credential (FreeCredentialsHandle)
|
|
SECURITY_HANDLE _hContext; //handle to the context (DeleteSecurityContext)
|
|
|
|
|
|
UserCredentials _creds; // user credentials
|
|
static List<CacheItem> _credCache; // credential Cache
|
|
static object _credLock = new object(); //thread lock for cached credentials
|
|
|
|
//package info
|
|
uint _cbMaxToken = 0;
|
|
string _packageName;
|
|
|
|
string _target; //name of server or SPN to verify
|
|
TcpClient _tcpClient;
|
|
|
|
|
|
static TraceSource _trace = new TraceSource("Intel.Management.Wsman.Schannel");
|
|
|
|
public SchannelClient(string packageName)
|
|
{
|
|
|
|
_trace.TraceInformation("Request Schannel package for {0}",packageName);
|
|
|
|
// store package info in a structure (static)
|
|
IntPtr pkgPtr;
|
|
int status = QuerySecurityPackageInfo(packageName, out pkgPtr);
|
|
if (status == 0)
|
|
{
|
|
SecPkgInfo pkgInfo = SecPkgInfo.FromIntPtr(pkgPtr);
|
|
_cbMaxToken = pkgInfo.cbMaxToken;
|
|
_packageName = pkgInfo.Name;
|
|
status = FreeContextBuffer(pkgPtr);
|
|
}
|
|
|
|
|
|
// create a credential cache
|
|
lock (_credLock)
|
|
{
|
|
if (_credCache == null)
|
|
_credCache = new List<CacheItem>();
|
|
}
|
|
}
|
|
|
|
public string Target
|
|
{
|
|
get { return _target; }
|
|
set { _target = value; }
|
|
}
|
|
|
|
public X509Certificate2 GetSessionCerticate()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public bool GetSessionInfo(out string SessionId)
|
|
{
|
|
SessionId = string.Empty;
|
|
return false;
|
|
}
|
|
|
|
|
|
// acquire credentials for a user
|
|
public int AcquireCredentials(string user, string domain, SecureString password)
|
|
{
|
|
|
|
WindowsCredentials winCreds = new WindowsCredentials();
|
|
|
|
winCreds.UserName = user;
|
|
winCreds.DomainName = domain;
|
|
winCreds.Password = password;
|
|
|
|
_creds = winCreds;
|
|
|
|
int status = 0;
|
|
lock (_credLock)
|
|
{
|
|
long expirers;
|
|
status = AcquireCredentials(out expirers);
|
|
}
|
|
|
|
return status;
|
|
|
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Acquire credentials for MTLS handshake
|
|
/// </summary>
|
|
/// <param name="certificate">Certificate Credentials</param>
|
|
public void AcquireCredentials(X509Certificate2 certificate)
|
|
{
|
|
AcquireCredentials(certificate, true, true, false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Acquire credentials for TLS handshake
|
|
/// </summary>
|
|
public void AcquireCredentials(X509Certificate2 certificate, bool verifyCa, bool verifyHost, bool sentRoot)
|
|
{
|
|
|
|
_creds = new CertificateCredentials();
|
|
|
|
if (certificate != null)
|
|
{
|
|
//_trace.TraceInformation("Acquire Credentials for certificate {0}", certificate.Subject);
|
|
((CertificateCredentials)_creds).Certificate = certificate;
|
|
|
|
((CertificateCredentials)_creds).SkipDefaultCreds();
|
|
|
|
if (sentRoot)
|
|
((CertificateCredentials)_creds).SendRoot();
|
|
|
|
if (!verifyCa)
|
|
((CertificateCredentials)_creds).SkipCACheck();
|
|
|
|
if (!verifyHost)
|
|
((CertificateCredentials)_creds).SkipCNCheck();
|
|
|
|
}
|
|
else
|
|
{
|
|
_trace.TraceInformation("Acquire Credentials (no certificates specified)");
|
|
}
|
|
|
|
|
|
lock (_credLock)
|
|
{
|
|
long expirers;
|
|
int status = AcquireCredentials(out expirers);
|
|
}
|
|
}
|
|
|
|
private int AcquireCredentials(out long expiers)
|
|
{
|
|
// assume the creds are locked when this is called
|
|
|
|
//CacheItem removeItem = null;
|
|
// find exising item in cache existing cred
|
|
foreach (CacheItem cacheItem in _credCache)
|
|
{
|
|
if (cacheItem.Credentials.CompareTo(_creds))
|
|
{
|
|
_hCredHandle.HighPart=cacheItem.HighPart;
|
|
_hCredHandle.LowPart=cacheItem.LowPart;
|
|
|
|
//get a copy of the connection flags
|
|
|
|
expiers = cacheItem.Expiers;
|
|
|
|
_trace.TraceInformation("Found cached credential");
|
|
return 0;
|
|
} // end cached handle not freed
|
|
}//end for
|
|
|
|
expiers = 0;
|
|
|
|
int status = AcquireCredentialsHandle(null,
|
|
_packageName,
|
|
2, //2=SECPKG_CRED_OUTBOUND
|
|
IntPtr.Zero, //
|
|
_creds, //handle to schannel creds (client certificates)
|
|
IntPtr.Zero,
|
|
IntPtr.Zero,
|
|
ref _hCredHandle, //acquired cred Handle
|
|
ref expiers);
|
|
|
|
|
|
|
|
|
|
if (status == 0)
|
|
{
|
|
_trace.TraceInformation("Credential Handle {0}", this.GetHandleMessage(ref _hCredHandle));
|
|
_trace.TraceInformation("Acquired Credentials added to cache");
|
|
_credCache.Add(new CacheItem(_hCredHandle.HighPart, _hCredHandle.LowPart, _creds, expiers));
|
|
}
|
|
else
|
|
{
|
|
_trace.TraceInformation("Acquire Credentials returned {0}", GetSchannelMessage(status));
|
|
throw new WsmanConnectionException(GetSchannelMessage(status));
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
#region IDisposableMembers
|
|
|
|
private bool _disposed = false;
|
|
|
|
public void Dispose(bool disposing)
|
|
{
|
|
if (_disposed)
|
|
return;
|
|
if (disposing)
|
|
{
|
|
_creds?.Dispose();
|
|
_creds = null;
|
|
}
|
|
_disposed = true;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
CloseContext();
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
|
|
}
|
|
|
|
~SchannelClient()
|
|
{
|
|
Dispose(false);
|
|
}
|
|
#endregion
|
|
public string GetToken(string prevToken)
|
|
{
|
|
string result = null;
|
|
|
|
//inFlags = Delegate=1, MutualAuth=2, ReplayDetect=4, SequenceDetect=8, Confidentiality=16, Connection=2048
|
|
//allocate memory=256
|
|
|
|
uint flags = 1 | 2 | 4 | 8 | 16 | 2048 | 256;
|
|
|
|
long expiers = 0;
|
|
uint contextAttr = 0;
|
|
int status = 0;
|
|
|
|
ChannelOutputBuffer outBuff = new ChannelOutputBuffer();
|
|
InputBuffer inputBuff = new InputBuffer();
|
|
|
|
|
|
byte[] token = null;
|
|
if (prevToken != null)
|
|
{
|
|
|
|
int pos = prevToken.LastIndexOf(" ");
|
|
|
|
string tokenString = prevToken.Substring(pos + 1);
|
|
|
|
if (string.Compare(tokenString,"Negotiate",true)!=0)
|
|
{
|
|
token = Convert.FromBase64String(tokenString);
|
|
}
|
|
}
|
|
|
|
if (token==null)
|
|
{
|
|
CloseContext();
|
|
|
|
status = InitializeSecurityContextInit(
|
|
ref _hCredHandle,
|
|
IntPtr.Zero,
|
|
_target,
|
|
flags, //Delegate=1, MutualAuth=2, ReplayDetect=4, SequenceDetect=8, Confidentiality=16, Connection=2048,
|
|
0,
|
|
16, //SECURITY_NATIVE_DREP=16
|
|
IntPtr.Zero,
|
|
0,
|
|
ref _hContext,
|
|
outBuff,
|
|
ref contextAttr,
|
|
ref expiers);
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
inputBuff = new InputBuffer(1);
|
|
inputBuff[0].BufferType=BufferType.Token;
|
|
|
|
|
|
|
|
|
|
|
|
inputBuff[0].SetBuffer(token, 0, token.Length);
|
|
|
|
status = InitializeSecurityContextExchange(
|
|
ref _hCredHandle,
|
|
IntPtr.Zero,
|
|
_target,
|
|
flags, //Delegate=1, MutualAuth=2, ReplayDetect=4, SequenceDetect=8, Confidentiality=16, Connection=2048,
|
|
0,
|
|
16, //SECURITY_NATIVE_DREP=16
|
|
inputBuff,
|
|
0,
|
|
ref _hContext,
|
|
outBuff,
|
|
ref contextAttr,
|
|
ref expiers);
|
|
}
|
|
|
|
_trace.TraceInformation("InitializeSecurityContext({0}) returned {1}", _target, GetSchannelMessage(status));
|
|
_trace.TraceInformation("New Context is {0}", _hContext.ToString());
|
|
|
|
if (status >= 0)
|
|
{
|
|
|
|
result = _packageName + " " + Convert.ToBase64String(outBuff[0].Data, 0, outBuff[0].Data.Length);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public void CloseContext()
|
|
{
|
|
|
|
if (!_hContext.LowPart.Equals(IntPtr.Zero) ||
|
|
!_hContext.HighPart.Equals(IntPtr.Zero))
|
|
{
|
|
int status = DeleteSecurityContext(ref _hContext);
|
|
if (status == 0)
|
|
{
|
|
_hContext.HighPart = IntPtr.Zero;
|
|
_hContext.LowPart = IntPtr.Zero;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void CloseNotify()
|
|
{
|
|
|
|
long expiers = 0;
|
|
uint contextAttrs = 0;
|
|
|
|
InputBuffer buffer = new InputBuffer(1);
|
|
buffer[0].BufferType=BufferType.Token;
|
|
byte[] data = BitConverter.GetBytes((int)1);
|
|
|
|
|
|
buffer[0].SetBuffer(data, 0, data.Length);
|
|
|
|
int status=ApplyControlToken(ref _hContext,buffer);
|
|
_trace.TraceInformation("ApplyControlToken for Shutdown retured {0}", GetSchannelMessage(status));
|
|
|
|
|
|
ChannelOutputBuffer outBuff = new ChannelOutputBuffer();
|
|
InputBuffer inBuff = new InputBuffer();
|
|
|
|
|
|
|
|
status = InitializeSecurityContext(
|
|
ref _hCredHandle,
|
|
ref _hContext,
|
|
_target,
|
|
49436, //ISC_REQ_SEQUENCE_DETECT|ISC_REQ_REPLAY_DETECT|ISC_REQ_CONFIDENTIALITY|ISC_RET_EXTENDED_ERROR |ISC_REQ_ALLOCATE_MEMORY |ISC_REQ_STREAM;
|
|
0,
|
|
16, //SECURITY_NATIVE_DREP
|
|
inBuff,
|
|
0,
|
|
IntPtr.Zero,
|
|
outBuff,
|
|
ref contextAttrs,
|
|
ref expiers);
|
|
|
|
_trace.TraceInformation("InitializeSecurityContext for shutdown retured {0}", GetSchannelMessage(status));
|
|
|
|
if (status == 0)
|
|
SendBuffer(outBuff[0]);
|
|
|
|
_tcpClient = null;
|
|
|
|
|
|
CloseContext();
|
|
}
|
|
|
|
public void WriteMessage(string message)
|
|
{
|
|
|
|
|
|
//SECPKG_ATTR_STREAM_SIZES=4
|
|
StreamSizeAttribute sizes = new StreamSizeAttribute();
|
|
int status = QueryContextAttributes(ref _hContext, 4, sizes);
|
|
_trace.TraceInformation("QueryContextAttributes for SECPKG_ATTR_STREAM_SIZES returned {0}", GetSchannelMessage(status));
|
|
|
|
if (status!=0)
|
|
throw new IOException(GetSchannelMessage(status));
|
|
|
|
|
|
byte[] dataString = Encoding.ASCII.GetBytes(message);
|
|
|
|
byte[] data = new byte[sizes.Header + dataString.Length + sizes.Trailer];
|
|
//copy the data into the buffer
|
|
Array.Copy(dataString,0,data,sizes.Header,dataString.Length);
|
|
|
|
|
|
InputBuffer input = new InputBuffer(4);
|
|
|
|
input[0].BufferType = BufferType.StreamHeader;
|
|
input[0].SetBuffer(data, 0, sizes.Header);
|
|
input[1].BufferType = BufferType.Data;
|
|
input[1].SetBuffer(data, sizes.Header, dataString.Length);
|
|
input[2].BufferType = BufferType.StreamTrailer;
|
|
input[2].SetBuffer(data, sizes.Header + dataString.Length,sizes.Trailer);
|
|
input[3].BufferType = BufferType.Empty;
|
|
|
|
status = EncryptMessage(ref _hContext, 0,input, 0);
|
|
_trace.TraceInformation("EncryptMessage returned {0}", GetSchannelMessage(status));
|
|
|
|
|
|
//send the data buffer
|
|
int total = input[0].Length + input[1].Length + input[2].Length;
|
|
if (status == 0)
|
|
_tcpClient.GetStream().Write(data, 0, total);
|
|
else
|
|
throw new IOException(GetSchannelMessage(status));
|
|
|
|
|
|
_trace.TraceInformation("Encrypted message sent {0} bytes", total);
|
|
|
|
|
|
}
|
|
|
|
private int ExpectingData(byte[] data)
|
|
{
|
|
//we have 5 bytes
|
|
if (data.Length >= 5)
|
|
{
|
|
int messageType = (int)data[0];
|
|
int majorVersion = (int)data[1];
|
|
int minorVersion = (int)data[2];
|
|
byte[] sizeArray = { data[4], data[3],0 ,0 };
|
|
int len= BitConverter.ToInt32(sizeArray, 0);
|
|
|
|
}
|
|
return 5;
|
|
}
|
|
|
|
public int ReadMessage(byte[] data,int offset,int buffLen)
|
|
{
|
|
|
|
const int SEC_E_OK = 0;
|
|
//const int SEC_E_INCOMPLETE_MESSAGE = -2146893032;
|
|
const int SEC_I_RENEGOTIATE = 590625;
|
|
|
|
int status;
|
|
InputBuffer input = new InputBuffer(4);
|
|
|
|
_trace.TraceInformation("Reading data from server");
|
|
|
|
byte[] tlsData = ReadRecord();
|
|
int copyLen = 0;
|
|
|
|
|
|
_trace.TraceInformation("Recieved {0} encrypted bytes from server", tlsData.Length);
|
|
|
|
input[0].BufferType = BufferType.Data;
|
|
input[0].SetBuffer(tlsData, 0, tlsData.Length);
|
|
input[1].BufferType = BufferType.Empty;
|
|
input[1].SetBuffer(null, 0, 0);
|
|
input[2].BufferType = BufferType.Empty;
|
|
input[2].SetBuffer(null, 0, 0);
|
|
input[3].BufferType = BufferType.Empty;
|
|
input[3].SetBuffer(null, 0, 0);
|
|
|
|
status = DecryptMessage(ref _hContext, input, 0, IntPtr.Zero);
|
|
_trace.TraceInformation("DecryptMessage returned {0}", GetSchannelMessage(status));
|
|
|
|
if (status >= 0)
|
|
{
|
|
|
|
if (status == SEC_I_RENEGOTIATE)
|
|
{
|
|
throw new IOException();
|
|
}
|
|
|
|
for (int i = 0; i < input.Count; i++)
|
|
{
|
|
if (input[i].BufferType == BufferType.Data)
|
|
{
|
|
copyLen = input[i].Length;
|
|
if (copyLen > buffLen)
|
|
{
|
|
copyLen = buffLen;
|
|
//store left over for next read
|
|
}
|
|
|
|
//result = new byte[input[i].Length];
|
|
Array.Copy(input[i].Data, input[i].Offset, data, 0, copyLen);
|
|
|
|
|
|
}
|
|
} //for input[i]
|
|
}// if status>0
|
|
|
|
return copyLen;
|
|
}
|
|
|
|
protected void SendBuffer(SecurityBuffer buffer)
|
|
{
|
|
if (buffer.Data != null && buffer.Length > 0)
|
|
{
|
|
_tcpClient.GetStream().Write(buffer.Data, buffer.Offset, buffer.Length);
|
|
}
|
|
}
|
|
|
|
public byte[] ReadRecord()
|
|
{
|
|
|
|
// for SSL Record layer potocol verying header is at least 5 bytes
|
|
byte[] data = new byte[5];
|
|
|
|
int offset=0;
|
|
int br=0;
|
|
do
|
|
{
|
|
br = _tcpClient.GetStream().Read(data, offset, data.Length-offset);
|
|
offset += br;
|
|
if (br == 0 && offset > 0)
|
|
throw new IOException("Server Unexpecity closed connection");
|
|
if (br==0 && offset==0)
|
|
return new byte[0];
|
|
} while (offset < data.Length);
|
|
|
|
//we have 5 byte
|
|
int messageType = (int)data[0];
|
|
int majorVersion = (int)data[1];
|
|
int minorVersion = (int)data[2];
|
|
byte[] sizeArray = { data[4], data[3], 0, 0 };
|
|
int len = BitConverter.ToInt32(sizeArray, 0);
|
|
byte[] temp = data;
|
|
data = new byte[len + data.Length];
|
|
Array.Copy(temp, 0, data, 0, temp.Length);
|
|
offset = 5;
|
|
br = 0;
|
|
do
|
|
{
|
|
br = _tcpClient.GetStream().Read(data, offset, data.Length - offset);
|
|
if (br == 0)
|
|
throw new IOException("Server Unexpetily disconnected");
|
|
offset += br;
|
|
}
|
|
while (offset < data.Length);
|
|
return data;
|
|
}
|
|
|
|
private void PerformClientLoop(bool doInitialRead)
|
|
{
|
|
bool doRead = doInitialRead;
|
|
|
|
int offset = 0;
|
|
long exp = 0;
|
|
|
|
uint contextAttrs;
|
|
byte[] data = new byte[_cbMaxToken]; //read from the server
|
|
|
|
SecurityHandle nullHandle = new NullSecuirtyHandle();
|
|
|
|
const int SEC_E_OK = 0;
|
|
const int SEC_I_CONTINUE_NEEDED = 590610;
|
|
const int SEC_E_INCOMPLETE_MESSAGE = -2146893032;
|
|
const int SEC_I_INCOMPLETE_CREDENTIALS = 590624;
|
|
const int ISC_RET_EXTENDED_ERROR = 16384;
|
|
|
|
int status = SEC_I_CONTINUE_NEEDED;
|
|
|
|
while (status == SEC_I_CONTINUE_NEEDED || status == SEC_I_INCOMPLETE_CREDENTIALS ||
|
|
status == SEC_E_INCOMPLETE_MESSAGE)
|
|
{
|
|
|
|
if (doRead)
|
|
{
|
|
offset += _tcpClient.GetStream().Read(data, offset, data.Length - offset);
|
|
if (offset == 0)
|
|
{
|
|
status = SEC_E_INCOMPLETE_MESSAGE;
|
|
break;
|
|
}
|
|
//ExpectingData(data);
|
|
}
|
|
doRead = true;
|
|
|
|
//
|
|
// Set up the input buffers. Buffer 0 is used to pass in data
|
|
// received from the server. Schannel will consume some or all
|
|
// of this. Leftover data (if any) will be placed in buffer 1 and
|
|
// given a buffer type of SECBUFFER_EXTRA.
|
|
//
|
|
contextAttrs = 0;
|
|
exp = 0;
|
|
|
|
ChannelOutputBuffer outBuff = new ChannelOutputBuffer();
|
|
InputBuffer inBuff = new InputBuffer(2);
|
|
inBuff[0].BufferType = BufferType.Token;
|
|
|
|
|
|
|
|
inBuff[0].SetBuffer(data, 0, offset);
|
|
inBuff[1].BufferType = BufferType.Empty;
|
|
|
|
status = InitializeSecurityContext(
|
|
ref _hCredHandle,
|
|
ref _hContext,
|
|
_target,
|
|
49436, //ISC_REQ_SEQUENCE_DETECT|ISC_REQ_REPLAY_DETECT|ISC_REQ_CONFIDENTIALITY|ISC_RET_EXTENDED_ERROR |ISC_REQ_ALLOCATE_MEMORY |ISC_REQ_STREAM;
|
|
0,
|
|
16, //SECURITY_NATIVE_DREP
|
|
inBuff,
|
|
0,
|
|
IntPtr.Zero,
|
|
outBuff,
|
|
ref contextAttrs,
|
|
ref exp);
|
|
|
|
_trace.TraceInformation("InitializeSecurityContext returned {0}", GetSchannelMessage(status));
|
|
|
|
// If InitializeSecurityContext was successful (or if the error was
|
|
// one of the special extended ones), send the contends of the output
|
|
// buffer to the server.
|
|
//
|
|
|
|
if (status == SEC_E_OK || status == SEC_I_CONTINUE_NEEDED ||
|
|
(status < 0 && (contextAttrs & ISC_RET_EXTENDED_ERROR) > 0))
|
|
{
|
|
//if (outBuff[0].Length>0) ExpectingData(outBuff[0].Data);
|
|
|
|
SendBuffer(outBuff[0]);
|
|
}
|
|
|
|
|
|
//
|
|
// If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE,
|
|
// then we need to read more data from the server and try again.
|
|
//
|
|
|
|
if (status == SEC_E_INCOMPLETE_MESSAGE)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (status == SEC_I_INCOMPLETE_CREDENTIALS)
|
|
{
|
|
//we have a problem with the credentials
|
|
}
|
|
|
|
|
|
//copy extra to offset data
|
|
int extraIndex = -1;
|
|
for (int i = 0; i < inBuff.Count; i++)
|
|
{
|
|
if (inBuff[i].BufferType == BufferType.Extra)
|
|
{
|
|
extraIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (extraIndex>=0)
|
|
{
|
|
|
|
offset = 0;
|
|
Array.Copy(inBuff[extraIndex].Data, 0, data, offset, inBuff[extraIndex].Length);
|
|
offset = inBuff[extraIndex].Length;
|
|
}
|
|
else
|
|
{
|
|
offset = 0;
|
|
}
|
|
|
|
|
|
|
|
if (status == SEC_E_OK)
|
|
{
|
|
// If the "extra" buffer contains data, this is encrypted application
|
|
// protocol layer stuff. It needs to be saved. The application layer
|
|
// will later decrypt it with DecryptMessage.
|
|
//
|
|
|
|
//value extra buffer
|
|
|
|
if (offset > 0)
|
|
throw new InvalidDataException();
|
|
|
|
break;
|
|
}
|
|
}//End while
|
|
|
|
if (status != SEC_E_OK)
|
|
{
|
|
//ToDO Fix
|
|
//_hContext.Close();
|
|
throw new Exception(GetSchannelMessage(status));
|
|
}
|
|
}
|
|
|
|
public void PerformHandshake(TcpClient tcpClient)
|
|
{
|
|
long expiers = 0;
|
|
uint contextAttr = 0;
|
|
int status = 0;
|
|
|
|
_tcpClient = tcpClient;
|
|
|
|
SecurityHandle nullHandle = new NullSecuirtyHandle();
|
|
|
|
|
|
//TODO: release previous context
|
|
|
|
|
|
if (_target == null)
|
|
_target = _tcpClient.Client.RemoteEndPoint.ToString();
|
|
|
|
|
|
ChannelOutputBuffer outBuff = new ChannelOutputBuffer();
|
|
InputBuffer inputBuff = new InputBuffer();
|
|
|
|
|
|
status = InitializeSecurityContextInit(
|
|
ref _hCredHandle,
|
|
IntPtr.Zero,
|
|
_target,
|
|
49436, //ISC_REQ_SEQUENCE_DETECT|ISC_REQ_REPLAY_DETECT|ISC_REQ_CONFIDENTIALITY|ISC_RET_EXTENDED_ERROR |ISC_REQ_ALLOCATE_MEMORY |ISC_REQ_STREAM;
|
|
0,
|
|
16, //SECURITY_NATIVE_DREP=16
|
|
IntPtr.Zero,
|
|
0,
|
|
ref _hContext,
|
|
outBuff,
|
|
ref contextAttr,
|
|
ref expiers);
|
|
|
|
|
|
|
|
_trace.TraceInformation("InitializeSecurityContext({0}) returned {1}", _target, GetSchannelMessage(status));
|
|
_trace.TraceInformation("New Context is {0}", this.GetHandleMessage(ref _hCredHandle));
|
|
|
|
// check the buffer
|
|
if (status >= 0)
|
|
SendBuffer(outBuff[0]);//send the buffer
|
|
|
|
if (status > 0)
|
|
PerformClientLoop(true);
|
|
|
|
}
|
|
|
|
public uint SChannelRequestFlags
|
|
{
|
|
|
|
get
|
|
{
|
|
//ISC_REQ_SEQUENCE_DETECT |
|
|
//ISC_REQ_REPLAY_DETECT |
|
|
//ISC_REQ_CONFIDENTIALITY |
|
|
//ISC_RET_EXTENDED_ERROR |
|
|
//ISC_REQ_ALLOCATE_MEMORY |
|
|
//ISC_REQ_STREAM;
|
|
return 49436;
|
|
}
|
|
|
|
}
|
|
|
|
private string GetHandleMessage(ref SECURITY_HANDLE handle)
|
|
{
|
|
return handle.HighPart + "," + handle.LowPart;
|
|
}
|
|
|
|
public string GetSchannelMessage(int status)
|
|
{
|
|
string result=string.Empty;
|
|
switch ((uint)status)
|
|
{
|
|
case 0x00000000:
|
|
result = "Completed successfully.";
|
|
break;
|
|
case 0x80090300:
|
|
result="Not enough memory is available to complete this request";
|
|
break;
|
|
case 0x80090301:
|
|
result="The handle specified is invalid";
|
|
break;
|
|
case 0x80090302:
|
|
result="The function requested is not supported";
|
|
break;
|
|
case 0x80090303:
|
|
result="The specified target is unknown or unreachable";
|
|
break;
|
|
case 0x80090304:
|
|
result="The Local Security Authority cannot be contacted";
|
|
break;
|
|
case 0x80090305:
|
|
result="The requested security package does not exist";
|
|
break;
|
|
case 0x80090306:
|
|
result="The caller is not the owner of the desired credentials";
|
|
break;
|
|
case 0x80090307:
|
|
result="The security package failed to initialize, and cannot be installed";
|
|
break;
|
|
case 0x80090308:
|
|
result="The token supplied to the function is invalid";
|
|
break;
|
|
case 0x80090309:
|
|
result="The security package is not able to marshall the logon buffer, so the logon attempt has failed";
|
|
break;
|
|
case 0x8009030A:
|
|
result="The per-message Quality of Protection is not supported by the security package";
|
|
break;
|
|
case 0x8009030B:
|
|
result="The security context does not allow impersonation of the client";
|
|
break;
|
|
case 0x8009030C:
|
|
result="The logon attempt failed";
|
|
break;
|
|
case 0x8009030D:
|
|
result="The credentials supplied to the package were not recognized";
|
|
break;
|
|
case 0x8009030E:
|
|
result="No credentials are available in the security package";
|
|
break;
|
|
case 0x8009030F:
|
|
result="The message supplied for verification has been altered";
|
|
break;
|
|
case 0x80090310:
|
|
result="The message supplied for verification is out of sequence";
|
|
break;
|
|
case 0x80090311:
|
|
result="No authority could be contacted for authentication.";
|
|
break;
|
|
case 0x00090312:
|
|
result="The function completed successfully, but must be called again to complete the context";
|
|
break;
|
|
case 0x00090313:
|
|
result="The function completed successfully, but CompleteToken must be called";
|
|
break;
|
|
case 0x00090314:
|
|
result="The function completed successfully, but both CompleteToken and this function must be called to complete the context";
|
|
break;
|
|
case 0x00090315:
|
|
result="The logon was completed, but no network authority was available. The logon was made using locally known information";
|
|
break;
|
|
case 0x80090316:
|
|
result="The requested security package does not exist";
|
|
break;
|
|
case 0x80090317:
|
|
result="The context has expired and can no longer be used.";
|
|
break;
|
|
case 0x80090318:
|
|
result="The supplied message is incomplete. The signature was not verified.";
|
|
break;
|
|
case 0x00090320:
|
|
result="The credentials supplied were not complete, and could not be verified. Additional information can be returned from the context.";
|
|
break;
|
|
case 0x80090321:
|
|
result="The buffers supplied to a function was too small.";
|
|
break;
|
|
case 0x00090321:
|
|
result = "The context data must be renegotiated with the peer.";
|
|
break;
|
|
case 0x80090322:
|
|
result = "The target principal name is incorrect";
|
|
break;
|
|
case 0x80090325:
|
|
result = "The certificate chain was issued by an authority that is not trusted.";
|
|
break;
|
|
case 0x80090326:
|
|
result = "The message recieved was unexpected or badly formatted.";
|
|
break;
|
|
case 0x80090327:
|
|
result = "The certificate recieved from the remote server has not validated correctly";
|
|
break;
|
|
|
|
case 0x80090329:
|
|
result = "The specified data could not be encrypted";
|
|
break;
|
|
case 0x80090330:
|
|
result = "The specified data could not be decrypted.";
|
|
break;
|
|
default:
|
|
result = "SChannel Error " + status.ToString("X8");
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Security provider for TLS handshake
|
|
/// </summary>
|
|
public static string SchannelPackageName
|
|
{
|
|
get { return "Microsoft Unified Security Protocol Provider"; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// TraceSource for Schannel Tracing
|
|
/// </summary>
|
|
public static TraceSource TraceSource
|
|
{
|
|
get
|
|
{
|
|
return _trace;
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Schannel Buffer type
|
|
/// </summary>
|
|
protected enum BufferType : int
|
|
{
|
|
Empty=0,
|
|
Data=1,
|
|
Token=2,
|
|
Params=3,
|
|
Missing=4,
|
|
Extra=5,
|
|
StreamTrailer=6,
|
|
StreamHeader=7,
|
|
Negotiate=8,
|
|
Padding=9,
|
|
Stream=10,
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// An Schannel Security buffer
|
|
/// </summary>
|
|
/// <remarks></remarks>
|
|
protected class SecurityBuffer
|
|
{
|
|
//Schannal data
|
|
int _bufferSize;
|
|
BufferType _bufferType;
|
|
byte[] _data;
|
|
// internal data
|
|
int _offset;
|
|
|
|
public SecurityBuffer()
|
|
{
|
|
}
|
|
|
|
public BufferType BufferType
|
|
{
|
|
get { return _bufferType;}
|
|
set { _bufferType=value;}
|
|
}
|
|
|
|
public byte[] Data
|
|
{
|
|
get { return _data; }
|
|
}
|
|
|
|
public int Length
|
|
{
|
|
get {return _bufferSize; }
|
|
}
|
|
|
|
public int Offset
|
|
{
|
|
get { return _offset; }
|
|
}
|
|
|
|
public void SetBuffer(byte[] data,int offset, int len)
|
|
{
|
|
_data=data;
|
|
_offset=offset;
|
|
_bufferSize=len;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// A description of schannel SecurityBuffers
|
|
/// </summary>
|
|
/// <remarks>InputBuffer,ChannelOutputBuffer inherit this</remarks>
|
|
protected class SecurityBufferDescription
|
|
{
|
|
SecurityBuffer[] _buffers;
|
|
|
|
protected SecurityBufferDescription()
|
|
{
|
|
_buffers = new SecurityBuffer[0];
|
|
}
|
|
|
|
protected SecurityBufferDescription(int size)
|
|
{
|
|
_buffers = new SecurityBuffer[size];
|
|
for (int i = 0; i < size; i++)
|
|
_buffers[i] = new SecurityBuffer();
|
|
}
|
|
|
|
public SecurityBuffer this[int index]
|
|
{
|
|
get { return _buffers[index]; }
|
|
}
|
|
|
|
public int Count
|
|
{
|
|
get { return _buffers.Length; }
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// An Schannel Input buffer
|
|
/// </summary>
|
|
/// <remarks>A buffer size of 0 will result in a null value being marshaled</remarks>
|
|
protected class InputBuffer : SecurityBufferDescription
|
|
{
|
|
|
|
public InputBuffer()
|
|
: base(0)
|
|
{
|
|
}
|
|
|
|
public InputBuffer(int size)
|
|
: base(size)
|
|
{
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// An Schannel Allocated output buffer
|
|
/// </summary>
|
|
protected class ChannelOutputBuffer : SecurityBufferDescription
|
|
{
|
|
public ChannelOutputBuffer() : base(1)
|
|
{
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Base definition of a Security Handle
|
|
/// </summary>
|
|
protected class SecurityHandle
|
|
{
|
|
protected SecurityHandle()
|
|
{
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents a Null Security Handle
|
|
/// </summary>
|
|
protected class NullSecuirtyHandle : SecurityHandle
|
|
{
|
|
public NullSecuirtyHandle()
|
|
: base()
|
|
{
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Base Security Handle with a HI/Low pointers
|
|
/// </summary>
|
|
protected class HiLoHandle : SecurityHandle
|
|
{
|
|
|
|
SECURITY_HANDLE _handle;
|
|
|
|
protected HiLoHandle()
|
|
{
|
|
_handle.HighPart = IntPtr.Zero;
|
|
_handle.LowPart = IntPtr.Zero;
|
|
}
|
|
|
|
public IntPtr HighPart
|
|
{
|
|
get { return _handle.HighPart; }
|
|
set { _handle.HighPart = value; }
|
|
|
|
}
|
|
public IntPtr LowPart
|
|
{
|
|
get { return _handle.LowPart; }
|
|
set { _handle.LowPart = value; }
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// A Security Credential Passed to AcquireCredentialsHandle()
|
|
/// </summary>
|
|
protected class UserCredentials : IDisposable
|
|
{
|
|
public virtual IntPtr MarshalToPtr() { return IntPtr.Zero; }
|
|
|
|
public virtual bool CompareTo(UserCredentials creds)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#region IDisposableMembers
|
|
|
|
private bool _disposed = false;
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
_disposed = true;
|
|
}
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
~UserCredentials()
|
|
{
|
|
Dispose(false);
|
|
}
|
|
|
|
#endregion
|
|
}//End UserCredentials
|
|
|
|
|
|
/// <summary>
|
|
/// A Null Credential Passed to AcquireCredentialsHandle()
|
|
/// </summary>
|
|
protected class NullCredential : UserCredentials
|
|
{
|
|
}// End NullCredential
|
|
|
|
/// <summary>
|
|
/// A Windows User Credential to
|
|
/// </summary>
|
|
protected class WindowsCredentials : UserCredentials
|
|
{
|
|
string _user;
|
|
string _domain;
|
|
SecureString _password;
|
|
|
|
|
|
public WindowsCredentials()
|
|
: base()
|
|
{
|
|
|
|
}
|
|
|
|
public string UserName
|
|
{
|
|
get { return _user; }
|
|
set { _user = value; }
|
|
}
|
|
|
|
public string DomainName
|
|
{
|
|
get { return _domain; }
|
|
set { _domain = value; }
|
|
}
|
|
|
|
public SecureString Password
|
|
{
|
|
get { return _password; }
|
|
|
|
set
|
|
{
|
|
_password?.Dispose();
|
|
_password = value ?? new SecureString();
|
|
}
|
|
}
|
|
public override IntPtr MarshalToPtr()
|
|
{
|
|
IntPtr ptr;
|
|
SEC_WINNT_AUTH_IDENTITY identity = new SEC_WINNT_AUTH_IDENTITY();
|
|
identity.Flags = 2;//Unicode
|
|
|
|
identity.User = _user;
|
|
if (_user == null)
|
|
identity.UserLength = 0;
|
|
else
|
|
identity.UserLength = _user.Length;
|
|
|
|
identity.Domain = _domain;
|
|
if (_domain == null)
|
|
identity.DomainLength = 0;
|
|
else
|
|
identity.DomainLength = _domain.Length;
|
|
|
|
identity.Password = _password?.Copy();
|
|
if (_password == null)
|
|
identity.PasswordLength = 0;
|
|
else
|
|
identity.PasswordLength = _password.Length;
|
|
|
|
ptr = Marshal.AllocHGlobal(Marshal.SizeOf(identity));
|
|
|
|
|
|
//old struct has nothing to dispose so (true) means nothing
|
|
Marshal.StructureToPtr(identity, ptr, false);
|
|
|
|
return ptr;
|
|
}
|
|
|
|
|
|
public override bool CompareTo(UserCredentials creds)
|
|
{
|
|
if (creds is WindowsCredentials)
|
|
{
|
|
WindowsCredentials winCreds = (WindowsCredentials)creds;
|
|
if (winCreds.DomainName == null || winCreds.UserName == null)
|
|
{
|
|
return UserName == null && DomainName == null;
|
|
|
|
}
|
|
|
|
if (winCreds.UserName.Equals(UserName) &&
|
|
winCreds.DomainName.Equals(DomainName))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return base.CompareTo(creds);
|
|
}
|
|
|
|
|
|
#region IDisposableMembers
|
|
|
|
private bool _disposed = false;
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
if (_disposed)
|
|
return;
|
|
|
|
base.Dispose(disposing);
|
|
|
|
if (disposing)
|
|
{
|
|
_password?.Dispose();
|
|
}
|
|
_disposed = true;
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|
|
|
|
protected class CertificateCredentials : UserCredentials
|
|
{
|
|
X509Certificate2 _cert;
|
|
uint _flags;
|
|
|
|
public CertificateCredentials()
|
|
: base()
|
|
{
|
|
|
|
}
|
|
|
|
public override IntPtr MarshalToPtr()
|
|
{
|
|
IntPtr ptr = IntPtr.Zero;
|
|
if (_cert != null || _flags != 0)
|
|
{
|
|
SCHANNEL_CRED cred = new SCHANNEL_CRED();
|
|
cred.dwVersion = 4;//SCHANNEL_CRED_VERSION=4
|
|
|
|
cred.dwFlags = _flags; //manual verification
|
|
//cred.dwFlags = 262168;
|
|
cred.cCreds = 0;
|
|
|
|
if (_cert != null) cred.cCreds = 1;
|
|
|
|
ptr = Marshal.AllocHGlobal(Marshal.SizeOf(cred) + (IntPtr.Size * (int)cred.cCreds));
|
|
|
|
|
|
|
|
if (cred.cCreds > 0)
|
|
{
|
|
cred.paCred = new IntPtr(ptr.ToInt64() + Marshal.SizeOf(cred));
|
|
}
|
|
|
|
Marshal.StructureToPtr(cred, ptr, false);
|
|
|
|
if (cred.cCreds > 0)
|
|
{
|
|
|
|
Marshal.WriteIntPtr(cred.paCred, _cert.Handle);
|
|
IntPtr tt = Marshal.ReadIntPtr(cred.paCred);
|
|
}
|
|
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
|
|
|
|
public X509Certificate2 Certificate
|
|
{
|
|
get { return _cert; }
|
|
set { _cert = value; }
|
|
}
|
|
|
|
|
|
public void SendRoot()
|
|
{
|
|
//SCH_CRED_NO_DEFAULT_CREDS=0x00000010;
|
|
//SCH_SEND_ROOT_CERT=0x00040000
|
|
_flags = _flags |0x00040000 | 0x00000010;
|
|
}
|
|
|
|
public void SkipCNCheck()
|
|
{
|
|
//SCH_CRED_NO_DEFAULT_CREDS=0x00000010;
|
|
//SCH_CRED_NO_SERVERNAME_CHECK = 0x00000004;
|
|
_flags = _flags |0x00000004 | 0x00000010;
|
|
}
|
|
|
|
public void SkipCACheck()
|
|
{
|
|
//SCH_CRED_NO_DEFAULT_CREDS=0x00000010;
|
|
//SCH_CRED_MANUAL_CRED_VALIDATION=0x00000008
|
|
_flags = _flags |0x00000008 | 0x00000010;
|
|
}
|
|
|
|
public void SkipDefaultCreds()
|
|
{
|
|
//SCH_CRED_NO_DEFAULT_CREDS=0x00000010;
|
|
//SCH_CRED_MANUAL_CRED_VALIDATION=0x00000008
|
|
_flags = 0x00000010;
|
|
}
|
|
|
|
|
|
public override bool CompareTo(UserCredentials creds)
|
|
{
|
|
|
|
bool certMatch = false;
|
|
bool flagMatch = false;
|
|
if (creds is CertificateCredentials)
|
|
{
|
|
CertificateCredentials certCreds = (CertificateCredentials)creds;
|
|
|
|
if (certCreds.Certificate != null && _cert != null)
|
|
{
|
|
certMatch = certCreds.Certificate.Thumbprint.Equals(_cert.Thumbprint);
|
|
}
|
|
else
|
|
{
|
|
certMatch = certCreds.Certificate == null && _cert == null;
|
|
}
|
|
|
|
flagMatch = certCreds._flags ==_flags;
|
|
|
|
|
|
}
|
|
return certMatch && flagMatch;
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// A Cached Credential Item
|
|
/// </summary>
|
|
protected class CacheItem
|
|
{
|
|
IntPtr _highPtr;
|
|
IntPtr _lowPtr;
|
|
UserCredentials _credentials;
|
|
long _expires;
|
|
|
|
public CacheItem(IntPtr hiPtr,IntPtr lowPtr, UserCredentials auth, long expires)
|
|
{
|
|
_highPtr = hiPtr;
|
|
_lowPtr = lowPtr;
|
|
_credentials = auth;
|
|
_expires = expires;
|
|
}
|
|
|
|
public UserCredentials Credentials
|
|
{
|
|
get { return _credentials; }
|
|
}
|
|
|
|
public IntPtr HighPart
|
|
{
|
|
get { return _highPtr; }
|
|
}
|
|
|
|
public IntPtr LowPart
|
|
{
|
|
get { return _lowPtr; }
|
|
}
|
|
|
|
public long Expiers
|
|
{
|
|
get { return _expires; }
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// A Security Context Attribute
|
|
/// </summary>
|
|
protected class SecurityAttribute
|
|
{
|
|
protected SecurityAttribute()
|
|
: base()
|
|
{
|
|
}
|
|
|
|
public virtual void FreeNavtive(IntPtr ptr)
|
|
{
|
|
Marshal.FreeHGlobal(ptr);
|
|
}
|
|
|
|
public virtual IntPtr MarshalToNavtive()
|
|
{
|
|
return IntPtr.Zero;
|
|
}
|
|
|
|
public virtual void MarshalFromNavtive(IntPtr ptr)
|
|
{
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// A Security Context Attribute
|
|
/// </summary>
|
|
protected class StreamSizeAttribute : SecurityAttribute
|
|
{
|
|
SecPkgContext_StreamSizes _attribute;
|
|
public StreamSizeAttribute()
|
|
: base()
|
|
{
|
|
}
|
|
|
|
public override IntPtr MarshalToNavtive()
|
|
{
|
|
|
|
IntPtr result = Marshal.AllocHGlobal(Marshal.SizeOf(_attribute));
|
|
return result;
|
|
}
|
|
|
|
public override void MarshalFromNavtive(IntPtr ptr)
|
|
{
|
|
_attribute = (SecPkgContext_StreamSizes)Marshal.PtrToStructure(ptr, typeof(SecPkgContext_StreamSizes));
|
|
}
|
|
|
|
public int Header
|
|
{
|
|
get { return (int)_attribute.cbHeader; }
|
|
}
|
|
|
|
public int Trailer
|
|
{
|
|
get { return (int)_attribute.cbTrailer; }
|
|
}
|
|
|
|
public int MaximumMessage
|
|
{
|
|
get { return (int)_attribute.cbMaximumMessage; }
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Custom Marshaler for Schannel API Structures
|
|
/// </summary>
|
|
/// <remarks>MashalCookie is used to select Marshaler for the specific type</remarks>
|
|
|
|
protected class ChannelMarshaler : ICustomMarshaler
|
|
{
|
|
object _objectToMashal; // the managed object being marshaled
|
|
|
|
public ChannelMarshaler()
|
|
{
|
|
}
|
|
|
|
public int GetNativeDataSize()
|
|
{
|
|
return -1;//never gets called?
|
|
}
|
|
|
|
public void CleanUpManagedData(object managedObject)
|
|
{
|
|
// nothing to clean up
|
|
}
|
|
|
|
public void CleanUpNativeData(IntPtr ptr)
|
|
{
|
|
if (!ptr.Equals(IntPtr.Zero)) Marshal.FreeCoTaskMem(ptr);
|
|
}
|
|
|
|
public IntPtr MarshalManagedToNative(object managedObject)
|
|
{
|
|
IntPtr result=IntPtr.Zero;
|
|
|
|
if (managedObject is NullSecuirtyHandle)
|
|
{
|
|
_objectToMashal = managedObject;
|
|
}
|
|
else if (managedObject is HiLoHandle)
|
|
{
|
|
|
|
|
|
_objectToMashal = managedObject;
|
|
|
|
int x = Marshal.SizeOf(typeof(SECURITY_HANDLE));
|
|
x.ToString();
|
|
|
|
result = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(SECURITY_HANDLE)));
|
|
|
|
Marshal.WriteIntPtr(result, 0, ((HiLoHandle)managedObject).LowPart);
|
|
Marshal.WriteIntPtr(result, IntPtr.Size, ((HiLoHandle)managedObject).HighPart);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public object MarshalNativeToManaged(IntPtr ptr)
|
|
{
|
|
|
|
if (_objectToMashal is NullSecuirtyHandle)
|
|
{
|
|
|
|
}
|
|
else if (_objectToMashal is HiLoHandle)
|
|
{
|
|
|
|
((HiLoHandle)_objectToMashal).LowPart = Marshal.ReadIntPtr(ptr, 0 );
|
|
((HiLoHandle)_objectToMashal).HighPart = Marshal.ReadIntPtr(ptr, IntPtr.Size );
|
|
}
|
|
|
|
|
|
return _objectToMashal;
|
|
}
|
|
|
|
public static ICustomMarshaler GetInstance(string cookie)
|
|
{
|
|
ICustomMarshaler result=null;
|
|
|
|
switch (cookie)
|
|
{
|
|
case "Input":
|
|
result = new InputBufferMarshaler();
|
|
break;
|
|
case "Output":
|
|
result = new OutputBufferMarshaler();
|
|
break;
|
|
case "Handle":
|
|
result = new ChannelMarshaler();
|
|
break;
|
|
case "Attribute":
|
|
result = new AttributeMarshaler();
|
|
break;
|
|
case "Credential":
|
|
result = new CredentialMarshaler();
|
|
break;
|
|
default:
|
|
throw new ArgumentException("Unknown Schannel Marshal Cookie");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Attribute Marshaler for Schannel API Structures
|
|
/// </summary>
|
|
/// <remarks>MashalCookie is used to select Marshaler for the specific type</remarks>
|
|
protected class AttributeMarshaler : ICustomMarshaler
|
|
{
|
|
SecurityAttribute _objectToMarshal;
|
|
|
|
public AttributeMarshaler()
|
|
{
|
|
}
|
|
|
|
public int GetNativeDataSize()
|
|
{
|
|
return -1;//never gets called?
|
|
}
|
|
|
|
public void CleanUpManagedData(object managedObject)
|
|
{
|
|
_objectToMarshal = null;
|
|
}
|
|
|
|
public void CleanUpNativeData(IntPtr ptr)
|
|
{
|
|
_objectToMarshal.FreeNavtive(ptr);
|
|
}
|
|
|
|
public IntPtr MarshalManagedToNative(object managedObject)
|
|
{
|
|
_objectToMarshal = (SecurityAttribute)managedObject;
|
|
return _objectToMarshal.MarshalToNavtive();
|
|
}
|
|
|
|
public object MarshalNativeToManaged(IntPtr ptr)
|
|
{
|
|
_objectToMarshal.MarshalFromNavtive(ptr);
|
|
return _objectToMarshal;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Credential Marshaler for Schannel API Structures
|
|
/// </summary>
|
|
/// <remarks>MashalCookie is used to select Marshaler for the specific type</remarks>
|
|
protected class CredentialMarshaler : ICustomMarshaler
|
|
{
|
|
|
|
|
|
public CredentialMarshaler()
|
|
{
|
|
}
|
|
|
|
public int GetNativeDataSize()
|
|
{
|
|
return -1;//never gets called?
|
|
}
|
|
|
|
public void CleanUpManagedData(object managedObject)
|
|
{
|
|
|
|
}
|
|
|
|
public void CleanUpNativeData(IntPtr ptr)
|
|
{
|
|
//_objectToMarshal.FreeNavtive(ptr);
|
|
if (!ptr.Equals(IntPtr.Zero))
|
|
Marshal.FreeHGlobal(ptr);
|
|
}
|
|
|
|
public IntPtr MarshalManagedToNative(object managedObject)
|
|
{
|
|
UserCredentials userCreds = (UserCredentials)managedObject;
|
|
return userCreds.MarshalToPtr();
|
|
}
|
|
|
|
public object MarshalNativeToManaged(IntPtr ptr)
|
|
{
|
|
//_objectToMarshal.MarshalFromNavtive(ptr);
|
|
//return _objectToMarshal;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Mashaler for Schannel Allocated OuputBuffers
|
|
/// </summary>
|
|
internal class OutputBufferMarshaler : ICustomMarshaler
|
|
{
|
|
ChannelOutputBuffer _objectToMarshal;
|
|
|
|
public OutputBufferMarshaler()
|
|
{
|
|
}
|
|
|
|
public int GetNativeDataSize()
|
|
{
|
|
return -1;//never gets called?
|
|
}
|
|
|
|
public void CleanUpManagedData(object managedObject)
|
|
{
|
|
// nothing to clean up
|
|
}
|
|
|
|
public void CleanUpNativeData(IntPtr ptr)
|
|
{
|
|
Marshal.FreeHGlobal(ptr);
|
|
}
|
|
|
|
public IntPtr MarshalManagedToNative(object managedObject)
|
|
{
|
|
IntPtr result = IntPtr.Zero;
|
|
_objectToMarshal = (ChannelOutputBuffer)managedObject;
|
|
|
|
// alocate enough memory
|
|
result = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SecBufferDesc)) + Marshal.SizeOf(typeof(SecBuffer)));
|
|
|
|
//write the description
|
|
Marshal.WriteInt32(result, 0, 0); //write SECBUFFER_VERSION
|
|
Marshal.WriteInt32(result, sizeof(uint), 1); //write buffer count
|
|
|
|
//create an array pointer to a buffer
|
|
IntPtr pBuffer = new IntPtr(result.ToInt64() + Marshal.SizeOf(typeof(SecBufferDesc)));
|
|
Marshal.WriteInt32(pBuffer, 0, 0); //write buffer size
|
|
Marshal.WriteInt32(pBuffer, sizeof(uint), (int)BufferType.Token); //write buffer type
|
|
Marshal.WriteIntPtr(pBuffer, sizeof(uint) + sizeof(uint), IntPtr.Zero); //write pointer
|
|
|
|
//write the array pointer
|
|
Marshal.WriteIntPtr(result, sizeof(uint) + sizeof(uint), pBuffer); //write buffer count
|
|
|
|
return result;
|
|
}
|
|
|
|
public object MarshalNativeToManaged(IntPtr ptr)
|
|
{
|
|
//read the description
|
|
int buffSize = Marshal.ReadInt32(ptr, sizeof(uint)); //read buffer count
|
|
IntPtr pBuffers = Marshal.ReadIntPtr(ptr, sizeof(uint) + sizeof(uint)); //read buffer
|
|
|
|
int cb = Marshal.ReadInt32(pBuffers, 0); //read buffer size
|
|
IntPtr pCtxData = Marshal.ReadIntPtr(pBuffers, sizeof(uint) + sizeof(uint)); //read buffer
|
|
|
|
ChannelOutputBuffer result = _objectToMarshal;
|
|
|
|
if (result==null)
|
|
result = new ChannelOutputBuffer();
|
|
|
|
if (!pCtxData.Equals(IntPtr.Zero) && cb > 0)
|
|
{
|
|
byte[] data = new byte[cb];
|
|
Marshal.Copy(pCtxData, data, 0, data.Length);
|
|
int status=FreeContextBuffer(pCtxData);
|
|
result[0].SetBuffer(data,0,cb);
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Mashaler for Schannel InputBuffers
|
|
/// </summary>
|
|
internal class InputBufferMarshaler : ICustomMarshaler
|
|
{
|
|
InputBuffer _objectToMarshal;
|
|
|
|
public InputBufferMarshaler()
|
|
{
|
|
}
|
|
|
|
public int GetNativeDataSize()
|
|
{
|
|
return -1; //never gets called?
|
|
}
|
|
|
|
public void CleanUpManagedData(object managedObject)
|
|
{
|
|
}
|
|
|
|
public void CleanUpNativeData(IntPtr ptr)
|
|
{
|
|
if (!ptr.Equals(IntPtr.Zero)) Marshal.FreeHGlobal(ptr);
|
|
}
|
|
|
|
|
|
|
|
public IntPtr MarshalManagedToNative(object managedObject)
|
|
{
|
|
IntPtr result = IntPtr.Zero;
|
|
|
|
_objectToMarshal = (InputBuffer)managedObject;
|
|
|
|
if (_objectToMarshal.Count>0)
|
|
{
|
|
//calc total size
|
|
int total = Marshal.SizeOf(typeof(SecBufferDesc));
|
|
total += Marshal.SizeOf(typeof(SecBuffer))*_objectToMarshal.Count;
|
|
|
|
int dataLen = 0;
|
|
|
|
//get size of all buffers
|
|
for (int i=0;i<_objectToMarshal.Count;i++)
|
|
dataLen+=_objectToMarshal[i].Length;
|
|
|
|
total += dataLen;
|
|
|
|
result = Marshal.AllocHGlobal(total);
|
|
|
|
for (int i = 0; i < total; i++)
|
|
Marshal.WriteByte(result, i, (byte)0);
|
|
|
|
|
|
//write the description
|
|
Marshal.WriteInt32(result, 0, 0); //write SECBUFFER_VERSION
|
|
Marshal.WriteInt32(result, sizeof(uint), _objectToMarshal.Count); //write buffer count
|
|
|
|
//create an array pointer to a buffer
|
|
IntPtr pBuffer = new IntPtr(result.ToInt64() + Marshal.SizeOf(typeof(SecBufferDesc)));
|
|
Marshal.WriteIntPtr(result, sizeof(uint)+sizeof(uint), pBuffer); //write buffer
|
|
|
|
|
|
int arrayOffset =0;
|
|
int dataOffet = Marshal.SizeOf(typeof(SecBufferDesc))+(Marshal.SizeOf(typeof(SecBuffer))*_objectToMarshal.Count);
|
|
byte[] baseData = null;
|
|
|
|
for (int i=0;i<_objectToMarshal.Count;i++)
|
|
{
|
|
//write buffer size
|
|
Marshal.WriteInt32(pBuffer, arrayOffset,_objectToMarshal[i].Length);
|
|
//write buffer type
|
|
Marshal.WriteInt32(pBuffer, arrayOffset +sizeof(uint),(int)_objectToMarshal[i].BufferType);
|
|
|
|
if (i == 0)
|
|
baseData = _objectToMarshal[i].Data;
|
|
|
|
|
|
|
|
//write buffer Pointer
|
|
if (_objectToMarshal[i].Length>0)
|
|
{
|
|
IntPtr dataPtr = new IntPtr(result.ToInt64()+dataOffet);
|
|
//only copy the data if its not null (otherwise its uninitalized data)
|
|
Marshal.Copy(
|
|
baseData,
|
|
_objectToMarshal[i].Offset,
|
|
dataPtr,
|
|
_objectToMarshal[i].Length);
|
|
|
|
|
|
|
|
|
|
//write pointer
|
|
Marshal.WriteIntPtr(pBuffer, arrayOffset+ sizeof(uint) + sizeof(uint), dataPtr);
|
|
}
|
|
else
|
|
Marshal.WriteIntPtr(pBuffer,arrayOffset+ sizeof(uint) + sizeof(uint), IntPtr.Zero);
|
|
|
|
arrayOffset+=Marshal.SizeOf(typeof(SecBuffer));
|
|
dataOffet+=_objectToMarshal[i].Length;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
public object MarshalNativeToManaged(IntPtr ptr)
|
|
{
|
|
//read the description
|
|
int buffNums = Marshal.ReadInt32(ptr, sizeof(uint)); //read buffer count
|
|
IntPtr pBuffers = Marshal.ReadIntPtr(ptr, sizeof(uint) + sizeof(uint)); //read buffer
|
|
|
|
InputBuffer result = _objectToMarshal;
|
|
|
|
int arrayOffset =0;
|
|
long baseLong=0;
|
|
byte[] baseData = null;
|
|
|
|
int prevLen=0;
|
|
IntPtr prevBuffer=IntPtr.Zero;
|
|
|
|
for (int i = 0; i < buffNums; i++)
|
|
{
|
|
//read buffer size
|
|
int buffSize = Marshal.ReadInt32(pBuffers, arrayOffset);
|
|
BufferType buffType = (BufferType)Marshal.ReadInt32(pBuffers, arrayOffset +sizeof(uint));
|
|
IntPtr buffPtr = Marshal.ReadIntPtr(pBuffers, arrayOffset+ sizeof(uint) + sizeof(uint));
|
|
|
|
_objectToMarshal[i].BufferType = buffType;
|
|
|
|
// the buffer is the managed data and Navtive data
|
|
if (buffType == BufferType.Extra)
|
|
{
|
|
|
|
int of = prevLen - buffSize;
|
|
baseLong = prevBuffer.ToInt64()+of;
|
|
buffPtr = new IntPtr( baseLong);
|
|
|
|
}
|
|
|
|
if (i == 0)
|
|
{
|
|
baseLong = buffPtr.ToInt64();
|
|
baseData = _objectToMarshal[i].Data;
|
|
prevLen = buffSize;
|
|
prevBuffer = buffPtr;
|
|
}
|
|
|
|
try
|
|
{
|
|
_objectToMarshal[i].SetBuffer(baseData, (int)(buffPtr.ToInt64() - baseLong), buffSize);//Conversion from long to int
|
|
}
|
|
catch (System.OverflowException e)
|
|
{
|
|
throw new System.OverflowException(e.ToString());
|
|
}
|
|
|
|
if (buffSize > 0 && !buffPtr.Equals(IntPtr.Zero))
|
|
{
|
|
|
|
|
|
Marshal.Copy(buffPtr,
|
|
baseData,
|
|
_objectToMarshal[i].Offset,
|
|
buffSize);
|
|
|
|
|
|
}
|
|
|
|
_objectToMarshal[i].BufferType = buffType; // update the buffer type (should be token ot Extra)
|
|
arrayOffset += Marshal.SizeOf(typeof(SecBuffer));
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
[DllImport("secur32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
private static extern int AcquireCredentialsHandle(
|
|
string pszPrincipal,
|
|
string pszPackage,
|
|
int fCredentialUse,
|
|
IntPtr PAuthenticationID,
|
|
[In, MarshalAs(UnmanagedType.CustomMarshaler,
|
|
MarshalTypeRef = typeof(ChannelMarshaler), MarshalCookie = "Credential")]
|
|
UserCredentials pAuthData,
|
|
IntPtr pGetKeyFn,
|
|
IntPtr pvGetKeyArgument,
|
|
ref SECURITY_HANDLE phCredential,
|
|
ref long ptsExpiry);
|
|
|
|
[DllImport("secur32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
private static extern int InitializeSecurityContext(
|
|
ref SECURITY_HANDLE phCredential,
|
|
ref SECURITY_HANDLE phContext,
|
|
string pszTargetName,
|
|
uint fContextReq,
|
|
uint Reserved1,
|
|
uint TargetDataRep,
|
|
[In, Out, MarshalAs(UnmanagedType.CustomMarshaler,
|
|
MarshalTypeRef = typeof(ChannelMarshaler), MarshalCookie = "Input")]
|
|
SecurityBufferDescription pInput,
|
|
uint Reserved2,
|
|
IntPtr hNewContext,
|
|
[In,Out,MarshalAs(UnmanagedType.CustomMarshaler,
|
|
MarshalTypeRef = typeof(ChannelMarshaler), MarshalCookie = "Output")]
|
|
SecurityBufferDescription pOutput,
|
|
ref uint pfContextAttr,
|
|
ref long ptsExpiry);
|
|
|
|
|
|
[DllImport("secur32.dll", EntryPoint = "InitializeSecurityContext", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
private static extern int InitializeSecurityContextInit(
|
|
ref SECURITY_HANDLE phCredential,
|
|
IntPtr phContext, //this is null on input
|
|
string pszTargetName,
|
|
uint fContextReq,
|
|
uint Reserved1,
|
|
uint TargetDataRep,
|
|
|
|
IntPtr pInput, //this is null on init call
|
|
uint Reserved2,
|
|
ref SECURITY_HANDLE hNewContext, // this is output
|
|
[In, Out, MarshalAs(UnmanagedType.CustomMarshaler,
|
|
MarshalTypeRef = typeof(ChannelMarshaler), MarshalCookie = "Output")]
|
|
SecurityBufferDescription pOutput,
|
|
ref uint pfContextAttr,
|
|
ref long ptsExpiry);
|
|
|
|
[DllImport("secur32.dll", EntryPoint = "InitializeSecurityContext", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
private static extern int InitializeSecurityContextExchange(
|
|
ref SECURITY_HANDLE phCredential,
|
|
IntPtr pContext, //this is null on exhange
|
|
string pszTargetName,
|
|
uint fContextReq,
|
|
uint Reserved1,
|
|
uint TargetDataRep,
|
|
[In, Out, MarshalAs(UnmanagedType.CustomMarshaler,
|
|
MarshalTypeRef = typeof(ChannelMarshaler), MarshalCookie = "Input")]
|
|
SecurityBufferDescription pInput,
|
|
uint Reserved2,
|
|
ref SECURITY_HANDLE hNewContext, // this is output
|
|
[In, Out, MarshalAs(UnmanagedType.CustomMarshaler,
|
|
MarshalTypeRef = typeof(ChannelMarshaler), MarshalCookie = "Output")]
|
|
SecurityBufferDescription pOutput,
|
|
ref uint pfContextAttr,
|
|
ref long ptsExpiry);
|
|
|
|
|
|
[DllImport("secur32.dll", SetLastError = true)]
|
|
private static extern int EncryptMessage(
|
|
ref SECURITY_HANDLE hContext,
|
|
uint fQoP,
|
|
[In,Out, MarshalAs(UnmanagedType.CustomMarshaler,
|
|
MarshalTypeRef = typeof(ChannelMarshaler), MarshalCookie = "Input")]
|
|
SecurityBufferDescription buffer,
|
|
ulong seqNum);
|
|
|
|
[DllImport("secur32.dll", SetLastError = true)]
|
|
private static extern int DecryptMessage(
|
|
ref SECURITY_HANDLE hContext,
|
|
[In, Out, MarshalAs(UnmanagedType.CustomMarshaler,
|
|
MarshalTypeRef = typeof(ChannelMarshaler), MarshalCookie = "Input")]
|
|
SecurityBufferDescription buffer,
|
|
ulong seqNum,
|
|
IntPtr fQoP
|
|
);
|
|
|
|
[DllImport("secur32.dll", SetLastError = true)]
|
|
private static extern int ApplyControlToken(
|
|
ref SECURITY_HANDLE hContext,
|
|
[In, Out, MarshalAs(UnmanagedType.CustomMarshaler,
|
|
MarshalTypeRef = typeof(ChannelMarshaler), MarshalCookie = "Input")]
|
|
SecurityBufferDescription buffer);
|
|
|
|
[DllImport("secur32.dll", SetLastError = true)]
|
|
private static extern int FreeContextBuffer(IntPtr ptrToBuffer);
|
|
|
|
[DllImport("secur32.dll", SetLastError = true)]
|
|
private static extern int DeleteSecurityContext(
|
|
ref SECURITY_HANDLE hContext);
|
|
|
|
[DllImport("secur32.dll", SetLastError = true)]
|
|
private static extern int FreeCredentialsHandle(
|
|
ref SECURITY_HANDLE phCredHandle);
|
|
|
|
[DllImport("secur32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
private static extern int QuerySecurityPackageInfo(
|
|
string packageName,
|
|
out IntPtr packageInfo);
|
|
|
|
[DllImport("secur32.dll", SetLastError = true)]
|
|
private static extern int QueryContextAttributes(
|
|
ref SECURITY_HANDLE hContext,
|
|
uint ulAttribute,
|
|
[In,Out, MarshalAs(UnmanagedType.CustomMarshaler,
|
|
MarshalTypeRef = typeof(ChannelMarshaler), MarshalCookie = "Attribute")]
|
|
SecurityAttribute buffer);
|
|
|
|
|
|
|
|
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
private struct SecPkgContext_StreamSizes
|
|
{
|
|
public uint cbHeader;
|
|
public uint cbTrailer;
|
|
public uint cbMaximumMessage;
|
|
public uint cBuffers;
|
|
public uint cbBlockSize;
|
|
}
|
|
|
|
|
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
|
private struct SEC_WINNT_AUTH_IDENTITY
|
|
{
|
|
public string User;
|
|
public int UserLength;
|
|
|
|
public string Domain;
|
|
public int DomainLength;
|
|
|
|
public SecureString Password;
|
|
public int PasswordLength;
|
|
|
|
public int Flags;//UNICODE=2,ASNI=1
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
|
|
private struct SecPkgInfo
|
|
{
|
|
public uint fCapabilities;
|
|
public ushort wVersion;
|
|
public ushort wRPCID;
|
|
public uint cbMaxToken;
|
|
public string Name;
|
|
public string Comment;
|
|
|
|
public static SecPkgInfo FromIntPtr(IntPtr ptr)
|
|
{
|
|
return (SecPkgInfo)Marshal.PtrToStructure(ptr, typeof(SecPkgInfo));
|
|
}
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
private struct SCHANNEL_CRED
|
|
{
|
|
public uint dwVersion;
|
|
public uint cCreds;
|
|
public IntPtr paCred;
|
|
public IntPtr hRootStore;
|
|
public uint cMappers;
|
|
public IntPtr aphMappers;
|
|
public uint cSupportedAlgs;
|
|
public IntPtr palgSupportedAlgs;
|
|
public uint grbitEnabledProtocols;
|
|
public uint dwMinimumCipherStrength;
|
|
public uint dwMaximumCipherStrength;
|
|
public uint dwSessionLifespan;
|
|
public uint dwFlags;
|
|
public uint dwCredFormat;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
private struct SECURITY_HANDLE
|
|
{
|
|
public IntPtr LowPart;
|
|
public IntPtr HighPart;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
private struct SecBufferDesc
|
|
{
|
|
public uint ulVersion;// Version number SECBUFFER_VERSION=0
|
|
public uint cBuffers;// Number of buffers
|
|
public IntPtr pBuffers;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
private struct SecBuffer
|
|
{
|
|
public uint cbBuffer;
|
|
public uint BufferType;
|
|
public IntPtr pvBuffer;
|
|
|
|
public static IntPtr Create(int size)
|
|
{
|
|
SecBuffer outBuffer = new SecBuffer();
|
|
IntPtr result = Marshal.AllocHGlobal(Marshal.SizeOf(outBuffer)*size);
|
|
//zero fill
|
|
for (int i = 0; i < (Marshal.SizeOf(outBuffer) * size); i++)
|
|
Marshal.WriteByte(result, i, 0);
|
|
|
|
return result;
|
|
}
|
|
}
|
|
} //end class
|
|
|
|
|
|
|
|
|
|
|
|
}//end namespace
|