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

View file

@ -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);

View file

@ -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,

View file

@ -186,7 +186,6 @@
<Compile Include="Server\ConnectionState\SMB2Session.cs" />
<Compile Include="Server\Exceptions\InvalidRequestException.cs" />
<Compile Include="Server\Exceptions\UnsupportedInformationLevelException.cs" />
<Compile Include="Server\Helpers\LogonHelper.cs" />
<Compile Include="Server\Helpers\ServerPathUtils.cs" />
<Compile Include="Server\NameServer.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;
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;
}

View file

@ -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);
}

View file

@ -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?;

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();
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;
}
/// <summary>
@ -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.
/// </summary>
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;
}
}
}
}