|
//------------------------------------------------------------------------------
// <copyright file="_NTAuthentication.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Net {
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Threading;
using System.Globalization;
using System.Security.Authentication.ExtendedProtection;
using System.Security.Principal;
using System.Security.Permissions;
using System.Net.Security;
// #define ISC_REQ_DELEGATE 0x00000001
// #define ISC_REQ_MUTUAL_AUTH 0x00000002
// #define ISC_REQ_REPLAY_DETECT 0x00000004
// #define ISC_REQ_SEQUENCE_DETECT 0x00000008
// #define ISC_REQ_CONFIDENTIALITY 0x00000010
// #define ISC_REQ_USE_SESSION_KEY 0x00000020
// #define ISC_REQ_PROMPT_FOR_CREDS 0x00000040
// #define ISC_REQ_USE_SUPPLIED_CREDS 0x00000080
// #define ISC_REQ_ALLOCATE_MEMORY 0x00000100
// #define ISC_REQ_USE_DCE_STYLE 0x00000200
// #define ISC_REQ_DATAGRAM 0x00000400
// #define ISC_REQ_CONNECTION 0x00000800
// #define ISC_REQ_CALL_LEVEL 0x00001000
// #define ISC_REQ_FRAGMENT_SUPPLIED 0x00002000
// #define ISC_REQ_EXTENDED_ERROR 0x00004000
// #define ISC_REQ_STREAM 0x00008000
// #define ISC_REQ_INTEGRITY 0x00010000
// #define ISC_REQ_IDENTIFY 0x00020000
// #define ISC_REQ_NULL_SESSION 0x00040000
// #define ISC_REQ_MANUAL_CRED_VALIDATION 0x00080000
// #define ISC_REQ_RESERVED1 0x00100000
// #define ISC_REQ_FRAGMENT_TO_FIT 0x00200000
// #define ISC_REQ_HTTP 0x10000000
// Win7 SP1 +
// #define ISC_REQ_UNVERIFIED_TARGET_NAME 0x20000000
// #define ASC_REQ_DELEGATE 0x00000001
// #define ASC_REQ_MUTUAL_AUTH 0x00000002
// #define ASC_REQ_REPLAY_DETECT 0x00000004
// #define ASC_REQ_SEQUENCE_DETECT 0x00000008
// #define ASC_REQ_CONFIDENTIALITY 0x00000010
// #define ASC_REQ_USE_SESSION_KEY 0x00000020
// #define ASC_REQ_ALLOCATE_MEMORY 0x00000100
// #define ASC_REQ_USE_DCE_STYLE 0x00000200
// #define ASC_REQ_DATAGRAM 0x00000400
// #define ASC_REQ_CONNECTION 0x00000800
// #define ASC_REQ_CALL_LEVEL 0x00001000
// #define ASC_REQ_EXTENDED_ERROR 0x00008000
// #define ASC_REQ_STREAM 0x00010000
// #define ASC_REQ_INTEGRITY 0x00020000
// #define ASC_REQ_LICENSING 0x00040000
// #define ASC_REQ_IDENTIFY 0x00080000
// #define ASC_REQ_ALLOW_NULL_SESSION 0x00100000
// #define ASC_REQ_ALLOW_NON_USER_LOGONS 0x00200000
// #define ASC_REQ_ALLOW_CONTEXT_REPLAY 0x00400000
// #define ASC_REQ_FRAGMENT_TO_FIT 0x00800000
// #define ASC_REQ_FRAGMENT_SUPPLIED 0x00002000
// #define ASC_REQ_NO_TOKEN 0x01000000
// #define ASC_REQ_HTTP 0x10000000
[Flags]
internal enum ContextFlags {
Zero = 0,
// The server in the transport application can
// build new security contexts impersonating the
// client that will be accepted by other servers
// as the client's contexts.
Delegate = 0x00000001,
// The communicating parties must authenticate
// their identities to each other. Without MutualAuth,
// the client authenticates its identity to the server.
// With MutualAuth, the server also must authenticate
// its identity to the client.
MutualAuth = 0x00000002,
// The security package detects replayed packets and
// notifies the caller if a packet has been replayed.
// The use of this flag implies all of the conditions
// specified by the Integrity flag.
ReplayDetect = 0x00000004,
// The context must be allowed to detect out-of-order
// delivery of packets later through the message support
// functions. Use of this flag implies all of the
// conditions specified by the Integrity flag.
SequenceDetect = 0x00000008,
// The context must protect data while in transit.
// Confidentiality is supported for NTLM with Microsoft
// Windows NT version 4.0, SP4 and later and with the
// Kerberos protocol in Microsoft Windows 2000 and later.
Confidentiality = 0x00000010,
UseSessionKey = 0x00000020,
AllocateMemory = 0x00000100,
// Connection semantics must be used.
Connection = 0x00000800,
// Client applications requiring extended error messages specify the
// ISC_REQ_EXTENDED_ERROR flag when calling the InitializeSecurityContext
// Server applications requiring extended error messages set
// the ASC_REQ_EXTENDED_ERROR flag when calling AcceptSecurityContext.
InitExtendedError = 0x00004000,
AcceptExtendedError = 0x00008000,
// A transport application requests stream semantics
// by setting the ISC_REQ_STREAM and ASC_REQ_STREAM
// flags in the calls to the InitializeSecurityContext
// and AcceptSecurityContext functions
InitStream = 0x00008000,
AcceptStream = 0x00010000,
// Buffer integrity can be verified; however, replayed
// and out-of-sequence messages will not be detected
InitIntegrity = 0x00010000, // ISC_REQ_INTEGRITY
AcceptIntegrity = 0x00020000, // ASC_REQ_INTEGRITY
InitManualCredValidation = 0x00080000, // ISC_REQ_MANUAL_CRED_VALIDATION
InitUseSuppliedCreds = 0x00000080, // ISC_REQ_USE_SUPPLIED_CREDS
InitIdentify = 0x00020000, // ISC_REQ_IDENTIFY
AcceptIdentify = 0x00080000, // ASC_REQ_IDENTIFY
ProxyBindings = 0x04000000, // ASC_REQ_PROXY_BINDINGS
AllowMissingBindings = 0x10000000, // ASC_REQ_ALLOW_MISSING_BINDINGS
UnverifiedTargetName = 0x20000000, // ISC_REQ_UNVERIFIED_TARGET_NAME
}
internal class NTAuthentication {
static private int s_UniqueGroupId = 1;
static private ContextCallback s_InitializeCallback = new ContextCallback(InitializeCallback);
private bool m_IsServer;
private SafeFreeCredentials m_CredentialsHandle;
private SafeDeleteContext m_SecurityContext;
private string m_Spn;
private string m_ClientSpecifiedSpn;
private int m_TokenSize;
private ContextFlags m_RequestedContextFlags;
private ContextFlags m_ContextFlags;
private string m_UniqueUserId;
private bool m_IsCompleted;
private string m_ProtocolName;
private SecSizes m_Sizes;
private string m_LastProtocolName;
private string m_Package;
private ChannelBinding m_ChannelBinding;
//
// Properties
//
internal string UniqueUserId {
get {
return m_UniqueUserId;
}
}
// The semantic of this propoerty is "Don't call me again".
// It can be completed either with success or error
// The latest case is signalled by IsValidContext==false
internal bool IsCompleted {
get {
return m_IsCompleted;
}
}
internal bool IsValidContext {
get {
return !(m_SecurityContext == null || m_SecurityContext.IsInvalid);
}
}
internal string AssociatedName {
get {
if (!(IsValidContext && IsCompleted))
throw new Win32Exception((int)SecurityStatus.InvalidHandle);
string name = SSPIWrapper.QueryContextAttributes(GlobalSSPI.SSPIAuth, m_SecurityContext, ContextAttribute.Names) as string;
GlobalLog.Print("NTAuthentication: The context is associated with [" + name + "]");
return name;
}
}
internal bool IsConfidentialityFlag {
get {
return (m_ContextFlags & ContextFlags.Confidentiality) != 0;
}
}
internal bool IsIntegrityFlag {
get {
return (m_ContextFlags & (m_IsServer?ContextFlags.AcceptIntegrity:ContextFlags.InitIntegrity)) != 0;
}
}
internal bool IsMutualAuthFlag {
get {
return (m_ContextFlags & ContextFlags.MutualAuth) != 0;
}
}
internal bool IsDelegationFlag {
get {
return (m_ContextFlags & ContextFlags.Delegate) != 0;
}
}
internal bool IsIdentifyFlag {
get {
return (m_ContextFlags & (m_IsServer?ContextFlags.AcceptIdentify:ContextFlags.InitIdentify)) != 0;
}
}
internal string Spn {
get {
return m_Spn;
}
}
internal string ClientSpecifiedSpn {
get {
if (m_ClientSpecifiedSpn == null) {
m_ClientSpecifiedSpn = GetClientSpecifiedSpn();
}
return m_ClientSpecifiedSpn;
}
}
internal bool OSSupportsExtendedProtection {
get {
GlobalLog.Assert(IsCompleted && IsValidContext, "NTAuthentication#{0}::OSSupportsExtendedProtection|The context is not completed or invalid.", ValidationHelper.HashString(this));
int errorCode;
SSPIWrapper.QueryContextAttributes(GlobalSSPI.SSPIAuth, m_SecurityContext,
ContextAttribute.ClientSpecifiedSpn, out errorCode);
// We consider any error other than Unsupported to mean that the underlying OS
// supports extended protection. Most likely it will be TargetUnknown.
return ((SecurityStatus)errorCode != SecurityStatus.Unsupported);
}
}
//
// True indicates this instance is for Server and will use AcceptSecurityContext SSPI API
//
internal bool IsServer {
get {
return m_IsServer;
}
}
//
internal bool IsKerberos
{
get {
if (m_LastProtocolName == null)
m_LastProtocolName = ProtocolName;
return (object) m_LastProtocolName == (object) NegotiationInfoClass.Kerberos;
}
}
internal bool IsNTLM
{
get {
if (m_LastProtocolName == null)
m_LastProtocolName = ProtocolName;
return (object) m_LastProtocolName == (object) NegotiationInfoClass.NTLM;
}
}
internal string Package
{
get
{
return m_Package;
}
}
internal string ProtocolName {
get {
// NB: May return string.Empty if the auth is not done yet or failed
if (m_ProtocolName==null)
{
NegotiationInfoClass negotiationInfo = null;
if (IsValidContext)
{
negotiationInfo = SSPIWrapper.QueryContextAttributes(GlobalSSPI.SSPIAuth, m_SecurityContext, ContextAttribute.NegotiationInfo) as NegotiationInfoClass;
if (IsCompleted) {
if (negotiationInfo != null)
{
//cache it only when it's completed
m_ProtocolName = negotiationInfo.AuthenticationPackage;
}
}
}
return negotiationInfo == null? string.Empty: negotiationInfo.AuthenticationPackage;
}
return m_ProtocolName;
}
}
internal SecSizes Sizes {
get {
GlobalLog.Assert(IsCompleted && IsValidContext, "NTAuthentication#{0}::MaxDataSize|The context is not completed or invalid.", ValidationHelper.HashString(this));
if (m_Sizes == null) {
m_Sizes = SSPIWrapper.QueryContextAttributes(
GlobalSSPI.SSPIAuth,
m_SecurityContext,
ContextAttribute.Sizes
) as SecSizes;
}
return m_Sizes;
}
}
internal ChannelBinding ChannelBinding
{
get { return m_ChannelBinding; }
}
//
// .Ctors
//
//
// Use only for client HTTP authentication
//
internal NTAuthentication(string package, NetworkCredential networkCredential, SpnToken spnToken,
WebRequest request, ChannelBinding channelBinding) :
this(false, package, networkCredential, spnToken.Spn, GetHttpContextFlags(request, spnToken.IsTrusted),
request.GetWritingContext(), channelBinding)
{
//
// In order to prevent a race condition where one request could
// steal a connection from another request, before a handshake is
// complete, we create a new Group for each authentication request.
//
if (package == NtlmClient.AuthType || package == NegotiateClient.AuthType) {
m_UniqueUserId = (Interlocked.Increment(ref s_UniqueGroupId)).ToString(NumberFormatInfo.InvariantInfo) + m_UniqueUserId;
}
}
//
private static ContextFlags GetHttpContextFlags(WebRequest request, bool trustedSpn)
{
ContextFlags contextFlags = ContextFlags.Connection;
if (request.ImpersonationLevel == TokenImpersonationLevel.Anonymous)
throw new NotSupportedException(SR.GetString(SR.net_auth_no_anonymous_support));
else if(request.ImpersonationLevel == TokenImpersonationLevel.Identification)
contextFlags |= ContextFlags.InitIdentify;
else if(request.ImpersonationLevel == TokenImpersonationLevel.Delegation)
contextFlags |= ContextFlags.Delegate;
if (request.AuthenticationLevel == AuthenticationLevel.MutualAuthRequested || request.AuthenticationLevel == AuthenticationLevel.MutualAuthRequired)
contextFlags |= ContextFlags.MutualAuth;
// CBT: If the SPN came from an untrusted source we should tell the server by setting this flag
if (!trustedSpn && ComNetOS.IsWin7Sp1orLater)
contextFlags |= ContextFlags.UnverifiedTargetName;
return contextFlags;
}
//
// This constructor is for a general (non-HTTP) authentication handshake using SSPI
// Works for both client and server sides.
//
// Security: we may need to impersonate on user behalf as to temporarily restore original thread token.
[SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.ControlPrincipal)]
internal NTAuthentication(bool isServer, string package, NetworkCredential credential, string spn, ContextFlags requestedContextFlags, ContextAwareResult context, ChannelBinding channelBinding)
{
//
// check if we're using DefaultCredentials
//
if (credential is SystemNetworkCredential)
{
//
#if DEBUG
GlobalLog.Assert(context == null || context.IdentityRequested, "NTAuthentication#{0}::.ctor|Authentication required when it wasn't expected. (Maybe Credentials was changed on another thread?)", ValidationHelper.HashString(this));
#endif
WindowsIdentity w = context == null ? null : context.Identity;
try
{
IDisposable ctx = w == null ? null : w.Impersonate();
if (ctx != null)
{
using (ctx)
{
Initialize(isServer, package, credential, spn, requestedContextFlags, channelBinding);
}
}
else
{
ExecutionContext x = context == null ? null : context.ContextCopy;
if (x == null)
{
Initialize(isServer, package, credential, spn, requestedContextFlags, channelBinding);
}
else
{
ExecutionContext.Run(x, s_InitializeCallback, new InitializeCallbackContext(this, isServer, package, credential, spn, requestedContextFlags, channelBinding));
}
}
}
catch
{
// Prevent the impersonation from leaking to upstack exception filters.
throw;
}
}
else
{
Initialize(isServer, package, credential, spn, requestedContextFlags, channelBinding);
}
}
//
// This overload does not attmept to impersonate because the caller either did it already or the original thread context is still preserved
//
internal NTAuthentication(bool isServer, string package, NetworkCredential credential, string spn, ContextFlags requestedContextFlags, ChannelBinding channelBinding) {
Initialize(isServer, package, credential, spn, requestedContextFlags, channelBinding);
}
//
// This overload always uses the default credentials for the process.
//
[SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.ControlPrincipal)]
internal NTAuthentication(bool isServer, string package, string spn, ContextFlags requestedContextFlags, ChannelBinding channelBinding)
{
try
{
using (WindowsIdentity.Impersonate(IntPtr.Zero))
{
Initialize(isServer, package, SystemNetworkCredential.defaultCredential, spn, requestedContextFlags, channelBinding);
}
}
catch
{
// Avoid exception filter attacks.
throw;
}
}
private class InitializeCallbackContext
{
internal InitializeCallbackContext(NTAuthentication thisPtr, bool isServer, string package, NetworkCredential credential, string spn, ContextFlags requestedContextFlags, ChannelBinding channelBinding)
{
this.thisPtr = thisPtr;
this.isServer = isServer;
this.package = package;
this.credential = credential;
this.spn = spn;
this.requestedContextFlags = requestedContextFlags;
this.channelBinding = channelBinding;
}
internal readonly NTAuthentication thisPtr;
internal readonly bool isServer;
internal readonly string package;
internal readonly NetworkCredential credential;
internal readonly string spn;
internal readonly ContextFlags requestedContextFlags;
internal readonly ChannelBinding channelBinding;
}
private static void InitializeCallback(object state)
{
InitializeCallbackContext context = (InitializeCallbackContext)state;
context.thisPtr.Initialize(context.isServer, context.package, context.credential, context.spn, context.requestedContextFlags, context.channelBinding);
}
//
private void Initialize(bool isServer, string package, NetworkCredential credential, string spn, ContextFlags requestedContextFlags, ChannelBinding channelBinding) {
GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::.ctor() package:" + ValidationHelper.ToString(package) + " spn:" + ValidationHelper.ToString(spn) + " flags :" + requestedContextFlags.ToString());
m_TokenSize = SSPIWrapper.GetVerifyPackageInfo(GlobalSSPI.SSPIAuth, package, true).MaxToken;
m_IsServer = isServer;
m_Spn = spn;
m_SecurityContext = null;
m_RequestedContextFlags = requestedContextFlags;
m_Package = package;
m_ChannelBinding = channelBinding;
GlobalLog.Print("Peer SPN-> '" + m_Spn + "'");
//
// check if we're using DefaultCredentials
//
if (credential is SystemNetworkCredential)
{
GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::.ctor(): using DefaultCredentials");
m_CredentialsHandle = SSPIWrapper.AcquireDefaultCredential(
GlobalSSPI.SSPIAuth,
package,
(m_IsServer? CredentialUse.Inbound: CredentialUse.Outbound));
m_UniqueUserId = "/S"; // save off for unique connection marking ONLY used by HTTP client
}
else if (ComNetOS.IsWin7orLater)
{
unsafe
{
SafeSspiAuthDataHandle authData = null;
try
{
SecurityStatus result = UnsafeNclNativeMethods.SspiHelper.SspiEncodeStringsAsAuthIdentity(
credential.InternalGetUserName(), credential.InternalGetDomain(),
credential.InternalGetPassword(), out authData);
if (result != SecurityStatus.OK)
{
if (Logging.On) Logging.PrintError(Logging.Web, SR.GetString(SR.net_log_operation_failed_with_error, "SspiEncodeStringsAsAuthIdentity()", String.Format(CultureInfo.CurrentCulture, "0x{0:X}", (int)result)));
throw new Win32Exception((int)result);
}
m_CredentialsHandle = SSPIWrapper.AcquireCredentialsHandle(GlobalSSPI.SSPIAuth,
package, (m_IsServer ? CredentialUse.Inbound : CredentialUse.Outbound), ref authData);
}
finally
{
if (authData != null)
{
authData.Close();
}
}
}
}
else
{
//
// we're not using DefaultCredentials, we need a
// AuthIdentity struct to contain credentials
// SECREVIEW:
// we'll save username/domain in temp strings, to avoid decrypting multiple times.
// password is only used once
//
string username = credential.InternalGetUserName();
string domain = credential.InternalGetDomain();
// ATTN:
// NetworkCredential class does not differentiate between null and "" but SSPI packages treat these cases differently
// For NTLM we want to keep "" for Wdigest.Dll we should use null.
AuthIdentity authIdentity = new AuthIdentity(username, credential.InternalGetPassword(), (object)package == (object)NegotiationInfoClass.WDigest && (domain == null || domain.Length == 0)? null: domain);
m_UniqueUserId = domain + "/" + username + "/U"; // save off for unique connection marking ONLY used by HTTP client
GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::.ctor(): using authIdentity:" + authIdentity.ToString());
m_CredentialsHandle = SSPIWrapper.AcquireCredentialsHandle(
GlobalSSPI.SSPIAuth,
package,
(m_IsServer? CredentialUse.Inbound: CredentialUse.Outbound),
ref authIdentity
);
}
}
//
// Methods
//
// This will return a client token during authentication on the server side.
// This token can be used directly or with impersonation.
// We use it to create a WindowsIdentity and hand it out to the server app.
internal SafeCloseHandle GetContextToken(out SecurityStatus status)
{
GlobalLog.Assert(IsCompleted && IsValidContext, "NTAuthentication#{0}::GetContextToken|Should be called only when completed with success, currently is not!", ValidationHelper.HashString(this));
GlobalLog.Assert(IsServer, "NTAuthentication#{0}::GetContextToken|The method must not be called by the client side!", ValidationHelper.HashString(this));
if (!IsValidContext) {
throw new Win32Exception((int)SecurityStatus.InvalidHandle);
}
SafeCloseHandle token = null;
status = (SecurityStatus) SSPIWrapper.QuerySecurityContextToken(
GlobalSSPI.SSPIAuth,
m_SecurityContext,
out token);
return token;
}
internal SafeCloseHandle GetContextToken()
{
SecurityStatus status;
SafeCloseHandle token = GetContextToken(out status);
if (status != SecurityStatus.OK) {
throw new Win32Exception((int)status);
}
return token;
}
internal void CloseContext()
{
if (m_SecurityContext != null && !m_SecurityContext.IsClosed)
m_SecurityContext.Close();
}
//
// NTAuth::GetOutgoingBlob()
// Created: 12-01-1999: L.M.
// Description:
// Accepts a base64 encoded incoming security blob and returns
// a base 64 encoded outgoing security blob
//
// This method is for HttpWebRequest usage only as it has semantic bound to it
internal string GetOutgoingBlob(string incomingBlob) {
GlobalLog.Enter("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingBlob", incomingBlob);
byte[] decodedIncomingBlob = null;
if (incomingBlob != null && incomingBlob.Length > 0) {
decodedIncomingBlob = Convert.FromBase64String(incomingBlob);
}
byte[] decodedOutgoingBlob = null;
if ((IsValidContext || IsCompleted) && decodedIncomingBlob == null) {
// we tried auth previously, now we got a null blob, we're done. this happens
// with Kerberos & valid credentials on the domain but no ACLs on the resource
GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingBlob() null blob AND m_SecurityContext#" + ValidationHelper.HashString(m_SecurityContext) + "::Handle:[0x" + m_SecurityContext.ToString() + "]");
m_IsCompleted = true;
}
else {
SecurityStatus statusCode;
#if TRAVE
try {
#endif
decodedOutgoingBlob = GetOutgoingBlob(decodedIncomingBlob, true, out statusCode);
#if TRAVE
} catch (Exception exception) {
if (NclUtilities.IsFatal(exception)) throw;
GlobalLog.LeaveException("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingBlob", exception);
throw;
}
#endif
}
string outgoingBlob = null;
if (decodedOutgoingBlob != null && decodedOutgoingBlob.Length > 0) {
outgoingBlob = Convert.ToBase64String(decodedOutgoingBlob);
}
//This is only for HttpWebRequest that does not need security context anymore
if (IsCompleted)
{
string name = ProtocolName; // cache the only info needed from a completed context before closing it
CloseContext();
}
GlobalLog.Leave("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingBlob", outgoingBlob);
return outgoingBlob;
}
// NTAuth::GetOutgoingBlob()
// Created: 12-01-1999: L.M.
// Description:
// Accepts an incoming binary security blob and returns
// an outgoing binary security blob
internal byte[] GetOutgoingBlob(byte[] incomingBlob, bool throwOnError, out SecurityStatus statusCode)
{
GlobalLog.Enter("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingBlob", ((incomingBlob == null) ? "0" : incomingBlob.Length.ToString(NumberFormatInfo.InvariantInfo)) + " bytes");
List<SecurityBuffer> list = new List<SecurityBuffer>(2);
if (incomingBlob != null) {
list.Add(new SecurityBuffer(incomingBlob, BufferType.Token));
}
if (m_ChannelBinding != null) {
list.Add(new SecurityBuffer(m_ChannelBinding));
}
SecurityBuffer[] inSecurityBufferArray = null;
if (list.Count > 0)
{
inSecurityBufferArray = list.ToArray();
}
SecurityBuffer outSecurityBuffer = new SecurityBuffer(m_TokenSize, BufferType.Token);
bool firstTime = m_SecurityContext == null;
try {
if (!m_IsServer) {
// client session
statusCode = (SecurityStatus)SSPIWrapper.InitializeSecurityContext(
GlobalSSPI.SSPIAuth,
m_CredentialsHandle,
ref m_SecurityContext,
m_Spn,
m_RequestedContextFlags,
Endianness.Network,
inSecurityBufferArray,
outSecurityBuffer,
ref m_ContextFlags);
GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingBlob() SSPIWrapper.InitializeSecurityContext() returns statusCode:0x" + ((int) statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")");
if (statusCode == SecurityStatus.CompleteNeeded)
{
SecurityBuffer[] inSecurityBuffers = new SecurityBuffer[1];
inSecurityBuffers[0] = outSecurityBuffer;
statusCode = (SecurityStatus) SSPIWrapper.CompleteAuthToken(
GlobalSSPI.SSPIAuth,
ref m_SecurityContext,
inSecurityBuffers );
GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob() SSPIWrapper.CompleteAuthToken() returns statusCode:0x" + ((int) statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")");
outSecurityBuffer.token = null;
}
}
else {
// server session
statusCode = (SecurityStatus)SSPIWrapper.AcceptSecurityContext(
GlobalSSPI.SSPIAuth,
m_CredentialsHandle,
ref m_SecurityContext,
m_RequestedContextFlags,
Endianness.Network,
inSecurityBufferArray,
outSecurityBuffer,
ref m_ContextFlags);
GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingBlob() SSPIWrapper.AcceptSecurityContext() returns statusCode:0x" + ((int) statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")");
}
}
finally {
//
// Assuming the ISC or ASC has referenced the credential on the first successful call,
// we want to decrement the effective ref count by "disposing" it.
// The real dispose will happen when the security context is closed.
// Note if the first call was not successfull the handle is physically destroyed here
//
if (firstTime && m_CredentialsHandle != null)
m_CredentialsHandle.Close();
}
if (((int) statusCode & unchecked((int) 0x80000000)) != 0)
{
CloseContext();
m_IsCompleted = true;
if (throwOnError) {
Win32Exception exception = new Win32Exception((int) statusCode);
GlobalLog.Leave("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingBlob", "Win32Exception:" + exception);
throw exception;
}
GlobalLog.Leave("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingBlob", "null statusCode:0x" + ((int) statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")");
return null;
}
else if (firstTime && m_CredentialsHandle != null)
{
// cache until it is pushed out by newly incoming handles
SSPIHandleCache.CacheCredential(m_CredentialsHandle);
}
// the return value from SSPI will tell us correctly if the
// handshake is over or not: http://msdn.microsoft.com/library/psdk/secspi/sspiref_67p0.htm
// we also have to consider the case in which SSPI formed a new context, in this case we're done as well.
if (statusCode == SecurityStatus.OK)
{
// we're sucessfully done
GlobalLog.Assert(statusCode == SecurityStatus.OK, "NTAuthentication#{0}::GetOutgoingBlob()|statusCode:[0x{1:x8}] ({2}) m_SecurityContext#{3}::Handle:[{4}] [STATUS != OK]", ValidationHelper.HashString(this), (int)statusCode, statusCode, ValidationHelper.HashString(m_SecurityContext), ValidationHelper.ToString(m_SecurityContext));
m_IsCompleted = true;
}
else {
// we need to continue
GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingBlob() need continue statusCode:[0x" + ((int) statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + "] (" + statusCode.ToString() + ") m_SecurityContext#" + ValidationHelper.HashString(m_SecurityContext) + "::Handle:" + ValidationHelper.ToString(m_SecurityContext) + "]");
}
// GlobalLog.Print("out token = " + outSecurityBuffer.ToString());
// GlobalLog.Dump(outSecurityBuffer.token);
GlobalLog.Leave("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingBlob", "IsCompleted:" + IsCompleted.ToString());
return outSecurityBuffer.token;
}
// for Server side (IIS 6.0) see: \\netindex\Sources\inetsrv\iis\iisrearc\iisplus\ulw3\digestprovider.cxx
// for Client side (HTTP.SYS) see: \\netindex\Sources\net\http\sys\ucauth.c
internal string GetOutgoingDigestBlob(string incomingBlob, string requestMethod, string requestedUri, string realm, bool isClientPreAuth, bool throwOnError, out SecurityStatus statusCode)
{
GlobalLog.Enter("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob", incomingBlob);
// second time call with 3 incoming buffers to select HTTP client.
// we should get back a SecurityStatus.OK and a non null outgoingBlob.
SecurityBuffer[] inSecurityBuffers = null;
SecurityBuffer outSecurityBuffer = new SecurityBuffer(m_TokenSize, isClientPreAuth ? BufferType.Parameters : BufferType.Token);
bool firstTime = m_SecurityContext == null;
try {
if (!m_IsServer) {
// client session
if (!isClientPreAuth) {
if (incomingBlob != null)
{
List<SecurityBuffer> list = new List<SecurityBuffer>(5);
list.Add(new SecurityBuffer(WebHeaderCollection.HeaderEncoding.GetBytes(incomingBlob), BufferType.Token));
list.Add(new SecurityBuffer(WebHeaderCollection.HeaderEncoding.GetBytes(requestMethod), BufferType.Parameters));
list.Add(new SecurityBuffer(null, BufferType.Parameters));
list.Add(new SecurityBuffer(Encoding.Unicode.GetBytes(m_Spn), BufferType.TargetHost));
if (m_ChannelBinding != null) {
list.Add(new SecurityBuffer(m_ChannelBinding));
}
inSecurityBuffers = list.ToArray();
}
statusCode = (SecurityStatus) SSPIWrapper.InitializeSecurityContext(
GlobalSSPI.SSPIAuth,
m_CredentialsHandle,
ref m_SecurityContext,
requestedUri, // this must match the Uri in the HTTP status line for the current request
m_RequestedContextFlags,
Endianness.Network,
inSecurityBuffers,
outSecurityBuffer,
ref m_ContextFlags );
GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob() SSPIWrapper.InitializeSecurityContext() returns statusCode:0x" + ((int) statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")");
}
else {
#if WDIGEST_PREAUTH
inSecurityBuffers = new SecurityBuffer[] {
new SecurityBuffer(null, BufferType.Token),
new SecurityBuffer(WebHeaderCollection.HeaderEncoding.GetBytes(requestMethod), BufferType.Parameters),
new SecurityBuffer(WebHeaderCollection.HeaderEncoding.GetBytes(requestedUri), BufferType.Parameters),
new SecurityBuffer(null, BufferType.Parameters),
outSecurityBuffer,
};
statusCode = (SecurityStatus) SSPIWrapper.MakeSignature(GlobalSSPI.SSPIAuth, m_SecurityContext, inSecurityBuffers, 0);
GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob() SSPIWrapper.MakeSignature() returns statusCode:0x" + ((int) statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")");
#else
statusCode = SecurityStatus.OK;
GlobalLog.Assert("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob()", "Invalid code path.");
#endif
}
}
else {
// server session
List<SecurityBuffer> list = new List<SecurityBuffer>(6);
list.Add(incomingBlob == null ? new SecurityBuffer(0, BufferType.Token) : new SecurityBuffer(WebHeaderCollection.HeaderEncoding.GetBytes(incomingBlob), BufferType.Token));
list.Add(requestMethod == null ? new SecurityBuffer(0, BufferType.Parameters) : new SecurityBuffer(WebHeaderCollection.HeaderEncoding.GetBytes(requestMethod), BufferType.Parameters));
list.Add(requestedUri == null ? new SecurityBuffer(0, BufferType.Parameters) : new SecurityBuffer(WebHeaderCollection.HeaderEncoding.GetBytes(requestedUri), BufferType.Parameters));
list.Add(new SecurityBuffer(0, BufferType.Parameters));
list.Add(realm == null ? new SecurityBuffer(0, BufferType.Parameters) : new SecurityBuffer(Encoding.Unicode.GetBytes(realm), BufferType.Parameters));
if (m_ChannelBinding != null) {
list.Add(new SecurityBuffer(m_ChannelBinding));
}
inSecurityBuffers = list.ToArray();
statusCode = (SecurityStatus) SSPIWrapper.AcceptSecurityContext(
GlobalSSPI.SSPIAuth,
m_CredentialsHandle,
ref m_SecurityContext,
m_RequestedContextFlags,
Endianness.Network,
inSecurityBuffers,
outSecurityBuffer,
ref m_ContextFlags );
GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob() SSPIWrapper.AcceptSecurityContext() returns statusCode:0x" + ((int)statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")");
if (statusCode == SecurityStatus.CompleteNeeded)
{
inSecurityBuffers[4] = outSecurityBuffer;
statusCode = (SecurityStatus) SSPIWrapper.CompleteAuthToken(
GlobalSSPI.SSPIAuth,
ref m_SecurityContext,
inSecurityBuffers );
GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob() SSPIWrapper.CompleteAuthToken() returns statusCode:0x" + ((int)statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")");
outSecurityBuffer.token = null;
}
}
}
finally {
//
// Assuming the ISC or ASC has referenced the credential on the first successful call,
// we want to decrement the effective ref count by "disposing" it.
// The real dispose will happen when the security context is closed.
// Note if the first call was not successfull the handle is physically destroyed here
//
if (firstTime && m_CredentialsHandle != null)
m_CredentialsHandle.Close();
}
if (((int) statusCode & unchecked((int) 0x80000000)) != 0)
{
CloseContext();
if (throwOnError) {
Win32Exception exception = new Win32Exception((int) statusCode);
GlobalLog.Leave("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob", "Win32Exception:" + exception);
throw exception;
}
GlobalLog.Leave("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob", "null statusCode:0x" + ((int) statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")");
return null;
}
else if (firstTime && m_CredentialsHandle != null)
{
// cache until it is pushed out by newly incoming handles
SSPIHandleCache.CacheCredential(m_CredentialsHandle);
}
// the return value from SSPI will tell us correctly if the
// handshake is over or not: http://msdn.microsoft.com/library/psdk/secspi/sspiref_67p0.htm
if (statusCode == SecurityStatus.OK)
{
// we're done, cleanup
m_IsCompleted = true;
}
else {
// we need to continue
GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob() need continue statusCode:[0x" + ((int) statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + "] (" + statusCode.ToString() + ") m_SecurityContext#" + ValidationHelper.HashString(m_SecurityContext) + "::Handle:" + ValidationHelper.ToString(m_SecurityContext) + "]");
}
GlobalLog.Print("out token = " + outSecurityBuffer.ToString());
GlobalLog.Dump(outSecurityBuffer.token);
GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob() IsCompleted:" + IsCompleted.ToString());
byte[] decodedOutgoingBlob = outSecurityBuffer.token;
string outgoingBlob = null;
if (decodedOutgoingBlob!=null && decodedOutgoingBlob.Length>0) {
outgoingBlob = WebHeaderCollection.HeaderEncoding.GetString(decodedOutgoingBlob, 0, outSecurityBuffer.size);
}
GlobalLog.Leave("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob", outgoingBlob);
return outgoingBlob;
}
internal int Encrypt(byte[] buffer, int offset, int count, ref byte[] output, uint sequenceNumber) {
SecSizes sizes = Sizes;
try
{
int maxCount = checked(Int32.MaxValue - 4 - sizes.BlockSize - sizes.SecurityTrailer);
if (count > maxCount || count < 0)
{
throw new ArgumentOutOfRangeException("count", SR.GetString(SR.net_io_out_range, maxCount));
}
}
catch(Exception e)
{
if (!NclUtilities.IsFatal(e)){
GlobalLog.Assert(false, "NTAuthentication#" + ValidationHelper.HashString(this) + "::Encrypt", "Arguments out of range.");
}
throw;
}
int resultSize = count + sizes.SecurityTrailer + sizes.BlockSize;
if (output == null || output.Length < resultSize+4)
{
output = new byte[resultSize+4];
}
// make a copy of user data for in-place encryption
Buffer.BlockCopy(buffer, offset, output, 4 + sizes.SecurityTrailer, count);
// prepare buffers TOKEN(signautre), DATA and Padding
SecurityBuffer[] securityBuffer = new SecurityBuffer[3];
securityBuffer[0] = new SecurityBuffer(output, 4, sizes.SecurityTrailer, BufferType.Token);
securityBuffer[1] = new SecurityBuffer(output, 4 + sizes.SecurityTrailer, count, BufferType.Data);
securityBuffer[2] = new SecurityBuffer(output, 4 + sizes.SecurityTrailer + count, sizes.BlockSize, BufferType.Padding);
int errorCode;
if (IsConfidentialityFlag)
{
errorCode = SSPIWrapper.EncryptMessage(GlobalSSPI.SSPIAuth, m_SecurityContext, securityBuffer, sequenceNumber);
}
else
{
if (IsNTLM)
securityBuffer[1].type |= BufferType.ReadOnlyFlag;
errorCode = SSPIWrapper.MakeSignature(GlobalSSPI.SSPIAuth, m_SecurityContext, securityBuffer, 0);
}
if (errorCode != 0) {
GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::Encrypt() throw Error = " + errorCode.ToString("x", NumberFormatInfo.InvariantInfo));
throw new Win32Exception(errorCode);
}
// Compacting the result...
resultSize = securityBuffer[0].size;
bool forceCopy = false;
if (resultSize != sizes.SecurityTrailer)
{
forceCopy = true;
Buffer.BlockCopy(output, securityBuffer[1].offset, output, 4 + resultSize, securityBuffer[1].size);
}
resultSize += securityBuffer[1].size;
if (securityBuffer[2].size != 0 && (forceCopy || resultSize != (count + sizes.SecurityTrailer)))
Buffer.BlockCopy(output, securityBuffer[2].offset, output, 4 + resultSize, securityBuffer[2].size);
resultSize += securityBuffer[2].size;
unchecked {
output[0] = (byte)((resultSize) & 0xFF);
output[1] = (byte)(((resultSize)>>8) & 0xFF);
output[2] = (byte)(((resultSize)>>16) & 0xFF);
output[3] = (byte)(((resultSize)>>24) & 0xFF);
}
return resultSize+4;
}
internal int Decrypt(byte[] payload, int offset, int count, out int newOffset, uint expectedSeqNumber)
{
if (offset < 0 || offset > (payload == null ? 0 : payload.Length))
{
GlobalLog.Assert(false, "NTAuthentication#" + ValidationHelper.HashString(this) + "::Decrypt", "Argument 'offset' out of range.");
throw new ArgumentOutOfRangeException("offset");
}
if (count < 0 || count > (payload == null ? 0 : payload.Length - offset))
{
GlobalLog.Assert(false, "NTAuthentication#" + ValidationHelper.HashString(this) + "::Decrypt", "Argument 'count' out of range.");
throw new ArgumentOutOfRangeException("count");
}
if (IsNTLM)
return DecryptNtlm(payload, offset, count, out newOffset, expectedSeqNumber);
//
// Kerberos and up
//
SecurityBuffer[] securityBuffer = new SecurityBuffer[2];
securityBuffer[0] = new SecurityBuffer(payload, offset, count, BufferType.Stream);
securityBuffer[1] = new SecurityBuffer(0, BufferType.Data);
int errorCode;
if (IsConfidentialityFlag)
{
errorCode = SSPIWrapper.DecryptMessage(GlobalSSPI.SSPIAuth, m_SecurityContext, securityBuffer, expectedSeqNumber);
}
else
{
errorCode = SSPIWrapper.VerifySignature(GlobalSSPI.SSPIAuth, m_SecurityContext, securityBuffer, expectedSeqNumber);
}
if (errorCode != 0)
{
GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::Decrypt() throw Error = " + errorCode.ToString("x", NumberFormatInfo.InvariantInfo));
throw new Win32Exception(errorCode);
}
if (securityBuffer[1].type != BufferType.Data)
throw new InternalException();
newOffset = securityBuffer[1].offset;
return securityBuffer[1].size;
}
private string GetClientSpecifiedSpn()
{
GlobalLog.Assert(IsValidContext && IsCompleted, "NTAuthentication: Trying to get the client SPN before handshaking is done!");
string spn = SSPIWrapper.QueryContextAttributes(GlobalSSPI.SSPIAuth, m_SecurityContext,
ContextAttribute.ClientSpecifiedSpn) as string;
GlobalLog.Print("NTAuthentication: The client specified SPN is [" + spn + "]");
return spn;
}
//
private int DecryptNtlm(byte[] payload, int offset, int count, out int newOffset, uint expectedSeqNumber)
{
// For the most part the arguments are verified in Encrypt().
if (count < 16)
{
GlobalLog.Assert(false, "NTAuthentication#" + ValidationHelper.HashString(this) + "::DecryptNtlm", "Argument 'count' out of range.");
throw new ArgumentOutOfRangeException("count");
}
SecurityBuffer[] securityBuffer = new SecurityBuffer[2];
securityBuffer[0] = new SecurityBuffer(payload, offset, 16, BufferType.Token);
securityBuffer[1] = new SecurityBuffer(payload, offset + 16, count-16, BufferType.Data);
int errorCode;
BufferType realDataType = BufferType.Data;
if (IsConfidentialityFlag)
{
errorCode = SSPIWrapper.DecryptMessage(GlobalSSPI.SSPIAuth, m_SecurityContext, securityBuffer, expectedSeqNumber);
}
else
{
realDataType |= BufferType.ReadOnlyFlag;
securityBuffer[1].type = realDataType;
errorCode = SSPIWrapper.VerifySignature(GlobalSSPI.SSPIAuth, m_SecurityContext, securityBuffer, expectedSeqNumber);
}
if (errorCode != 0)
{
GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::Decrypt() throw Error = " + errorCode.ToString("x", NumberFormatInfo.InvariantInfo));
throw new Win32Exception(errorCode);
}
if (securityBuffer[1].type != realDataType)
throw new InternalException();
newOffset = securityBuffer[1].offset;
return securityBuffer[1].size;
}
//
// VerifySignature
//
// Adapted from Decrypt method above as a more generic message
// signature verify method for SMTP AUTH GSSAPI (SASL).
// Decrypt method, used NegotiateStream, couldn't be used due
// to special cases for NTLM.
//
// See SmtpNegotiateAuthenticationModule class for caller.
//
internal int VerifySignature(byte[] buffer, int offset, int count) {
// validate offset within length
if (offset < 0 || offset > (buffer == null ? 0 : buffer.Length)) {
GlobalLog.Assert(
false,
"NTAuthentication#" +
ValidationHelper.HashString(this) +
"::VerifySignature",
"Argument 'offset' out of range.");
throw new ArgumentOutOfRangeException("offset");
}
// validate count within offset and end of buffer
if (count < 0 ||
count > (buffer == null ? 0 : buffer.Length - offset)) {
GlobalLog.Assert(
false,
"NTAuthentication#" +
ValidationHelper.HashString(this) +
"::VerifySignature",
"Argument 'count' out of range.");
throw new ArgumentOutOfRangeException("count");
}
// setup security buffers for ssp call
// one points at signed data
// two will receive payload if signature is valid
SecurityBuffer[] securityBuffer = new SecurityBuffer[2];
securityBuffer[0] =
new SecurityBuffer(buffer, offset, count, BufferType.Stream);
securityBuffer[1] = new SecurityBuffer(0, BufferType.Data);
// call SSP function
int errorCode = SSPIWrapper.VerifySignature(
GlobalSSPI.SSPIAuth,
m_SecurityContext,
securityBuffer,
0);
// throw if error
if (errorCode != 0)
{
GlobalLog.Print(
"NTAuthentication#" +
ValidationHelper.HashString(this) +
"::VerifySignature() threw Error = " +
errorCode.ToString("x",
NumberFormatInfo.InvariantInfo));
throw new Win32Exception(errorCode);
}
// not sure why this is here - retained from Encrypt code above
if (securityBuffer[1].type != BufferType.Data)
throw new InternalException();
// return validated payload size
return securityBuffer[1].size;
}
//
// MakeSignature
//
// Adapted from Encrypt method above as a more generic message
// signing method for SMTP AUTH GSSAPI (SASL).
// Encrypt method, used for NegotiateStream, put size at head of
// message. Don't need that
//
// See SmtpNegotiateAuthenticationModule class for caller.
//
internal int MakeSignature(
byte[] buffer,
int offset,
int count,
ref byte[] output) {
SecSizes sizes = Sizes;
// alloc new output buffer if not supplied or too small
int resultSize = count + sizes.MaxSignature;
if (output == null || output.Length < resultSize)
{
output = new byte[resultSize];
}
// make a copy of user data for in-place encryption
Buffer.BlockCopy(buffer, offset, output, sizes.MaxSignature, count);
// setup security buffers for ssp call
SecurityBuffer[] securityBuffer = new SecurityBuffer[2];
securityBuffer[0] = new SecurityBuffer(output, 0, sizes.MaxSignature, BufferType.Token);
securityBuffer[1] = new SecurityBuffer(output, sizes.MaxSignature, count, BufferType.Data);
// call SSP Function
int errorCode = SSPIWrapper.MakeSignature(
GlobalSSPI.SSPIAuth,
m_SecurityContext,
securityBuffer,
0);
// throw if error
if (errorCode != 0) {
GlobalLog.Print(
"NTAuthentication#" +
ValidationHelper.HashString(this) +
"::Encrypt() throw Error = " +
errorCode.ToString("x", NumberFormatInfo.InvariantInfo));
throw new Win32Exception(errorCode);
}
// return signed size
return securityBuffer[0].size + securityBuffer[1].size;
}
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
internal struct AuthIdentity {
// see SEC_WINNT_AUTH_IDENTITY_W
internal string UserName;
internal int UserNameLength;
internal string Domain;
internal int DomainLength;
internal string Password;
internal int PasswordLength;
internal int Flags;
internal AuthIdentity(string userName, string password, string domain) {
UserName = userName;
UserNameLength = userName==null ? 0 : userName.Length;
Password = password;
PasswordLength = password==null ? 0 : password.Length;
Domain = domain;
DomainLength = domain==null ? 0 : domain.Length;
// Flags are 2 for Unicode and 1 for ANSI. We use 2 on NT and 1 on Win9x.
Flags = 2;
}
public override string ToString() {
return ValidationHelper.ToString(Domain) + "\\" + ValidationHelper.ToString(UserName);
}
}
}
|