mirror of
https://github.com/TalAloni/SMBLibrary.git
synced 2025-07-21 16:55: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.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
using SMBLibrary.Authentication.GSSAPI;
|
||||
using SMBLibrary.Authentication.NTLM;
|
||||
using Utilities;
|
||||
|
||||
|
@ -15,27 +14,8 @@ namespace SMBLibrary.Client
|
|||
{
|
||||
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.NegotiateFlags = NegotiateFlags.UnicodeEncoding |
|
||||
NegotiateFlags.OEMEncoding |
|
||||
|
@ -65,44 +45,14 @@ namespace SMBLibrary.Client
|
|||
negotiateMessage.Version = NTLMVersion.Server2003;
|
||||
negotiateMessage.DomainName = domainName;
|
||||
negotiateMessage.Workstation = Environment.MachineName;
|
||||
if (useGSSAPI)
|
||||
{
|
||||
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;
|
||||
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)
|
||||
{
|
||||
return null;
|
||||
|
@ -212,17 +162,8 @@ namespace SMBLibrary.Client
|
|||
sessionKey = keyExchangeKey;
|
||||
}
|
||||
|
||||
if (useGSSAPI)
|
||||
{
|
||||
SimpleProtectedNegotiationTokenResponse outputToken = new SimpleProtectedNegotiationTokenResponse();
|
||||
outputToken.ResponseToken = authenticateMessage.GetBytes();
|
||||
return outputToken.GetBytes();
|
||||
}
|
||||
else
|
||||
{
|
||||
return authenticateMessage.GetBytes();
|
||||
}
|
||||
}
|
||||
|
||||
private static ChallengeMessage GetChallengeMessage(byte[] messageBytes)
|
||||
{
|
||||
|
@ -243,17 +184,5 @@ namespace SMBLibrary.Client
|
|||
}
|
||||
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.Threading;
|
||||
using SMBLibrary.Authentication.NTLM;
|
||||
using SMBLibrary.Client.Authentication;
|
||||
using SMBLibrary.NetBios;
|
||||
using SMBLibrary.Services;
|
||||
using SMBLibrary.SMB1;
|
||||
|
@ -289,7 +290,8 @@ namespace SMBLibrary.Client
|
|||
}
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return NTStatus.SEC_E_INVALID_TOKEN;
|
||||
}
|
||||
m_sessionKey = authenticationClient.GetSessionKey();
|
||||
|
||||
m_userID = reply.Header.UID;
|
||||
request = new SessionSetupAndXRequestExtended();
|
||||
|
|
|
@ -10,6 +10,7 @@ using System.Diagnostics;
|
|||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using SMBLibrary.Client.Authentication;
|
||||
using SMBLibrary.NetBios;
|
||||
using SMBLibrary.SMB2;
|
||||
using Utilities;
|
||||
|
@ -208,13 +209,20 @@ namespace SMBLibrary.Client
|
|||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
string spn = string.Format("cifs/{0}", m_serverName);
|
||||
byte[] authenticateMessage = NTLMAuthenticationHelper.GetAuthenticateMessage(((SessionSetupResponse)response).SecurityBuffer, domainName, userName, password, spn, authenticationMethod, out m_sessionKey);
|
||||
byte[] authenticateMessage = authenticationClient.InitializeSecurityContext(((SessionSetupResponse)response).SecurityBuffer);
|
||||
if (authenticateMessage == null)
|
||||
{
|
||||
return NTStatus.SEC_E_INVALID_TOKEN;
|
||||
}
|
||||
m_sessionKey = authenticationClient.GetSessionKey();
|
||||
|
||||
m_sessionID = response.Header.SessionID;
|
||||
request = new SessionSetupRequest();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue