diff --git a/SMBLibrary/Authentication/NTLM/IndependentNTLMAuthenticationProvider.cs b/SMBLibrary/Authentication/NTLM/IndependentNTLMAuthenticationProvider.cs
index d0ef096..523f131 100644
--- a/SMBLibrary/Authentication/NTLM/IndependentNTLMAuthenticationProvider.cs
+++ b/SMBLibrary/Authentication/NTLM/IndependentNTLMAuthenticationProvider.cs
@@ -40,7 +40,7 @@ namespace SMBLibrary.Authentication.NTLM
m_GetUserPassword = getUserPassword;
}
- public override Win32Error GetChallengeMessage(out object context, NegotiateMessage negotiateMessage, out ChallengeMessage challengeMessage)
+ public override NTStatus GetChallengeMessage(out object context, NegotiateMessage negotiateMessage, out ChallengeMessage challengeMessage)
{
byte[] serverChallenge = GenerateServerChallenge();
context = new AuthContext(negotiateMessage.Workstation, serverChallenge);
@@ -71,15 +71,20 @@ namespace SMBLibrary.Authentication.NTLM
challengeMessage.ServerChallenge = serverChallenge;
challengeMessage.TargetInfo = AVPairUtils.GetAVPairSequence(Environment.MachineName, Environment.MachineName);
challengeMessage.Version = NTLMVersion.Server2003;
- return Win32Error.ERROR_SUCCESS;
+ return NTStatus.SEC_I_CONTINUE_NEEDED;
}
- public override Win32Error Authenticate(object context, AuthenticateMessage message)
+ public override NTStatus Authenticate(object context, AuthenticateMessage message)
{
AuthContext authContext = context as AuthContext;
if (authContext == null)
{
- return Win32Error.ERROR_NO_TOKEN;
+ // There are two possible reasons for authContext to be null:
+ // 1. We have a bug in our implementation, let's assume that's not the case,
+ // according to [MS-SMB2] 3.3.5.5.1 we aren't allowed to return SEC_E_INVALID_HANDLE anyway.
+ // 2. The client sent AuthenticateMessage without sending NegotiateMessage first,
+ // in this case the correct response is SEC_E_INVALID_TOKEN.
+ return NTStatus.SEC_E_INVALID_TOKEN;
}
authContext.UserName = message.UserName;
@@ -89,11 +94,11 @@ namespace SMBLibrary.Authentication.NTLM
if (this.EnableGuestLogin)
{
authContext.IsGuest = true;
- return Win32Error.ERROR_SUCCESS;
+ return NTStatus.STATUS_SUCCESS;
}
else
{
- return Win32Error.ERROR_LOGON_FAILURE;
+ return NTStatus.STATUS_LOGON_FAILURE;
}
}
@@ -103,11 +108,11 @@ namespace SMBLibrary.Authentication.NTLM
if (this.EnableGuestLogin)
{
authContext.IsGuest = true;
- return Win32Error.ERROR_SUCCESS;
+ return NTStatus.STATUS_SUCCESS;
}
else
{
- return Win32Error.ERROR_LOGON_FAILURE;
+ return NTStatus.STATUS_LOGON_FAILURE;
}
}
@@ -133,11 +138,11 @@ namespace SMBLibrary.Authentication.NTLM
if (success)
{
- return Win32Error.ERROR_SUCCESS;
+ return NTStatus.STATUS_SUCCESS;
}
else
{
- return Win32Error.ERROR_LOGON_FAILURE;
+ return NTStatus.STATUS_LOGON_FAILURE;
}
}
diff --git a/SMBLibrary/Authentication/NTLM/NTLMAuthenticationProviderBase.cs b/SMBLibrary/Authentication/NTLM/NTLMAuthenticationProviderBase.cs
index 90a92bb..b285864 100644
--- a/SMBLibrary/Authentication/NTLM/NTLMAuthenticationProviderBase.cs
+++ b/SMBLibrary/Authentication/NTLM/NTLMAuthenticationProviderBase.cs
@@ -12,9 +12,9 @@ namespace SMBLibrary.Authentication.NTLM
{
public abstract class NTLMAuthenticationProviderBase
{
- public abstract Win32Error GetChallengeMessage(out object context, NegotiateMessage negotiateMessage, out ChallengeMessage challengeMessage);
+ public abstract NTStatus GetChallengeMessage(out object context, NegotiateMessage negotiateMessage, out ChallengeMessage challengeMessage);
- public abstract Win32Error Authenticate(object context, AuthenticateMessage authenticateMessage);
+ public abstract NTStatus Authenticate(object context, AuthenticateMessage authenticateMessage);
public abstract void DeleteSecurityContext(ref object context);
diff --git a/SMBLibrary/Enums/NTStatus.cs b/SMBLibrary/Enums/NTStatus.cs
index d7c9322..15cdc80 100644
--- a/SMBLibrary/Enums/NTStatus.cs
+++ b/SMBLibrary/Enums/NTStatus.cs
@@ -4,8 +4,10 @@ namespace SMBLibrary
public enum NTStatus : uint
{
STATUS_SUCCESS = 0x00000000,
+ SEC_I_CONTINUE_NEEDED = 0x00090312,
STATUS_OBJECT_NAME_EXISTS = 0x40000000,
STATUS_NO_MORE_FILES = 0x80000006,
+ SEC_E_INVALID_TOKEN = 0x80090308,
STATUS_NOT_IMPLEMENTED = 0xC0000002,
STATUS_INVALID_INFO_CLASS = 0xC0000003,
STATUS_INVALID_HANDLE = 0xC0000008,
diff --git a/SMBLibrary/SMBLibrary.csproj b/SMBLibrary/SMBLibrary.csproj
index 22fdc64..e1e99da 100644
--- a/SMBLibrary/SMBLibrary.csproj
+++ b/SMBLibrary/SMBLibrary.csproj
@@ -186,7 +186,6 @@
-
diff --git a/SMBLibrary/Server/Helpers/LogonHelper.cs b/SMBLibrary/Server/Helpers/LogonHelper.cs
deleted file mode 100644
index 74fb7dd..0000000
--- a/SMBLibrary/Server/Helpers/LogonHelper.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-/* Copyright (C) 2014-2017 Tal Aloni . 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 System;
-using System.Collections.Generic;
-using Utilities;
-
-namespace SMBLibrary.Server
-{
- public class LogonHelper
- {
- public static NTStatus ToNTStatus(Win32Error errorCode)
- {
- switch (errorCode)
- {
- case Win32Error.ERROR_ACCOUNT_RESTRICTION:
- return NTStatus.STATUS_ACCOUNT_RESTRICTION;
- case Win32Error.ERROR_INVALID_LOGON_HOURS:
- return NTStatus.STATUS_INVALID_LOGON_HOURS;
- case Win32Error.ERROR_INVALID_WORKSTATION:
- return NTStatus.STATUS_INVALID_WORKSTATION;
- case Win32Error.ERROR_PASSWORD_EXPIRED:
- return NTStatus.STATUS_PASSWORD_EXPIRED;
- case Win32Error.ERROR_ACCOUNT_DISABLED:
- return NTStatus.STATUS_ACCOUNT_DISABLED;
- case Win32Error.ERROR_LOGON_TYPE_NOT_GRANTED:
- return NTStatus.STATUS_LOGON_TYPE_NOT_GRANTED;
- case Win32Error.ERROR_ACCOUNT_EXPIRED:
- return NTStatus.STATUS_ACCOUNT_EXPIRED;
- case Win32Error.ERROR_PASSWORD_MUST_CHANGE:
- return NTStatus.STATUS_PASSWORD_MUST_CHANGE;
- case Win32Error.ERROR_ACCOUNT_LOCKED_OUT:
- return NTStatus.STATUS_ACCOUNT_LOCKED_OUT;
- default:
- return NTStatus.STATUS_LOGON_FAILURE;
- }
- }
- }
-}
diff --git a/SMBLibrary/Server/SMB1/NegotiateHelper.cs b/SMBLibrary/Server/SMB1/NegotiateHelper.cs
index 69211b3..7fe5cf5 100644
--- a/SMBLibrary/Server/SMB1/NegotiateHelper.cs
+++ b/SMBLibrary/Server/SMB1/NegotiateHelper.cs
@@ -40,8 +40,8 @@ namespace SMBLibrary.Server.SMB1
response.ServerTimeZone = (short)-TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now).TotalMinutes;
NegotiateMessage negotiateMessage = CreateNegotiateMessage();
ChallengeMessage challengeMessage;
- Win32Error status = securityProvider.GetChallengeMessage(out state.AuthenticationContext, negotiateMessage, out challengeMessage);
- if (status == Win32Error.ERROR_SUCCESS)
+ NTStatus status = securityProvider.GetChallengeMessage(out state.AuthenticationContext, negotiateMessage, out challengeMessage);
+ if (status == NTStatus.SEC_I_CONTINUE_NEEDED)
{
response.Challenge = challengeMessage.ServerChallenge;
}
diff --git a/SMBLibrary/Server/SMB1/SessionSetupHelper.cs b/SMBLibrary/Server/SMB1/SessionSetupHelper.cs
index 05d3485..99701b4 100644
--- a/SMBLibrary/Server/SMB1/SessionSetupHelper.cs
+++ b/SMBLibrary/Server/SMB1/SessionSetupHelper.cs
@@ -26,11 +26,10 @@ namespace SMBLibrary.Server.SMB1
// However, the domain controller itself does not use this field.
// See: http://msdn.microsoft.com/en-us/library/windows/desktop/aa378749%28v=vs.85%29.aspx
AuthenticateMessage message = CreateAuthenticateMessage(request.AccountName, request.OEMPassword, request.UnicodePassword);
- Win32Error loginStatus = securityProvider.Authenticate(state.AuthenticationContext, message);
- if (loginStatus != Win32Error.ERROR_SUCCESS)
+ header.Status = securityProvider.Authenticate(state.AuthenticationContext, message);
+ if (header.Status != NTStatus.STATUS_SUCCESS)
{
- state.LogToServer(Severity.Information, "User '{0}' failed authentication, Win32 error: {1}", message.UserName, loginStatus);
- header.Status = LogonHelper.ToNTStatus(loginStatus);
+ state.LogToServer(Severity.Information, "User '{0}' failed authentication, NTStatus: {1}", message.UserName, header.Status);
return new ErrorResponse(request.CommandName);
}
@@ -105,10 +104,10 @@ namespace SMBLibrary.Server.SMB1
{
NegotiateMessage negotiateMessage = new NegotiateMessage(messageBytes);
ChallengeMessage challengeMessage;
- Win32Error status = securityProvider.GetChallengeMessage(out state.AuthenticationContext, negotiateMessage, out challengeMessage);
- if (status != Win32Error.ERROR_SUCCESS)
+ NTStatus status = securityProvider.GetChallengeMessage(out state.AuthenticationContext, negotiateMessage, out challengeMessage);
+ if (status != NTStatus.SEC_I_CONTINUE_NEEDED)
{
- header.Status = NTStatus.STATUS_LOGON_FAILURE;
+ header.Status = status;
return new ErrorResponse(request.CommandName);
}
@@ -125,11 +124,10 @@ namespace SMBLibrary.Server.SMB1
else // MessageTypeName.Authenticate
{
AuthenticateMessage authenticateMessage = new AuthenticateMessage(messageBytes);
- Win32Error loginStatus = securityProvider.Authenticate(state.AuthenticationContext, authenticateMessage);
- if (loginStatus != Win32Error.ERROR_SUCCESS)
+ header.Status = securityProvider.Authenticate(state.AuthenticationContext, authenticateMessage);
+ if (header.Status != NTStatus.STATUS_SUCCESS)
{
- state.LogToServer(Severity.Information, "User '{0}' failed authentication, Win32 error: {1}", authenticateMessage.UserName, loginStatus);
- header.Status = LogonHelper.ToNTStatus(loginStatus);
+ state.LogToServer(Severity.Information, "User '{0}' failed authentication, NTStatus: {1}", authenticateMessage.UserName, header.Status);
return new ErrorResponse(request.CommandName);
}
diff --git a/SMBLibrary/Server/SMB2/SessionSetupHelper.cs b/SMBLibrary/Server/SMB2/SessionSetupHelper.cs
index e4a5544..386c47d 100644
--- a/SMBLibrary/Server/SMB2/SessionSetupHelper.cs
+++ b/SMBLibrary/Server/SMB2/SessionSetupHelper.cs
@@ -50,10 +50,10 @@ namespace SMBLibrary.Server.SMB2
{
NegotiateMessage negotiateMessage = new NegotiateMessage(messageBytes);
ChallengeMessage challengeMessage;
- Win32Error status = securityProvider.GetChallengeMessage(out state.AuthenticationContext, negotiateMessage, out challengeMessage);
- if (status != Win32Error.ERROR_SUCCESS)
+ NTStatus status = securityProvider.GetChallengeMessage(out state.AuthenticationContext, negotiateMessage, out challengeMessage);
+ if (status != NTStatus.SEC_I_CONTINUE_NEEDED)
{
- return new ErrorResponse(request.CommandName, NTStatus.STATUS_LOGON_FAILURE);
+ return new ErrorResponse(request.CommandName, status);
}
if (isRawMessage)
@@ -69,12 +69,11 @@ namespace SMBLibrary.Server.SMB2
else // MessageTypeName.Authenticate
{
AuthenticateMessage authenticateMessage = new AuthenticateMessage(messageBytes);
- Win32Error loginStatus = securityProvider.Authenticate(state.AuthenticationContext, authenticateMessage);
- if (loginStatus != Win32Error.ERROR_SUCCESS)
+ NTStatus loginStatus = securityProvider.Authenticate(state.AuthenticationContext, authenticateMessage);
+ if (loginStatus != NTStatus.STATUS_SUCCESS)
{
- state.LogToServer(Severity.Information, "User '{0}' failed authentication, Win32 error: {1}", authenticateMessage.UserName, loginStatus);
- NTStatus status = LogonHelper.ToNTStatus(loginStatus);
- return new ErrorResponse(request.CommandName, status);
+ state.LogToServer(Severity.Information, "User '{0}' failed authentication, NTStatus: {1}", authenticateMessage.UserName, loginStatus);
+ return new ErrorResponse(request.CommandName, loginStatus);
}
bool? isGuest = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.IsGuest) as bool?;
diff --git a/SMBLibrary/Win32/IntegratedNTLMAuthenticationProvider.cs b/SMBLibrary/Win32/IntegratedNTLMAuthenticationProvider.cs
index b734a05..3fd3d84 100644
--- a/SMBLibrary/Win32/IntegratedNTLMAuthenticationProvider.cs
+++ b/SMBLibrary/Win32/IntegratedNTLMAuthenticationProvider.cs
@@ -33,7 +33,7 @@ namespace SMBLibrary.Win32.Security
}
}
- public override Win32Error GetChallengeMessage(out object context, NegotiateMessage negotiateMessage, out ChallengeMessage challengeMessage)
+ public override NTStatus GetChallengeMessage(out object context, NegotiateMessage negotiateMessage, out ChallengeMessage challengeMessage)
{
byte[] negotiateMessageBytes = negotiateMessage.GetBytes();
SecHandle serverContext;
@@ -46,12 +46,13 @@ namespace SMBLibrary.Win32.Security
{
context = null;
challengeMessage = null;
- return (Win32Error)Marshal.GetLastWin32Error();
+ // We assume that the problem is not with our implementation.
+ return NTStatus.SEC_E_INVALID_TOKEN;
}
context = new AuthContext(serverContext, negotiateMessage.Workstation);
challengeMessage = new ChallengeMessage(challengeMessageBytes);
- return Win32Error.ERROR_SUCCESS;
+ return NTStatus.SEC_I_CONTINUE_NEEDED;
}
///
@@ -59,12 +60,17 @@ namespace SMBLibrary.Win32.Security
/// 1. The correct password is blank and 'limitblankpassworduse' is set to 1.
/// 2. The user is listed in the "Deny access to this computer from the network" list.
///
- public override Win32Error Authenticate(object context, AuthenticateMessage message)
+ public override NTStatus Authenticate(object context, AuthenticateMessage message)
{
AuthContext authContext = context as AuthContext;
if (authContext == null)
{
- return Win32Error.ERROR_NO_TOKEN;
+ // There are two possible reasons for authContext to be null:
+ // 1. We have a bug in our implementation, let's assume that's not the case,
+ // according to [MS-SMB2] 3.3.5.5.1 we aren't allowed to return SEC_E_INVALID_HANDLE anyway.
+ // 2. The client sent AuthenticateMessage without sending NegotiateMessage first,
+ // in this case the correct response is SEC_E_INVALID_TOKEN.
+ return NTStatus.SEC_E_INVALID_TOKEN;
}
authContext.UserName = message.UserName;
@@ -74,11 +80,11 @@ namespace SMBLibrary.Win32.Security
if (this.EnableGuestLogin)
{
authContext.IsGuest = true;
- return Win32Error.ERROR_SUCCESS;
+ return NTStatus.STATUS_SUCCESS;
}
else
{
- return Win32Error.ERROR_LOGON_FAILURE;
+ return NTStatus.STATUS_LOGON_FAILURE;
}
}
@@ -90,12 +96,13 @@ namespace SMBLibrary.Win32.Security
}
catch (Exception)
{
- return (Win32Error)Marshal.GetLastWin32Error();
+ // We assume that the problem is not with our implementation.
+ return NTStatus.SEC_E_INVALID_TOKEN;
}
if (success)
{
- return Win32Error.ERROR_SUCCESS;
+ return NTStatus.STATUS_SUCCESS;
}
else
{
@@ -110,11 +117,11 @@ namespace SMBLibrary.Win32.Security
if (allowFallback && this.EnableGuestLogin)
{
authContext.IsGuest = true;
- return Win32Error.ERROR_SUCCESS;
+ return NTStatus.STATUS_SUCCESS;
}
else
{
- return result;
+ return ToNTStatus(result);
}
}
}
@@ -172,5 +179,34 @@ namespace SMBLibrary.Win32.Security
{
return NetworkAPI.IsUserExists(userName);
}
+
+ public static NTStatus ToNTStatus(Win32Error errorCode)
+ {
+ switch (errorCode)
+ {
+ case Win32Error.ERROR_NO_TOKEN:
+ return NTStatus.SEC_E_INVALID_TOKEN;
+ case Win32Error.ERROR_ACCOUNT_RESTRICTION:
+ return NTStatus.STATUS_ACCOUNT_RESTRICTION;
+ case Win32Error.ERROR_INVALID_LOGON_HOURS:
+ return NTStatus.STATUS_INVALID_LOGON_HOURS;
+ case Win32Error.ERROR_INVALID_WORKSTATION:
+ return NTStatus.STATUS_INVALID_WORKSTATION;
+ case Win32Error.ERROR_PASSWORD_EXPIRED:
+ return NTStatus.STATUS_PASSWORD_EXPIRED;
+ case Win32Error.ERROR_ACCOUNT_DISABLED:
+ return NTStatus.STATUS_ACCOUNT_DISABLED;
+ case Win32Error.ERROR_LOGON_TYPE_NOT_GRANTED:
+ return NTStatus.STATUS_LOGON_TYPE_NOT_GRANTED;
+ case Win32Error.ERROR_ACCOUNT_EXPIRED:
+ return NTStatus.STATUS_ACCOUNT_EXPIRED;
+ case Win32Error.ERROR_PASSWORD_MUST_CHANGE:
+ return NTStatus.STATUS_PASSWORD_MUST_CHANGE;
+ case Win32Error.ERROR_ACCOUNT_LOCKED_OUT:
+ return NTStatus.STATUS_ACCOUNT_LOCKED_OUT;
+ default:
+ return NTStatus.STATUS_LOGON_FAILURE;
+ }
+ }
}
}