{**************************************************************************************************} { } { Project JEDI Code Library (JCL) } { } { The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); } { you may not use this file except in compliance with the License. You may obtain a copy of the } { License at http://www.mozilla.org/MPL/ } { } { Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF } { ANY KIND, either express or implied. See the License for the specific language governing rights } { and limitations under the License. } { } { The Original Code is JclLANMan.pas. } { } { The Initial Developer of the Original Code is Peter Friese. } { Portions created by Peter Friese are Copyright (C) Peter Friese. All Rights Reserved. } { } { Contributors: } { Peter Friese } { Andreas Hausladen (ahuser) } { Robert Marquardt (marquardt) } { Matthias Thoma (mthoma) } { Petr Vones (pvones) } { } {**************************************************************************************************} { } { This unit contains routines and classes to handle user and group management tasks. As the name } { implies, it uses the LAN Manager API. } { } {**************************************************************************************************} // Last modified: $Date: 2005/02/25 07:20:15 $ // For history see end of file // Comments to Win9x compatibility of the functions used in this unit // The following function exist at last since Win95C, but return always // the error ERROR_CALL_NOT_IMPLEMENTED // AllocateAndInitializeSid, LookupAccountSID, FreeSID unit JclLANMan; {$I jcl.inc} interface uses Windows, SysUtils, Classes; // User Management type TNetUserFlag = (ufAccountDisable, ufHomedirRequired, ufLockout, ufPasswordNotRequired, ufPasswordCantChange, ufDontExpirePassword, ufMNSLogonAccount); TNetUserFlags = set of TNetUserFlag; TNetUserInfoFlag = (uifScript, uifTempDuplicateAccount, uifNormalAccount, uifInterdomainTrustAccount, uifWorkstationTrustAccount, uifServerTrustAccount); TNetUserInfoFlags = set of TNetUserInfoFlag; TNetUserPriv = (upUnknown, upGuest, upUser, upAdmin); TNetUserAuthFlag = (afOpPrint, afOpComm, afOpServer, afOpAccounts); TNetUserAuthFlags = set of TNetUserAuthFlag; TNetWellKnownRID = (wkrAdmins, wkrUsers, wkrGuests, wkrPowerUsers, wkrBackupOPs, wkrReplicator, wkrEveryone); function CreateAccount(const Server, Username, Fullname, Password, Description, Homedir, Script: string; const PasswordNeverExpires: Boolean = True): Boolean; function CreateLocalAccount(const Username, Fullname, Password, Description, Homedir, Script: string; const PasswordNeverExpires: Boolean = True): Boolean; function DeleteAccount(const Servername, Username: string): Boolean; function DeleteLocalAccount(Username: string): Boolean; function CreateLocalGroup(const Server, Groupname, Description: string): Boolean; function CreateGlobalGroup(const Server, Groupname, Description: string): Boolean; function DeleteLocalGroup(const Server, Groupname: string): Boolean; function GetLocalGroups(const Server: string; const Groups: TStrings): Boolean; function GetGlobalGroups(const Server: string; const Groups: TStrings): Boolean; function LocalGroupExists(const Group: string): Boolean; function GlobalGroupExists(const Server, Group: string): Boolean; function AddAccountToLocalGroup(const Accountname, Groupname: string): Boolean; function LookupGroupName(const Server: string; const RID: TNetWellKnownRID): string; procedure ParseAccountName(const QualifiedName: string; var Domain, UserName: string); function IsLocalAccount(const AccountName: string): Boolean; implementation uses JclBase, JclStrings, JclSysInfo, JclWin32; function CreateAccount(const Server, Username, Fullname, Password, Description, Homedir, Script: string; const PasswordNeverExpires: Boolean): Boolean; var wServer, wUsername, wFullname, wPassword, wDescription, wHomedir, wScript: WideString; Details: USER_INFO_2; Err: NET_API_STATUS; ParmErr: DWORD; begin wServer := Server; wUsername := Username; wFullname := Fullname; wPassword := Password; wDescription := Description; wScript := Script; wHomedir := Homedir; FillChar(Details, SizeOf(Details), #0); with Details do begin usri2_name := PWideChar(wUsername); usri2_full_name := PWideChar(wFullname); usri2_password := PWideChar(wPassword); usri2_comment := PWideChar(wDescription); usri2_priv := USER_PRIV_USER; usri2_flags := UF_SCRIPT; if PassWordNeverExpires then usri2_flags := usri2_flags or UF_DONT_EXPIRE_PASSWD; usri2_script_path := PWideChar(wScript); usri2_home_dir := PWideChar(wHomedir); usri2_acct_expires := TIMEQ_FOREVER; end; Err := RtdlNetUserAdd(PWideChar(wServer), 2, @Details, @ParmErr); Result := (Err = NERR_SUCCESS); end; function CreateLocalAccount(const Username, Fullname, Password, Description, Homedir, Script: string; const PasswordNeverExpires: Boolean): Boolean; begin Result := CreateAccount('', Username, Fullname, Password, Description, Homedir, Script, PassWordNeverExpires); end; function DeleteAccount(const Servername, Username: string): Boolean; var wServername, wUsername: WideString; Err: NET_API_STATUS; begin wServername := Servername; wUsername := Username; Err := RtdlNetUserDel(PWideChar(wServername), PWideChar(wUsername)); Result := (Err = NERR_SUCCESS); end; function DeleteLocalAccount(Username: string): Boolean; begin Result := DeleteAccount('', Username); end; function CreateGlobalGroup(const Server, Groupname, Description: string): Boolean; var wServer, wGroupname, wDescription: WideString; Details: GROUP_INFO_1; Err: NET_API_STATUS; ParmErr: DWORD; begin wServer := Server; wGroupname := Groupname; wDescription := Description; FillChar(Details, SizeOf(Details), #0); Details.grpi1_name := PWideChar(wGroupName); Details.grpi1_comment := PWideChar(wDescription); Err := RtdlNetGroupAdd(PWideChar(wServer), 1, @Details, @ParmErr); Result := (Err = NERR_SUCCESS); end; function CreateLocalGroup(const Server, Groupname, Description: string): Boolean; var wServer, wGroupname, wDescription: WideString; Details: LOCALGROUP_INFO_1; Err: NET_API_STATUS; ParmErr: DWORD; begin wServer := Server; wGroupname := Groupname; wDescription := Description; FillChar(Details, SizeOf(Details), #0); Details.lgrpi1_name := PWideChar(wGroupName); Details.lgrpi1_comment := PWideChar(wDescription); Err := RtdlNetLocalGroupAdd(PWideChar(wServer), 1, @Details, @ParmErr); Result := (Err = NERR_SUCCESS); end; function DeleteLocalGroup(const Server, Groupname: string): Boolean; var wServername, wUsername: WideString; Err: NET_API_STATUS; begin wServername := Server; wUsername := Groupname; Err := RtdlNetLocalGroupDel(PWideChar(wServername), PWideChar(wUsername)); Result := (Err = NERR_SUCCESS); end; function GetLocalGroups(const Server: string; const Groups: TStrings): Boolean; var Err: NET_API_STATUS; wServername: WideString; Buffer: PByte; Details: PLocalGroupInfo0; EntriesRead, TotalEntries: Cardinal; I: Integer; begin wServername := Server; Err := RtdlNetLocalGroupEnum(PWideChar(wServername), 0, Buffer, MAX_PREFERRED_LENGTH, EntriesRead, TotalEntries, nil); if Err = NERR_SUCCESS then begin Details := PLocalGroupInfo0(Buffer); Groups.BeginUpdate; try for I := 0 to EntriesRead - 1 do begin Groups.Add(Details^.lgrpi0_name); Inc(Details); end; finally Groups.EndUpdate; end; end; RtdlNetApiBufferFree(Buffer); Result := (Err = NERR_SUCCESS); end; function GetGlobalGroups(const Server: string; const Groups: TStrings): Boolean; var Err: NET_API_STATUS; wServername: WideString; Buffer: PByte; Details: PGroupInfo0; EntriesRead, TotalEntries: Cardinal; I: Integer; begin wServername := Server; Err := RtdlNetGroupEnum(PWideChar(wServername), 0, Buffer, MAX_PREFERRED_LENGTH, EntriesRead, TotalEntries, nil); if Err = NERR_SUCCESS then begin Details := PGroupInfo0(Buffer); // (rom) is 'None' locale independent? if (EntriesRead <> 1) or (Details^.grpi0_name <> 'None') then begin Groups.BeginUpdate; try for I := 0 to EntriesRead - 1 do begin Groups.Add(Details^.grpi0_name); Inc(Details); end; finally Groups.EndUpdate; end; end; end else RaiseLastOSError; RtdlNetApiBufferFree(Buffer); Result := (Err = NERR_SUCCESS); end; function LocalGroupExists(const Group: string): Boolean; var Groups: TStringList; begin Groups := TStringList.Create; try GetLocalGroups('', Groups); Result := (Groups.IndexOf(Group) >= 0); finally Groups.Free; end; end; function GlobalGroupExists(const Server, Group: string): Boolean; var Groups: TStringList; begin Groups := TStringList.Create; try GetGlobalGroups(Server, Groups); Result := (Groups.IndexOf(Group) >= 0); finally Groups.Free; end; end; function DeleteGlobalGroup(const Server, Groupname: string): Boolean; var wServername, wUsername: WideString; Err: NET_API_STATUS; begin wServername := Server; wUsername := Groupname; Err := RtdlNetGroupDel(PWideChar(wServername), PWideChar(wUsername)); Result := (Err = NERR_SUCCESS); end; function AddAccountToLocalGroup(const Accountname, Groupname: string): Boolean; var Err: NET_API_STATUS; wAccountname, wGroupname: WideString; Details: LOCALGROUP_MEMBERS_INFO_3; begin wGroupname := Groupname; wAccountname := AccountName; Details.lgrmi3_domainandname := PWideChar(wAccountname); Err := RtdlNetLocalGroupAddMembers(nil, PWideChar(wGroupname), 3, @Details, 1); Result := (Err = NERR_SUCCESS); end; function RIDToDWORD(const RID: TNetWellKnownRID): DWORD; begin case RID of wkrAdmins: Result := DOMAIN_ALIAS_RID_ADMINS; wkrUsers: Result := DOMAIN_ALIAS_RID_USERS; wkrGuests: Result := DOMAIN_ALIAS_RID_GUESTS; wkrPowerUsers: Result := DOMAIN_ALIAS_RID_POWER_USERS; wkrBackupOPs: Result := DOMAIN_ALIAS_RID_BACKUP_OPS; wkrReplicator: Result := DOMAIN_ALIAS_RID_REPLICATOR; else // (wkrEveryone) Result := SECURITY_WORLD_RID; end; end; function DWORDToRID(const RID: DWORD): TNetWellKnownRID; begin case RID of DOMAIN_ALIAS_RID_ADMINS: Result := wkrAdmins; DOMAIN_ALIAS_RID_USERS: Result := wkrUsers; DOMAIN_ALIAS_RID_GUESTS: Result := wkrGuests; DOMAIN_ALIAS_RID_POWER_USERS: Result := wkrPowerUsers; DOMAIN_ALIAS_RID_BACKUP_OPS: Result := wkrBackupOPs; DOMAIN_ALIAS_RID_REPLICATOR: Result := wkrReplicator; else // (SECURITY_WORLD_RID) Result := wkrEveryone; end; end; function LookupGroupName(const Server: string; const RID: TNetWellKnownRID): string; var sia: SID_IDENTIFIER_AUTHORITY; rd1, rd2: DWORD; ridCount: Integer; sd: PSID; AccountNameLen, DomainNameLen: DWORD; SidNameUse: SID_NAME_USE; begin Result := ''; rd2 := 0; if RID = wkrEveryOne then begin sia := SECURITY_WORLD_SID_AUTHORITY; rd1 := RIDToDWORD(RID); ridCount := 1; end else begin sia := SECURITY_NT_AUTHORITY; rd1 := SECURITY_BUILTIN_DOMAIN_RID; rd2 := RIDToDWORD(RID); ridCount := 2; end; if AllocateAndInitializeSid(sia, ridCount, rd1, rd2, 0, 0, 0, 0, 0, 0, sd) then try AccountNameLen := 0; DomainNameLen := 0; if not LookupAccountSID(PChar(Server), sd, PChar(Result), AccountNameLen, nil, DomainNameLen, SidNameUse) then SetLength(Result, AccountNamelen); if LookupAccountSID(PChar(Server), sd, PChar(Result), AccountNameLen, nil, DomainNameLen, sidNameUse) then StrResetLength(Result) else RaiseLastOSError; finally FreeSID(sd); end; end; procedure ParseAccountName(const QualifiedName: string; var Domain, UserName: string); var Parts: TStringList; begin Parts := TStringList.Create; try StrTokenToStrings(QualifiedName, '\', Parts); if Parts.Count = 1 then UserName := Parts[0] else begin Domain := Parts[0]; UserName := Parts[1]; end; finally Parts.Free; end; end; function IsLocalAccount(const AccountName: string): Boolean; var Domain: string; UserName: string; LocalServerName: string; begin LocalServerName := GetLocalComputerName; ParseAccountName(AccountName, Domain, UserName); Result := (Domain = '') or (Domain = LocalServerName); end; // History: // $Log: JclLANMan.pas,v $ // Revision 1.12 2005/02/25 07:20:15 marquardt // add section lines // // Revision 1.11 2005/02/24 16:34:52 marquardt // remove divider lines, add section lines (unfinished) // // Revision 1.10 2005/02/06 07:45:42 marquardt // fixed non-compiling IsLocalAccount // // Revision 1.9 2005/02/06 03:36:50 mthoma // Added feature [Code Library 0000805]: IsLocalAccount does not work with names like serveruser. // // Revision 1.8 2004/10/17 21:00:15 mthoma // cleaning // // Revision 1.7 2004/07/31 06:21:03 marquardt // fixing TStringLists, adding BeginUpdate/EndUpdate, finalization improved // // Revision 1.6 2004/05/05 07:33:49 rrossmair // header updated according to new policy: initial developers & contributors listed // // Revision 1.5 2004/04/06 04:55:17 // adapt compiler conditions, add log entry // end.