Visit our Sponsor   Visit our Sponsor
delphi3000.com - the free delphi knowledge platform
delphi3000.com - the free delphi knowledge platform
497 Users Online NOW
Have a look at your member-status

connecting people's knowledge


  - Recent ArticlesRSS feed for Recent Articles on delphi3000.com
  - List of All Articles
  - Top Viewed Articles
  - Articles (+Attachem.)
  - Articles Of Interest
  - Categories
  - Top Uploader
  - Search
  - Index

  - My Home
  - Submit an Article
  - My Articles
  - My Personal Data
  - My Bookmarks
  - Activities
  - Login/Logout

  - Sign Up
  - Why Sign Up
  - Newsletter

  - Press
  - Advertise

  - Contact
  - Feedback





Community
Borland
ClubeDelphi
Dr. Bob
UK-BUG
Delphi Meetings
Planeta Delphi



Loremo - the 1.5 liter car coming in 2009




Startblatt.de






Share this article with friendsShare this article with friends
Rate this articleRate this article - to keep the quality of delphi3000.com !
Comment this article or read through previous comments (8)


LogonUser() Win API call vs SSPI callFormat this article printer-friendly!Bookmark function is only available for registered users!
LogonUserSSPI()
Product:
Delphi 5.x (or higher)
Category:
Win API
Skill Level:
Scoring:
Last Update:
10/09/2006
Search Keys:
delphi delphi3000 article borland vcl code-snippet SSPI API LogonUser
Times Scored:
9
Visits:
15072
Uploader: Mike Heydon
Company: EOH
Reference: mheydon@pgbison.co.za
 
Question/Problem/Abstract:
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.







Please rate this article!
Skill level:
BeginnerExpert

Useful:
No!Very!

Overall rating:
PoorExcellent



Comments to this article
Write a new comment
Looks like a good trick!
    Joe Shwom (Jan 14 2005 12:57AM)

Hope this works I need to have my XP PC login and my application running and if a user exits or is inactive get a domain login and password and if valid reenable GUI access to my application. Sounds like this would do the trick. DO I need to do a logout?
Respond

Weird behaviour
    Khash Sajadi (Dec 24 2004 12:11PM)

It is rather odd that this code works fine on Windows 2000, Windows 2003 Server and Windows XP (non of them member of a domain) and another Windows XP belonging to a domain, but it's not working on my Windows XP (no-domain) that I compiled it on.

Is there any explanation for this?
Respond

RE: Weird behaviour
Khash Sajadi (Dec 24 2004 12:37PM)

Just to add more information to my question:
All XPs are SP2.
And by "not working" I mean it always returns TRUE as the result of function when called no matter what the user,password and domain is.
Respond

RE: RE: Weird behaviour
shantanu singh (Aug 2 2005 7:25AM)

Yes Got it.
I faced the same problem and search the net for this. The solution which is from
http://support.microsoft.com/default.aspx?scid=kb;EN-US;180548

is as follows:


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.


I hope this would be helpful.
Regards
Shantanu Singh
Respond

Errors implementing this ...
    andrew radmore (Aug 24 2004 3:00PM)

using the sspi I get one of two errors from this either
Access violation at address 7C341505 in module 'Secur32.dll' using your exact coding or "Insufficient system resources exist to complete the requested service" when I take out the offending "FDelSecContext(hcTxt);" Line. Could you suggest any fix for this?  

andrew.radmore@carval.co.uk

Thanx
Andrew
Respond

RE: Errors implementing this ...
Mike Heydon (Aug 24 2004 3:16PM)

Sounds like an outdated DLL.

What windows ver are you using ?
Does secure32.dll or security.dll exist ?

Don't know if compiler switches are affecting you but try these

