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; + } + } } }