Solved

Need to retrieve XP and NT password

Posted on 2004-08-07
1
855 Views
Last Modified: 2010-04-16
Hi.
I have a shell program that needs to know usernames and their passwords in order to grant permissions for launching other programs. (for use with XP, NT, 2000 and domains)

Passwords:
a) Password compare: In this requirement I have, there is no need to know the password itself, but rather I need a function that compares whatever the user inputs in my program, which sends it somewhere (to a SAM or domain) . In other words, what my program needs is an answer ( Yes or No) when I say "is this the current user password".

Users:
a) Need to know (and launch, if applicable) the function or windows program (or applet?) that manages (add, delete, modify)  users to the local computer or the domain, assuming that the current logged person has the rights to do it. Something like the "right click on my computer->manage->local users and groups" console.

b) I need to add users to my program by choosing them selectively from the current users list, without going to the user administration programs. Like a "peek list" of valid user names. Something like the user select in the folder security dialogs, where you have "check names" also.

c) Need a function that queries the windows user database and respond Yes or No to the question "is JULIO a valid user in this machine" (where "JULIO" is a placeholder for the string I actually will query".  Of course it would be ok if I have extra information in a simple manner, like the groups that this user belongs to, without having to spend time on complicated C++ like structures.

Thanks,

Julio Debroy


 
0
Comment
Question by:jadebroy
1 Comment
 
LVL 17

Accepted Solution

by:
Wim ten Brink earned 250 total points
ID: 11749141
What you want is possible but you'll risk that accounts get locked if they're validated too often with the wrong password. You also risk inserting a nasty security leak in your system instead of making it more secure. I did receive the following unit once but don't know if it really works...

unit SSPLogon;

interface

uses
  Windows;

function ValidateUser(const User: string; const Password: string; const ADomain: string = ''): Boolean;

type
  ISSP_Login = interface
    function GetDomain: string;
    function GetLastErr: DWORD;
    function ValidateUser(const User: string; const Password: string; const ADomain: string = ''): Boolean;
    procedure SetDomain(const Value: string);
    property Domain: string read GetDomain write SetDomain;
    property LastError: DWORD read GetLastErr;
  end;

function SSP_Login: ISSP_Login;

implementation

const
  SEC_E_OK = 0;
  SEC_WINNT_AUTH_IDENTITY_ANSI = $01;
  SECPKG_CRED_INBOUND = $00000001;
  SECPKG_CRED_OUTBOUND = $00000002;
  SECPKG_CRED_BOTH = $00000003;
  SECPKG_CRED_DEFAULT = $00000004;
  SECPKG_CRED_RESERVED = $F0000000;
  SECBUFFER_VERSION = 0;
  SECBUFFER_EMPTY = 0; // Undefined, replaced by provider
  SECBUFFER_DATA = 1; // Packet data
  SECBUFFER_TOKEN = 2; // Security token
  SECBUFFER_PKG_PARAMS = 3; // Package specific parameters
  SECBUFFER_MISSING = 4; // Missing Data indicator
  SECBUFFER_EXTRA = 5; // Extra data
  SECBUFFER_STREAM_TRAILER = 6; // Security Trailer
  SECBUFFER_STREAM_HEADER = 7; // Security Header
  SECBUFFER_NEGOTIATION_INFO = 8; // Hints from the negotiation pkg
  SECBUFFER_PADDING = 9; // non-data padding
  SECBUFFER_STREAM = 10; // whole encrypted message
  SECBUFFER_ATTRMASK = $F0000000;
  SECBUFFER_READONLY = $80000000; // Buffer is read-only
  SECBUFFER_RESERVED = $40000000;
  SECURITY_NATIVE_DREP = $00000010;
  SECURITY_NETWORK_DREP = $00000000;
  SEC_I_CONTINUE_NEEDED = $00090312;
  SEC_I_COMPLETE_NEEDED = $00090313;
  SEC_I_COMPLETE_AND_CONTINUE = $00090314;

type
  PSecHandle = ^TSecHandle;
  TSecHandle = packed record
    dwLower: DWORD;
    dwUpper: DWORD
  end;
  TCredHandle = TSecHandle;
  PCredHandle = PSecHandle;
  TCtxtHandle = TSecHandle;
  PCtxtHandle = PSecHandle;
  TSecurityStatus = LongInt;
  TTimeStamp = record
    Time: Integer; // Number of milliseconds since midnight
    Date: Integer; // One plus number of days since 1/1/0001
  end;
  PSecWinntAuthIdentity = ^TSecWinntAuthIdentity;
  TSecWinntAuthIdentity = packed record
    User: PChar;
    UserLength: DWORD;
    Domain: PChar;
    DomainLength: DWORD;
    Password: PChar;
    PasswordLength: DWORD;
    Flags: DWORD
  end;
  PSecBuffer = ^TSecBuffer;
  TSecBuffer = packed record
    cbBuffer: DWORD;
    BufferType: DWORD; // Type of the buffer (below)
    pvBuffer: Pointer;
  end;
  PSecBufferDesc = ^TSecBufferDesc;
  TSecBufferDesc = packed record
    ulVersion: DWORD;
    cBuffers: DWORD; // Number of buffers
    pBuffers: PSecBuffer;
  end;
  PAuthSeq = ^TAuthSeq;
  TAuthSeq = packed record
    _fNewConversation: BOOL;
    _hcred: TCredHandle;
    _fHaveCredHandle: BOOL;
    _fHaveCtxtHandle: BOOL;
    _hctxt: TSecHandle;
  end;
  PAuthNode = ^TAuthNode;
  TAuthNode = packed record
    dwKey: DWORD;
    pAuthData: PAuthSeq;
    pNext: PAuthNode;
  end;
  PSecPkgInfo = ^TSecPkgInfo;
  TSecPkgInfo = record
    fCapabilities: DWORD; // Capability bitmask
    wVersion: WORD; // Version of driver
    wRPCID: WORD; // ID for RPC Runtime
    cbMaxToken: DWORD; // Size of authentication token (max)
    Name: PChar; // Name of package
    Comment: PChar; // Comment
  end;
  ENUMERATE_SECURITY_PACKAGES_FN_A = function(var cPackages: DWORD; var PackageInfo: PSecPkgInfo): TSecurityStatus; stdcall;
  QUERY_SECURITY_PACKAGE_INFO_FN_A = function(packageName: PChar; var info: PSecPkgInfo): TSecurityStatus; stdcall;
  QUERY_CREDENTIALS_ATTRIBUTES_FN_A = function(phCredential: pCredHandle; ulAttribute: DWORD; buffer: Pointer): TSecurityStatus; stdcall;
  EXPORT_SECURITY_CONTEXT_FN = function(hContext: PCtxtHandle; flags: DWORD; pPackedContext: PSecBuffer; var token: Pointer): TSecurityStatus; stdcall;
  SEC_GET_KEY_FN = procedure(Arg: Pointer; Principal: Pointer; KeyVer: DWORD; var Key: Pointer; var status: TSecurityStatus); stdcall;
  ACQUIRE_CREDENTIALS_HANDLE_FN_A = function(pszPrincipal: PChar; pszPackage: PChar; fCredentialUse: DWORD; pvLogonID: Pointer; pAuthData: Pointer; pGetKeyFn: SEC_GET_KEY_FN; pvGetKeyArgument: Pointer; var phCredential: TCredHandle; var ptsExpiry: TTimeStamp): TSecurityStatus; stdcall;
  FREE_CREDENTIALS_HANDLE_FN = function(credHandle: PCredHandle): TSecurityStatus; stdcall;
  INITIALIZE_SECURITY_CONTEXT_FN_A = function(phCredential: PCredHandle; phContent: PCtxtHandle; pszTargetName: PChar; fContextReq: DWORD; Reserved1: DWORD; TargetDataRep: DWORD; pInput: PSecBufferDesc; Reserved2: DWORD; phNewContext: PCtxtHandle; pOutput: PSecBufferDesc; var pfContextAttr: DWORD; var ptsExpiry: TTimeStamp): TSecurityStatus; stdcall;
  ACCEPT_SECURITY_CONTEXT_FN = function(phCredential: PCredHandle; phContext: PCtxtHandle; pInput: PSecBufferDesc; fContextReq: DWORD; TargetDataRep: DWORD; phNewContext: PCtxtHandle; pOutput: PSecBufferDesc; var pfContextAttr: DWORD; var ptsExpiry: TTimeStamp): TSecurityStatus; stdcall;
  COMPLETE_AUTH_TOKEN_FN = function(phContext: PCtxtHandle; pToken: PSecBufferDesc): TSecurityStatus; stdcall;
  DELETE_SECURITY_CONTEXT_FN = function(phContext: PCtxtHandle): TSecurityStatus; stdcall;
  APPLY_CONTROL_TOKEN_FN = function(phContext: PCtxtHandle; pInput: PSecBufferDesc): TSecurityStatus; stdcall;
  QUERY_CONTEXT_ATTRIBUTES_FN_A = function(phContext: PCtxtHandle; alAttribute: DWORD; pBuffer: Pointer): TSecurityStatus; stdcall;
  IMPERSONATE_SECURITY_CONTEXT_FN = function(phContext: PCtxtHandle): TSecurityStatus; stdcall;
  REVERT_SECURITY_CONTEXT_FN = function(phContext: PCtxtHandle): TSecurityStatus; stdcall;
  MAKE_SIGNATURE_FN = function(phContext: PCtxtHandle; fQOP: DWORD; pMessage: PSecBufferDesc; MessageSeqNo: DWORD): TSecurityStatus; stdcall;
  VERIFY_SIGNATURE_FN = function(phContext: PCtxtHandle; pMessage: PSecBufferDesc; MessageSeqNo: DWORD; var fQOP: DWORD): TSecurityStatus; stdcall;
  FREE_CONTEXT_BUFFER_FN = function(contextBuffer: Pointer): TSecurityStatus; stdcall;
  IMPORT_SECURITY_CONTEXT_FN_A = function(pszPackage: PChar; pPackedContext: PSecBuffer; Token: Pointer; phContext: PCtxtHandle): TSecurityStatus; stdcall;
  ADD_CREDENTIALS_FN_A = function(hCredentials: PCredHandle; pszPrincipal: PChar; pszPackage: PChar; fCredentialUse: DWORD; pAuthData: Pointer; pGetKeyFn: SEC_GET_KEY_FN; pvGetKeyArgument: Pointer; var ptsExpiry: TTimeStamp): TSecurityStatus; stdcall;
  QUERY_SECURITY_CONTEXT_TOKEN_FN = function(phContext: PCtxtHandle; var token: Pointer): TSecurityStatus; stdcall;
  ENCRYPT_MESSAGE_FN = function(phContext: PCtxtHandle; fQOP: DWORD; pMessage: PSecBufferDesc; MessageSeqNo: DWORD): TSecurityStatus; stdcall;
  DECRYPT_MESSAGE_FN = function(phContext: PCtxtHandle; pMessage: PSecBufferDesc; MessageSeqNo: DWORD; fQOP: DWORD): TSecurityStatus; stdcall;
  PSecFunctionTable = ^TSecFunctionTable;
  TSecFunctionTable = record
    dwVersion: LongInt;
    EnumerateSecurityPackagesA: ENUMERATE_SECURITY_PACKAGES_FN_A;
    QueryCredentialsAttributesA: QUERY_CREDENTIALS_ATTRIBUTES_FN_A;
    AcquireCredentialsHandleA: ACQUIRE_CREDENTIALS_HANDLE_FN_A;
    FreeCredentialHandle: FREE_CREDENTIALS_HANDLE_FN;
    Reserved2: FARPROC;
    InitializeSecurityContextA: INITIALIZE_SECURITY_CONTEXT_FN_A;
    AcceptSecurityContext: ACCEPT_SECURITY_CONTEXT_FN;
    CompleteAuthToken: COMPLETE_AUTH_TOKEN_FN;
    DeleteSecurityContext: DELETE_SECURITY_CONTEXT_FN;
    ApplyControlToken: APPLY_CONTROL_TOKEN_FN;
    QueryContextAttributesA: QUERY_CONTEXT_ATTRIBUTES_FN_A;
    ImpersonateSecurityContext: IMPERSONATE_SECURITY_CONTEXT_FN;
    RevertSecurityContext: REVERT_SECURITY_CONTEXT_FN;
    MakeSignature: MAKE_SIGNATURE_FN;
    VerifySignature: VERIFY_SIGNATURE_FN;
    FreeContextBuffer: FREE_CONTEXT_BUFFER_FN;
    QuerySecurityPackageInfoA: QUERY_SECURITY_PACKAGE_INFO_FN_A;
    Reserved3: FARPROC;
    Reserved4: FARPROC;
    ExportSecurityContext: EXPORT_SECURITY_CONTEXT_FN;
    ImportSecurityContextA: IMPORT_SECURITY_CONTEXT_FN_A;
    AddCredentialsA: ADD_CREDENTIALS_FN_A;
    Reserved8: FARPROC;
    QuerySecurityContextToken: QUERY_SECURITY_CONTEXT_TOKEN_FN;
    EncryptMessage: ENCRYPT_MESSAGE_FN;
    DecryptMessage: DECRYPT_MESSAGE_FN;
  end;
  INIT_SECURITY_ENTRYPOINT_FN_A = function: PSecFunctionTable; stdcall;

type
  TSSPLogin = class(TInterfacedObject, ISSP_Login)
  private
    FAuthIdent: TSecWINNTAuthIdentity;
    FClientBuff: PChar;
    FDomain: string;
    FLastErr: DWORD;
    FMaxMsg: DWORD;
    FNodeList: PAuthNode;
    FSecFN: PSecFunctionTable;
    FServerBuff: PChar;
  protected
    function FreeEntry(dwKey: DWORD): Boolean;
    function GenClientContext(dwKey: DWORD; cbIn: DWORD; var cbOut: DWORD; var bDone: Boolean): Boolean;
    function GenServerContext(dwKey: DWORD; cbIn: DWORD; var cbOut: DWORD; var bDone: Boolean): Boolean;
    function GetDomain: string;
    function GetEntry(dwKey: DWORD; var Node: PAuthNode): Boolean;
    function GetLastErr: DWORD;
    function InitSession(dwKey: DWORD): Boolean;
    function NewEntry(dwKey: DWORD; var Node: PAuthNode): Boolean;
    function TermSession(dwKey: DWORD): Boolean;
    procedure ClearAuthIdent;
    procedure SetAuthIdent(User, Password: string);
    procedure SetDomain(const Value: string);
  public
    constructor Create;
    destructor Destroy; override;
    function ValidateUser(const User: string; const Password: string; const ADomain: string = ''): Boolean;
    property Domain: string read GetDomain write SetDomain;
    property LastError: DWORD read GetLastErr;
  end;

const
  // Dll names based on OS
  SEC_DLL_NT = 'security.dll';
  SEC_DLL = 'secur32.dll';
  // Misc constants
  SEC_INIT_NAME = 'InitSecurityInterfaceA';
  SEC_PKG_NAME = 'NTLM';

var // Protected variables (used solely for the handling of the security library)
  hSecurity: THandle = 0;
  dwLastErr: DWORD = 0;
  cbMaxMessage: DWORD = 0;
  psfTable: PSecFunctionTable = nil;
  FSSP_Login: ISSP_Login;

procedure TSSPLogin.ClearAuthIdent;
begin
  // Free memory
  if Assigned(FAuthIdent.Domain) then FreeMem(FAuthIdent.Domain);
  if Assigned(FAuthIdent.User) then FreeMem(FAuthIdent.User);
  if Assigned(FAuthIdent.Password) then FreeMem(FAuthIdent.Password);
  // Zero the struct out
  ZeroMemory(@FAuthIdent, SizeOf(TSecWINNTAuthIdentity));
end;

constructor TSSPLogin.Create;
begin
  // Perform inherited
  inherited Create;
  // Set starting values
  ZeroMemory(@FAuthIdent, SizeOf(TSecWINNTAuthIdentity));
  FLastErr := dwLastErr;
  FSecFN := psfTable;
  FNodeList := nil;
  FMaxMsg := cbMaxMessage;
  GetMem(FClientBuff, FMaxMsg);
  ZeroMemory(FClientBuff, FMaxMsg);
  GetMem(FServerBuff, FMaxMsg);
  ZeroMemory(FServerBuff, FMaxMsg);
end;

destructor TSSPLogin.Destroy;
var
  pTemp: PAuthNode;
begin
  // Free memory
  FreeMem(FClientBuff);
  FreeMem(FServerBuff);
  // Free all nodes from list
  while Assigned(FNodeList) do begin
    // Set the node list head to the next node
    pTemp := FNodeList;
    FNodeList := pTemp^.pNext;
    // Free the auth data
    FreeMem(pTemp^.pAuthData);
    // Free the node
    FreeMem(pTemp);
  end;
  // Perform inherited
  inherited Destroy;
end;

function TSSPLogin.FreeEntry(dwKey: DWORD): Boolean;
var
  pMark: PAuthNode;
  pWalk: PAuthNode;
begin
  // Setup for walking the list
  pMark := nil;
  pWalk := FNodeList;
  // Walk the list
  while Assigned(pWalk) do begin
    // Break if we found the key
    if (pWalk^.dwKey = dwKey) then break;
    // Walk next (save last node)
    pMark := pWalk;
    pWalk := pWalk^.pNext;
  end;
  // True if pWalk is assigned
  result := Assigned(pWalk);
  if result then begin
    // Relink the nodes to handle the delete
    if Assigned(pMark) then begin
      // Relink the nodes
      pMark^.pNext := pWalk^.pNext;
    end
    else begin
      // Reset the list head
      FNodeList := pWalk^.pNext;
    end;
    // Free the node data
    if Assigned(pWalk^.pAuthData) then FreeMem(pWalk^.pAuthData);
    // Free the node
    FreeMem(pWalk);
  end;
end;

function TSSPLogin.GenClientContext(dwKey: DWORD; cbIn: DWORD; var cbOut: DWORD; var bDone: Boolean): Boolean;
var
  OutBuffDesc: TSecBufferDesc;
  OutSecBuff: TSecBuffer;
  InBuffDesc: TSecBufferDesc;
  InSecBuff: TSecBuffer;
  ssResult: TSecurityStatus;
  tsLife: TTimeStamp;
  CtxAttr: DWORD;
  pNode: PAuthNode;
  phctxt: PCtxtHandle;
  pBuffDesc: PSecBufferDesc;
begin
  // Get the entry
  result := GetEntry(dwKey, pNode);
  if result then begin
    // Check for new conversation
    if pNode^.pAuthData^._fNewConversation then begin
      // Aquire credentials
      result := (FSecFN^.AcquireCredentialsHandleA(nil, SEC_PKG_NAME, SECPKG_CRED_OUTBOUND, nil, @FAuthIdent, nil, nil, pNode^.pAuthData^._hcred, tsLife) = SEC_E_OK);
      // Check result
      if result then begin
        pNode^.pAuthData^._fHaveCredHandle := True;
      end
      else begin
        exit;
      end;
    end;
    // Setup output buffer
    OutBuffDesc.ulVersion := 0;
    OutBuffDesc.cBuffers := 1;
    OutBuffDesc.pBuffers := @OutSecBuff;
    OutSecBuff.cbBuffer := cbOut;
    OutSecBuff.BufferType := SECBUFFER_TOKEN;
    OutSecBuff.pvBuffer := FClientBuff;
    // Setup input buffer
    if not (pNode^.pAuthData^._fNewConversation) then begin
      InBuffDesc.ulVersion := 0;
      InBuffDesc.cBuffers := 1;
      InBuffDesc.pBuffers := @InSecBuff;
      InSecBuff.cbBuffer := cbIn;
      InSecBuff.BufferType := SECBUFFER_TOKEN;
      InSecBuff.pvBuffer := FServerBuff;
      phctxt := @pNode^.pAuthData^._hctxt;
      pBuffDesc := @InBuffDesc
    end
    else begin
      pBuffDesc := nil;
      phctxt := nil;
    end;
    // Initialize the security context
    ssResult := FSecFN^.InitializeSecurityContextA(@pNode^.pAuthData^._hcred, phctxt, 'AuthSamp', 0, 0, SECURITY_NATIVE_DREP, pBuffDesc, 0, @pNode^.pAuthData^._hctxt, @OutBuffDesc, CtxAttr, tsLife);
    if (ssResult < SEC_E_OK) then begin
      result := False;
    end
    else begin
      // Now have the context handle
      pNode^.pAuthData^._fHaveCtxtHandle := True;
      if (ssResult = SEC_I_COMPLETE_NEEDED) or (ssResult = SEC_I_COMPLETE_AND_CONTINUE) then begin
        if Assigned(FSecFN^.CompleteAuthToken) then begin
          ssResult := FSecFN^.CompleteAuthToken(@pNode^.pAuthData^._hctxt, @OutBuffDesc);
          if (ssResult < SEC_E_OK) then result := False;
        end;
      end;
    end;
    if result then begin
      cbOut := OutSecBuff.cbBuffer;
      if pNode^.pAuthData^._fNewConversation then pNode^.pAuthData^._fNewConversation := False;
      bDone := (ssResult <> SEC_I_CONTINUE_NEEDED) and (ssResult <> SEC_I_COMPLETE_AND_CONTINUE);
    end;
  end;
end;

function TSSPLogin.GenServerContext(dwKey: DWORD; cbIn: DWORD; var cbOut: DWORD; var bDone: Boolean): Boolean;
var
  OutBuffDesc: TSecBufferDesc;
  OutSecBuff: TSecBuffer;
  InBuffDesc: TSecBufferDesc;
  InSecBuff: TSecBuffer;
  ssResult: TSecurityStatus;
  tsLife: TTimeStamp;
  CtxAttr: DWORD;
  pNode: PAuthNode;
  phctxt: PCtxtHandle;
begin
  // Get the entry
  result := GetEntry(dwKey, pNode);
  if result then begin
    // Check for new conversation
    if pNode^.pAuthData^._fNewConversation then begin
      // Aquire credentials
      result := (FSecFN^.AcquireCredentialsHandleA(nil, SEC_PKG_NAME, SECPKG_CRED_INBOUND, nil, nil, nil, nil, pNode^.pAuthData^._hcred, tsLife) = SEC_E_OK);
      // Check result
      if result then begin
        pNode^.pAuthData^._fHaveCredHandle := True;
      end
      else begin
        exit;
      end;
    end;
    // Prepare output buffer
    OutBuffDesc.ulVersion := 0;
    OutBuffDesc.cBuffers := 1;
    OutBuffDesc.pBuffers := @OutSecBuff;
    OutSecBuff.cbBuffer := cbOut;
    OutSecBuff.BufferType := SECBUFFER_TOKEN;
    OutSecBuff.pvBuffer := FServerBuff;
    // Prepare input buffer
    InBuffDesc.ulVersion := 0;
    InBuffDesc.cBuffers := 1;
    InBuffDesc.pBuffers := @InSecBuff;
    InSecBuff.cbBuffer := cbIn;
    InSecBuff.BufferType := SECBUFFER_TOKEN;
    InSecBuff.pvBuffer := FClientBuff;
    if pNode^.pAuthData^._fNewConversation then begin
      phctxt := nil;
    end
    else begin
      phctxt := @pNode^.pAuthData^._hctxt;
    end;
    // Accept the security context
    ssResult := FSecFN^.AcceptSecurityContext(@pNode^.pAuthData^._hcred, phctxt, @InBuffDesc, 0, SECURITY_NATIVE_DREP, @pNode^.pAuthData^._hctxt, @OutBuffDesc, CtxAttr, tsLife);
    if (ssResult < SEC_E_OK) then begin
      result := False;
    end
    else begin
      // Have the context handle
      pNode^.pAuthData^._fHaveCtxtHandle := True;
      // Complete the token if needed
      if (ssResult = SEC_I_COMPLETE_NEEDED) or (ssResult = SEC_I_COMPLETE_AND_CONTINUE) then begin
        if Assigned(FSecFN^.CompleteAuthToken) then begin
          ssResult := FSecFN^.CompleteAuthToken(@pNode^.pAuthData^._hctxt, @OutBuffDesc);
          if (ssResult < 0) then result := False;
        end
        else begin
          result := False;
        end;
      end;
    end;
    if result then begin
      cbOut := OutSecBuff.cbBuffer;
      if pNode^.pAuthData^._fNewConversation then pNode^.pAuthData^._fNewConversation := False;
      bDone := (ssResult <> SEC_I_CONTINUE_NEEDED) and (ssResult <> SEC_I_COMPLETE_AND_CONTINUE);
    end;
  end;
end;

function TSSPLogin.GetDomain: string;
begin
  Result := FDomain;
end;

function TSSPLogin.GetEntry(dwKey: DWORD; var Node: PAuthNode): Boolean;
begin
  // Clear the output buffer
  Node := nil;
  // Locate the entry identified by key in the list
  if (FNodeList = nil) then begin
    // Failure
    result := False;
  end
  else begin
    // Walk the list
    Node := FNodeList;
    while Assigned(Node) do begin
      // Break if we found the key
      if (Node^.dwKey = dwKey) then break;
      // Walk next
      Node := Node^.pNext;
    end;
    // If node is assigned, then success
    result := Assigned(Node);
  end;
end;

function TSSPLogin.GetLastErr: DWORD;
begin
  Result := FLastErr;
end;

function TSSPLogin.InitSession(dwKey: DWORD): Boolean;
var
  pTemp: PAuthNode;
begin
  // Add the new node
  result := NewEntry(dwKey, pTemp);
end;

function TSSPLogin.NewEntry(dwKey: DWORD; var Node: PAuthNode): Boolean;
var
  pWalk: PAuthNode;
begin
  // Resource protection
  Node := nil;
  try
    // Allocate memory for the node and the auth sequence record
    GetMem(Node, SizeOf(TAuthNode));
    ZeroMemory(Node, SizeOf(TAuthNode));
    Node^.dwKey := dwKey;
    GetMem(Node^.pAuthData, SizeOf(TAuthSeq));
    with Node^.pAuthData^ do begin
      _fNewConversation := True;
      _fHaveCredHandle := False;
      _fHaveCtxtHandle := False;
    end;
    result := True;
  except
    // Clean up memory
    if Assigned(Node) then begin
      if Assigned(Node^.pAuthData) then FreeMem(Node^.pAuthData);
      FreeMem(Node);
    end;
    result := False;
  end;
  // If result is true then we need to add this to the list
  if result then begin
    // If head of list is nil then assign
    if (FNodeList = nil) then begin
      FNodeList := Node;
    end
    else begin
      // Walk the list
      pWalk := FNodeList;
      while Assigned(pWalk^.pNext) do
        pWalk := pWalk^.pNext;
      // Set the next node
      pWalk^.pNext := Node;
    end;
  end;
end;

procedure TSSPLogin.SetAuthIdent(User, Password: string);
begin
  // Clear the struct first
  ClearAuthIdent;
  // Set the domain name
  if (FDomain <> '') then begin
    GetMem(FAuthIdent.Domain, Succ(Length(FDomain)));
    lstrcpy(FAuthIdent.Domain, PChar(FDomain));
    FAuthIdent.DomainLength := Length(FDomain);
  end
  else begin
    // Get the local computer name to use as the domain to validate against
    GetMem(FAuthIdent.Domain, MAX_COMPUTERNAME_LENGTH);
    FAuthIdent.DomainLength := MAX_COMPUTERNAME_LENGTH;
    if not (GetComputerName(FAuthIdent.Domain, FAuthIdent.DomainLength)) then begin
      FreeMem(FAuthIdent.Domain);
      FAuthIdent.Domain := nil;
      FAuthIdent.DomainLength := 0;
    end;
  end;
  // Handle user setting
  if (User <> '') then begin
    GetMem(FAuthIdent.User, Succ(Length(User)));
    lstrcpy(FAuthIdent.User, PChar(User));
    FAuthIdent.UserLength := Length(User);
  end;
  // Handle password
  GetMem(FAuthIdent.Password, Succ(Length(Password)));
  if (Length(Password) = 0) then begin
    FAuthIdent.Password[0] := #0;
  end
  else begin
    lstrcpy(FAuthIdent.Password, PChar(Password));
  end;
  FAuthIdent.PasswordLength := Length(Password);
  // Set flags
  FAuthIdent.Flags := SEC_WINNT_AUTH_IDENTITY_ANSI;
end;

procedure TSSPLogin.SetDomain(const Value: string);
begin
  FDomain := Value;
end;

function TSSPLogin.TermSession(dwKey: DWORD): Boolean;
var
  pTemp: PAuthNode;
begin
  // Get the entry from the list
  if GetEntry(dwKey, pTemp) then begin
    // Make sure auth sequence and function table is assigned
    if Assigned(pTemp^.pAuthData) and Assigned(FSecFN) then begin
      // Free context and credential handles
      with pTemp^.pAuthData^ do begin
        if _fHaveCtxtHandle then FSecFN^.DeleteSecurityContext(@pTemp^.pAuthData^._hctxt);
        if _fHaveCredHandle then FSecFN^.FreeCredentialHandle(@pTemp^.pAuthData^._hcred);
      end;
      // Free the node
      result := FreeEntry(dwKey);
    end
    else begin
      // Failure
      result := False;
    end;
  end
  else begin
    // Failure
    result := False;
  end;
end;

function TSSPLogin.ValidateUser(const User: string; const Password: string; const ADomain: string = ''): Boolean;
var
  bSess: array[0..1] of Boolean;
  cbIn: DWORD;
  cbOut: DWORD;
  bDone: Boolean;
  index: Integer;
  OldDomain: string;
begin
  OldDomain := Domain;
  if (Length(ADomain) <> 0) then Domain := ADomain;
  // Check to make sure that the function table retrieved
  if Assigned(FSecFN) then begin
    // Set default result
    result := True;
    // Init the sessions
    for index := 0 to 1 do begin
      bSess[index] := InitSession(index);
      result := result and bSess[index];
    end;
    // Check sessions
    if result then begin
      // Change default result
      result := False;
      // Set the domain, user, and password information
      SetAuthIdent(User, Password);
      // Prepare client message (negotiate)
      cbOut := FMaxMsg;
      if GenClientContext(0, 0, cbOut, bDone) then begin
        // Prepare server message (negotiate)
        cbIn := cbOut;
        cbOut := FMaxMsg;
        if GenServerContext(1, cbIn, cbOut, bDone) then begin
          // Prepare client message (authenticate)
          cbIn := cbOut;
          cbOut := FMaxMsg;
          if GenClientContext(0, cbIn, cbOut, bDone) then begin
            // Prepare server message (authenticate)
            cbIn := cbOut;
            cbOut := FMaxMsg;
            // Result is dependant on this last call
            result := GenServerContext(1, cbIn, cbOut, bDone);
          end;
        end;
      end;
    end;
    // Free the sessions
    for index := 1 downto 0 do begin
      if bSess[index] then TermSession(index);
    end;
  end
  else begin
    // Failure
    result := False;
  end;
  Domain := OldDomain;
end;

function SSP_Login: ISSP_Login;
begin
  Result := FSSP_Login;
end;

procedure InitPackage;
// Initialize the security library on startup
var
  lpInit: INIT_SECURITY_ENTRYPOINT_FN_A;
  lpSPI: PSecPkgInfo;
  osvi: TOSVersionInfo;
  ssResult: TSecurityStatus;
begin
  // Determine windows version so we know which library to bind to
  osvi.dwOSVersionInfoSize := SizeOf(TOSVersionInfo);
  if GetVersionEx(osvi) then begin
    if (osvi.dwPlatformId = VER_PLATFORM_WIN32_NT) then begin
      hSecurity := LoadLibrary(SEC_DLL_NT);
    end
    else begin
      hSecurity := LoadLibrary(SEC_DLL);
    end;
  end
  else begin
    hSecurity := LoadLibrary(SEC_DLL_NT);
  end;
  // If the handle is null then nothing we can do
  if (hSecurity = 0) then begin
    dwLastErr := GetLastError;
  end
  else begin
    // Get the function address
    @lpInit := GetProcAddress(hSecurity, SEC_INIT_NAME);
    if not (Assigned(lpInit)) then begin
      dwLastErr := GetLastError;
    end
    else begin
      // Get the function table
      psfTable := lpInit;
      if not (Assigned(psfTable)) then begin
        dwLastErr := GetLastError;
      end
      else begin
        // Query for the NTLM package info
        ssResult := psfTable^.QuerySecurityPackageInfoA(SEC_PKG_NAME, lpSPI);
        if (ssResult <> SEC_E_OK) then begin
          dwLastErr := GetLastError;
        end
        else begin
          cbMaxMessage := lpSPI^.cbMaxToken;
          psfTable^.FreeContextBuffer(lpSPI);
        end;
      end;
    end;
  end;
end;

function ValidateUser(const User: string; const Password: string; const ADomain: string = ''): Boolean;
begin
  with SSP_Login do begin
    try
      Result := ValidateUser(User, Password, ADomain);
    except Result := False;
    end;
  end;
end;

initialization
  // Initialize the security dll
  InitPackage;
  FSSP_Login := TSSPLogin.Create;
finalization
  FSSP_Login := nil;
  // Assign nil table pointer
  psfTable := nil;
  // Free security library
  if (hSecurity <> 0) then FreeLibrary(hSecurity);
end.
--------------------------------------------------------------

The ISSP_Login interface provides some way to keep track of users on a given domain. With the SSP_Login function you can access the methods of a globally defined object. The ValidateUser() function provides a quick check.

But as I said, I'm unsure if it works correctly. It should work, though.

Handling the users on your system should be as simple as using Active Directory. If you have it installed on your system, use it. If not, install it. The Active Directory components (which you need to import from the related type libraries) will provide you whatever access you need. (Listing users, adding, modifying and deleting users, etc.) Learn more about AD is what I would advise you because it's a lot more complex than you think, but it also handles most of the headaches with the security.
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
Internet Business Fax to Email Made Easy - With eFax Corporate (http://www.enterprise.efax.com), you'll receive a dedicated online fax number, which is used the same way as a typical analog fax number. You'll receive secure faxes in your email, fr…
You have products, that come in variants and want to set different prices for them? Watch this micro tutorial that describes how to configure prices for Magento super attributes. Assigning simple products to configurable: We assigned simple products…

757 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

21 Experts available now in Live!

Get 1:1 Help Now