1 //------------------------------------------------------------------------------
2 // <copyright file="_NTAuthentication.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6 
7 namespace System.Net {
8     using System.Runtime.InteropServices;
9     using System.Collections.Generic;
10     using System.ComponentModel;
11     using System.Text;
12     using System.Threading;
13     using System.Globalization;
14     using System.Security.Authentication.ExtendedProtection;
15     using System.Security.Principal;
16     using System.Security.Permissions;
17     using System.Net.Security;
18 
19     // #define ISC_REQ_DELEGATE                0x00000001
20     // #define ISC_REQ_MUTUAL_AUTH             0x00000002
21     // #define ISC_REQ_REPLAY_DETECT           0x00000004
22     // #define ISC_REQ_SEQUENCE_DETECT         0x00000008
23     // #define ISC_REQ_CONFIDENTIALITY         0x00000010
24     // #define ISC_REQ_USE_SESSION_KEY         0x00000020
25     // #define ISC_REQ_PROMPT_FOR_CREDS        0x00000040
26     // #define ISC_REQ_USE_SUPPLIED_CREDS      0x00000080
27     // #define ISC_REQ_ALLOCATE_MEMORY         0x00000100
28     // #define ISC_REQ_USE_DCE_STYLE           0x00000200
29     // #define ISC_REQ_DATAGRAM                0x00000400
30     // #define ISC_REQ_CONNECTION              0x00000800
31     // #define ISC_REQ_CALL_LEVEL              0x00001000
32     // #define ISC_REQ_FRAGMENT_SUPPLIED       0x00002000
33     // #define ISC_REQ_EXTENDED_ERROR          0x00004000
34     // #define ISC_REQ_STREAM                  0x00008000
35     // #define ISC_REQ_INTEGRITY               0x00010000
36     // #define ISC_REQ_IDENTIFY                0x00020000
37     // #define ISC_REQ_NULL_SESSION            0x00040000
38     // #define ISC_REQ_MANUAL_CRED_VALIDATION  0x00080000
39     // #define ISC_REQ_RESERVED1               0x00100000
40     // #define ISC_REQ_FRAGMENT_TO_FIT         0x00200000
41     // #define ISC_REQ_HTTP                    0x10000000
42     // Win7 SP1 +
43     // #define ISC_REQ_UNVERIFIED_TARGET_NAME  0x20000000
44 
45     // #define ASC_REQ_DELEGATE                0x00000001
46     // #define ASC_REQ_MUTUAL_AUTH             0x00000002
47     // #define ASC_REQ_REPLAY_DETECT           0x00000004
48     // #define ASC_REQ_SEQUENCE_DETECT         0x00000008
49     // #define ASC_REQ_CONFIDENTIALITY         0x00000010
50     // #define ASC_REQ_USE_SESSION_KEY         0x00000020
51     // #define ASC_REQ_ALLOCATE_MEMORY         0x00000100
52     // #define ASC_REQ_USE_DCE_STYLE           0x00000200
53     // #define ASC_REQ_DATAGRAM                0x00000400
54     // #define ASC_REQ_CONNECTION              0x00000800
55     // #define ASC_REQ_CALL_LEVEL              0x00001000
56     // #define ASC_REQ_EXTENDED_ERROR          0x00008000
57     // #define ASC_REQ_STREAM                  0x00010000
58     // #define ASC_REQ_INTEGRITY               0x00020000
59     // #define ASC_REQ_LICENSING               0x00040000
60     // #define ASC_REQ_IDENTIFY                0x00080000
61     // #define ASC_REQ_ALLOW_NULL_SESSION      0x00100000
62     // #define ASC_REQ_ALLOW_NON_USER_LOGONS   0x00200000
63     // #define ASC_REQ_ALLOW_CONTEXT_REPLAY    0x00400000
64     // #define ASC_REQ_FRAGMENT_TO_FIT         0x00800000
65     // #define ASC_REQ_FRAGMENT_SUPPLIED       0x00002000
66     // #define ASC_REQ_NO_TOKEN                0x01000000
67     // #define ASC_REQ_HTTP                    0x10000000
68 
69     [Flags]
70     internal enum ContextFlags {
71         Zero            = 0,
72         // The server in the transport application can
73         // build new security contexts impersonating the
74         // client that will be accepted by other servers
75         // as the client's contexts.
76         Delegate        = 0x00000001,
77         // The communicating parties must authenticate
78         // their identities to each other. Without MutualAuth,
79         // the client authenticates its identity to the server.
80         // With MutualAuth, the server also must authenticate
81         // its identity to the client.
82         MutualAuth      = 0x00000002,
83         // The security package detects replayed packets and
84         // notifies the caller if a packet has been replayed.
85         // The use of this flag implies all of the conditions
86         // specified by the Integrity flag.
87         ReplayDetect    = 0x00000004,
88         // The context must be allowed to detect out-of-order
89         // delivery of packets later through the message support
90         // functions. Use of this flag implies all of the
91         // conditions specified by the Integrity flag.
92         SequenceDetect  = 0x00000008,
93         // The context must protect data while in transit.
94         // Confidentiality is supported for NTLM with Microsoft
95         // Windows NT version 4.0, SP4 and later and with the
96         // Kerberos protocol in Microsoft Windows 2000 and later.
97         Confidentiality = 0x00000010,
98         UseSessionKey   = 0x00000020,
99         AllocateMemory  = 0x00000100,
100 
101         // Connection semantics must be used.
102         Connection      = 0x00000800,
103 
104         // Client applications requiring extended error messages specify the
105         // ISC_REQ_EXTENDED_ERROR flag when calling the InitializeSecurityContext
106         // Server applications requiring extended error messages set
107         // the ASC_REQ_EXTENDED_ERROR flag when calling AcceptSecurityContext.
108         InitExtendedError    = 0x00004000,
109         AcceptExtendedError  = 0x00008000,
110         // A transport application requests stream semantics
111         // by setting the ISC_REQ_STREAM and ASC_REQ_STREAM
112         // flags in the calls to the InitializeSecurityContext
113         // and AcceptSecurityContext functions
114         InitStream          = 0x00008000,
115         AcceptStream        = 0x00010000,
116         // Buffer integrity can be verified; however, replayed
117         // and out-of-sequence messages will not be detected
118         InitIntegrity       = 0x00010000,       // ISC_REQ_INTEGRITY
119         AcceptIntegrity     = 0x00020000,       // ASC_REQ_INTEGRITY
120 
121         InitManualCredValidation    = 0x00080000,   // ISC_REQ_MANUAL_CRED_VALIDATION
122         InitUseSuppliedCreds        = 0x00000080,   // ISC_REQ_USE_SUPPLIED_CREDS
123         InitIdentify                = 0x00020000,   // ISC_REQ_IDENTIFY
124         AcceptIdentify              = 0x00080000,   // ASC_REQ_IDENTIFY
125 
126         ProxyBindings               = 0x04000000,   // ASC_REQ_PROXY_BINDINGS
127         AllowMissingBindings        = 0x10000000,   // ASC_REQ_ALLOW_MISSING_BINDINGS
128 
129         UnverifiedTargetName        = 0x20000000,   // ISC_REQ_UNVERIFIED_TARGET_NAME
130     }
131 
132 #if MONO_NOT_IMPLEMENTED
133 
134     internal class NTAuthentication {
135 
136         static private int s_UniqueGroupId = 1;
137         static private ContextCallback s_InitializeCallback = new ContextCallback(InitializeCallback);
138 
139         private bool m_IsServer;
140 
141         private SafeFreeCredentials m_CredentialsHandle;
142         private SafeDeleteContext   m_SecurityContext;
143         private string m_Spn;
144         private string m_ClientSpecifiedSpn;
145 
146         private int m_TokenSize;
147         private ContextFlags m_RequestedContextFlags;
148         private ContextFlags m_ContextFlags;
149         private string m_UniqueUserId;
150 
151         private bool m_IsCompleted;
152         private string m_ProtocolName;
153         private SecSizes m_Sizes;
154         private string m_LastProtocolName;
155         private string m_Package;
156 
157         private ChannelBinding m_ChannelBinding;
158 
159         //
160         // Properties
161         //
162         internal string UniqueUserId {
163             get {
164                 return m_UniqueUserId;
165             }
166         }
167 
168         // The semantic of this propoerty is "Don't call me again".
169         // It can be completed either with success or error
170         // The latest case is signalled by IsValidContext==false
171         internal bool IsCompleted {
172             get {
173                 return m_IsCompleted;
174             }
175         }
176 
177         internal bool IsValidContext {
178             get {
179                 return !(m_SecurityContext == null || m_SecurityContext.IsInvalid);
180             }
181         }
182 
183         internal string AssociatedName {
184             get {
185                 if (!(IsValidContext && IsCompleted))
186                     throw new Win32Exception((int)SecurityStatus.InvalidHandle);
187 
188                 string name = SSPIWrapper.QueryContextAttributes(GlobalSSPI.SSPIAuth, m_SecurityContext, ContextAttribute.Names) as string;
189                 GlobalLog.Print("NTAuthentication: The context is associated with [" + name + "]");
190                 return name;
191             }
192         }
193 
194         internal bool IsConfidentialityFlag {
195             get {
196                 return (m_ContextFlags & ContextFlags.Confidentiality) != 0;
197             }
198         }
199 
200         internal bool IsIntegrityFlag {
201             get {
202                 return (m_ContextFlags & (m_IsServer?ContextFlags.AcceptIntegrity:ContextFlags.InitIntegrity)) != 0;
203             }
204         }
205 
206         internal bool IsMutualAuthFlag {
207             get {
208                 return (m_ContextFlags & ContextFlags.MutualAuth) != 0;
209             }
210         }
211 
212         internal bool IsDelegationFlag {
213             get {
214                 return (m_ContextFlags & ContextFlags.Delegate) != 0;
215             }
216         }
217 
218         internal bool IsIdentifyFlag {
219             get {
220                 return (m_ContextFlags & (m_IsServer?ContextFlags.AcceptIdentify:ContextFlags.InitIdentify)) != 0;
221             }
222         }
223 
224         internal string Spn {
225             get {
226                 return m_Spn;
227             }
228         }
229 
230         internal string ClientSpecifiedSpn {
231             get {
232                 if (m_ClientSpecifiedSpn == null) {
233                     m_ClientSpecifiedSpn = GetClientSpecifiedSpn();
234                 }
235                 return m_ClientSpecifiedSpn;
236             }
237         }
238 
239         internal bool OSSupportsExtendedProtection {
240             get {
241                 GlobalLog.Assert(IsCompleted && IsValidContext, "NTAuthentication#{0}::OSSupportsExtendedProtection|The context is not completed or invalid.", ValidationHelper.HashString(this));
242 
243                 int errorCode;
244                 SSPIWrapper.QueryContextAttributes(GlobalSSPI.SSPIAuth, m_SecurityContext,
245                     ContextAttribute.ClientSpecifiedSpn, out errorCode);
246 
247                 // We consider any error other than Unsupported to mean that the underlying OS
248                 // supports extended protection.  Most likely it will be TargetUnknown.
249                 return ((SecurityStatus)errorCode != SecurityStatus.Unsupported);
250             }
251         }
252 
253         //
254         // True indicates this instance is for Server and will use AcceptSecurityContext SSPI API
255         //
256         internal bool IsServer {
257             get {
258                 return m_IsServer;
259             }
260         }
261 
262         //
263         internal bool IsKerberos
264         {
265             get {
266                 if (m_LastProtocolName  == null)
267                     m_LastProtocolName = ProtocolName;
268 
269                 return (object) m_LastProtocolName == (object) NegotiationInfoClass.Kerberos;
270             }
271         }
272         internal bool IsNTLM
273         {
274             get {
275                 if (m_LastProtocolName  == null)
276                     m_LastProtocolName = ProtocolName;
277 
278                 return (object) m_LastProtocolName == (object) NegotiationInfoClass.NTLM;
279             }
280         }
281 
282         internal string Package
283         {
284             get
285             {
286                 return m_Package;
287             }
288         }
289 
290         internal string ProtocolName {
291             get {
292                 // NB: May return string.Empty if the auth is not done yet or failed
293                 if (m_ProtocolName==null)
294                 {
295                     NegotiationInfoClass negotiationInfo = null;
296 
297                     if (IsValidContext)
298                     {
299                         negotiationInfo = SSPIWrapper.QueryContextAttributes(GlobalSSPI.SSPIAuth, m_SecurityContext, ContextAttribute.NegotiationInfo) as NegotiationInfoClass;
300                         if (IsCompleted) {
301                             if (negotiationInfo != null)
302                             {
303                                 //cache it only when it's completed
304                                 m_ProtocolName = negotiationInfo.AuthenticationPackage;
305                             }
306                         }
307                     }
308                     return negotiationInfo == null? string.Empty: negotiationInfo.AuthenticationPackage;
309                 }
310                 return m_ProtocolName;
311             }
312         }
313 
314         internal SecSizes Sizes {
315             get {
316                 GlobalLog.Assert(IsCompleted && IsValidContext, "NTAuthentication#{0}::MaxDataSize|The context is not completed or invalid.", ValidationHelper.HashString(this));
317                 if (m_Sizes == null) {
318                     m_Sizes = SSPIWrapper.QueryContextAttributes(
319                                   GlobalSSPI.SSPIAuth,
320                                   m_SecurityContext,
321                                   ContextAttribute.Sizes
322                                   ) as SecSizes;
323                 }
324                 return m_Sizes;
325             }
326         }
327 
328         internal ChannelBinding ChannelBinding
329         {
330             get { return m_ChannelBinding; }
331         }
332 
333         //
334         // .Ctors
335         //
336 
337         //
338         // Use only for client HTTP authentication
339         //
NTAuthentication(string package, NetworkCredential networkCredential, SpnToken spnToken, WebRequest request, ChannelBinding channelBinding)340         internal NTAuthentication(string package, NetworkCredential networkCredential, SpnToken spnToken,
341                 WebRequest request, ChannelBinding channelBinding) :
342             this(false, package, networkCredential, spnToken.Spn, GetHttpContextFlags(request, spnToken.IsTrusted),
343                 request.GetWritingContext(), channelBinding)
344         {
345             //
346             //  In order to prevent a race condition where one request could
347             //  steal a connection from another request, before a handshake is
348             //  complete, we create a new Group for each authentication request.
349             //
350             if (package == NtlmClient.AuthType || package == NegotiateClient.AuthType) {
351                 m_UniqueUserId = (Interlocked.Increment(ref s_UniqueGroupId)).ToString(NumberFormatInfo.InvariantInfo) + m_UniqueUserId;
352             }
353         }
354         //
GetHttpContextFlags(WebRequest request, bool trustedSpn)355         private static ContextFlags GetHttpContextFlags(WebRequest request, bool trustedSpn)
356         {
357             ContextFlags contextFlags = ContextFlags.Connection;
358 
359             if (request.ImpersonationLevel == TokenImpersonationLevel.Anonymous)
360                 throw new NotSupportedException(SR.GetString(SR.net_auth_no_anonymous_support));
361             else if(request.ImpersonationLevel == TokenImpersonationLevel.Identification)
362                 contextFlags |= ContextFlags.InitIdentify;
363             else if(request.ImpersonationLevel == TokenImpersonationLevel.Delegation)
364                 contextFlags |= ContextFlags.Delegate;
365 
366             if (request.AuthenticationLevel == AuthenticationLevel.MutualAuthRequested || request.AuthenticationLevel == AuthenticationLevel.MutualAuthRequired)
367                 contextFlags |= ContextFlags.MutualAuth;
368 
369             // CBT: If the SPN came from an untrusted source we should tell the server by setting this flag
370             if (!trustedSpn && ComNetOS.IsWin7Sp1orLater)
371                 contextFlags |= ContextFlags.UnverifiedTargetName;
372 
373             return contextFlags;
374         }
375 
376         //
377         // This constructor is for a general (non-HTTP) authentication handshake using SSPI
378         // Works for both client and server sides.
379         //
380         // Security: we may need to impersonate on user behalf as to temporarily restore original thread token.
381         [SecurityPermission(SecurityAction.Assert, Flags=SecurityPermissionFlag.ControlPrincipal)]
NTAuthentication(bool isServer, string package, NetworkCredential credential, string spn, ContextFlags requestedContextFlags, ContextAwareResult context, ChannelBinding channelBinding)382         internal NTAuthentication(bool isServer, string package, NetworkCredential credential, string spn, ContextFlags requestedContextFlags, ContextAwareResult context, ChannelBinding channelBinding)
383         {
384             //
385             // check if we're using DefaultCredentials
386             //
387             if (credential is SystemNetworkCredential)
388             {
389                 //
390 #if DEBUG
391                 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));
392 #endif
393 
394                 WindowsIdentity w = context == null ? null : context.Identity;
395                 try
396                 {
397                     IDisposable ctx = w == null ? null : w.Impersonate();
398                     if (ctx != null)
399                     {
400                         using (ctx)
401                         {
402                             Initialize(isServer, package, credential, spn, requestedContextFlags, channelBinding);
403                         }
404                     }
405                     else
406                     {
407                         ExecutionContext x = context == null ? null : context.ContextCopy;
408                         if (x == null)
409                         {
410                             Initialize(isServer, package, credential, spn, requestedContextFlags, channelBinding);
411                         }
412                         else
413                         {
414                             ExecutionContext.Run(x, s_InitializeCallback, new InitializeCallbackContext(this, isServer, package, credential, spn, requestedContextFlags, channelBinding));
415                         }
416                     }
417                 }
418                 catch
419                 {
420                     // Prevent the impersonation from leaking to upstack exception filters.
421                     throw;
422                 }
423             }
424             else
425             {
426                 Initialize(isServer, package, credential, spn, requestedContextFlags, channelBinding);
427             }
428         }
429 
430         //
431         // This overload does not attmept to impersonate because the caller either did it already or the original thread context is still preserved
432         //
NTAuthentication(bool isServer, string package, NetworkCredential credential, string spn, ContextFlags requestedContextFlags, ChannelBinding channelBinding)433         internal NTAuthentication(bool isServer, string package, NetworkCredential credential, string spn, ContextFlags requestedContextFlags, ChannelBinding channelBinding) {
434             Initialize(isServer, package, credential, spn, requestedContextFlags, channelBinding);
435         }
436 
437         //
438         // This overload always uses the default credentials for the process.
439         //
440         [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.ControlPrincipal)]
NTAuthentication(bool isServer, string package, string spn, ContextFlags requestedContextFlags, ChannelBinding channelBinding)441         internal NTAuthentication(bool isServer, string package, string spn, ContextFlags requestedContextFlags, ChannelBinding channelBinding)
442         {
443             try
444             {
445                 using (WindowsIdentity.Impersonate(IntPtr.Zero))
446                 {
447                     Initialize(isServer, package, SystemNetworkCredential.defaultCredential, spn, requestedContextFlags, channelBinding);
448                 }
449             }
450             catch
451             {
452                 // Avoid exception filter attacks.
453                 throw;
454             }
455         }
456 
457         private class InitializeCallbackContext
458         {
InitializeCallbackContext(NTAuthentication thisPtr, bool isServer, string package, NetworkCredential credential, string spn, ContextFlags requestedContextFlags, ChannelBinding channelBinding)459             internal InitializeCallbackContext(NTAuthentication thisPtr, bool isServer, string package, NetworkCredential credential, string spn, ContextFlags requestedContextFlags, ChannelBinding channelBinding)
460             {
461                 this.thisPtr = thisPtr;
462                 this.isServer = isServer;
463                 this.package = package;
464                 this.credential = credential;
465                 this.spn = spn;
466                 this.requestedContextFlags = requestedContextFlags;
467                 this.channelBinding = channelBinding;
468             }
469 
470             internal readonly NTAuthentication thisPtr;
471             internal readonly bool isServer;
472             internal readonly string package;
473             internal readonly NetworkCredential credential;
474             internal readonly string spn;
475             internal readonly ContextFlags requestedContextFlags;
476             internal readonly ChannelBinding channelBinding;
477         }
478 
InitializeCallback(object state)479         private static void InitializeCallback(object state)
480         {
481             InitializeCallbackContext context = (InitializeCallbackContext)state;
482             context.thisPtr.Initialize(context.isServer, context.package, context.credential, context.spn, context.requestedContextFlags, context.channelBinding);
483         }
484 
485         //
Initialize(bool isServer, string package, NetworkCredential credential, string spn, ContextFlags requestedContextFlags, ChannelBinding channelBinding)486         private void Initialize(bool isServer, string package, NetworkCredential credential, string spn, ContextFlags requestedContextFlags, ChannelBinding channelBinding) {
487             GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::.ctor() package:" + ValidationHelper.ToString(package) + " spn:" + ValidationHelper.ToString(spn) + " flags :" + requestedContextFlags.ToString());
488             m_TokenSize = SSPIWrapper.GetVerifyPackageInfo(GlobalSSPI.SSPIAuth, package, true).MaxToken;
489             m_IsServer = isServer;
490             m_Spn = spn;
491             m_SecurityContext = null;
492             m_RequestedContextFlags = requestedContextFlags;
493             m_Package = package;
494             m_ChannelBinding = channelBinding;
495 
496             GlobalLog.Print("Peer SPN-> '" + m_Spn + "'");
497             //
498             // check if we're using DefaultCredentials
499             //
500             if (credential is SystemNetworkCredential)
501             {
502                 GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::.ctor(): using DefaultCredentials");
503                 m_CredentialsHandle = SSPIWrapper.AcquireDefaultCredential(
504                                                     GlobalSSPI.SSPIAuth,
505                                                     package,
506                                                     (m_IsServer? CredentialUse.Inbound: CredentialUse.Outbound));
507                 m_UniqueUserId = "/S"; // save off for unique connection marking ONLY used by HTTP client
508             }
509             else if (ComNetOS.IsWin7orLater)
510             {
511                 unsafe
512                 {
513                     SafeSspiAuthDataHandle authData = null;
514                     try
515                     {
516                         SecurityStatus result = UnsafeNclNativeMethods.SspiHelper.SspiEncodeStringsAsAuthIdentity(
517                             credential.InternalGetUserName(), credential.InternalGetDomain(),
518                             credential.InternalGetPassword(), out authData);
519 
520                         if (result != SecurityStatus.OK)
521                         {
522                             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)));
523                             throw new Win32Exception((int)result);
524                         }
525 
526                         m_CredentialsHandle = SSPIWrapper.AcquireCredentialsHandle(GlobalSSPI.SSPIAuth,
527                             package, (m_IsServer ? CredentialUse.Inbound : CredentialUse.Outbound), ref authData);
528                     }
529                     finally
530                     {
531                         if (authData != null)
532                         {
533                             authData.Close();
534                         }
535                     }
536                 }
537             }
538             else
539             {
540 
541                 //
542                 // we're not using DefaultCredentials, we need a
543                 // AuthIdentity struct to contain credentials
544                 // SECREVIEW:
545                 // we'll save username/domain in temp strings, to avoid decrypting multiple times.
546                 // password is only used once
547                 //
548                 string username = credential.InternalGetUserName();
549 
550                 string domain = credential.InternalGetDomain();
551                 // ATTN:
552                 // NetworkCredential class does not differentiate between null and "" but SSPI packages treat these cases differently
553                 // For NTLM we want to keep "" for Wdigest.Dll we should use null.
554                 AuthIdentity authIdentity = new AuthIdentity(username, credential.InternalGetPassword(), (object)package == (object)NegotiationInfoClass.WDigest && (domain == null || domain.Length == 0)? null: domain);
555 
556                 m_UniqueUserId = domain + "/" + username + "/U"; // save off for unique connection marking ONLY used by HTTP client
557 
558                 GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::.ctor(): using authIdentity:" + authIdentity.ToString());
559 
560                 m_CredentialsHandle = SSPIWrapper.AcquireCredentialsHandle(
561                                                     GlobalSSPI.SSPIAuth,
562                                                     package,
563                                                     (m_IsServer? CredentialUse.Inbound: CredentialUse.Outbound),
564                                                     ref authIdentity
565                                                     );
566             }
567         }
568 
569         //
570         // Methods
571         //
572 
573 
574         // This will return an client token when conducted authentication on server side'
575         // This token can be used ofr impersanation
576         // We use it to create a WindowsIdentity and hand it out to the server app.
GetContextToken(out SecurityStatus status)577         internal SafeCloseHandle GetContextToken(out SecurityStatus status)
578         {
579             GlobalLog.Assert(IsCompleted && IsValidContext, "NTAuthentication#{0}::GetContextToken|Should be called only when completed with success, currently is not!", ValidationHelper.HashString(this));
580             GlobalLog.Assert(IsServer, "NTAuthentication#{0}::GetContextToken|The method must not be called by the client side!", ValidationHelper.HashString(this));
581 
582             if (!IsValidContext) {
583                 throw new Win32Exception((int)SecurityStatus.InvalidHandle);
584             }
585 
586 
587             SafeCloseHandle token = null;
588             status = (SecurityStatus) SSPIWrapper.QuerySecurityContextToken(
589                 GlobalSSPI.SSPIAuth,
590                 m_SecurityContext,
591                 out token);
592 
593             return token;
594         }
595 
GetContextToken()596         internal SafeCloseHandle GetContextToken()
597         {
598             SecurityStatus status;
599             SafeCloseHandle token = GetContextToken(out status);
600             if (status != SecurityStatus.OK) {
601                 throw new Win32Exception((int)status);
602             }
603             return token;
604         }
605 
CloseContext()606         internal void CloseContext()
607         {
608             if (m_SecurityContext != null && !m_SecurityContext.IsClosed)
609                 m_SecurityContext.Close();
610         }
611 
612         //
613         // NTAuth::GetOutgoingBlob()
614         // Created:   12-01-1999: L.M.
615         // Description:
616         // Accepts a base64 encoded incoming security blob and returns
617         // a base 64 encoded outgoing security blob
618         //
619         // This method is for HttpWebRequest usage only as it has semantic bound to it
GetOutgoingBlob(string incomingBlob)620         internal string GetOutgoingBlob(string incomingBlob) {
621             GlobalLog.Enter("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingBlob", incomingBlob);
622             byte[] decodedIncomingBlob = null;
623             if (incomingBlob != null && incomingBlob.Length > 0) {
624                 decodedIncomingBlob = Convert.FromBase64String(incomingBlob);
625             }
626             byte[] decodedOutgoingBlob = null;
627 
628             if ((IsValidContext || IsCompleted) && decodedIncomingBlob == null) {
629                 // we tried auth previously, now we got a null blob, we're done. this happens
630                 // with Kerberos & valid credentials on the domain but no ACLs on the resource
631                 GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingBlob() null blob AND m_SecurityContext#" + ValidationHelper.HashString(m_SecurityContext) + "::Handle:[0x" + m_SecurityContext.ToString() + "]");
632                 m_IsCompleted = true;
633             }
634             else {
635                 SecurityStatus statusCode;
636 #if TRAVE
637                 try {
638 #endif
639                     decodedOutgoingBlob = GetOutgoingBlob(decodedIncomingBlob, true, out statusCode);
640 #if TRAVE
641                 } catch (Exception exception) {
642                     if (NclUtilities.IsFatal(exception)) throw;
643 
644                     GlobalLog.LeaveException("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingBlob", exception);
645                     throw;
646                 }
647 #endif
648             }
649 
650             string outgoingBlob = null;
651             if (decodedOutgoingBlob != null && decodedOutgoingBlob.Length > 0) {
652                 outgoingBlob = Convert.ToBase64String(decodedOutgoingBlob);
653             }
654 
655             //This is only for HttpWebRequest that does not need security context anymore
656             if (IsCompleted)
657             {
658                 string name = ProtocolName; // cache the only info needed from a completed context before closing it
659                 CloseContext();
660             }
661             GlobalLog.Leave("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingBlob", outgoingBlob);
662             return outgoingBlob;
663         }
664 
665         // NTAuth::GetOutgoingBlob()
666         // Created:   12-01-1999: L.M.
667         // Description:
668         // Accepts an incoming binary security blob  and returns
669         // an outgoing binary security blob
GetOutgoingBlob(byte[] incomingBlob, bool throwOnError, out SecurityStatus statusCode)670         internal byte[] GetOutgoingBlob(byte[] incomingBlob, bool throwOnError, out SecurityStatus statusCode)
671         {
672             GlobalLog.Enter("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingBlob", ((incomingBlob == null) ? "0" : incomingBlob.Length.ToString(NumberFormatInfo.InvariantInfo)) + " bytes");
673 
674             List<SecurityBuffer> list = new List<SecurityBuffer>(2);
675 
676             if (incomingBlob != null) {
677                 list.Add(new SecurityBuffer(incomingBlob, BufferType.Token));
678             }
679             if (m_ChannelBinding != null) {
680                 list.Add(new SecurityBuffer(m_ChannelBinding));
681             }
682 
683             SecurityBuffer[] inSecurityBufferArray = null;
684             if (list.Count > 0)
685             {
686                 inSecurityBufferArray = list.ToArray();
687             }
688 
689             SecurityBuffer outSecurityBuffer = new SecurityBuffer(m_TokenSize, BufferType.Token);
690 
691             bool firstTime = m_SecurityContext == null;
692             try {
693                 if (!m_IsServer) {
694                     // client session
695                     statusCode = (SecurityStatus)SSPIWrapper.InitializeSecurityContext(
696                         GlobalSSPI.SSPIAuth,
697                         m_CredentialsHandle,
698                         ref m_SecurityContext,
699                         m_Spn,
700                         m_RequestedContextFlags,
701                         Endianness.Network,
702                         inSecurityBufferArray,
703                         outSecurityBuffer,
704                         ref m_ContextFlags);
705 
706                     GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingBlob() SSPIWrapper.InitializeSecurityContext() returns statusCode:0x" + ((int) statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")");
707 
708                     if (statusCode == SecurityStatus.CompleteNeeded)
709                     {
710                         SecurityBuffer[] inSecurityBuffers = new SecurityBuffer[1];
711                         inSecurityBuffers[0] = outSecurityBuffer;
712 
713                         statusCode = (SecurityStatus) SSPIWrapper.CompleteAuthToken(
714                             GlobalSSPI.SSPIAuth,
715                             ref m_SecurityContext,
716                             inSecurityBuffers );
717 
718                         GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob() SSPIWrapper.CompleteAuthToken() returns statusCode:0x" + ((int) statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")");
719                         outSecurityBuffer.token = null;
720                     }
721                 }
722                 else {
723                     // server session
724                     statusCode = (SecurityStatus)SSPIWrapper.AcceptSecurityContext(
725                         GlobalSSPI.SSPIAuth,
726                         m_CredentialsHandle,
727                         ref m_SecurityContext,
728                         m_RequestedContextFlags,
729                         Endianness.Network,
730                         inSecurityBufferArray,
731                         outSecurityBuffer,
732                         ref m_ContextFlags);
733 
734                     GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingBlob() SSPIWrapper.AcceptSecurityContext() returns statusCode:0x" + ((int) statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")");
735                 }
736             }
737             finally {
738                 //
739                 // Assuming the ISC or ASC has referenced the credential on the first successful call,
740                 // we want to decrement the effective ref count by "disposing" it.
741                 // The real dispose will happen when the security context is closed.
742                 // Note if the first call was not successfull the handle is physically destroyed here
743                 //
744               if (firstTime && m_CredentialsHandle != null)
745                   m_CredentialsHandle.Close();
746             }
747 
748 
749           if (((int) statusCode & unchecked((int) 0x80000000)) != 0)
750           {
751                 CloseContext();
752                 m_IsCompleted = true;
753                 if (throwOnError) {
754                     Win32Exception exception = new Win32Exception((int) statusCode);
755                     GlobalLog.Leave("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingBlob", "Win32Exception:" + exception);
756                     throw exception;
757                 }
758                 GlobalLog.Leave("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingBlob", "null statusCode:0x" + ((int) statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")");
759                 return null;
760             }
761             else if (firstTime && m_CredentialsHandle != null)
762             {
763                 // cache until it is pushed out by newly incoming handles
764                 SSPIHandleCache.CacheCredential(m_CredentialsHandle);
765             }
766 
767             // the return value from SSPI will tell us correctly if the
768             // handshake is over or not: http://msdn.microsoft.com/library/psdk/secspi/sspiref_67p0.htm
769             // we also have to consider the case in which SSPI formed a new context, in this case we're done as well.
770             if (statusCode == SecurityStatus.OK)
771             {
772                 // we're sucessfully done
773                 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));
774                 m_IsCompleted = true;
775             }
776             else {
777                 // we need to continue
778                 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) + "]");
779             }
780 //            GlobalLog.Print("out token = " + outSecurityBuffer.ToString());
781 //            GlobalLog.Dump(outSecurityBuffer.token);
782             GlobalLog.Leave("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingBlob", "IsCompleted:" + IsCompleted.ToString());
783             return outSecurityBuffer.token;
784         }
785 
786         // for Server side (IIS 6.0) see: \\netindex\Sources\inetsrv\iis\iisrearc\iisplus\ulw3\digestprovider.cxx
787         // for Client side (HTTP.SYS) see: \\netindex\Sources\net\http\sys\ucauth.c
GetOutgoingDigestBlob(string incomingBlob, string requestMethod, string requestedUri, string realm, bool isClientPreAuth, bool throwOnError, out SecurityStatus statusCode)788         internal string GetOutgoingDigestBlob(string incomingBlob, string requestMethod, string requestedUri, string realm, bool isClientPreAuth, bool throwOnError, out SecurityStatus statusCode)
789         {
790             GlobalLog.Enter("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob", incomingBlob);
791 
792             // second time call with 3 incoming buffers to select HTTP client.
793             // we should get back a SecurityStatus.OK and a non null outgoingBlob.
794             SecurityBuffer[] inSecurityBuffers = null;
795             SecurityBuffer outSecurityBuffer = new SecurityBuffer(m_TokenSize, isClientPreAuth ? BufferType.Parameters : BufferType.Token);
796 
797             bool firstTime = m_SecurityContext == null;
798             try {
799                 if (!m_IsServer) {
800                     // client session
801 
802                     if (!isClientPreAuth) {
803 
804                         if (incomingBlob != null)
805                         {
806                             List<SecurityBuffer> list = new List<SecurityBuffer>(5);
807 
808                             list.Add(new SecurityBuffer(WebHeaderCollection.HeaderEncoding.GetBytes(incomingBlob), BufferType.Token));
809                             list.Add(new SecurityBuffer(WebHeaderCollection.HeaderEncoding.GetBytes(requestMethod), BufferType.Parameters));
810                             list.Add(new SecurityBuffer(null, BufferType.Parameters));
811                             list.Add(new SecurityBuffer(Encoding.Unicode.GetBytes(m_Spn), BufferType.TargetHost));
812 
813                             if (m_ChannelBinding != null) {
814                                 list.Add(new SecurityBuffer(m_ChannelBinding));
815                             }
816 
817                             inSecurityBuffers = list.ToArray();
818                         }
819 
820                         statusCode = (SecurityStatus) SSPIWrapper.InitializeSecurityContext(
821                             GlobalSSPI.SSPIAuth,
822                             m_CredentialsHandle,
823                             ref m_SecurityContext,
824                             requestedUri, // this must match the Uri in the HTTP status line for the current request
825                             m_RequestedContextFlags,
826                             Endianness.Network,
827                             inSecurityBuffers,
828                             outSecurityBuffer,
829                             ref m_ContextFlags );
830 
831                         GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob() SSPIWrapper.InitializeSecurityContext() returns statusCode:0x" + ((int) statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")");
832                     }
833                     else {
834 #if WDIGEST_PREAUTH
835                         inSecurityBuffers = new SecurityBuffer[] {
836                             new SecurityBuffer(null, BufferType.Token),
837                             new SecurityBuffer(WebHeaderCollection.HeaderEncoding.GetBytes(requestMethod), BufferType.Parameters),
838                             new SecurityBuffer(WebHeaderCollection.HeaderEncoding.GetBytes(requestedUri), BufferType.Parameters),
839                             new SecurityBuffer(null, BufferType.Parameters),
840                             outSecurityBuffer,
841                         };
842 
843                         statusCode = (SecurityStatus) SSPIWrapper.MakeSignature(GlobalSSPI.SSPIAuth, m_SecurityContext, inSecurityBuffers, 0);
844 
845                         GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob() SSPIWrapper.MakeSignature() returns statusCode:0x" + ((int) statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")");
846 #else
847                         statusCode = SecurityStatus.OK;
848                         GlobalLog.Assert("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob()", "Invalid code path.");
849 #endif
850                     }
851                 }
852                 else {
853                     // server session
854                     List<SecurityBuffer> list = new List<SecurityBuffer>(6);
855 
856                     list.Add(incomingBlob == null ? new SecurityBuffer(0, BufferType.Token) : new SecurityBuffer(WebHeaderCollection.HeaderEncoding.GetBytes(incomingBlob), BufferType.Token));
857                     list.Add(requestMethod == null ? new SecurityBuffer(0, BufferType.Parameters) : new SecurityBuffer(WebHeaderCollection.HeaderEncoding.GetBytes(requestMethod), BufferType.Parameters));
858                     list.Add(requestedUri == null ? new SecurityBuffer(0, BufferType.Parameters) : new SecurityBuffer(WebHeaderCollection.HeaderEncoding.GetBytes(requestedUri), BufferType.Parameters));
859                     list.Add(new SecurityBuffer(0, BufferType.Parameters));
860                     list.Add(realm == null ? new SecurityBuffer(0, BufferType.Parameters) : new SecurityBuffer(Encoding.Unicode.GetBytes(realm), BufferType.Parameters));
861 
862                     if (m_ChannelBinding != null) {
863                         list.Add(new SecurityBuffer(m_ChannelBinding));
864                     }
865 
866                     inSecurityBuffers = list.ToArray();
867 
868                     statusCode = (SecurityStatus) SSPIWrapper.AcceptSecurityContext(
869                         GlobalSSPI.SSPIAuth,
870                         m_CredentialsHandle,
871                         ref m_SecurityContext,
872                         m_RequestedContextFlags,
873                         Endianness.Network,
874                         inSecurityBuffers,
875                         outSecurityBuffer,
876                         ref m_ContextFlags );
877 
878                     GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob() SSPIWrapper.AcceptSecurityContext() returns statusCode:0x" + ((int)statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")");
879 
880                     if (statusCode == SecurityStatus.CompleteNeeded)
881                     {
882                         inSecurityBuffers[4] = outSecurityBuffer;
883 
884                         statusCode = (SecurityStatus) SSPIWrapper.CompleteAuthToken(
885                                 GlobalSSPI.SSPIAuth,
886                                 ref m_SecurityContext,
887                                 inSecurityBuffers );
888 
889                         GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob() SSPIWrapper.CompleteAuthToken() returns statusCode:0x" + ((int)statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")");
890 
891                         outSecurityBuffer.token = null;
892                     }
893                 }
894             }
895             finally {
896                 //
897                 // Assuming the ISC or ASC has referenced the credential on the first successful call,
898                 // we want to decrement the effective ref count by "disposing" it.
899                 // The real dispose will happen when the security context is closed.
900                 // Note if the first call was not successfull the handle is physically destroyed here
901                 //
902               if (firstTime && m_CredentialsHandle != null)
903                   m_CredentialsHandle.Close();
904             }
905 
906 
907             if (((int) statusCode & unchecked((int) 0x80000000)) != 0)
908             {
909                 CloseContext();
910                 if (throwOnError) {
911                     Win32Exception exception = new Win32Exception((int) statusCode);
912                     GlobalLog.Leave("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob", "Win32Exception:" + exception);
913                     throw exception;
914                 }
915                 GlobalLog.Leave("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob", "null statusCode:0x" + ((int) statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")");
916                 return null;
917             }
918             else if (firstTime && m_CredentialsHandle != null)
919             {
920                 // cache until it is pushed out by newly incoming handles
921                 SSPIHandleCache.CacheCredential(m_CredentialsHandle);
922             }
923 
924 
925             // the return value from SSPI will tell us correctly if the
926             // handshake is over or not: http://msdn.microsoft.com/library/psdk/secspi/sspiref_67p0.htm
927             if (statusCode == SecurityStatus.OK)
928             {
929                 // we're done, cleanup
930                 m_IsCompleted = true;
931             }
932             else {
933                 // we need to continue
934                 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) + "]");
935             }
936             GlobalLog.Print("out token = " + outSecurityBuffer.ToString());
937             GlobalLog.Dump(outSecurityBuffer.token);
938             GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob() IsCompleted:" + IsCompleted.ToString());
939 
940             byte[] decodedOutgoingBlob = outSecurityBuffer.token;
941             string outgoingBlob = null;
942             if (decodedOutgoingBlob!=null && decodedOutgoingBlob.Length>0) {
943                 outgoingBlob = WebHeaderCollection.HeaderEncoding.GetString(decodedOutgoingBlob, 0, outSecurityBuffer.size);
944             }
945             GlobalLog.Leave("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob", outgoingBlob);
946             return outgoingBlob;
947         }
948 
Encrypt(byte[] buffer, int offset, int count, ref byte[] output, uint sequenceNumber)949         internal int Encrypt(byte[] buffer, int offset, int count, ref byte[] output, uint sequenceNumber) {
950             SecSizes sizes = Sizes;
951 
952             try
953             {
954                 int maxCount = checked(Int32.MaxValue - 4 - sizes.BlockSize - sizes.SecurityTrailer);
955 
956                 if (count > maxCount || count < 0)
957                 {
958                     throw new ArgumentOutOfRangeException("count", SR.GetString(SR.net_io_out_range, maxCount));
959                 }
960             }
961             catch(Exception e)
962             {
963                 if (!NclUtilities.IsFatal(e)){
964                     GlobalLog.Assert(false, "NTAuthentication#" + ValidationHelper.HashString(this) + "::Encrypt", "Arguments out of range.");
965                 }
966                 throw;
967             }
968 
969             int resultSize = count + sizes.SecurityTrailer + sizes.BlockSize;
970             if (output == null || output.Length < resultSize+4)
971             {
972                 output = new byte[resultSize+4];
973             }
974 
975             // make a copy of user data for in-place encryption
976             Buffer.BlockCopy(buffer, offset, output, 4 + sizes.SecurityTrailer, count);
977 
978             // prepare buffers TOKEN(signautre), DATA and Padding
979             SecurityBuffer[] securityBuffer = new SecurityBuffer[3];
980             securityBuffer[0] = new SecurityBuffer(output, 4, sizes.SecurityTrailer, BufferType.Token);
981             securityBuffer[1] = new SecurityBuffer(output, 4 + sizes.SecurityTrailer, count, BufferType.Data);
982             securityBuffer[2] = new SecurityBuffer(output, 4 + sizes.SecurityTrailer + count, sizes.BlockSize, BufferType.Padding);
983 
984             int errorCode;
985             if (IsConfidentialityFlag)
986             {
987                 errorCode = SSPIWrapper.EncryptMessage(GlobalSSPI.SSPIAuth, m_SecurityContext, securityBuffer, sequenceNumber);
988             }
989             else
990             {
991                 if (IsNTLM)
992                     securityBuffer[1].type |= BufferType.ReadOnlyFlag;
993                 errorCode = SSPIWrapper.MakeSignature(GlobalSSPI.SSPIAuth, m_SecurityContext, securityBuffer, 0);
994             }
995 
996 
997             if (errorCode != 0) {
998                 GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::Encrypt() throw Error = " + errorCode.ToString("x", NumberFormatInfo.InvariantInfo));
999                 throw new Win32Exception(errorCode);
1000             }
1001 
1002             // Compacting the result...
1003             resultSize = securityBuffer[0].size;
1004             bool forceCopy = false;
1005             if (resultSize != sizes.SecurityTrailer)
1006             {
1007                 forceCopy = true;
1008                 Buffer.BlockCopy(output, securityBuffer[1].offset, output, 4 + resultSize, securityBuffer[1].size);
1009             }
1010 
1011             resultSize += securityBuffer[1].size;
1012             if (securityBuffer[2].size != 0 && (forceCopy || resultSize != (count + sizes.SecurityTrailer)))
1013                 Buffer.BlockCopy(output, securityBuffer[2].offset, output, 4 + resultSize, securityBuffer[2].size);
1014 
1015             resultSize += securityBuffer[2].size;
1016 
1017             unchecked {
1018                 output[0] = (byte)((resultSize) & 0xFF);
1019                 output[1] = (byte)(((resultSize)>>8) & 0xFF);
1020                 output[2] = (byte)(((resultSize)>>16) & 0xFF);
1021                 output[3] = (byte)(((resultSize)>>24) & 0xFF);
1022             }
1023             return resultSize+4;
1024         }
1025 
Decrypt(byte[] payload, int offset, int count, out int newOffset, uint expectedSeqNumber)1026         internal int Decrypt(byte[] payload, int offset, int count, out int newOffset, uint expectedSeqNumber)
1027         {
1028             if (offset < 0 || offset > (payload == null ? 0 : payload.Length))
1029             {
1030                 GlobalLog.Assert(false, "NTAuthentication#" + ValidationHelper.HashString(this) + "::Decrypt", "Argument 'offset' out of range.");
1031                 throw new ArgumentOutOfRangeException("offset");
1032             }
1033             if (count < 0 || count > (payload == null ? 0 : payload.Length - offset))
1034             {
1035                 GlobalLog.Assert(false, "NTAuthentication#" + ValidationHelper.HashString(this) + "::Decrypt", "Argument 'count' out of range.");
1036                 throw new ArgumentOutOfRangeException("count");
1037             }
1038 
1039             if (IsNTLM)
1040                 return DecryptNtlm(payload, offset, count, out newOffset, expectedSeqNumber);
1041 
1042             //
1043             // Kerberos and up
1044             //
1045 
1046             SecurityBuffer[] securityBuffer = new SecurityBuffer[2];
1047             securityBuffer[0] = new SecurityBuffer(payload, offset, count, BufferType.Stream);
1048             securityBuffer[1] = new SecurityBuffer(0, BufferType.Data);
1049 
1050             int errorCode;
1051             if (IsConfidentialityFlag)
1052             {
1053                 errorCode = SSPIWrapper.DecryptMessage(GlobalSSPI.SSPIAuth, m_SecurityContext, securityBuffer, expectedSeqNumber);
1054             }
1055             else
1056             {
1057                 errorCode = SSPIWrapper.VerifySignature(GlobalSSPI.SSPIAuth, m_SecurityContext, securityBuffer, expectedSeqNumber);
1058             }
1059 
1060             if (errorCode != 0)
1061             {
1062                 GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::Decrypt() throw Error = " + errorCode.ToString("x", NumberFormatInfo.InvariantInfo));
1063                 throw new Win32Exception(errorCode);
1064             }
1065 
1066             if (securityBuffer[1].type != BufferType.Data)
1067                 throw new InternalException();
1068 
1069             newOffset = securityBuffer[1].offset;
1070             return securityBuffer[1].size;
1071 
1072         }
1073 
GetClientSpecifiedSpn()1074         private string GetClientSpecifiedSpn()
1075         {
1076             GlobalLog.Assert(IsValidContext && IsCompleted, "NTAuthentication: Trying to get the client SPN before handshaking is done!");
1077 
1078             string spn = SSPIWrapper.QueryContextAttributes(GlobalSSPI.SSPIAuth, m_SecurityContext,
1079                 ContextAttribute.ClientSpecifiedSpn) as string;
1080 
1081             GlobalLog.Print("NTAuthentication: The client specified SPN is [" + spn + "]");
1082             return spn;
1083         }
1084 
1085         //
DecryptNtlm(byte[] payload, int offset, int count, out int newOffset, uint expectedSeqNumber)1086         private int DecryptNtlm(byte[] payload, int offset, int count, out int newOffset, uint expectedSeqNumber)
1087         {
1088             // For the most part the arguments are verified in Encrypt().
1089             if (count < 16)
1090             {
1091                 GlobalLog.Assert(false, "NTAuthentication#" + ValidationHelper.HashString(this) + "::DecryptNtlm", "Argument 'count' out of range.");
1092                 throw new ArgumentOutOfRangeException("count");
1093             }
1094 
1095             SecurityBuffer[] securityBuffer = new SecurityBuffer[2];
1096             securityBuffer[0] = new SecurityBuffer(payload, offset, 16, BufferType.Token);
1097             securityBuffer[1] = new SecurityBuffer(payload, offset + 16, count-16, BufferType.Data);
1098 
1099             int errorCode;
1100             BufferType realDataType = BufferType.Data;
1101 
1102             if (IsConfidentialityFlag)
1103             {
1104                 errorCode = SSPIWrapper.DecryptMessage(GlobalSSPI.SSPIAuth, m_SecurityContext, securityBuffer, expectedSeqNumber);
1105             }
1106             else
1107             {
1108                 realDataType |= BufferType.ReadOnlyFlag;
1109                 securityBuffer[1].type = realDataType;
1110                 errorCode = SSPIWrapper.VerifySignature(GlobalSSPI.SSPIAuth, m_SecurityContext, securityBuffer, expectedSeqNumber);
1111             }
1112 
1113             if (errorCode != 0)
1114             {
1115                 GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::Decrypt() throw Error = " + errorCode.ToString("x", NumberFormatInfo.InvariantInfo));
1116                 throw new Win32Exception(errorCode);
1117             }
1118 
1119             if (securityBuffer[1].type != realDataType)
1120                 throw new InternalException();
1121 
1122             newOffset = securityBuffer[1].offset;
1123             return securityBuffer[1].size;
1124         }
1125 
1126         //
1127         // VerifySignature
1128         //
1129         // Adapted from Decrypt method above as a more generic message
1130         // signature verify method for SMTP AUTH GSSAPI (SASL).
1131         // Decrypt method, used NegotiateStream, couldn't be used due
1132         // to special cases for NTLM.
1133         //
1134         // See SmtpNegotiateAuthenticationModule class for caller.
1135         //
VerifySignature(byte[] buffer, int offset, int count)1136         internal int VerifySignature(byte[] buffer, int offset, int count) {
1137 
1138             // validate offset within length
1139             if (offset < 0 || offset > (buffer == null ? 0 : buffer.Length)) {
1140                 GlobalLog.Assert(
1141                             false,
1142                             "NTAuthentication#" +
1143                             ValidationHelper.HashString(this) +
1144                             "::VerifySignature",
1145                             "Argument 'offset' out of range.");
1146                 throw new ArgumentOutOfRangeException("offset");
1147             }
1148 
1149             // validate count within offset and end of buffer
1150             if (count < 0 ||
1151                 count > (buffer == null ? 0 : buffer.Length - offset)) {
1152                 GlobalLog.Assert(
1153                             false,
1154                             "NTAuthentication#" +
1155                             ValidationHelper.HashString(this) +
1156                             "::VerifySignature",
1157                             "Argument 'count' out of range.");
1158                 throw new ArgumentOutOfRangeException("count");
1159             }
1160 
1161             // setup security buffers for ssp call
1162             // one points at signed data
1163             // two will receive payload if signature is valid
1164             SecurityBuffer[] securityBuffer = new SecurityBuffer[2];
1165             securityBuffer[0] =
1166                 new SecurityBuffer(buffer, offset, count, BufferType.Stream);
1167             securityBuffer[1] = new SecurityBuffer(0, BufferType.Data);
1168 
1169             // call SSP function
1170             int errorCode = SSPIWrapper.VerifySignature(
1171                                 GlobalSSPI.SSPIAuth,
1172                                 m_SecurityContext,
1173                                 securityBuffer,
1174                                 0);
1175 
1176             // throw if error
1177             if (errorCode != 0)
1178             {
1179                 GlobalLog.Print(
1180                             "NTAuthentication#" +
1181                             ValidationHelper.HashString(this) +
1182                             "::VerifySignature() threw Error = " +
1183                             errorCode.ToString("x",
1184                                 NumberFormatInfo.InvariantInfo));
1185                 throw new Win32Exception(errorCode);
1186             }
1187 
1188             // not sure why this is here - retained from Encrypt code above
1189             if (securityBuffer[1].type != BufferType.Data)
1190                 throw new InternalException();
1191 
1192             // return validated payload size
1193             return securityBuffer[1].size;
1194         }
1195 
1196         //
1197         // MakeSignature
1198         //
1199         // Adapted from Encrypt method above as a more generic message
1200         // signing method for SMTP AUTH GSSAPI (SASL).
1201         // Encrypt method, used for NegotiateStream, put size at head of
1202         // message.  Don't need that
1203         //
1204         // See SmtpNegotiateAuthenticationModule class for caller.
1205         //
MakeSignature( byte[] buffer, int offset, int count, ref byte[] output)1206         internal int MakeSignature(
1207                         byte[] buffer,
1208                         int offset,
1209                         int count,
1210                         ref byte[] output) {
1211             SecSizes sizes = Sizes;
1212 
1213 
1214             // alloc new output buffer if not supplied or too small
1215             int resultSize = count + sizes.MaxSignature;
1216             if (output == null || output.Length < resultSize)
1217             {
1218                 output = new byte[resultSize];
1219             }
1220 
1221             // make a copy of user data for in-place encryption
1222             Buffer.BlockCopy(buffer, offset, output, sizes.MaxSignature, count);
1223 
1224             // setup security buffers for ssp call
1225             SecurityBuffer[] securityBuffer = new SecurityBuffer[2];
1226             securityBuffer[0] = new SecurityBuffer(output, 0, sizes.MaxSignature, BufferType.Token);
1227             securityBuffer[1] = new SecurityBuffer(output, sizes.MaxSignature, count, BufferType.Data);
1228 
1229             // call SSP Function
1230             int errorCode = SSPIWrapper.MakeSignature(
1231                                 GlobalSSPI.SSPIAuth,
1232                                 m_SecurityContext,
1233                                 securityBuffer,
1234                                 0);
1235 
1236             // throw if error
1237             if (errorCode != 0) {
1238                 GlobalLog.Print(
1239                     "NTAuthentication#" +
1240                     ValidationHelper.HashString(this) +
1241                     "::Encrypt() throw Error = " +
1242                     errorCode.ToString("x", NumberFormatInfo.InvariantInfo));
1243                 throw new Win32Exception(errorCode);
1244             }
1245 
1246             // return signed size
1247             return securityBuffer[0].size + securityBuffer[1].size;
1248         }
1249     }
1250 
1251     [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
1252     internal struct AuthIdentity {
1253         // see SEC_WINNT_AUTH_IDENTITY_W
1254         internal string UserName;
1255         internal int UserNameLength;
1256         internal string Domain;
1257         internal int DomainLength;
1258         internal string Password;
1259         internal int PasswordLength;
1260         internal int Flags;
1261 
AuthIdentitySystem.Net.AuthIdentity1262         internal AuthIdentity(string userName, string password, string domain) {
1263             UserName = userName;
1264             UserNameLength = userName==null ? 0 : userName.Length;
1265             Password = password;
1266             PasswordLength = password==null ? 0 : password.Length;
1267             Domain = domain;
1268             DomainLength = domain==null ? 0 : domain.Length;
1269             // Flags are 2 for Unicode and 1 for ANSI. We use 2 on NT and 1 on Win9x.
1270             Flags = 2;
1271         }
ToStringSystem.Net.AuthIdentity1272         public override string ToString() {
1273             return ValidationHelper.ToString(Domain) + "\\" + ValidationHelper.ToString(UserName);
1274         }
1275     }
1276 #endif
1277 }
1278