NTLMAuthenticationProviderBase: Return NTStatus instead of Win32Error

This commit is contained in:
Tal Aloni 2017-02-18 13:59:24 +02:00
parent 254daa5025
commit 45dc792558
9 changed files with 84 additions and 87 deletions

View file

@ -40,7 +40,7 @@ namespace SMBLibrary.Authentication.NTLM
m_GetUserPassword = getUserPassword; 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(); byte[] serverChallenge = GenerateServerChallenge();
context = new AuthContext(negotiateMessage.Workstation, serverChallenge); context = new AuthContext(negotiateMessage.Workstation, serverChallenge);
@ -71,15 +71,20 @@ namespace SMBLibrary.Authentication.NTLM
challengeMessage.ServerChallenge = serverChallenge; challengeMessage.ServerChallenge = serverChallenge;
challengeMessage.TargetInfo = AVPairUtils.GetAVPairSequence(Environment.MachineName, Environment.MachineName); challengeMessage.TargetInfo = AVPairUtils.GetAVPairSequence(Environment.MachineName, Environment.MachineName);
challengeMessage.Version = NTLMVersion.Server2003; 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; AuthContext authContext = context as AuthContext;
if (authContext == null) 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; authContext.UserName = message.UserName;
@ -89,11 +94,11 @@ namespace SMBLibrary.Authentication.NTLM
if (this.EnableGuestLogin) if (this.EnableGuestLogin)
{ {
authContext.IsGuest = true; authContext.IsGuest = true;
return Win32Error.ERROR_SUCCESS; return NTStatus.STATUS_SUCCESS;
} }
else else
{ {
return Win32Error.ERROR_LOGON_FAILURE; return NTStatus.STATUS_LOGON_FAILURE;
} }
} }
@ -103,11 +108,11 @@ namespace SMBLibrary.Authentication.NTLM
if (this.EnableGuestLogin) if (this.EnableGuestLogin)
{ {
authContext.IsGuest = true; authContext.IsGuest = true;
return Win32Error.ERROR_SUCCESS; return NTStatus.STATUS_SUCCESS;
} }
else else
{ {
return Win32Error.ERROR_LOGON_FAILURE; return NTStatus.STATUS_LOGON_FAILURE;
} }
} }
@ -133,11 +138,11 @@ namespace SMBLibrary.Authentication.NTLM
if (success) if (success)
{ {
return Win32Error.ERROR_SUCCESS; return NTStatus.STATUS_SUCCESS;
} }
else else
{ {
return Win32Error.ERROR_LOGON_FAILURE; return NTStatus.STATUS_LOGON_FAILURE;
} }
} }

View file

