System Delphi

Title: LogonUser() Win API call vs SSPI call
Question: In my NT/W2000 Net Admin Unit I have the following call ...
function ValidateUserLogonAPI(const UserName : string;
const Domain : string;
const PassWord : string) : boolean;
var Retvar : boolean;
LHandle : THandle;
begin
Retvar := LogonUser(PChar(UserName),
PChar(Domain),PChar(PassWord),
LOGON32_LOGON_NETWORK,
LOGON32_PROVIDER_DEFAULT,
LHandle);
if Retvar then CloseHandle(LHandle);
Result := Retvar;
end;
This call can fail with "INSUFFICIENT_PRIVILEGES". On searching the web the following text was found ...
"The LogonUser API has been available and documented since Windows NT 3.51, and is commonly used to verify user credentials. This API is available on Windows NT, Windows 2000, and Windows XP. Unfortunately, there are some restrictions on using LogonUser that are not always convenient to satisfy.
The first and biggest of these restrictions is that on Windows NT and Windows 2000, the process that is calling LogonUser must have the SE_TCB_NAME privilege (in User Manager, this is the "Act as part of the Operating System" right). The SE_TCB_NAME privilege is very powerful and should not be granted to any arbitrary user just so that they can run an
application that needs to validate credentials. The recommended method is to call LogonUser from a service that is running in the local system account, because the local system account already has the SE_TCB_NAME privilege.
NOTE: LogonUser Win32 API does not require TCB privilege
in .NET Server, however, for downlevel compatibility, this is still the best approach.
On Windows XP, it is no longer required that a process have the SE_TCB_NAME privilege in order to call LogonUser. Therefore, the simplest method to validate a user's credentials on Windows XP, is to call the LogonUser API.
One other problem with LogonUser is that the API is not implemented on Windows 95, Windows 98, or Windows Millennium Edition.
As another option, you can use the Security Support Provider Interface (SSPI) to do a network style logon with provided user credentials. This method of validation has the advantage of not requiring any special privilege. The end result of using the SSPI services to validate the credentials is a logon that is analogous to calling the LogonUser API with the LOGON32_LOGON_NETWORK logon type. The biggest downside to this type of logon is that you cannot access remote network resources after impersonating a network type logon. If your application is calling LogonUser with the LOGON32_LOGON_INTERACTIVE logon type to workaround Windows NT's inability to perform delegation, then the SSPI logon/validation will probably not be a viable alternative."
The following function encapsulates SECUR32.DLL and uses the SSPI calls. (SECURITY.DLL is also availlable on W95 etc. if anyone wants to map to this as well)
EXTRA NOTES (Oct 2006)
On Windows XP, the ForceGuest registry value is set to 1 by default in the following registry key:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa
On a Windows XP computer that is a member of a workgroup:
If ForceGuest is enabled (set to 1), SSPI will always try to log on using the Guest account.
If the Guest account is enabled, an SSPI logon will succeed as Guest for any user credentials.
If the Guest account is disabled, an SSPI logon will fail even for valid credentials.
If ForceGuest is disabled (set to 0), SSPI will log on as the specified user.
Answer:
unit ValLogonW2000;
interface
uses Windows;
// Prototype
function LogonUserSSPI(const UserName,Domain,Password : string) : boolean;
// -------------------------------------------------------------------------
implementation
type
// Secur32.dll function prototypes
TQueryPackageInfo = function(PackageName : PChar;
var PackageInfo : pointer) : integer; stdcall;
TFreeContextBuffer = function(pBuffer : pointer) : integer; stdcall;
TFreeCredentialsHandle = function(var hCred : Int64) : integer; stdcall;
TDeleteSecurityContext = function(var hCred : Int64) : integer; stdcall;
TAcquireCredentialsHandle = function(pszPrincipal : PChar;
pszPackage : PChar;
fCredentialUse : DWORD;
pvLogonID : DWORD;
pAuthData : pointer;
pGetKeyFn : DWORD;
pvGetKeyArgument : pointer;
var phCredential : Int64;
var ptsExpiry : DWORD) : integer; stdcall;
TInitializeSecurityContext = function(var phCredential : Int64;
phContext : pointer;
pszTargetName : PChar;
fContextReq : DWORD;
Reserved1 : DWORD;
TargetDataRep : DWORD;
pInput : pointer;
Reserved2 : DWORD;
var phNewContext : Int64;
pOutput : pointer;
var pfContextAttr : Int64;
var ptsExpiry : DWORD) : integer; stdcall;
TAcceptSecurityContext = function(var phCredential : Int64;
phContext : pointer;
pInput : pointer;
fContextReq : DWORD;
TargetDataRep : DWORD;
var phNewContext : Int64;
pOutput : pointer;
var pfContextAttr : Int64;
var ptsExpiry : DWORD) : integer; stdcall;
// AcquireCredentialsHandle() Internal Structure
PAuthIdentity = ^TAuthIdentity;
TAuthIdentity = packed record
User : PChar;
UserLength : DWORD;
Domain : PChar;
DomainLength : DWORD;
Password : PChar;
PasswordLength : DWORD;
Flags : DWORD;
end;
// QuerySecurityPackageInfo Internal Structure
PSecPkgInfo = ^TSecPkgInfo;
TSecPkgInfo = packed record
Capabilities : DWORD;
Version : WORD;
RPCID : WORD;
MaxToken : DWORD;
Name : PChar;
Comment : PChar;
end;
// InitializeSecurityContext() Internal structure
PSecBuffer = ^TSecBuffer;
TSecBuffer = packed record
cbBuffer : DWORD;
BufferType : DWORD;
pvBuffer : pointer;
end;
PSecBuffDesc = ^TSecBuffDesc;
TSecBuffDesc = packed record
ulVersion : DWORD;
cBuffers : DWORD;
pBuffers : PSecBuffer;
end;
function LogonUserSSPI(const UserName,Domain,Password : string) : boolean;
var Retvar : boolean;
FSecHandle : THandle;
AuthIdentity : TAuthIdentity;
pIdentity : PAuthIdentity;
ContextAttr,
hcTxt2,hCred2,
hcTxt,hCred : Int64;
pBuffer : pointer;
E : integer;
MaxToken,
LifeTime : DWORD;
InSecBuff,InSecBuff2,
OutSecBuff,OutSecBuff2 : TSecBuffer;
InBuffDesc,InBuffDesc2,
OutBuffDesc,OutBuffDesc2 : TSecBuffDesc;
pOut,pOut2,
pBuffDesc,pBuffDesc2 : pointer;
FQueryPackageInfo : TQueryPackageInfo;
FFreeContextBuffer : TFreeContextBuffer;
FAcquireCredHandle : TAcquireCredentialsHandle;
FFreeCredHandle : TFreeCredentialsHandle;
FInitSecContext : TInitializeSecurityContext;
FDelSecContext : TDeleteSecurityContext;
FAcceptSecContext : TAcceptSecurityContext;
begin
Retvar := false;
FSecHandle := LoadLibrary('SECUR32.DLL');
FQueryPackageInfo := nil;
FFreeContextBuffer := nil;
FAcquireCredHandle := nil;
FFreeCredHandle := nil;
FInitSecContext := nil;
FDelSecContext := nil;
FAcceptSecContext := nil;
if FSecHandle 0 then begin
@FQueryPackageInfo := GetProcAddress(FSecHandle,'QuerySecurityPackageInfoA');
@FFreeContextBuffer := GetProcAddress(FSecHandle,'FreeContextBuffer');
@FAcquireCredHandle := GetProcAddress(FSecHandle,'AcquireCredentialsHandleA');
@FInitSecContext := GetProcAddress(FSecHandle,'InitializeSecurityContextA');
@FFreeCredHandle := GetProcAddress(FSecHandle,'FreeCredentialsHandle');
@FDelSecContext := GetProcAddress(FSecHandle,'DeleteSecurityContext');
@FAcceptSecContext := GetProcAddress(FSecHandle,'AcceptSecurityContext');
end;
if FSecHandle 0 then begin
AuthIdentity.User := PChar(UserName);
AuthIdentity.UserLength := length(UserName);
AuthIdentity.Domain := PChar(Domain);
AuthIdentity.DomainLength := length(Domain);
AuthIdentity.Password := PChar(Password);
AuthIdentity.PasswordLength := length(Password);
AuthIdentity.Flags := 1; // SEC_WINNT_AUTH_IDENTITY_ANSI
pIdentity := @AuthIdentity;
if FQueryPackageInfo('NTLM',pBuffer) = NO_ERROR then begin
MaxToken := PSecPkgInfo(pBuffer).MaxToken;
FFreeContextBuffer(pBuffer);
// Negotiate Client Initialisation
if FAcquireCredHandle(nil,'NTLM',2,0,pIdentity,0,
nil,hCred,LifeTime) = NO_ERROR then begin
pOut := HeapAlloc(GetProcessHeap,8,MaxToken);
pOut2 := HeapAlloc(GetProcessHeap,8,MaxToken);
OutSecBuff.pvBuffer := pOut;
OutSecBuff.cbBuffer := MaxToken;
OutSecBuff.BufferType := 2; // SEC_BUFFER_TOKEN
OutBuffDesc.ulVersion := 0;
OutBuffDesc.cBuffers := 1;
OutBuffDesc.pBuffers := @OutSecBuff;
pBuffDesc := @OutBuffDesc;
E := FInitSecContext(hCred,nil,'AuthSamp',0,0,16,nil,0,
hcTxt,pBuffDesc,ContextAttr,LifeTime);
// Challenge
if (E = 0) and
(FAcquireCredHandle(nil,'NTLM',1,0,nil,0,
nil,hCred2,LifeTime) = NO_ERROR) then begin
InSecBuff2.cbBuffer := OutSecBuff.cbBuffer;
InSecBuff2.pvBuffer := OutSecBuff.pvBuffer;
InSecBuff2.BufferType := 2; // SEC_BUFFER_TOKEN
InBuffDesc2.ulVersion := 0;
InBuffDesc2.cBuffers := 1;
InBuffDesc2.pBuffers := @InSecBuff2;
OutSecBuff2.cbBuffer := MaxToken;
OutSecBuff2.pvBuffer := pOut2;
OutSecBuff2.BufferType := 2; // SEC_BUFFER_TOKEN
OutBuffDesc2.ulVersion := 0;
OutBuffDesc2.cBuffers := 1;
OutBuffDesc2.pBuffers := @OutSecBuff2;
pBuffDesc := @InBuffDesc2;
pBuffDesc2 := @OutBuffDesc2;
E := FAcceptSecContext(hCred2,nil,pBuffDesc,0,16,hcTxt2,pBuffDesc2,
ContextAttr,LifeTime);
if E = 0 then begin
// Authenticate
InSecBuff.cbBuffer := OutSecBuff2.cbBuffer;
InSecBuff.pvBuffer := OutSecBuff2.pvBuffer;
InSecBuff.BufferType := 2;
InBuffDesc.ulVersion := 0;
InBuffDesc.cBuffers := 1;
InBuffDesc.pBuffers := @InSecBuff;
OutSecBuff.cbBuffer := MaxToken;
pBuffDesc := @InBuffDesc;
pBuffDesc2 := @OutBuffDesc;
E := FInitSecContext(hCred,@hcTxt,'AuthSamp',0,0,16,pBuffDesc,0,
hcTxt,pBuffDesc2,ContextAttr,LifeTime);
if E = 0 then begin
InSecBuff2.cbBuffer := OutSecBuff.cbBuffer;
InSecBuff2.pvBuffer := OutSecBuff.pvBuffer;
OutSecBuff2.cbBuffer := MaxToken;
pBuffDesc := @InBuffDesc2;
pBuffDesc2 := @OutBuffDesc2;
E := FAcceptSecContext(hCred2, @hcTxt2, pBuffDesc,
0,16,hcTxt2, pBuffDesc2,
ContextAttr,LifeTime);
Retvar := (E = 0);
end;
end;
FDelSecContext(hcTxt2);
FFreeCredHandle(hCred2);
end;
FDelSecContext(hcTxt);
FFreeCredHandle(hCred);
HeapFree(GetProcessHeap,0,pOut);
HeapFree(GetProcessHeap,0,pOut2);
end;
end;
end;
if FSecHandle 0 then
try
FreeLibrary(FSecHandle);
except end;
Result := Retvar;
end;
end.