windows-nt/Source/XPSP1/NT/base/ntsetup/win95upg/w95upgnt/migmain/security.c
2020-09-26 16:20:57 +08:00

1265 lines
29 KiB
C

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
security.c
Abstract:
Helpers for NT security API.
Author:
Jim Schmidt (jimschm) 05-Feb-1997
Revision History:
ovidiut 14-Mar-2000 Updated CreateLocalAccount for encrypted password feature
jimschm 02-Jun-1999 Added SetRegKeySecurity
jimschm 18-Mar-1998 Updated CreateLocalAccount for random password
feature. Added password change if account
already exists.
--*/
#include "pch.h"
#include "migmainp.h"
#include "security.h"
#include "encrypt.h"
#include <ntdsapi.h>
#include <dsgetdc.h>
#ifndef UNICODE
#error UNICODE definition required for account lookup code
#endif
#define UNDOCUMENTED_UI_FLAG 0x0200
//
// NT 5 - net share-specific flag
//
DWORD
ConvertNetRightsToAccessMask (
IN DWORD Flags
)
/*++
Routine Description:
Routine that converts LAN Man flags into NT security flags.
Arguments:
Flags - Access flags used with NetAccess* APIs
Return value:
A DWORD containing the NT security flags.
--*/
{
DWORD OutFlags;
if (Flags == ACCESS_READ) {
//
// Read only permissions
//
OutFlags = FILE_GENERIC_READ|FILE_GENERIC_EXECUTE;
} else if (Flags == ACCESS_WRITE) {
//
// Change only permission
//
OutFlags = FILE_GENERIC_WRITE|DELETE;
} else if (Flags == (ACCESS_READ|ACCESS_WRITE)) {
//
// Full control permissions
//
OutFlags = FILE_ALL_ACCESS|UNDOCUMENTED_UI_FLAG;
} else {
//
// Unsupported options... disable the share
//
OutFlags = 0;
DEBUGMSG ((DBG_VERBOSE, "Unsupported permission %u was translated to disable permission", Flags));
}
return OutFlags;
}
DWORD
AddAclMember (
IN OUT PGROWBUFFER GrowBuf,
IN PCTSTR UserOrGroup,
IN DWORD Attributes
)
/*++
Routine Description:
Appends user/group account, attributes and enable flag to a list
of members. This funciton is used to build a list of members which
is passed to CreateAclFromMemberList to create an ACL.
Arguments:
GrowBuf - A GROWBUFFER variable that is zero-initialized
UserOrGroup - String specifying user name or group
Attributes - A list of access rights (a combination of flags
from NetAccess* APIs). Currently the only flags
that are used are:
0 - Deny all access
ACCESS_READ - Read-Only access
ACCESS_WRITE - Change-Only access
ACCESS_READ|ACCESS_WRITE - Full access
Return value:
The number of bytes needed to store UserOrGroup, Attributes and Enabled,
or zero if the function fails. GrowBuf may be expanded to hold the
new data.
GrowBuf must be freed by the caller after the ACL is generated.
--*/
{
DWORD Size;
PACLMEMBER AclMemberPtr;
TCHAR RealName[MAX_USER_NAME];
DWORD OriginalAttribs;
BOOL Everyone;
PCTSTR p;
p = _tcschr (UserOrGroup, TEXT('\\'));
if (p) {
UserOrGroup = _tcsinc (p);
}
if (StringMatch (UserOrGroup, TEXT("*"))) {
_tcssafecpy (RealName, g_EveryoneStr, MAX_USER_NAME);
} else {
_tcssafecpy (RealName, UserOrGroup, MAX_USER_NAME);
}
Everyone = StringIMatch (RealName, g_EveryoneStr);
Size = SizeOfString (RealName) + sizeof (ACLMEMBER);
AclMemberPtr = (PACLMEMBER) GrowBuffer (GrowBuf, Size);
OriginalAttribs = Attributes;
if (!Attributes && !Everyone) {
Attributes = ACCESS_READ|ACCESS_WRITE;
}
AclMemberPtr->Attribs = ConvertNetRightsToAccessMask (Attributes);
AclMemberPtr->Enabled = Everyone || OriginalAttribs != 0;
AclMemberPtr->Failed = FALSE;
StringCopy (AclMemberPtr->UserOrGroup, RealName);
return Size;
}
PACL
CreateAclFromMemberList (
PBYTE AclMemberList,
DWORD MemberCount
)
/*++
Routine Description:
CreateAclFromMemberList takes a member list (prepared by AddAclMember)
and generates an ACL.
Arguments:
AclMemberList - A pointer to the buffer maintained by AddAclMember. This
is usually the Buf member of a GROWBUFFER variable.
MemberCount - The number of members in AclMemberList (i.e. the number of
AddAclMember calls)
Return value:
A pointer to a MemAlloc'd ACL, or NULL if an error occurred. Call
FreeMemberListAcl to free a non-NULL return value.
--*/
{
PACLMEMBER AclMemberPtr;
DWORD AllowedAceCount;
DWORD DeniedAceCount;
DWORD d;
PACL Acl = NULL;
DWORD AclSize;
BOOL b = FALSE;
UINT SidSize = 0;
__try {
//
// Create SID array for all members
//
AclMemberPtr = (PACLMEMBER) AclMemberList;
AllowedAceCount = 0;
DeniedAceCount = 0;
for (d = 0 ; d < MemberCount ; d++) {
AclMemberPtr->Sid = GetSidForUser (AclMemberPtr->UserOrGroup);
if (!AclMemberPtr->Sid) {
// Mark an error
AclMemberPtr->Failed = TRUE;
} else {
// Found SID, adjust ace count and sid size
if (AclMemberPtr->Enabled) {
AllowedAceCount++;
} else {
DeniedAceCount++;
}
SidSize += GetLengthSid (AclMemberPtr->Sid);
}
GetNextAclMember (&AclMemberPtr);
}
//
// Calculate size of ACL (an ACL struct plus the ACEs) and allocate it.
//
// We subtract a DWORD from the struct size because the actual size of all
// SidStart members is given by SidSize.
//
AclSize = sizeof (ACL) +
AllowedAceCount * (sizeof (ACCESS_ALLOWED_ACE) - sizeof (DWORD)) +
DeniedAceCount * (sizeof (ACCESS_DENIED_ACE) - sizeof (DWORD)) +
SidSize;
Acl = (PACL) MemAlloc (g_hHeap, 0, AclSize);
if (!Acl) {
LOG ((LOG_ERROR, "Couldn't allocate an ACL"));
__leave;
}
//
// Create the ACL
//
if (!InitializeAcl (Acl, AclSize, ACL_REVISION)) {
LOG ((LOG_ERROR, "Couldn't initialize ACL"));
__leave;
}
//
// Add the access-denied ACLs first
//
AclMemberPtr = (PACLMEMBER) AclMemberList;
for (d = 0 ; d < MemberCount ; d++) {
if (AclMemberPtr->Failed) {
continue;
}
if (!AclMemberPtr->Enabled) {
if (!AddAccessDeniedAce (
Acl,
ACL_REVISION,
AclMemberPtr->Attribs,
AclMemberPtr->Sid
)) {
LOG ((
LOG_ERROR,
"Couldn't add denied ACE for %s",
AclMemberPtr->UserOrGroup
));
}
}
GetNextAclMember (&AclMemberPtr);
}
//
// Add the access-enabled ACLs last
//
// Reset the SID pointer because CreateAclFromMemberList is the
// only ones who uses this member
//
AclMemberPtr = (PACLMEMBER) AclMemberList;
for (d = 0 ; d < MemberCount ; d++) {
if (AclMemberPtr->Failed) {
continue;
}
//
// Add member to list
//
if (AclMemberPtr->Enabled) {
if (!AddAccessAllowedAce (
Acl,
ACL_REVISION,
AclMemberPtr->Attribs,
AclMemberPtr->Sid
)) {
LOG ((
LOG_ERROR,
"Couldn't add allowed ACE for %s",
AclMemberPtr->UserOrGroup
));
}
}
AclMemberPtr->Sid = NULL;
GetNextAclMember (&AclMemberPtr);
}
b = TRUE;
}
__finally {
if (!b) {
if (Acl) {
MemFree (g_hHeap, 0, Acl);
}
Acl = NULL;
}
}
return Acl;
}
VOID
FreeMemberListAcl (
PACL Acl
)
/*++
Routine Description:
Routine to free the value returned by CreateAclFromMemberList
Arguments:
Acl - The return value of CreateAclFromMemberList
Return value:
none
--*/
{
if (Acl) {
MemFree (g_hHeap, 0, (LPVOID) Acl);
}
}
VOID
GetNextAclMember (
PACLMEMBER *AclMemberPtrToPtr
)
/*++
Routine Description:
GetNextAclMember adjusts an ACLMEMBER pointer to point to the next
member. Each member is a variable-length structure, so this funciton
is required to walk the structure array.
Arguments:
AclMemberPtrToPtr - A pointer to a PACLMEMBER variable.
Return value:
none
--*/
{
*AclMemberPtrToPtr = (PACLMEMBER) ((PBYTE) (*AclMemberPtrToPtr) +
sizeof (ACLMEMBER) +
SizeOfString ((*AclMemberPtrToPtr)->UserOrGroup)
);
}
LONG
CreateLocalAccount (
IN PACCOUNTPROPERTIES Properties,
IN PCWSTR User OPTIONAL
)
/*++
Routine Description:
CreateLocalAccount creates an account for a local user
Arguments:
Properties - Specifies a set of attributes for a user
User - An optional name to override Properties->User
Return value:
A Win32 error code
--*/
{
USER_INFO_3 ui;
PUSER_INFO_3 ExistingInfo;
DWORD rc;
LONG ErrParam;
PCWSTR UnicodeUser;
PCWSTR UnicodePassword;
PCWSTR UnicodeFullName;
PCWSTR UnicodeComment;
//
// Create local account
//
if (!User) {
User = Properties->User;
}
UnicodeUser = CreateUnicode (User);
UnicodePassword = CreateUnicode (Properties->Password);
UnicodeComment = CreateUnicode (Properties->AdminComment);
UnicodeFullName = CreateUnicode (Properties->FullName);
ZeroMemory (&ui, sizeof (ui));
ui.usri3_name = (PWSTR) UnicodeUser;
ui.usri3_password = (PWSTR) UnicodePassword;
ui.usri3_comment = (PWSTR) UnicodeComment;
ui.usri3_full_name = (PWSTR) UnicodeFullName;
ui.usri3_priv = USER_PRIV_USER; // do not change
//
// don't expire nor require a password for this account
//
ui.usri3_flags = UF_SCRIPT|UF_NORMAL_ACCOUNT|UF_DONT_EXPIRE_PASSWD|UF_PASSWD_NOTREQD;
ui.usri3_acct_expires = TIMEQ_FOREVER;
ui.usri3_max_storage = USER_MAXSTORAGE_UNLIMITED;
ui.usri3_primary_group_id = DOMAIN_GROUP_RID_USERS;
ui.usri3_max_storage = USER_MAXSTORAGE_UNLIMITED;
ui.usri3_acct_expires = TIMEQ_FOREVER;
ui.usri3_password_expired = (INT) g_ConfigOptions.ForcePasswordChange;
rc = NetUserAdd (NULL, 3, (PBYTE) &ui, &ErrParam);
if (rc == ERROR_SUCCESS) {
if (Properties->PasswordAttribs & PASSWORD_ATTR_ENCRYPTED) {
//
// change user's password using encrypted password APIs
//
rc = SetLocalUserEncryptedPassword (
User,
Properties->Password,
FALSE,
Properties->EncryptedPassword,
TRUE
);
if (rc != ERROR_SUCCESS) {
if (rc == ERROR_PASSWORD_RESTRICTION) {
LOG ((
LOG_WARNING,
"Unable to set supplied password on user %s because a password rule has been violated.",
User
));
} else if (rc == ERROR_INVALID_PARAMETER) {
LOG ((
LOG_WARNING,
"Illegal encrypted password supplied for user %s.",
User
));
} else {
LOG ((
LOG_WARNING,
"Unable to set password on user %s, rc=%u",
User,
rc
));
rc = ERROR_INVALID_PARAMETER;
}
}
}
} else if (rc == NERR_UserExists) {
//
// Try to change password if user already exists and this is the intent
//
DEBUGMSG ((DBG_WARNING, "User %s already exists", User));
if ((Properties->PasswordAttribs & PASSWORD_ATTR_DONT_CHANGE_IF_EXIST) == 0) {
if (Properties->PasswordAttribs & PASSWORD_ATTR_ENCRYPTED) {
rc = SetLocalUserEncryptedPassword (
User,
Properties->Password,
FALSE,
Properties->EncryptedPassword,
TRUE
);
if (rc != ERROR_SUCCESS) {
if (rc == ERROR_PASSWORD_RESTRICTION) {
LOG ((
LOG_WARNING,
"Unable to set supplied password on user %s because a password rule has been violated.",
User
));
} else if (rc == ERROR_INVALID_PARAMETER) {
LOG ((
LOG_WARNING,
"Illegal encrypted password supplied for user %s.",
User
));
} else {
LOG ((
LOG_WARNING,
"Unable to set password on user %s, rc=%u",
User,
rc
));
rc = ERROR_INVALID_PARAMETER;
}
}
} else {
rc = NetUserGetInfo (NULL, User, 3, (PBYTE *) &ExistingInfo);
if (rc == ERROR_SUCCESS) {
ExistingInfo->usri3_password = ui.usri3_password;
ExistingInfo->usri3_comment = ui.usri3_comment;
ExistingInfo->usri3_full_name = ui.usri3_full_name;
ExistingInfo->usri3_flags = ui.usri3_flags;
ExistingInfo->usri3_password_expired = ui.usri3_password_expired;
rc = NetUserSetInfo (NULL, User, 3, (PBYTE) ExistingInfo, &ErrParam);
NetApiBufferFree ((PVOID) ExistingInfo);
if (rc != ERROR_SUCCESS) {
LOG ((LOG_WARNING, "NetUserSetInfo failed for %s. rc=%u.", User, rc));
rc = ERROR_INVALID_PARAMETER;
}
} else {
LOG ((LOG_WARNING, "NetUserGetInfo failed for %s. rc=%u.", User, rc));
rc = ERROR_INVALID_PARAMETER;
}
}
} else {
rc = ERROR_SUCCESS;
}
} else {
LOG ((LOG_ERROR, "NetUserAdd failed for %s. ErrParam=%i.", User, ErrParam));
}
DestroyUnicode (UnicodeUser);
DestroyUnicode (UnicodePassword);
DestroyUnicode (UnicodeComment);
DestroyUnicode (UnicodeFullName);
return rc;
}
VOID
ClearAdminPassword (
VOID
)
{
ACCOUNTPROPERTIES Properties;
Properties.Password = L"";
Properties.AdminComment = L"";
Properties.User = g_AdministratorStr;
Properties.FullName = g_AdministratorStr;
CreateLocalAccount (&Properties, NULL);
}
BOOL
AddSidToLocalGroup (
PSID Sid,
PCWSTR Group
)
/*++
Routine Description:
Routine that adds the supplied SID to the Administrators group.
Arguments:
Sid - A valid security id for the user to be added to the
Administrators group
Group - Specifies the group name to join the user to
Return value:
TRUE if the member was added successfully
--*/
{
LOCALGROUP_MEMBERS_INFO_0 lgrmi0;
DWORD rc;
lgrmi0.lgrmi0_sid = Sid;
rc = NetLocalGroupAddMembers (
NULL,
Group,
0, // level 0
(PBYTE) &lgrmi0,
1 // member count
);
return rc == ERROR_SUCCESS;
}
NTSTATUS
pGetPrimaryDomainInfo (
POLICY_PRIMARY_DOMAIN_INFO **PrimaryInfoPtr
)
/*++
Routine Description:
Private function that retrieves the primary domain info.
Arguments:
PrimaryInfoPtr - Pointer to a variable to receive the address
of the POLICY_PRIMARY_DOMAIN_INFO structure
allocated by the Lsa APIs. Free memory by
calling LsaFreeMemory.
Return value:
NT status code indicating outcome
--*/
{
LSA_HANDLE policyHandle;
NTSTATUS status;
//
// Open local LSA policy to retrieve domain name
//
status = OpenPolicy (
NULL, // local target machine
POLICY_VIEW_LOCAL_INFORMATION, // Access type
&policyHandle // resultant policy handle
);
if (status == ERROR_SUCCESS) {
//
// Query LSA Primary domain info
//
status = LsaQueryInformationPolicy (
policyHandle,
PolicyPrimaryDomainInformation,
(PVOID *) PrimaryInfoPtr
);
LsaClose (policyHandle);
}
return status;
}
BOOL
GetPrimaryDomainName (
OUT PTSTR DomainName
)
{
NTSTATUS status;
POLICY_PRIMARY_DOMAIN_INFO *PrimaryInfo;
PCTSTR TcharName;
status = pGetPrimaryDomainInfo (&PrimaryInfo);
if (status == ERROR_SUCCESS) {
TcharName = ConvertWtoT (PrimaryInfo->Name.Buffer);
MYASSERT (TcharName);
StringCopy (DomainName, TcharName);
FreeWtoT (TcharName);
LsaFreeMemory (PrimaryInfo);
}
ELSE_DEBUGMSG ((DBG_WARNING, "Can't get primary domain info. rc=%u", status));
return status == ERROR_SUCCESS;
}
BOOL
GetPrimaryDomainSid (
OUT PBYTE DomainSid,
IN UINT MaxBytes
)
{
NTSTATUS status;
POLICY_PRIMARY_DOMAIN_INFO *PrimaryInfo;
UINT Size;
status = pGetPrimaryDomainInfo (&PrimaryInfo);
if (status == ERROR_SUCCESS) {
Size = GetLengthSid (PrimaryInfo->Sid);
if (MaxBytes < Size) {
status = ERROR_INSUFFICIENT_BUFFER;
} else {
CopyMemory (DomainSid, PrimaryInfo->Sid, Size);
}
LsaFreeMemory (PrimaryInfo);
}
ELSE_DEBUGMSG ((DBG_WARNING, "Can't get primary domain SID. rc=%u", status));
return status == ERROR_SUCCESS;
}
BOOL
IsMemberOfDomain (
VOID
)
/*++
Routine Description:
Determines if the machine is participating in a domain, or if it is
only participating in a workgroup. This determination is done by
obtaining the primary domain information, looking for the server's
SID. If the SID is NULL, the machine is not in a domain.
Arguments:
none
Return value:
TRUE if the machine is in a domain, FALSE if its in a workgroup.
--*/
{
NET_API_STATUS rc;
PWSTR WorkgroupOrDomain = NULL;
NETSETUP_JOIN_STATUS Type;
rc = NetGetJoinInformation (NULL, &WorkgroupOrDomain, &Type);
DEBUGMSG ((DBG_VERBOSE, "NetGetJoinInformation: name=%s, type=%u", WorkgroupOrDomain, Type));
if (WorkgroupOrDomain) {
NetApiBufferFree (WorkgroupOrDomain);
}
if (rc != ERROR_SUCCESS) {
LOG ((LOG_ERROR, "NetGetJoinInformation failed: error %u", rc));
}
return rc == ERROR_SUCCESS && Type == NetSetupDomainName;
#if 0
POLICY_PRIMARY_DOMAIN_INFO *PrimaryInfo;
BOOL b;
NTSTATUS rc;
rc = pGetPrimaryDomainInfo (&PrimaryInfo);
if (rc == ERROR_SUCCESS) {
b = PrimaryInfo->Sid != NULL;
} else {
b = FALSE;
SetLastError (rc);
LOG ((LOG_ERROR, "Can't get domain security info"));
}
// Domain name is in PrimaryInfo->Name.Buffer
LsaFreeMemory (PrimaryInfo) ;
return b;
#endif
}
LONG
GetAnyDC (
IN PCWSTR Domain,
IN PWSTR ServerBuf,
IN BOOL GetNewServer
)
/*++
Routine Description:
Gets the list of all domain controllers and randomly chooses one. If
the listed DC is not online, other listed DCs are queried until an
alive DC is found.
Arguments:
Domain - The name of the domain to find DCs for
ServerBuf - A buffer to hold the name of the server
Return value:
NT status code indicating outcome.
--*/
{
DWORD rc;
PDOMAIN_CONTROLLER_INFO dci;
DWORD Flags = DS_IS_FLAT_NAME;
//
// This API is fast because its WINS based...
//
rc = DsGetDcName (
NULL, // computer to remote to
Domain,
NULL, // Domain GUID
NULL, // Site GUID
Flags | (GetNewServer ? DS_FORCE_REDISCOVERY : 0),
&dci
);
if (rc == NO_ERROR) {
StringCopyW (ServerBuf, dci->DomainControllerAddress);
NetApiBufferFree (dci);
DEBUGMSG ((DBG_VERBOSE, "Found server %s for the %s domain", ServerBuf, Domain));
return rc;
}
return rc;
}
VOID
InitLsaString (
OUT PLSA_UNICODE_STRING LsaString,
IN PWSTR String
)
/*++
Routine Description:
LSA uses a special Pascal-style string structure. This
routine assigns String to a member of LsaString, and computes
its length and maximum length.
Arguments:
LsaString - A pointer to the structure to receive a pointer
to the nul-terminated string, the length in bytes
(excluding the nul), and the maximum length including
the nul.
Return value:
none
--*/
{
USHORT StringLength;
if (!String) {
ZeroMemory (LsaString, sizeof (LSA_UNICODE_STRING));
return;
}
StringLength = ByteCountW (String);
LsaString->Buffer = String;
LsaString->Length = StringLength;
LsaString->MaximumLength = StringLength + sizeof(WCHAR);
}
NTSTATUS
OpenPolicy (
IN PWSTR ServerName,
IN DWORD DesiredAccess,
OUT PLSA_HANDLE policyHandle
)
/*++
Routine Description:
A wrapper to simplify LsaOpenPolicy
Arguments:
ServerName - Supplies the server to open the policy on. Specify
NULL for local machine.
DesiredAccess - The access flags passed to the LSA API
policyHandle - Receives the policy handle if successful
Return value:
NT status code indicating outcome
--*/
{
LSA_OBJECT_ATTRIBUTES objectAttributes;
LSA_UNICODE_STRING ServerString;
PLSA_UNICODE_STRING Server;
//
// Always initialize the object attributes to all zeroes
//
ZeroMemory (&objectAttributes, sizeof(objectAttributes));
if (ServerName != NULL) {
//
// Make a LSA_UNICODE_STRING out of the PWSTR passed in
//
InitLsaString (&ServerString, ServerName);
Server = &ServerString;
} else {
Server = NULL;
}
//
// Attempt to open the policy
//
return LsaOpenPolicy (
Server,
&objectAttributes,
DesiredAccess,
policyHandle
);
}
BOOL
IsDomainController(
IN PWSTR Server,
OUT PBOOL DomainControllerFlag
)
/*++
Routine Description:
Queries if the machine is a server or workstation via
the NetServerGetInfo API.
Arguments:
Server - The machine to query, or NULL for the local machine
DomainControllerFlag - Receives TRUE if the machine is a
domain controller, or FALSE if the
machine is a workstation.
Return value:
TRUE if the API was successful, or FALSE if not. GetLastError
gives failure code.
--*/
{
PSERVER_INFO_101 si101;
NET_API_STATUS nas;
nas = NetServerGetInfo(
Server,
101, // info-level
(PBYTE *) &si101
);
if (nas != NO_ERROR) {
SetLastError (nas);
return FALSE;
}
if ((si101->sv101_type & SV_TYPE_DOMAIN_CTRL) ||
(si101->sv101_type & SV_TYPE_DOMAIN_BAKCTRL)) {
//
// We are dealing with a DC
//
*DomainControllerFlag = TRUE;
} else {
*DomainControllerFlag = FALSE;
}
NetApiBufferFree (si101);
return TRUE;
}
DWORD
pConvertFlagsToRights (
DWORD Flags
)
{
while (Flags > 0x0f) {
Flags >>= 4;
}
if (Flags & 0x01) {
return 0;
}
if (Flags == 0x02) {
return ACCESS_READ;
}
if (Flags == 0x04) {
return ACCESS_WRITE;
}
if ((Flags & 0x06) == 0x06) {
return ACCESS_READ|ACCESS_WRITE;
}
DEBUGMSG ((DBG_WHOOPS, "Undefined access flags specified: 0x%X", Flags));
return 0;
}
DWORD
SetRegKeySecurity (
IN PCTSTR KeyStr,
IN DWORD DaclFlags, OPTIONAL
IN PSID Owner, OPTIONAL
IN PSID PrimaryGroup, OPTIONAL
IN BOOL Recursive
)
/*++
Routine Description:
SetRegKeySecurity updates the security of a registry key, or an entire
registry node. The caller can change the DACL, owner or primary group.
Change of the SACL is intentionally not implemented.
Arguments:
KeyStr - Specifies the key to modify the permissions. If Recursive
is set to TRUE, this key will be updated along with all
subkeys.
DaclFlags - Specifies zero or more SF_* flags, indicating how access to
the key should be set.
Owner - Specifies the SID of the new owner.
PrimaryGroup - Specifies the SID of the primary group.
Recursive - Specifies TRUE to apply the security to the key and all of
its subkeys, or FALSE to update the key only, leaving the
subkeys alone.
Return Value:
A Win32 status code.
--*/
{
DWORD rc = ERROR_SUCCESS;
SECURITY_DESCRIPTOR sd;
GROWBUFFER AclMemberList = GROWBUF_INIT;
HKEY Key = NULL;
REGSAM OldSam;
DWORD AclMembers = 0;
PACL Acl = NULL;
SECURITY_INFORMATION WhatToSet = 0;
REGTREE_ENUM e;
LONG rc2;
_try {
//
// Open key with full permission
//
OldSam = SetRegOpenAccessMode (KEY_ALL_ACCESS);
Key = OpenRegKeyStr (KeyStr);
if (!Key) {
rc = GetLastError();
__leave;
}
//
// Prepare a security descriptor
//
InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION);
if (Owner) {
if (!SetSecurityDescriptorOwner (&sd, Owner, FALSE)) {
rc = GetLastError();
__leave;
}
WhatToSet |= OWNER_SECURITY_INFORMATION;
}
if (PrimaryGroup) {
if (!SetSecurityDescriptorGroup (&sd, PrimaryGroup, FALSE)) {
rc = GetLastError();
__leave;
}
WhatToSet |= GROUP_SECURITY_INFORMATION;
}
//
// Add the DACL
//
if (DaclFlags & SF_EVERYONE_MASK) {
AddAclMember (
&AclMemberList,
g_EveryoneStr,
pConvertFlagsToRights (DaclFlags & SF_EVERYONE_MASK)
);
AclMembers++;
}
if (DaclFlags & SF_ADMINISTRATORS_MASK) {
AddAclMember (
&AclMemberList,
g_AdministratorsGroupStr,
pConvertFlagsToRights (DaclFlags & SF_ADMINISTRATORS_MASK)
);
AclMembers++;
}
if (AclMembers) {
Acl = CreateAclFromMemberList (AclMemberList.Buf, AclMembers);
if (!Acl) {
rc = GetLastError();
__leave;
}
WhatToSet |= DACL_SECURITY_INFORMATION;
}
//
// Set the security
//
if (Recursive) {
DEBUGMSG_IF ((
rc != ERROR_SUCCESS,
DBG_WARNING,
"RegSetKeySecurity failed for %s with rc=%u",
KeyStr,
rc
));
if (EnumFirstRegKeyInTree (&e, KeyStr)) {
do {
rc2 = RegSetKeySecurity (e.CurrentKey->KeyHandle, WhatToSet, &sd);
if (rc2 != ERROR_SUCCESS) {
rc = (DWORD) rc2;
}
DEBUGMSG_IF ((
rc2 != ERROR_SUCCESS,
DBG_WARNING,
"RegSetKeySecurity failed for %s with rc=%u",
e.FullKeyName,
rc2
));
} while (EnumNextRegKeyInTree (&e));
}
} else {
rc = (DWORD) RegSetKeySecurity (Key, WhatToSet, &sd);
}
}
__finally {
FreeGrowBuffer (&AclMemberList);
if (Key) {
CloseRegKey (Key);
}
SetRegOpenAccessMode (OldSam);
if (Acl) {
FreeMemberListAcl (Acl);
}
}
return rc;
}