BSOne.SFC/Tocsg.Lib/VCL/Tocsg.WTS.pas

555 lines
16 KiB
Plaintext

{*******************************************************}
{ }
{ Tocsg.WTS }
{ }
{ Copyright (C) 2022 sunk }
{ }
{*******************************************************}
unit Tocsg.WTS;
interface
uses
WinApi.Windows, SysUtils, Generics.Collections, Winapi.Wtsapi32;
//{$IF CompilerVersion >= 37.0}
// Winapi.Wtsapi32
//{$ELSE}
// EM.Wtsapi32
//{$IFEND}
// ;
const
NEAR_SUCCESS = 0;
type
PWTSSessionEntryInfo = ^TWTSSessionEntryInfo;
TWTSSessionEntryInfo = record
dwSessionId: DWORD;
sWinStationName: String;
end;
TTgWTSSessionInfomation = class(TObject)
protected
lstSessionID_: TList<PWTSSessionEntryInfo>;
procedure OnSessionInfo(Sender: TObject; const Item: PWTSSessionEntryInfo;
Action: TCollectionNotification);
function GetCount: Integer;
function GetUserNameByIndex(nIndex: Integer): String;
function GetSessionIdByIndex(nIndex: Integer): DWORD;
public
Constructor Create;
Destructor Destroy; override;
procedure UpdateSessionInfo;
function GetUserNameBySsid(const dwSsid: DWORD): String;
property Count: Integer read GetCount;
property SessionIDs[nIndex: Integer]: DWORD read GetSessionIdByIndex;
property UserNames[nIndex: Integer]: String read GetUserNameByIndex;
end;
const
UF_SCRIPT = $0001;
{$EXTERNALSYM UF_SCRIPT}
UF_ACCOUNTDISABLE = $0002;
{$EXTERNALSYM UF_ACCOUNTDISABLE}
UF_HOMEDIR_REQUIRED = $0008;
{$EXTERNALSYM UF_HOMEDIR_REQUIRED}
UF_LOCKOUT = $0010;
{$EXTERNALSYM UF_LOCKOUT}
UF_PASSWD_NOTREQD = $0020;
{$EXTERNALSYM UF_PASSWD_NOTREQD}
UF_PASSWD_CANT_CHANGE = $0040;
{$EXTERNALSYM UF_PASSWD_CANT_CHANGE}
UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED = $0080;
{$EXTERNALSYM UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED}
//
// Account type bits as part of usri_flags.
//
UF_TEMP_DUPLICATE_ACCOUNT = $0100;
{$EXTERNALSYM UF_TEMP_DUPLICATE_ACCOUNT}
UF_NORMAL_ACCOUNT = $0200;
{$EXTERNALSYM UF_NORMAL_ACCOUNT}
UF_INTERDOMAIN_TRUST_ACCOUNT = $0800;
{$EXTERNALSYM UF_INTERDOMAIN_TRUST_ACCOUNT}
UF_WORKSTATION_TRUST_ACCOUNT = $1000;
{$EXTERNALSYM UF_WORKSTATION_TRUST_ACCOUNT}
UF_SERVER_TRUST_ACCOUNT = $2000;
{$EXTERNALSYM UF_SERVER_TRUST_ACCOUNT}
UF_MACHINE_ACCOUNT_MASK = UF_INTERDOMAIN_TRUST_ACCOUNT or UF_WORKSTATION_TRUST_ACCOUNT or UF_SERVER_TRUST_ACCOUNT;
{$EXTERNALSYM UF_MACHINE_ACCOUNT_MASK}
UF_ACCOUNT_TYPE_MASK = UF_TEMP_DUPLICATE_ACCOUNT or UF_NORMAL_ACCOUNT or UF_INTERDOMAIN_TRUST_ACCOUNT or UF_WORKSTATION_TRUST_ACCOUNT or UF_SERVER_TRUST_ACCOUNT;
{$EXTERNALSYM UF_ACCOUNT_TYPE_MASK}
UF_DONT_EXPIRE_PASSWD = $10000;
{$EXTERNALSYM UF_DONT_EXPIRE_PASSWD}
UF_MNS_LOGON_ACCOUNT = $20000;
{$EXTERNALSYM UF_MNS_LOGON_ACCOUNT}
UF_SMARTCARD_REQUIRED = $40000;
{$EXTERNALSYM UF_SMARTCARD_REQUIRED}
UF_TRUSTED_FOR_DELEGATION = $80000;
{$EXTERNALSYM UF_TRUSTED_FOR_DELEGATION}
UF_NOT_DELEGATED = $100000;
{$EXTERNALSYM UF_NOT_DELEGATED}
UF_USE_DES_KEY_ONLY = $200000;
{$EXTERNALSYM UF_USE_DES_KEY_ONLY}
UF_DONT_REQUIRE_PREAUTH = $400000;
{$EXTERNALSYM UF_DONT_REQUIRE_PREAUTH}
UF_PASSWORD_EXPIRED = DWORD($800000);
{$EXTERNALSYM UF_PASSWORD_EXPIRED}
UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION = $1000000;
{$EXTERNALSYM UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION}
UF_SETTABLE_BITS =
UF_SCRIPT or
UF_ACCOUNTDISABLE or
UF_LOCKOUT or
UF_HOMEDIR_REQUIRED or
UF_PASSWD_NOTREQD or
UF_PASSWD_CANT_CHANGE or
UF_ACCOUNT_TYPE_MASK or
UF_DONT_EXPIRE_PASSWD or
UF_MNS_LOGON_ACCOUNT or
UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED or
UF_SMARTCARD_REQUIRED or
UF_TRUSTED_FOR_DELEGATION or
UF_NOT_DELEGATED or
UF_USE_DES_KEY_ONLY or
UF_DONT_REQUIRE_PREAUTH or
UF_PASSWORD_EXPIRED or
UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION;
{$EXTERNALSYM UF_SETTABLE_BITS}
ENCRYPTED_PWLEN = 16;
type
_USER_INFO_1 = record
usri1_name: LPWSTR;
usri1_password: LPWSTR;
usri1_password_age: DWORD;
usri1_priv: DWORD;
usri1_home_dir: LPWSTR;
usri1_comment: LPWSTR;
usri1_flags: DWORD;
usri1_script_path: LPWSTR;
end;
{$EXTERNALSYM _USER_INFO_1}
USER_INFO_1 = _USER_INFO_1;
{$EXTERNALSYM USER_INFO_1}
TUserInfo1 = USER_INFO_1;
PUserInfo1 = ^TUserInfo1;
_USER_INFO_2 = record
usri2_name: LPWSTR;
usri2_password: LPWSTR;
usri2_password_age: DWORD;
usri2_priv: DWORD;
usri2_home_dir: LPWSTR;
usri2_comment: LPWSTR;
usri2_flags: DWORD;
usri2_script_path: LPWSTR;
usri2_auth_flags: DWORD;
usri2_full_name: LPWSTR;
usri2_usr_comment: LPWSTR;
usri2_parms: LPWSTR;
usri2_workstations: LPWSTR;
usri2_last_logon: DWORD;
usri2_last_logoff: DWORD;
usri2_acct_expires: DWORD;
usri2_max_storage: DWORD;
usri2_units_per_week: DWORD;
usri2_logon_hours: PBYTE;
usri2_bad_pw_count: DWORD;
usri2_num_logons: DWORD;
usri2_logon_server: LPWSTR;
usri2_country_code: DWORD;
usri2_code_page: DWORD;
end;
{$EXTERNALSYM _USER_INFO_2}
USER_INFO_2 = _USER_INFO_2;
{$EXTERNALSYM USER_INFO_2}
TUserInfo2 = USER_INFO_2;
PUserInfo2 = ^TUserInfo2;
_USER_INFO_22 = record
usri22_name: LPWSTR;
usri22_password: array [0..ENCRYPTED_PWLEN - 1] of BYTE;
usri22_password_age: DWORD;
usri22_priv: DWORD;
usri22_home_dir: LPWSTR;
usri22_comment: LPWSTR;
usri22_flags: DWORD;
usri22_script_path: LPWSTR;
usri22_auth_flags: DWORD;
usri22_full_name: LPWSTR;
usri22_usr_comment: LPWSTR;
usri22_parms: LPWSTR;
usri22_workstations: LPWSTR;
usri22_last_logon: DWORD;
usri22_last_logoff: DWORD;
usri22_acct_expires: DWORD;
usri22_max_storage: DWORD;
usri22_units_per_week: DWORD;
usri22_logon_hours: PBYTE;
usri22_bad_pw_count: DWORD;
usri22_num_logons: DWORD;
usri22_logon_server: LPWSTR;
usri22_country_code: DWORD;
usri22_code_page: DWORD;
end;
{$EXTERNALSYM _USER_INFO_22}
USER_INFO_22 = _USER_INFO_22;
{$EXTERNALSYM USER_INFO_22}
TUserInfo22 = USER_INFO_22;
PUserInfo22 = ^TUserInfo22;
// 사용자 계정에 띄어쓰기가 있는경우...
// WTSQuerySessionInformation()만으로는 풀네임을 구해올수 없다. 그래서 추가
PUserInfo23 = ^TUserInfo23;
_USER_INFO_23 = record
usri23_name: LPWSTR;
usri23_full_name: LPWSTR;
usri23_comment: LPWSTR;
usri23_flags: DWORD;
usri23_user_sid: PSID;
end;
{$EXTERNALSYM _USER_INFO_23}
TUserInfo23 = _USER_INFO_23;
USER_INFO_23 = _USER_INFO_23;
{$EXTERNALSYM USER_INFO_23}
TNetUserGetInfo = function(sServerName: PChar; sUserName: PChar; dwLevel: DWORD; var pBuf: Pointer): DWORD; stdcall;
TNetApiBufferFree = function(pBuf: Pointer): Integer; stdcall;
TNetUserChangePassword = function(sServerName, sUserName, sOldPass, sNewPass: PChar): DWORD; stdcall;
function NetUserGetInfo(sServerName: PChar; sUserName: PChar; dwLevel: DWORD; var pBuf: Pointer): DWORD;
function NetApiBufferFree(pBuf : Pointer) :Integer;
function NetUserChangePassword(sServerName, sUserName, sOldPass, sNewPass: String): DWORD;
function GetLastChangePasswordDT(sServerName, sUserName: String): TDateTime;
function GetLastLogOnDT(sServerName, sUserName: String): TDateTime;
function GetLastLogOnDTandBadCnt(sServerName, sUserName: String; var dtLastLogOn: TDateTime; var nBadCnt: Integer): Boolean;
function HasAccountPassword(sServerName, sUserName: String): Boolean;
function WTSGetActiveConsoleSessionId: DWORD; stdcall external 'Kernel32.dll';
function WTS_GetString(dwSessionID: DWORD; const aWTSInfoClass: WTS_INFO_CLASS): String;
function WTS_GetDWORD(dwSessionID: DWORD; const aWTSInfoClass: WTS_INFO_CLASS): DWORD;
function WTS_GetUserNameFromSessionID(dwSessionID: DWORD): String;
function WTS_GetCurrentUserName: String;
function WTS_GetActiveSessionUserName: String;
function WTS_GetActiveUserProfilePath: String;
implementation
uses
Tocsg.Exception, System.DateUtils, Tocsg.DateTime, Winapi.UserEnv;
var
_hNetApi32: THandle = 0;
_fnNetUserGetInfo: TNetUserGetInfo = nil;
_fnNetApiBufferFree: TNetApiBufferFree = nil;
_fnNetUserChangePassword: TNetUserChangePassword = nil;
function InitNetApi32Procedure: Boolean;
begin
if _hNetApi32 = 0 then
begin
// _hNetApi32 := GetModuleHandle('netapi32.dll');
_hNetApi32 := LoadLibrary('netapi32.dll');
if _hNetApi32 <> 0 then
begin
@_fnNetUserGetInfo := GetProcAddress(_hNetApi32, 'NetUserGetInfo');
@_fnNetApiBufferFree := GetProcAddress(_hNetApi32, 'NetApiBufferFree');
@_fnNetUserChangePassword := GetProcAddress(_hNetApi32, 'NetUserChangePassword');
end;
end;
Result := _hNetApi32 <> 0;
end;
function NetUserGetInfo(sServerName, sUserName: PChar; dwLevel: DWORD; var pBuf: Pointer): DWORD;
begin
Result := DWORD(-1);
if InitNetApi32Procedure and Assigned(_fnNetUserGetInfo) then
Result := _fnNetUserGetInfo(sServerName, sUserName, dwLevel, pBuf);
end;
function NetApiBufferFree(pBuf: Pointer) :Integer;
begin
Result := -1;
if InitNetApi32Procedure and Assigned(_fnNetUserGetInfo) then
Result := _fnNetApiBufferFree(pBuf);
end;
function NetUserChangePassword(sServerName, sUserName, sOldPass, sNewPass: String): DWORD;
begin
Result := DWORD(-1);
if InitNetApi32Procedure and Assigned(_fnNetUserChangePassword) then
Result := _fnNetUserChangePassword(PChar(sServerName), PChar(sUserName), PChar(sOldPass), PChar(sNewPass));
end;
function GetLastChangePasswordDT(sServerName, sUserName: String): TDateTime;
var
pInfo: PUserInfo1;
begin
Result := 0;
try
pInfo := nil;
if NetUserGetInfo(PChar(sServerName), PChar(sUserName), 1, Pointer(pInfo)) = 0 then
begin
Result := IncSecond(Now, pInfo.usri1_password_age * -1);
NetApiBufferFree(pInfo);
end;
except
on E: Exception do
ETgException.TraceException(E, 'Fail .. GetLastChangePasswordDT()');
end;
end;
function GetLastLogOnDT(sServerName, sUserName: String): TDateTime;
var
pInfo: PUserInfo2;
begin
Result := 0;
try
pInfo := nil;
if NetUserGetInfo(PChar(sServerName), PChar(sUserName), 2, Pointer(pInfo)) = 0 then
begin
Result := ConvTimeToDateTime(pInfo.usri2_last_logon);
NetApiBufferFree(pInfo);
end;
except
on E: Exception do
ETgException.TraceException(E, 'Fail .. GetLastLogOnDT()');
end;
end;
function GetLastLogOnDTandBadCnt(sServerName, sUserName: String; var dtLastLogOn: TDateTime; var nBadCnt: Integer): Boolean;
var
pInfo: PUserInfo2;
begin
Result := false;
dtLastLogOn := 0;
nBadCnt := 0;
try
pInfo := nil;
if NetUserGetInfo(PChar(sServerName), PChar(sUserName), 2, Pointer(pInfo)) = 0 then
begin
dtLastLogOn := ConvTimeToDateTime(pInfo.usri2_last_logon);
nBadCnt := pInfo.usri2_bad_pw_count;
NetApiBufferFree(pInfo);
Result := true;
end;
except
on E: Exception do
ETgException.TraceException(E, 'Fail .. GetLastLogOnDTandBadCnt()');
end;
end;
// 임계치 초과 시 계정이 잠긴다 24_0109 13:00:48 kku
function HasAccountPassword(sServerName, sUserName: String): Boolean;
var
bRet: Boolean;
hToken: THandle;
begin
try
bRet := LogonUser(PChar(sUserName), PChar(sServerName), '',
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, hToken);
Result := not bRet and (GetLastError <> 1327);
// 음 이걸로 체크하면 false 시 최근 비번 설정일이 변경된다...
// Result := NetUserChangePassword(sServerName, sUserName, '', '') = 86;
except
on E: Exception do
ETgException.TraceException(E, 'Fail .. HasAccountPassword()');
end;
end;
{ TTgWTSSessionInfomation }
Constructor TTgWTSSessionInfomation.Create;
begin
Inherited Create;
lstSessionID_ := TList<PWTSSessionEntryInfo>.Create;
lstSessionID_.OnNotify := OnSessionInfo;
UpdateSessionInfo;
end;
Destructor TTgWTSSessionInfomation.Destroy;
begin
FreeAndNil(lstSessionID_);
Inherited;
end;
procedure TTgWTSSessionInfomation.OnSessionInfo(Sender: TObject; const Item: PWTSSessionEntryInfo;
Action: TCollectionNotification);
begin
case Action of
cnAdded: ;
cnRemoved: Dispose(Item);
cnExtracted: ;
end;
end;
function TTgWTSSessionInfomation.GetUserNameByIndex(nIndex: Integer): String;
begin
Result := '';
if (nIndex >= 0) and (nIndex < lstSessionID_.Count) then
Result := WTS_GetUserNameFromSessionID(lstSessionID_[nIndex].dwSessionId);
end;
function TTgWTSSessionInfomation.GetSessionIdByIndex(nIndex: Integer): DWORD;
begin
Result := 0;
if (nIndex >= 0) and (nIndex < lstSessionID_.Count) then
Result := lstSessionID_[nIndex].dwSessionId;
end;
function TTgWTSSessionInfomation.GetCount: Integer;
begin
Result := lstSessionID_.Count;
end;
procedure TTgWTSSessionInfomation.UpdateSessionInfo;
var
pwsi, iter: PWTS_SESSION_INFO;
i, dwCount: DWORD;
pInfo: PWTSSessionEntryInfo;
begin
lstSessionID_.Clear;
if WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, pwsi, dwCount) then
begin
iter := pwsi;
for i := 0 to dwCount - 1 do
begin
New(pInfo);
pInfo.dwSessionId := iter.SessionId;
pInfo.sWinStationName := iter.pWinStationName;
lstSessionID_.Add(pInfo);
Inc(iter);
end;
WTSFreeMemory(pwsi);
end;
end;
function TTgWTSSessionInfomation.GetUserNameBySsid(const dwSsid: DWORD): String;
var
i: Integer;
begin
Result := '';
for i := 0 to lstSessionID_.Count - 1 do
if lstSessionID_[i].dwSessionId = dwSsid then
begin
Result := lstSessionID_[i].sWinStationName;
exit;
end;
end;
{ other }
function WTS_GetString(dwSessionID: DWORD; const aWTSInfoClass: WTS_INFO_CLASS): String;
var
pBuf : PChar;
dwReturn : DWORD;
begin
Result := '';
pBuf := nil;
dwReturn := 0;
// WTS_INFO_CLASS이 형식이 꼬였는지.. 그대로 넘겨주면 안된다.. 그래서 이렇게 두번 캐스팅 2012-01-09 sunk
if WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, dwSessionID,
aWTSInfoClass, pBuf, dwReturn) then
begin
if pBuf <> nil then
begin
Result := pBuf;
WTSFreeMemory(pBuf);
end;
end;
end;
function WTS_GetDWORD(dwSessionID: DWORD; const aWTSInfoClass: WTS_INFO_CLASS): DWORD;
var
pBuf : PChar;
dwReturn : DWORD;
begin
Result := 0;
pBuf := nil;
dwReturn := 0;
// WTS_INFO_CLASS이 형식이 꼬였는지.. 그대로 넘겨주면 안된다.. 그래서 이렇게 두번 캐스팅 2012-01-09 sunk
if WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, dwSessionID,
aWTSInfoClass, pBuf, dwReturn) then
begin
if pBuf <> nil then
begin
Result := DWORD(pBuf);
WTSFreeMemory(pBuf);
end;
end;
end;
function WTS_GetUserNameFromSessionID(dwSessionID: DWORD): String;
begin
Result := WTS_GetString(dwSessionID, WTSUserName);
end;
function WTS_GetCurrentUserName: String;
var
pBuf: Pointer;
sResult: String;
begin
Result := Trim(WTS_GetUserNameFromSessionID(WTS_CURRENT_SESSION));
if NetUserGetInfo(nil, PChar(Result), 23, pBuf) = NEAR_SUCCESS then
begin
try
if PUserInfo23(pBuf).usri23_full_name <> '' then
begin
SetLength(sResult, Length(PUserInfo23(pBuf).usri23_full_name));
StrCopy(PChar(sResult), PUserInfo23(pBuf).usri23_full_name);
Result := Trim(sResult);
end;
NetApiBufferFree(pBuf);
except
//
exit;
end;
end;
end;
function WTS_GetActiveSessionUserName: String;
begin
Result := WTS_GetUserNameFromSessionID(WTSGetActiveConsoleSessionId);
end;
function WTS_GetActiveUserProfilePath: String;
var
hUserToken: THandle;
dwSessionId: DWORD;
arrBuf: array[0..MAX_PATH] of Char;
dwSize: DWORD;
begin
Result := '';
dwSessionId := WTSGetActiveConsoleSessionId;
if WTSQueryUserToken(dwSessionId, hUserToken) then
try
dwSize := Length(arrBuf);
if GetUserProfileDirectory(hUserToken, arrBuf, dwSize) then
Result := arrBuf;
finally
CloseHandle(hUserToken);
end;
end;
//finalization
// if _hNetApi32 <> 0 then
// FreeLibrary(_hNetApi32);
end.