mirror of
https://github.com/TalAloni/SMBLibrary.git
synced 2025-07-22 01:05:54 +02:00
Client: Added API to provide custom authentication
This commit is contained in:
parent
a0b07521a1
commit
3a7e36b6d1
5 changed files with 172 additions and 81 deletions
16
SMBLibrary/Client/Authentication/IAuthenticationClient.cs
Normal file
16
SMBLibrary/Client/Authentication/IAuthenticationClient.cs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
/* Copyright (C) 2023-2023 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
|
||||||
|
*
|
||||||
|
* You can redistribute this program and/or modify it under the terms of
|
||||||
|
* the GNU Lesser Public License as published by the Free Software Foundation,
|
||||||
|
* either version 3 of the License, or (at your option) any later version.
|
||||||
|
*/
|
||||||
|
namespace SMBLibrary.Client.Authentication
|
||||||
|
{
|
||||||
|
public interface IAuthenticationClient
|
||||||
|
{
|
||||||
|
/// <returns>Credentials blob or null if security blob is invalid</returns>
|
||||||
|
byte[] InitializeSecurityContext(byte[] securityBlob);
|
||||||
|
|
||||||
|
byte[] GetSessionKey();
|
||||||
|
}
|
||||||
|
}
|
135
SMBLibrary/Client/Authentication/NTLMAuthenticationClient.cs
Normal file
135
SMBLibrary/Client/Authentication/NTLMAuthenticationClient.cs
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
/* Copyright (C) 2017-2023 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
|
||||||
|
*
|
||||||
|
* You can redistribute this program and/or modify it under the terms of
|
||||||
|
* the GNU Lesser Public License as published by the Free Software Foundation,
|
||||||
|
* either version 3 of the License, or (at your option) any later version.
|
||||||
|
*/
|
||||||
|
using SMBLibrary.Authentication.GSSAPI;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Utilities;
|
||||||
|
|
||||||
|
namespace SMBLibrary.Client.Authentication
|
||||||
|
{
|
||||||
|
public class NTLMAuthenticationClient : IAuthenticationClient
|
||||||
|
{
|
||||||
|
private string m_domainName;
|
||||||
|
private string m_userName;
|
||||||
|
private string m_password;
|
||||||
|
private string m_spn;
|
||||||
|
private byte[] m_sessionKey;
|
||||||
|
private AuthenticationMethod m_authenticationMethod;
|
||||||
|
|
||||||
|
private bool m_isNegotiationMessageAcquired = false;
|
||||||
|
|
||||||
|
public NTLMAuthenticationClient(string domainName, string userName, string password, string spn, AuthenticationMethod authenticationMethod)
|
||||||
|
{
|
||||||
|
m_domainName = domainName;
|
||||||
|
m_userName = userName;
|
||||||
|
m_password = password;
|
||||||
|
m_spn = spn;
|
||||||
|
m_authenticationMethod = authenticationMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] InitializeSecurityContext(byte[] securityBlob)
|
||||||
|
{
|
||||||
|
if (!m_isNegotiationMessageAcquired)
|
||||||
|
{
|
||||||
|
m_isNegotiationMessageAcquired = true;
|
||||||
|
return GetNegotiateMessage(securityBlob);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return GetAuthenticateMessage(securityBlob);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual byte[] GetNegotiateMessage(byte[] securityBlob)
|
||||||
|
{
|
||||||
|
bool useGSSAPI = false;
|
||||||
|
if (securityBlob.Length > 0)
|
||||||
|
{
|
||||||
|
SimpleProtectedNegotiationTokenInit spnegoToken = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
spnegoToken = SimpleProtectedNegotiationToken.ReadToken(securityBlob, 0, true) as SimpleProtectedNegotiationTokenInit;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spnegoToken == null || !ContainsMechanism(spnegoToken, GSSProvider.NTLMSSPIdentifier))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
useGSSAPI = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] negotiateMessageBytes = NTLMAuthenticationHelper.GetNegotiateMessage(m_domainName, m_userName, m_password, m_authenticationMethod);
|
||||||
|
if (useGSSAPI)
|
||||||
|
{
|
||||||
|
SimpleProtectedNegotiationTokenInit outputToken = new SimpleProtectedNegotiationTokenInit();
|
||||||
|
outputToken.MechanismTypeList = new List<byte[]>();
|
||||||
|
outputToken.MechanismTypeList.Add(GSSProvider.NTLMSSPIdentifier);
|
||||||
|
outputToken.MechanismToken = negotiateMessageBytes;
|
||||||
|
return outputToken.GetBytes(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return negotiateMessageBytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual byte[] GetAuthenticateMessage(byte[] securityBlob)
|
||||||
|
{
|
||||||
|
bool useGSSAPI = false;
|
||||||
|
SimpleProtectedNegotiationTokenResponse spnegoToken = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
spnegoToken = SimpleProtectedNegotiationToken.ReadToken(securityBlob, 0, false) as SimpleProtectedNegotiationTokenResponse;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] challengeMessageBytes;
|
||||||
|
if (spnegoToken != null)
|
||||||
|
{
|
||||||
|
challengeMessageBytes = spnegoToken.ResponseToken;
|
||||||
|
useGSSAPI = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
challengeMessageBytes = securityBlob;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] authenticationMessageBytes = NTLMAuthenticationHelper.GetAuthenticateMessage(challengeMessageBytes, m_domainName, m_userName, m_password, m_spn, m_authenticationMethod, out m_sessionKey);
|
||||||
|
if (useGSSAPI && authenticationMessageBytes != null)
|
||||||
|
{
|
||||||
|
SimpleProtectedNegotiationTokenResponse outputToken = new SimpleProtectedNegotiationTokenResponse();
|
||||||
|
outputToken.ResponseToken = authenticationMessageBytes;
|
||||||
|
return outputToken.GetBytes();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return authenticationMessageBytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual byte[] GetSessionKey()
|
||||||
|
{
|
||||||
|
return m_sessionKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool ContainsMechanism(SimpleProtectedNegotiationTokenInit token, byte[] mechanismIdentifier)
|
||||||
|
{
|
||||||
|
for (int index = 0; index < token.MechanismTypeList.Count; index++)
|
||||||
|
{
|
||||||
|
if (ByteUtils.AreByteArraysEqual(token.MechanismTypeList[index], mechanismIdentifier))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,7 +7,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using SMBLibrary.Authentication.GSSAPI;
|
|
||||||
using SMBLibrary.Authentication.NTLM;
|
using SMBLibrary.Authentication.NTLM;
|
||||||
using Utilities;
|
using Utilities;
|
||||||
|
|
||||||
|
@ -15,27 +14,8 @@ namespace SMBLibrary.Client
|
||||||
{
|
{
|
||||||
public class NTLMAuthenticationHelper
|
public class NTLMAuthenticationHelper
|
||||||
{
|
{
|
||||||
public static byte[] GetNegotiateMessage(byte[] securityBlob, string domainName, string userName, string password, AuthenticationMethod authenticationMethod)
|
public static byte[] GetNegotiateMessage(string domainName, string userName, string password, AuthenticationMethod authenticationMethod)
|
||||||
{
|
{
|
||||||
bool useGSSAPI = false;
|
|
||||||
if (securityBlob.Length > 0)
|
|
||||||
{
|
|
||||||
SimpleProtectedNegotiationTokenInit inputToken = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
inputToken = SimpleProtectedNegotiationToken.ReadToken(securityBlob, 0, true) as SimpleProtectedNegotiationTokenInit;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inputToken == null || !ContainsMechanism(inputToken, GSSProvider.NTLMSSPIdentifier))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
useGSSAPI = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
NegotiateMessage negotiateMessage = new NegotiateMessage();
|
NegotiateMessage negotiateMessage = new NegotiateMessage();
|
||||||
negotiateMessage.NegotiateFlags = NegotiateFlags.UnicodeEncoding |
|
negotiateMessage.NegotiateFlags = NegotiateFlags.UnicodeEncoding |
|
||||||
NegotiateFlags.OEMEncoding |
|
NegotiateFlags.OEMEncoding |
|
||||||
|
@ -65,44 +45,14 @@ namespace SMBLibrary.Client
|
||||||
negotiateMessage.Version = NTLMVersion.Server2003;
|
negotiateMessage.Version = NTLMVersion.Server2003;
|
||||||
negotiateMessage.DomainName = domainName;
|
negotiateMessage.DomainName = domainName;
|
||||||
negotiateMessage.Workstation = Environment.MachineName;
|
negotiateMessage.Workstation = Environment.MachineName;
|
||||||
if (useGSSAPI)
|
return negotiateMessage.GetBytes();
|
||||||
{
|
|
||||||
SimpleProtectedNegotiationTokenInit outputToken = new SimpleProtectedNegotiationTokenInit();
|
|
||||||
outputToken.MechanismTypeList = new List<byte[]>();
|
|
||||||
outputToken.MechanismTypeList.Add(GSSProvider.NTLMSSPIdentifier);
|
|
||||||
outputToken.MechanismToken = negotiateMessage.GetBytes();
|
|
||||||
return outputToken.GetBytes(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return negotiateMessage.GetBytes();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] GetAuthenticateMessage(byte[] securityBlob, string domainName, string userName, string password, string spn, AuthenticationMethod authenticationMethod, out byte[] sessionKey)
|
public static byte[] GetAuthenticateMessage(byte[] challengeMessageBytes, string domainName, string userName, string password, string spn, AuthenticationMethod authenticationMethod, out byte[] sessionKey)
|
||||||
{
|
{
|
||||||
sessionKey = null;
|
sessionKey = null;
|
||||||
bool useGSSAPI = false;
|
|
||||||
SimpleProtectedNegotiationTokenResponse inputToken = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
inputToken = SimpleProtectedNegotiationToken.ReadToken(securityBlob, 0, false) as SimpleProtectedNegotiationTokenResponse;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ChallengeMessage challengeMessage;
|
|
||||||
if (inputToken != null)
|
|
||||||
{
|
|
||||||
challengeMessage = GetChallengeMessage(inputToken.ResponseToken);
|
|
||||||
useGSSAPI = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
challengeMessage = GetChallengeMessage(securityBlob);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
ChallengeMessage challengeMessage = GetChallengeMessage(challengeMessageBytes);
|
||||||
if (challengeMessage == null)
|
if (challengeMessage == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
|
@ -212,16 +162,7 @@ namespace SMBLibrary.Client
|
||||||
sessionKey = keyExchangeKey;
|
sessionKey = keyExchangeKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useGSSAPI)
|
return authenticateMessage.GetBytes();
|
||||||
{
|
|
||||||
SimpleProtectedNegotiationTokenResponse outputToken = new SimpleProtectedNegotiationTokenResponse();
|
|
||||||
outputToken.ResponseToken = authenticateMessage.GetBytes();
|
|
||||||
return outputToken.GetBytes();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return authenticateMessage.GetBytes();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ChallengeMessage GetChallengeMessage(byte[] messageBytes)
|
private static ChallengeMessage GetChallengeMessage(byte[] messageBytes)
|
||||||
|
@ -243,17 +184,5 @@ namespace SMBLibrary.Client
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool ContainsMechanism(SimpleProtectedNegotiationTokenInit token, byte[] mechanismIdentifier)
|
|
||||||
{
|
|
||||||
for (int index = 0; index < token.MechanismTypeList.Count; index++)
|
|
||||||
{
|
|
||||||
if (ByteUtils.AreByteArraysEqual(token.MechanismTypeList[index], GSSProvider.NTLMSSPIdentifier))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using SMBLibrary.Authentication.NTLM;
|
using SMBLibrary.Authentication.NTLM;
|
||||||
|
using SMBLibrary.Client.Authentication;
|
||||||
using SMBLibrary.NetBios;
|
using SMBLibrary.NetBios;
|
||||||
using SMBLibrary.Services;
|
using SMBLibrary.Services;
|
||||||
using SMBLibrary.SMB1;
|
using SMBLibrary.SMB1;
|
||||||
|
@ -289,7 +290,8 @@ namespace SMBLibrary.Client
|
||||||
}
|
}
|
||||||
else // m_securityBlob != null
|
else // m_securityBlob != null
|
||||||
{
|
{
|
||||||
byte[] negotiateMessage = NTLMAuthenticationHelper.GetNegotiateMessage(m_securityBlob, domainName, userName, password, authenticationMethod);
|
NTLMAuthenticationClient authenticationClient = new NTLMAuthenticationClient(domainName, userName, password, null, authenticationMethod);
|
||||||
|
byte[] negotiateMessage = authenticationClient.InitializeSecurityContext(m_securityBlob);
|
||||||
if (negotiateMessage == null)
|
if (negotiateMessage == null)
|
||||||
{
|
{
|
||||||
return NTStatus.SEC_E_INVALID_TOKEN;
|
return NTStatus.SEC_E_INVALID_TOKEN;
|
||||||
|
@ -308,11 +310,12 @@ namespace SMBLibrary.Client
|
||||||
if (reply.Header.Status == NTStatus.STATUS_MORE_PROCESSING_REQUIRED && reply.Commands[0] is SessionSetupAndXResponseExtended)
|
if (reply.Header.Status == NTStatus.STATUS_MORE_PROCESSING_REQUIRED && reply.Commands[0] is SessionSetupAndXResponseExtended)
|
||||||
{
|
{
|
||||||
SessionSetupAndXResponseExtended response = (SessionSetupAndXResponseExtended)reply.Commands[0];
|
SessionSetupAndXResponseExtended response = (SessionSetupAndXResponseExtended)reply.Commands[0];
|
||||||
byte[] authenticateMessage = NTLMAuthenticationHelper.GetAuthenticateMessage(response.SecurityBlob, domainName, userName, password, null, authenticationMethod, out m_sessionKey);
|
byte[] authenticateMessage = authenticationClient.InitializeSecurityContext(response.SecurityBlob);
|
||||||
if (authenticateMessage == null)
|
if (authenticateMessage == null)
|
||||||
{
|
{
|
||||||
return NTStatus.SEC_E_INVALID_TOKEN;
|
return NTStatus.SEC_E_INVALID_TOKEN;
|
||||||
}
|
}
|
||||||
|
m_sessionKey = authenticationClient.GetSessionKey();
|
||||||
|
|
||||||
m_userID = reply.Header.UID;
|
m_userID = reply.Header.UID;
|
||||||
request = new SessionSetupAndXRequestExtended();
|
request = new SessionSetupAndXRequestExtended();
|
||||||
|
|
|
@ -10,6 +10,7 @@ using System.Diagnostics;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using SMBLibrary.Client.Authentication;
|
||||||
using SMBLibrary.NetBios;
|
using SMBLibrary.NetBios;
|
||||||
using SMBLibrary.SMB2;
|
using SMBLibrary.SMB2;
|
||||||
using Utilities;
|
using Utilities;
|
||||||
|
@ -208,13 +209,20 @@ namespace SMBLibrary.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
public NTStatus Login(string domainName, string userName, string password, AuthenticationMethod authenticationMethod)
|
public NTStatus Login(string domainName, string userName, string password, AuthenticationMethod authenticationMethod)
|
||||||
|
{
|
||||||
|
string spn = string.Format("cifs/{0}", m_serverName);
|
||||||
|
NTLMAuthenticationClient authenticationClient = new NTLMAuthenticationClient(domainName, userName, password, spn, authenticationMethod);
|
||||||
|
return Login(authenticationClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NTStatus Login(IAuthenticationClient authenticationClient)
|
||||||
{
|
{
|
||||||
if (!m_isConnected)
|
if (!m_isConnected)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("A connection must be successfully established before attempting login");
|
throw new InvalidOperationException("A connection must be successfully established before attempting login");
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] negotiateMessage = NTLMAuthenticationHelper.GetNegotiateMessage(m_securityBlob, domainName, userName, password, authenticationMethod);
|
byte[] negotiateMessage = authenticationClient.InitializeSecurityContext(m_securityBlob);
|
||||||
if (negotiateMessage == null)
|
if (negotiateMessage == null)
|
||||||
{
|
{
|
||||||
return NTStatus.SEC_E_INVALID_TOKEN;
|
return NTStatus.SEC_E_INVALID_TOKEN;
|
||||||
|
@ -229,12 +237,12 @@ namespace SMBLibrary.Client
|
||||||
{
|
{
|
||||||
if (response.Header.Status == NTStatus.STATUS_MORE_PROCESSING_REQUIRED && response is SessionSetupResponse)
|
if (response.Header.Status == NTStatus.STATUS_MORE_PROCESSING_REQUIRED && response is SessionSetupResponse)
|
||||||
{
|
{
|
||||||
string spn = string.Format("cifs/{0}", m_serverName);
|
byte[] authenticateMessage = authenticationClient.InitializeSecurityContext(((SessionSetupResponse)response).SecurityBuffer);
|
||||||
byte[] authenticateMessage = NTLMAuthenticationHelper.GetAuthenticateMessage(((SessionSetupResponse)response).SecurityBuffer, domainName, userName, password, spn, authenticationMethod, out m_sessionKey);
|
|
||||||
if (authenticateMessage == null)
|
if (authenticateMessage == null)
|
||||||
{
|
{
|
||||||
return NTStatus.SEC_E_INVALID_TOKEN;
|
return NTStatus.SEC_E_INVALID_TOKEN;
|
||||||
}
|
}
|
||||||
|
m_sessionKey = authenticationClient.GetSessionKey();
|
||||||
|
|
||||||
m_sessionID = response.Header.SessionID;
|
m_sessionID = response.Header.SessionID;
|
||||||
request = new SessionSetupRequest();
|
request = new SessionSetupRequest();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue