674 lines
19 KiB
C
674 lines
19 KiB
C
/******************************************************************************
|
|
*
|
|
* FILE.C
|
|
*
|
|
* This file contains routines based off the User Profile Editor utility.
|
|
*
|
|
* Copyright Citrix Systems, Inc. 1997
|
|
* Copyright (c) 1998-1999 Microsoft Corporation
|
|
*
|
|
* Author: Brad Anderson 1/20/97
|
|
*
|
|
* $Log: M:\nt\private\utils\citrix\cprofile\VCS\file.c $
|
|
*
|
|
* Rev 1.3 Jun 26 1997 18:18:38 billm
|
|
* move to WF40 tree
|
|
*
|
|
* Rev 1.2 23 Jun 1997 16:13:20 butchd
|
|
* update
|
|
*
|
|
* Rev 1.1 28 Jan 1997 20:06:34 BradA
|
|
* Fixed up some problems related to WF 2.0 changes
|
|
*
|
|
* Rev 1.0 27 Jan 1997 20:03:44 BradA
|
|
* Initial Version
|
|
*
|
|
******************************************************************************/
|
|
/****************************** Module Header ******************************\
|
|
* Module Name: upesave.c
|
|
*
|
|
* Copyright (c) 1992, Microsoft Corporation
|
|
*
|
|
* Handles OPening and saving of Profiles: default, system, current and user
|
|
* profiles.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#ifndef RC_INVOKED
|
|
#include <winstaw.h>
|
|
#include <syslib.h>
|
|
#include <tsappcmp.h>
|
|
#include <compatfl.h>
|
|
#include <utilsub.h>
|
|
#endif
|
|
|
|
#include "cprofile.h"
|
|
|
|
HKEY hkeyCurrentUser;
|
|
|
|
PSID gSystemSid; // Initialized in 'InitializeGlobalSids'
|
|
PSID gAdminsLocalGroup; // Initialized in 'InitializeGlobalSids
|
|
SID_IDENTIFIER_AUTHORITY gNtAuthority = SECURITY_NT_AUTHORITY;
|
|
|
|
#define SYSTEM_DEFAULT_SUBKEY TEXT(".DEFAULT")
|
|
#define TEMP_USER_SUBKEY TEXT("TEMP_USER")
|
|
#define TEMP_USER_HIVE_PATH TEXT("%systemroot%\\system32\\config\\")
|
|
#define TEMP_SAVE_HIVE TEXT("%systemroot%\\system32\\config\\HiveSave")
|
|
|
|
#define CITRIX_CLASSES L"\\Registry\\Machine\\Software\\Classes"
|
|
|
|
LPTSTR lpTempUserHive = NULL;
|
|
LPTSTR lpTempUserHivePath = NULL;
|
|
LPTSTR lpTempHiveKey;
|
|
extern TCHAR szDefExt[];
|
|
extern PSID gSystemSid;
|
|
|
|
BOOL AllocAndExpandEnvironmentStrings(LPTSTR String, LPTSTR *lpExpandedString);
|
|
VOID GetRegistryKeyFromPath(LPTSTR lpPath, LPTSTR *lpKey);
|
|
|
|
NTSTATUS
|
|
CtxDeleteKeyTree( HANDLE hKeyRoot,
|
|
PKEY_BASIC_INFORMATION pKeyInfo,
|
|
ULONG ulInfoSize );
|
|
|
|
PSECURITY_DESCRIPTOR GetSecurityInfo( LPTSTR File );
|
|
void FreeSecurityInfo(PSECURITY_DESCRIPTOR);
|
|
|
|
/***************************************************************************\
|
|
* ClearTempUserProfile
|
|
*
|
|
* Purpose : unloads the temp user profile loaded from a file, and deletes
|
|
* the temp file
|
|
*
|
|
* History:
|
|
* 11-20-92 JohanneC Created.
|
|
\***************************************************************************/
|
|
BOOL APIENTRY ClearTempUserProfile()
|
|
{
|
|
BOOL bRet;
|
|
|
|
if (hkeyCurrentUser == HKEY_CURRENT_USER)
|
|
return(TRUE);
|
|
|
|
//
|
|
// Close registry keys.
|
|
//
|
|
if (hkeyCurrentUser) {
|
|
RegCloseKey(hkeyCurrentUser);
|
|
}
|
|
|
|
hkeyCurrentUser = HKEY_CURRENT_USER;
|
|
|
|
bRet = (RegUnLoadKey(HKEY_USERS, lpTempHiveKey) == ERROR_SUCCESS);
|
|
|
|
if (*lpTempUserHive) {
|
|
DeleteFile(lpTempUserHive);
|
|
lstrcat(lpTempUserHive, TEXT(".log"));
|
|
DeleteFile(lpTempUserHive);
|
|
LocalFree(lpTempUserHive);
|
|
lpTempUserHive = NULL;
|
|
}
|
|
|
|
return(bRet);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* OpenUserProfile
|
|
*
|
|
* Purpose : Load an existing profile in the registry and unload previously
|
|
* loaded profile (and delete its tmp file).
|
|
*
|
|
* History:
|
|
* 11-20-92 JohanneC Created.
|
|
\***************************************************************************/
|
|
BOOL APIENTRY OpenUserProfile(LPTSTR szFilePath, PSID *pUserSid)
|
|
{
|
|
|
|
DWORD err;
|
|
|
|
//
|
|
// Copy the profile to a temp hive before loading it in the registry.
|
|
//
|
|
if (!lpTempUserHivePath) {
|
|
if (!AllocAndExpandEnvironmentStrings(TEMP_USER_HIVE_PATH, &lpTempUserHivePath))
|
|
return(FALSE);
|
|
}
|
|
|
|
lpTempUserHive = (LPTSTR)LocalAlloc(LPTR, sizeof(TCHAR) *
|
|
(lstrlen(lpTempUserHivePath) + 17));
|
|
if (!lpTempUserHive) {
|
|
return(FALSE);
|
|
}
|
|
|
|
if (!GetTempFileName(lpTempUserHivePath, TEXT("tmp"), 0, lpTempUserHive)) {
|
|
lstrcpy(lpTempUserHive, lpTempUserHivePath);
|
|
lstrcat(lpTempUserHive, TEXT("\\HiveOpen"));
|
|
}
|
|
|
|
if (CopyFile(szFilePath, lpTempUserHive, FALSE)) {
|
|
GetRegistryKeyFromPath(lpTempUserHive, &lpTempHiveKey);
|
|
if ((err = RegLoadKey(HKEY_USERS, lpTempHiveKey, lpTempUserHive)) == ERROR_SUCCESS) {
|
|
if ((err = RegOpenKeyEx(HKEY_USERS, lpTempHiveKey, 0,
|
|
MAXIMUM_ALLOWED,
|
|
&hkeyCurrentUser)) != ERROR_SUCCESS) {
|
|
//
|
|
// Error, do not have access to the profile.
|
|
//
|
|
ErrorPrintf(IDS_ERROR_PROFILE_LOAD_ERR, err);
|
|
ClearTempUserProfile();
|
|
return(FALSE);
|
|
}
|
|
}
|
|
else {
|
|
DeleteFile(lpTempUserHive);
|
|
lstrcat(lpTempUserHive, TEXT(".log"));
|
|
DeleteFile(lpTempUserHive);
|
|
LocalFree(lpTempUserHive);
|
|
|
|
//
|
|
// Could not load the user profile, check the error code
|
|
//
|
|
if (err == ERROR_BADDB) {
|
|
// bad format: not a profile registry file
|
|
ErrorPrintf(IDS_ERROR_BAD_PROFILE);
|
|
return(FALSE);
|
|
}
|
|
else {
|
|
// generic error message : Failed to load profile
|
|
ErrorPrintf(IDS_ERROR_PROFILE_LOAD_ERR, err);
|
|
return(FALSE);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// An error occured trying to load the profile.
|
|
//
|
|
DeleteFile(lpTempUserHive);
|
|
|
|
switch ( (err = GetLastError()) ) {
|
|
case ERROR_SHARING_VIOLATION:
|
|
ErrorPrintf(IDS_ERROR_PROFILE_INUSE);
|
|
break;
|
|
default:
|
|
ErrorPrintf(IDS_ERROR_PROFILE_LOAD_ERR, err);
|
|
break;
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Get the permitted user
|
|
//
|
|
*pUserSid = NULL;
|
|
return(TRUE);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* SaveUserProfile
|
|
*
|
|
* Purpose : Save the loaded profile as a file. The registry should already
|
|
* have the existing ACL's already set so nothing needs to change. The
|
|
* file ACL's do need to be copied from the original and applied to the
|
|
* saved file. This function assumes the orignal file exists.
|
|
*
|
|
\***************************************************************************/
|
|
BOOL APIENTRY SaveUserProfile(PSID pUserSid, LPTSTR lpFilePath)
|
|
{
|
|
LPTSTR lpTmpHive = NULL;
|
|
BOOL err = FALSE;
|
|
|
|
|
|
//
|
|
// Save the profile to a temp hive then copy it over.
|
|
//
|
|
|
|
if ( AllocAndExpandEnvironmentStrings(TEMP_SAVE_HIVE, &lpTmpHive) )
|
|
{
|
|
if( lpTmpHive != NULL )
|
|
{
|
|
DeleteFile(lpTmpHive);
|
|
|
|
if(RegSaveKey(hkeyCurrentUser, lpTmpHive, NULL) != ERROR_SUCCESS)
|
|
{
|
|
LocalFree(lpTmpHive);
|
|
lpTmpHive = NULL;
|
|
err = TRUE;
|
|
}
|
|
else
|
|
{
|
|
PSECURITY_DESCRIPTOR pSecDesc;
|
|
DWORD Attrib = GetFileAttributes(lpFilePath);
|
|
|
|
pSecDesc = GetSecurityInfo(lpFilePath);
|
|
SetFileAttributes(lpFilePath,FILE_ATTRIBUTE_ARCHIVE);
|
|
if(CopyFile(lpTmpHive, lpFilePath, FALSE))
|
|
{
|
|
DeleteFile(lpTmpHive);
|
|
LocalFree(lpTmpHive);
|
|
lpTmpHive = NULL;
|
|
if (pSecDesc)
|
|
{
|
|
SetFileSecurity(lpFilePath,
|
|
DACL_SECURITY_INFORMATION,
|
|
pSecDesc);
|
|
FreeSecurityInfo(pSecDesc);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(pSecDesc)
|
|
{
|
|
FreeSecurityInfo(pSecDesc);
|
|
}
|
|
err = TRUE;
|
|
}
|
|
if(0xffffffff != Attrib)
|
|
{
|
|
SetFileAttributes(lpFilePath,Attrib);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
err = TRUE;
|
|
}
|
|
|
|
if( lpTmpHive != NULL )
|
|
{
|
|
LocalFree(lpTmpHive);
|
|
}
|
|
|
|
return(!err);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* EnablePrivilege
|
|
*
|
|
* Enables/disabled the specified well-known privilege in the
|
|
* current process context
|
|
*
|
|
* Returns TRUE on success, FALSE on failure
|
|
*
|
|
* History:
|
|
* 12-05-91 Davidc Created
|
|
\***************************************************************************/
|
|
BOOL
|
|
EnablePrivilege(
|
|
DWORD Privilege,
|
|
BOOL Enable
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
#if 0
|
|
BOOL WasEnabled;
|
|
Status = RtlAdjustPrivilege(Privilege, Enable, TRUE, (PBOOLEAN)&WasEnabled);
|
|
return(NT_SUCCESS(Status));
|
|
#else
|
|
HANDLE ProcessToken;
|
|
LUID LuidPrivilege;
|
|
PTOKEN_PRIVILEGES NewPrivileges;
|
|
DWORD Length;
|
|
|
|
//
|
|
// Open our own token
|
|
//
|
|
|
|
Status = NtOpenProcessToken(
|
|
NtCurrentProcess(),
|
|
TOKEN_ADJUST_PRIVILEGES,
|
|
&ProcessToken
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Initialize the privilege adjustment structure
|
|
//
|
|
|
|
LuidPrivilege = RtlConvertLongToLuid(Privilege);
|
|
|
|
NewPrivileges = (PTOKEN_PRIVILEGES) LocalAlloc(LPTR, sizeof(TOKEN_PRIVILEGES) +
|
|
(1 - ANYSIZE_ARRAY) * sizeof(LUID_AND_ATTRIBUTES));
|
|
if (NewPrivileges == NULL) {
|
|
NtClose(ProcessToken);
|
|
return(FALSE);
|
|
}
|
|
|
|
NewPrivileges->PrivilegeCount = 1;
|
|
NewPrivileges->Privileges[0].Luid = LuidPrivilege;
|
|
//
|
|
// WORKAROUND: because of a bug in NtAdjustPrivileges which
|
|
// returns an error when you try to enable a privilege
|
|
// that is already enabled, we first try to disable it.
|
|
// to be removed when api is fixed.
|
|
//
|
|
NewPrivileges->Privileges[0].Attributes = 0;
|
|
|
|
Status = NtAdjustPrivilegesToken(
|
|
ProcessToken, // TokenHandle
|
|
(BOOLEAN)FALSE, // DisableAllPrivileges
|
|
NewPrivileges, // NewPrivileges
|
|
0, // BufferLength
|
|
NULL, // PreviousState (OPTIONAL)
|
|
&Length // ReturnLength
|
|
);
|
|
|
|
NewPrivileges->Privileges[0].Attributes = Enable ? SE_PRIVILEGE_ENABLED : 0;
|
|
//
|
|
// Enable the privilege
|
|
//
|
|
Status = NtAdjustPrivilegesToken(
|
|
ProcessToken, // TokenHandle
|
|
(BOOLEAN)FALSE, // DisableAllPrivileges
|
|
NewPrivileges, // NewPrivileges
|
|
0, // BufferLength
|
|
NULL, // PreviousState (OPTIONAL)
|
|
&Length // ReturnLength
|
|
);
|
|
|
|
LocalFree(NewPrivileges);
|
|
|
|
NtClose(ProcessToken);
|
|
|
|
if (Status) {
|
|
return(FALSE);
|
|
}
|
|
|
|
return(TRUE);
|
|
#endif
|
|
}
|
|
|
|
|
|
BOOL AllocAndExpandEnvironmentStrings(LPTSTR String, LPTSTR *lpExpandedString)
|
|
{
|
|
LPTSTR lptmp = NULL;
|
|
DWORD cchBuffer;
|
|
|
|
// Get the number of characters needed.
|
|
cchBuffer = ExpandEnvironmentStrings(String, lptmp, 0);
|
|
if (cchBuffer) {
|
|
cchBuffer++; // for NULL terminator
|
|
lptmp = (LPTSTR)LocalAlloc(LPTR, sizeof(TCHAR) * cchBuffer);
|
|
if (!lptmp) {
|
|
return(FALSE);
|
|
}
|
|
cchBuffer = ExpandEnvironmentStrings(String, lptmp, cchBuffer);
|
|
}
|
|
*lpExpandedString = lptmp;
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
VOID GetRegistryKeyFromPath(LPTSTR lpPath, LPTSTR *lpKey)
|
|
{
|
|
LPTSTR lptmp;
|
|
|
|
*lpKey = lpPath;
|
|
|
|
for (lptmp = lpPath; *lptmp; lptmp++) {
|
|
if (*lptmp == TEXT('\\')) {
|
|
*lpKey = lptmp+1;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* InitializeGlobalSids
|
|
*
|
|
* Initializes the various global Sids used in this module.
|
|
*
|
|
* History:
|
|
* 04-28-93 JohanneC Created
|
|
\***************************************************************************/
|
|
VOID InitializeGlobalSids()
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Build the admins local group SID
|
|
//
|
|
|
|
Status = RtlAllocateAndInitializeSid(
|
|
&gNtAuthority,
|
|
2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_ADMINS,
|
|
0, 0, 0, 0, 0, 0,
|
|
&gAdminsLocalGroup
|
|
);
|
|
|
|
//
|
|
// create System Sid
|
|
//
|
|
|
|
Status = RtlAllocateAndInitializeSid(
|
|
&gNtAuthority,
|
|
1,
|
|
SECURITY_LOCAL_SYSTEM_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&gSystemSid
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* ClearDisabledClasses
|
|
*
|
|
* This routine will check the compatibility flags for the user's Classes
|
|
* registry key, and remove the keys if mapping is disabled.
|
|
*
|
|
* ENTRY:
|
|
*
|
|
* EXIT:
|
|
* No return value.
|
|
*
|
|
****************************************************************************/
|
|
|
|
void ClearDisabledClasses(void)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG ulcnt = 0, ultemp = 0;
|
|
UNICODE_STRING UniPath;
|
|
OBJECT_ATTRIBUTES ObjAttr;
|
|
PKEY_BASIC_INFORMATION pKeyUserInfo = NULL;
|
|
HANDLE hKeyUser = NULL;
|
|
NTSTATUS Status2;
|
|
HANDLE hClassesKey;
|
|
WCHAR wcuser[MAX_PATH];
|
|
|
|
if ( ! hkeyCurrentUser) {
|
|
return;
|
|
}
|
|
|
|
GetTermsrCompatFlags(CITRIX_CLASSES, &ultemp, CompatibilityRegEntry);
|
|
if ( (ultemp & (TERMSRV_COMPAT_WIN32 | TERMSRV_COMPAT_NOREGMAP)) !=
|
|
(TERMSRV_COMPAT_WIN32 | TERMSRV_COMPAT_NOREGMAP)) {
|
|
return;
|
|
}
|
|
|
|
// Get a buffer for the key info
|
|
ulcnt = sizeof(KEY_BASIC_INFORMATION) + MAX_PATH*sizeof(WCHAR) + sizeof(WCHAR);
|
|
pKeyUserInfo = RtlAllocateHeap(RtlProcessHeap(),
|
|
0,
|
|
ulcnt);
|
|
if (!pKeyUserInfo) {
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
|
|
// We have the necessary buffers, start checking the keys
|
|
if (NT_SUCCESS(Status)) {
|
|
// Build up a string for this user's software section
|
|
wcscpy(wcuser, L"Software");
|
|
|
|
// Create a unicode string for the user key path
|
|
RtlInitUnicodeString(&UniPath, wcuser);
|
|
|
|
InitializeObjectAttributes(&ObjAttr,
|
|
&UniPath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
hkeyCurrentUser,
|
|
NULL);
|
|
|
|
Status = NtOpenKey(&hKeyUser,
|
|
KEY_READ | DELETE,
|
|
&ObjAttr);
|
|
|
|
RtlInitUnicodeString(&UniPath, L"Classes");
|
|
|
|
InitializeObjectAttributes(&ObjAttr,
|
|
&UniPath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
hKeyUser,
|
|
NULL);
|
|
Status2 = NtOpenKey(&hClassesKey, KEY_READ | DELETE, &ObjAttr);
|
|
if ( NT_SUCCESS(Status2) ) {
|
|
Status2 = CtxDeleteKeyTree(hClassesKey, pKeyUserInfo, ulcnt);
|
|
|
|
if ( !NT_SUCCESS(Status2)) {
|
|
}
|
|
NtClose(hClassesKey);
|
|
}
|
|
|
|
// If we allocated the system key, close it
|
|
if (hKeyUser) {
|
|
NtClose(hKeyUser);
|
|
}
|
|
}
|
|
|
|
// Free up any memory we allocated
|
|
if (pKeyUserInfo) {
|
|
RtlFreeHeap( RtlProcessHeap(), 0, pKeyUserInfo);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* CtxDeleteKeyTree
|
|
*
|
|
* Delete a subtree of registry keys
|
|
*
|
|
* ENTRY:
|
|
* hKeyRoot is a handle to the root key that will be deleted along with
|
|
* its children
|
|
* pKeyInfo is a pointer to a KEY_BASIC_INFORMATION buffer that is
|
|
* large enough to hold a MAX_PATH WCHAR string. It is
|
|
* reused and destroyed by each recursive call.
|
|
* ulInfoSize is the size of the pKeyInfo buffer
|
|
*
|
|
* EXIT:
|
|
* Status
|
|
*
|
|
****************************************************************************/
|
|
|
|
NTSTATUS
|
|
CtxDeleteKeyTree( HANDLE hKeyRoot,
|
|
PKEY_BASIC_INFORMATION pKeyInfo,
|
|
ULONG ulInfoSize )
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS, Status2;
|
|
UNICODE_STRING UniPath;
|
|
OBJECT_ATTRIBUTES ObjAttr;
|
|
ULONG ulcnt = 0;
|
|
ULONG ultemp;
|
|
HANDLE hKey;
|
|
|
|
// Go through each of the subkeys
|
|
while (NT_SUCCESS(Status)) {
|
|
|
|
Status = NtEnumerateKey(hKeyRoot,
|
|
ulcnt,
|
|
KeyBasicInformation,
|
|
pKeyInfo,
|
|
ulInfoSize,
|
|
&ultemp);
|
|
|
|
// Delete sub keys
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
// Null terminate the key name
|
|
pKeyInfo->Name[pKeyInfo->NameLength/sizeof(WCHAR)] = L'\0';
|
|
|
|
// Create a unicode string for the key name
|
|
RtlInitUnicodeString(&UniPath, pKeyInfo->Name);
|
|
|
|
InitializeObjectAttributes(&ObjAttr,
|
|
&UniPath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
hKeyRoot,
|
|
NULL);
|
|
|
|
// Open up the child key
|
|
Status2 = NtOpenKey(&hKey,
|
|
MAXIMUM_ALLOWED,
|
|
&ObjAttr);
|
|
|
|
if ( NT_SUCCESS(Status2) ) {
|
|
Status2 = CtxDeleteKeyTree ( hKey, pKeyInfo, ulInfoSize );
|
|
NtClose(hKey);
|
|
// If the key was not successfully deleted, we need
|
|
// to increment the enumerate index to guarantee
|
|
// that the alogorithm will complete.
|
|
if ( !NT_SUCCESS(Status2) ) {
|
|
++ulcnt;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we deleted all the sub-keys delete the curent key
|
|
if ( !ulcnt ) {
|
|
Status = NtDeleteKey(hKeyRoot);
|
|
}
|
|
else {
|
|
Status = STATUS_CANNOT_DELETE;
|
|
}
|
|
return ( Status );
|
|
}
|
|
|
|
|
|
PSECURITY_DESCRIPTOR
|
|
GetSecurityInfo(LPTSTR lpFilePath)
|
|
{
|
|
int SizeReq = 0;
|
|
PSECURITY_DESCRIPTOR pSecDesc = NULL;
|
|
|
|
GetFileSecurity(lpFilePath, DACL_SECURITY_INFORMATION, pSecDesc, 0,
|
|
&SizeReq);
|
|
if ( !SizeReq ) {
|
|
return (NULL);
|
|
}
|
|
|
|
pSecDesc = LocalAlloc(LPTR, SizeReq);
|
|
if ( pSecDesc ) {
|
|
if ( !GetFileSecurity(lpFilePath, DACL_SECURITY_INFORMATION, pSecDesc,
|
|
SizeReq, &SizeReq) ) {
|
|
LocalFree(pSecDesc);
|
|
pSecDesc = NULL;
|
|
}
|
|
}
|
|
return (pSecDesc);
|
|
}
|
|
|
|
void
|
|
FreeSecurityInfo(PSECURITY_DESCRIPTOR pSecDesc)
|
|
{
|
|
LocalFree(pSecDesc);
|
|
}
|
|
|