{$A8,B-,C+,D+,E-,F-,G+,H+,I+,J-,K-,L+,M-,N+,O+,P+,Q-,R-,S-,T-,U-,V+,W-,X+,Y+,Z1}
{$MINSTACKSIZE $00004000}
{$MAXSTACKSIZE $00100000}
{$IMAGEBASE $00400000}
{$APPTYPE GUI}
{$WARN SYMBOL_DEPRECATED ON}
{$WARN SYMBOL_LIBRARY ON}
{$WARN SYMBOL_PLATFORM ON}
{$WARN UNIT_LIBRARY ON}
{$WARN UNIT_PLATFORM ON}
{$WARN UNIT_DEPRECATED ON}
{$WARN HRESULT_COMPAT ON}
{$WARN HIDING_MEMBER ON}
{$WARN HIDDEN_VIRTUAL ON}
{$WARN GARBAGE ON}
{$WARN BOUNDS_ERROR ON}
{$WARN ZERO_NIL_COMPAT ON}
{$WARN STRING_CONST_TRUNCED ON}
{$WARN FOR_LOOP_VAR_VARPAR ON}
{$WARN TYPED_CONST_VARPAR ON}
{$WARN ASG_TO_TYPED_CONST ON}
{$WARN CASE_LABEL_RANGE ON}
{$WARN FOR_VARIABLE ON}
{$WARN CONSTRUCTING_ABSTRACT ON}
{$WARN COMPARISON_FALSE ON}
{$WARN COMPARISON_TRUE ON}
{$WARN COMPARING_SIGNED_UNSIGNED ON}
{$WARN COMBINING_SIGNED_UNSIGNED ON}
{$WARN UNSUPPORTED_CONSTRUCT ON}
{$WARN FILE_OPEN ON}
{$WARN FILE_OPEN_UNITSRC ON}
{$WARN BAD_GLOBAL_SYMBOL ON}
{$WARN DUPLICATE_CTOR_DTOR ON}
{$WARN INVALID_DIRECTIVE ON}
{$WARN PACKAGE_NO_LINK ON}
{$WARN PACKAGED_THREADVAR ON}
{$WARN IMPLICIT_IMPORT ON}
{$WARN HPPEMIT_IGNORED ON}
{$WARN NO_RETVAL ON}
{$WARN USE_BEFORE_DEF ON}
{$WARN FOR_LOOP_VAR_UNDEF ON}
{$WARN UNIT_NAME_MISMATCH ON}
{$WARN NO_CFG_FILE_FOUND ON}
{$WARN MESSAGE_DIRECTIVE ON}
{$WARN IMPLICIT_VARIANTS ON}
{$WARN UNICODE_TO_LOCALE ON}
{$WARN LOCALE_TO_UNICODE ON}
{$WARN IMAGEBASE_MULTIPLE ON}
{$WARN SUSPICIOUS_TYPECAST ON}
{$WARN PRIVATE_PROPACCESSOR ON}
{$WARN UNSAFE_TYPE OFF}
{$WARN UNSAFE_CODE OFF}
{$WARN UNSAFE_CAST OFF}
Respond

RE: RE: Errors implementing this ...
andrew radmore (Aug 25 2004 11:49AM)

Ok I will have to appologise your code works fine it is how I am trying to implement it. I am implementing sspi within an intraweb for delphi application in place of the logonuser because it has to authenticate with a remote server and the permissions for the logonuser cant be enabled for security reasons.

It seems to be working within intraweb but it wont authenticate.

The following dont seem to be setting correctly so could you elaberate on what they do :-

hcText~seems to implement as 0 although it doesnt in a standard delphi ap

pOut := HeapAlloc(GetProcessHeap(),8,MaxToken);
pOut2 := HeapAlloc(GetProcessHeap(),8,MaxToken);

these also seem to be blank when not in a standard app.

it seems likely that because pOut is not returning anything the FInitSecContext is not returning the correct value to E.

any help in this would be greatly appreciated.


Respond

good article
anonymus (Dec 13 2004 11:39AM)

Hello mike,
           .Iam a small time programmer.
the article was a very useful and important at this point of time for me.I was running a VB application for user authentication using SSPI.it worked fine on Windows XP.But when i added a japanese language support it just would not work.
               then i tried the logonUser and it worked.So i wonder if it is due to the fact that my Windows XP japanese version is not configured to get a remote authentication?thanks a lot.

shikoh

Respond














 
Sign up to consume product discounts for Bronze memberships !

read more


  Visit our Sponsor

 

  Community Ad of
I. Siticov
 
   














 







     
  Copyright © 2000 - 2007 delphi3000.com - All rights reserved. Terms of use. || Privacy
delphi3000.com is a service by bluestep.com IT-Services GmbH (Vienna)