中国程序员联盟 正在重新改版中ing 不便之处还请见谅 改版后将内容涉及java delphi .net php
 
  首页 | 数据库开发 | 网络通讯 | 多线程 | 多媒体开发 | 图像处理 | 程序人生 | 系统函数 | 控件开发 | Web服务
 
  当前位置:笨鱼delphi技术网>系统函数>文章内容

delphi 通过API SSPI登录

来源:国外 关于:Mike Heydon 发布时间:2007-07-03   [收藏] [推荐]
Problem/Question/Abstract:
LogonUser() Win API call vs SSPI call
Answer:
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     
    captibility, 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 available on W95 etc. if anyone wants to map to this as well)
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.

[浏览: 次]   
上一篇:delphi 不经确认关闭应用程序   下一篇:delphi 一个很有用的共享连接网络工具
[收藏] [推荐] [返回顶部] [打印本页] [关闭窗口]  
    评论加载中…
google adsense热点文章
·delphi Delphi中ShellExecute的妙用
·delphi 如何快速读取文本文件
·delphi 如何判断输入值是否中文
·delphi 在应用层截获键盘消息
·delphi delphi实现服务开启与关闭
·delphi 实时记录事件日志
·delphi 使MEMO自动滚动
·delphi 如何区分键盘两个Enter键
·delphi 切换界面的方法
·delphi 汉字输入法的编程及使用
·delphi Delphi程序输入法自动切换最简
·delphi 消息是由谁来发出又由谁来完成
     delphi技术网 | firefox 下载 | Avant Browser下载 | dedecms 技术网 | drupal 爱好者 | php 技术网
  Copyright@www.delphichm.com,2006-2009.All Rights Reserved.
 
程序员联盟 | delphi Java .net|QQ:707102932