@ -12,9 +12,9 @@ namespace SMBLibrary.Authentication.NTLM
{ {
public abstract class NTLMAuthenticationProviderBase 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); public abstract void DeleteSecurityContext(ref object context);

View file

@ -4,8 +4,10 @@ namespace SMBLibrary
public enum NTStatus : uint public enum NTStatus : uint
{ {
STATUS_SUCCESS = 0x00000000, STATUS_SUCCESS = 0x00000000,
SEC_I_CONTINUE_NEEDED = 0x00090312,
STATUS_OBJECT_NAME_EXISTS = 0x40000000, STATUS_OBJECT_NAME_EXISTS = 0x40000000,
STATUS_NO_MORE_FILES = 0x80000006, STATUS_NO_MORE_FILES = 0x80000006,
SEC_E_INVALID_TOKEN = 0x80090308,
STATUS_NOT_IMPLEMENTED = 0xC0000002, STATUS_NOT_IMPLEMENTED = 0xC0000002,
STATUS_INVALID_INFO_CLASS = 0xC0000003, STATUS_INVALID_INFO_CLASS = 0xC0000003,
STATUS_INVALID_HANDLE = 0xC0000008, STATUS_INVALID_HANDLE = 0xC0000008,

View file

@ -186,7 +186,6 @@
<Compile Include="Server\ConnectionState\SMB2Session.cs" /> <Compile Include="Server\ConnectionState\SMB2Session.cs" />
<Compile Include="Server\Exceptions\InvalidRequestException.cs" /> <Compile Include="Server\Exceptions\InvalidRequestException.cs" />
<Compile Include="Server\Exceptions\UnsupportedInformationLevelException.cs" /> <Compile Include="Server\Exceptions\UnsupportedInformationLevelException.cs" />
<Compile Include="Server\Helpers\LogonHelper.cs" />
<Compile Include="Server\Helpers\ServerPathUtils.cs" /> <Compile Include="Server\Helpers\ServerPathUtils.cs" />
<Compile Include="Server\NameServer.cs" /> <Compile Include="Server\NameServer.cs" />
<Compile Include="Server\Shares\FileSystemShare.cs" /> <Compile Include="Server\Shares\FileSystemShare.cs" />

View file

@ -1,42 +0,0 @@
/* Copyright (C) 2014-2017 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 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;
}
}
}
}

View file

@ -40,8 +40,8 @@ namespace SMBLibrary.Server.SMB1
response.ServerTimeZone = (short)-TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now).TotalMinutes; response.ServerTimeZone = (short)-TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now).TotalMinutes;
NegotiateMessage negotiateMessage = CreateNegotiateMessage(); NegotiateMessage negotiateMessage = CreateNegotiateMessage();
ChallengeMessage challengeMessage; ChallengeMessage challengeMessage;
Win32Error status = securityProvider.GetChallengeMessage(out state.AuthenticationContext, negotiateMessage, out challengeMessage); NTStatus status = securityProvider.GetChallengeMessage(out state.AuthenticationContext, negotiateMessage, out challengeMessage);
if (status == Win32Error.ERROR_SUCCESS) if (status == NTStatus.SEC_I_CONTINUE_NEEDED)
{ {
response.Challenge = challengeMessage.ServerChallenge; response.Challenge = challengeMessage.ServerChallenge;
} }

View file

@ -26,11 +26,10 @@ namespace SMBLibrary.Server.SMB1
// However, the domain controller itself does not use this field. // 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 // 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); AuthenticateMessage message = CreateAuthenticateMessage(request.AccountName, request.OEMPassword, request.UnicodePassword);
Win32Error loginStatus = securityProvider.Authenticate(state.AuthenticationContext, message); header.Status = securityProvider.Authenticate(state.AuthenticationContext, message);
if (loginStatus != Win32Error.ERROR_SUCCESS) if (header.Status != NTStatus.STATUS_SUCCESS)
{ {
state.LogToServer(Severity.Information, "User '{0}' failed authentication, Win32 error: {1}", message.UserName, loginStatus); state.LogToServer(Severity.Information, "User '{0}' failed authentication, NTStatus: {1}", message.UserName, header.Status);
header.Status = LogonHelper.ToNTStatus(loginStatus);
return new ErrorResponse(request.CommandName); return new ErrorResponse(request.CommandName);
} }
@ -105,10 +104,10 @@ namespace SMBLibrary.Server.SMB1
{ {
NegotiateMessage negotiateMessage = new NegotiateMessage(messageBytes); NegotiateMessage negotiateMessage = new NegotiateMessage(messageBytes);
ChallengeMessage challengeMessage; ChallengeMessage challengeMessage;
Win32Error status = securityProvider.GetChallengeMessage(out state.AuthenticationContext, negotiateMessage, out challengeMessage); NTStatus status = securityProvider.GetChallengeMessage(out state.AuthenticationContext, negotiateMessage, out challengeMessage);
if (status != Win32Error.ERROR_SUCCESS) if (status != NTStatus.SEC_I_CONTINUE_NEEDED)
{ {
header.Status = NTStatus.STATUS_LOGON_FAILURE; header.Status = status;
return new ErrorResponse(request.CommandName); return new ErrorResponse(request.CommandName);
} }
@ -125,11 +124,10 @@ namespace SMBLibrary.Server.SMB1
else // MessageTypeName.Authenticate else // MessageTypeName.Authenticate
{ {
AuthenticateMessage authenticateMessage = new AuthenticateMessage(messageBytes); AuthenticateMessage authenticateMessage = new AuthenticateMessage(messageBytes);
Win32Error loginStatus = securityProvider.Authenticate(state.AuthenticationContext, authenticateMessage); header.Status = securityProvider.Authenticate(state.AuthenticationContext, authenticateMessage);
if (loginStatus != Win32Error.ERROR_SUCCESS) if (header.Status != NTStatus.STATUS_SUCCESS)
{ {
state.LogToServer(Severity.Information, "User '{0}' failed authentication, Win32 error: {1}", authenticateMessage.UserName, loginStatus); state.LogToServer(Severity.Information, "User '{0}' failed authentication, NTStatus: {1}", authenticateMessage.UserName, header.Status);
header.Status = LogonHelper.ToNTStatus(loginStatus);
return new ErrorResponse(request.CommandName); return new ErrorResponse(request.CommandName);
} }

View file

@ -50,10 +50,10 @@ namespace SMBLibrary.Server.SMB2
{ {
NegotiateMessage negotiateMessage = new NegotiateMessage(messageBytes); NegotiateMessage negotiateMessage = new NegotiateMessage(messageBytes);
ChallengeMessage challengeMessage; ChallengeMessage challengeMessage;
Win32Error status = securityProvider.GetChallengeMessage(out state.AuthenticationContext, negotiateMessage, out challengeMessage); NTStatus status = securityProvider.GetChallengeMessage(out state.AuthenticationContext, negotiateMessage, out challengeMessage);
if (status != Win32Error.ERROR_SUCCESS) if (status != NTStatus.SEC_I_CONTINUE_NEEDED)
{ {
return new ErrorResponse(request.CommandName, NTStatus.STATUS_LOGON_FAILURE); return new ErrorResponse(request.CommandName, status);
} }
if (isRawMessage) if (isRawMessage)
@ -69,12 +69,11 @@ namespace SMBLibrary.Server.SMB2
else // MessageTypeName.Authenticate else // MessageTypeName.Authenticate
{ {
AuthenticateMessage authenticateMessage = new AuthenticateMessage(messageBytes); AuthenticateMessage authenticateMessage = new AuthenticateMessage(messageBytes);
Win32Error loginStatus = securityProvider.Authenticate(state.AuthenticationContext, authenticateMessage); NTStatus loginStatus = securityProvider.Authenticate(state.AuthenticationContext, authenticateMessage);
if (loginStatus != Win32Error.ERROR_SUCCESS) if (loginStatus != NTStatus.STATUS_SUCCESS)
{ {
state.LogToServer(Severity.Information, "User '{0}' failed authentication, Win32 error: {1}", authenticateMessage.UserName, loginStatus); state.LogToServer(Severity.Information, "User '{0}' failed authentication, NTStatus: {1}", authenticateMessage.UserName, loginStatus);
NTStatus status = LogonHelper.ToNTStatus(loginStatus); return new ErrorResponse(request.CommandName, loginStatus);
return new ErrorResponse(request.CommandName, status);
} }
bool? isGuest = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.IsGuest) as bool?; bool? isGuest = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.IsGuest) as bool?;

View file

@ -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(); byte[] negotiateMessageBytes = negotiateMessage.GetBytes();
SecHandle serverContext; SecHandle serverContext;
@ -46,12 +46,13 @@ namespace SMBLibrary.Win32.Security
{ {
context = null; context = null;
challengeMessage = 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); context = new AuthContext(serverContext, negotiateMessage.Workstation);
challengeMessage = new ChallengeMessage(challengeMessageBytes); challengeMessage = new ChallengeMessage(challengeMessageBytes);
return Win32Error.ERROR_SUCCESS; return NTStatus.SEC_I_CONTINUE_NEEDED;
} }
/// <summary> /// <summary>
@ -59,12 +60,17 @@ namespace SMBLibrary.Win32.Security
/// 1. The correct password is blank and 'limitblankpassworduse' is set to 1. /// 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. /// 2. The user is listed in the "Deny access to this computer from the network" list.
/// </summary> /// </summary>
public override Win32Error Authenticate(object context, AuthenticateMessage message) public override NTStatus Authenticate(object context, AuthenticateMessage message)
{ {
AuthContext authContext = context as AuthContext; AuthContext authContext = context as AuthContext;
if (authContext == null) 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; authContext.UserName = message.UserName;
@ -74,11 +80,11 @@ namespace SMBLibrary.Win32.Security
if (this.EnableGuestLogin) if (this.EnableGuestLogin)
{ {
authContext.IsGuest = true; authContext.IsGuest = true;
return Win32Error.ERROR_SUCCESS; return NTStatus.STATUS_SUCCESS;
} }
else else
{ {
return Win32Error.ERROR_LOGON_FAILURE; return NTStatus.STATUS_LOGON_FAILURE;
} }
} }
@ -90,12 +96,13 @@ namespace SMBLibrary.Win32.Security
} }
catch (Exception) catch (Exception)
{ {
return (Win32Error)Marshal.GetLastWin32Error(); // We assume that the problem is not with our implementation.
return NTStatus.SEC_E_INVALID_TOKEN;
} }
if (success) if (success)
{ {
return Win32Error.ERROR_SUCCESS; return NTStatus.STATUS_SUCCESS;
} }
else else
{ {
@ -110,11 +117,11 @@ namespace SMBLibrary.Win32.Security
if (allowFallback && this.EnableGuestLogin) if (allowFallback && this.EnableGuestLogin)
{ {
authContext.IsGuest = true; authContext.IsGuest = true;
return Win32Error.ERROR_SUCCESS; return NTStatus.STATUS_SUCCESS;
} }
else else
{ {
return result; return ToNTStatus(result);
} }
} }
} }
@ -172,5 +179,34 @@ namespace SMBLibrary.Win32.Security
{ {
return NetworkAPI.IsUserExists(userName); 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;
}
}
} }
} }