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