From 570b97eb47cd8f6c77f5d5149264f803cc6c6ede Mon Sep 17 00:00:00 2001 From: Przemyslaw Klys Date: Thu, 16 May 2024 18:36:21 +0200 Subject: [PATCH] Adds integrated auth (starter)? - no need for #36 - maybe starting point for #43? --- .../SaslMechanismNtlmIntegrated.cs | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 Sources/Mailozaurr/Authentication/SaslMechanismNtlmIntegrated.cs diff --git a/Sources/Mailozaurr/Authentication/SaslMechanismNtlmIntegrated.cs b/Sources/Mailozaurr/Authentication/SaslMechanismNtlmIntegrated.cs new file mode 100644 index 0000000..2c8a3cf --- /dev/null +++ b/Sources/Mailozaurr/Authentication/SaslMechanismNtlmIntegrated.cs @@ -0,0 +1,91 @@ +using NSspi; +using NSspi.Contexts; +using NSspi.Credentials; + +namespace Mailozaurr { + /// + /// The NTLM Integrated Auth SASL mechanism. + /// + /// + /// A SASL mechanism based on NTLM using the credentials of the current user + /// via Windows Integrated Authentication (SSPI). + /// + /// + public class SaslMechanismNtlmIntegrated : SaslMechanism { + enum LoginState { + Initial, + Challenge + } + + LoginState state; + ClientContext sspiContext; + + /// + /// Initializes a new instance of the class. + /// + /// + /// Creates a new NTLM Integrated Auth SASL context. + /// + public SaslMechanismNtlmIntegrated() : base(string.Empty, string.Empty) { + } + + /// + public override string MechanismName => "NTLM"; + + /// + public override bool SupportsInitialResponse => true; + + /// + /// The authenticated user name. + /// + public virtual string AuthenticatedUserName => sspiContext.ContextUserName; + + /// + /// + /// The SASL mechanism is already authenticated. + /// + protected override byte[] Challenge(byte[] token, int startIndex, int length, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); + + if (IsAuthenticated) { + throw new InvalidOperationException(); + } + + InitializeSSPIContext(); + + byte[] serverResponse; + + if (state == LoginState.Initial) { + sspiContext.Init(null, out serverResponse); + state = LoginState.Challenge; + } else { + sspiContext.Init(token, out serverResponse); + IsAuthenticated = true; + } + + return serverResponse; + } + + private void InitializeSSPIContext() { + if (sspiContext != null) { + return; + } + + var credential = new ClientCurrentCredential(PackageNames.Ntlm); + + sspiContext = new ClientContext( + credential, + string.Empty, + ContextAttrib.InitIntegrity + | ContextAttrib.ReplayDetect + | ContextAttrib.SequenceDetect + | ContextAttrib.Confidentiality); + } + + + public override void Reset() { + state = LoginState.Initial; + base.Reset(); + } + } +} \ No newline at end of file