Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F103064649
libwinutils.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Wed, Feb 26, 23:56
Size
46 KB
Mime Type
text/x-c
Expires
Fri, Feb 28, 23:56 (2 d)
Engine
blob
Format
Raw Data
Handle
24467688
Attached To
R3704 elastic-yarn
libwinutils.c
View Options
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
#pragma comment(lib, "authz.lib")
#pragma comment(lib, "netapi32.lib")
#include "winutils.h"
#include <authz.h>
#include <sddl.h>
/*
* The array of 12 months' three-letter abbreviations
*/
const LPCWSTR MONTHS[] = { L"Jan", L"Feb", L"Mar", L"Apr", L"May", L"Jun",
L"Jul", L"Aug", L"Sep", L"Oct", L"Nov", L"Dec" };
/*
* The WindowsAclMask and WinMasks contain the definitions used to establish
* the mapping between Unix and Windows.
* We set up the mapping with the following rules.
* 1. Everyone will have WIN_ALL permissions;
* 2. Owner will always have WIN_OWNER_SE permissions in addition;
* 2. When Unix read/write/excute permission is set on the file, the
* corresponding Windows allow ACE will be added to the file.
* More details and explaination can be found in the following white paper:
* http://technet.microsoft.com/en-us/library/bb463216.aspx
*/
const ACCESS_MASK WinMasks[WIN_MASKS_TOTAL] =
{
/* WIN_READ */
FILE_READ_DATA,
/* WIN_WRITE */
FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_APPEND_DATA | FILE_WRITE_EA |
FILE_DELETE_CHILD,
/* WIN_EXECUTE */
FILE_EXECUTE,
/* WIN_OWNER_SE */
DELETE | WRITE_DAC | WRITE_OWNER | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES,
/* WIN_ALL */
READ_CONTROL | FILE_READ_EA | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
};
//----------------------------------------------------------------------------
// Function: GetFileInformationByName
//
// Description:
// To retrieve the by handle file information given the file name
//
// Returns:
// ERROR_SUCCESS: on success
// error code: otherwise
//
// Notes:
// If followLink parameter is set to TRUE, we will follow the symbolic link
// or junction point to get the target file information. Otherwise, the
// information for the symbolic link or junction point is retrieved.
//
DWORD GetFileInformationByName(
__in LPCWSTR pathName,
__in BOOL followLink,
__out LPBY_HANDLE_FILE_INFORMATION lpFileInformation)
{
HANDLE fileHandle = INVALID_HANDLE_VALUE;
BOOL isSymlink = FALSE;
BOOL isJunction = FALSE;
DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS;
DWORD dwErrorCode = ERROR_SUCCESS;
assert(lpFileInformation != NULL);
if (!followLink)
{
if ((dwErrorCode = SymbolicLinkCheck(pathName, &isSymlink)) != ERROR_SUCCESS)
return dwErrorCode;
if ((dwErrorCode = JunctionPointCheck(pathName, &isJunction)) != ERROR_SUCCESS)
return dwErrorCode;
if (isSymlink || isJunction)
dwFlagsAndAttributes |= FILE_FLAG_OPEN_REPARSE_POINT;
}
fileHandle = CreateFileW(
pathName,
FILE_READ_ATTRIBUTES,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
dwFlagsAndAttributes,
NULL);
if (fileHandle == INVALID_HANDLE_VALUE)
{
dwErrorCode = GetLastError();
return dwErrorCode;
}
if (!GetFileInformationByHandle(fileHandle, lpFileInformation))
{
dwErrorCode = GetLastError();
CloseHandle(fileHandle);
return dwErrorCode;
}
CloseHandle(fileHandle);
return dwErrorCode;
}
//----------------------------------------------------------------------------
// Function: IsLongWindowsPath
//
// Description:
// Checks if the path is longer than MAX_PATH in which case it needs to be
// prepended with \\?\ for Windows OS to understand it.
//
// Returns:
// TRUE long path
// FALSE otherwise
static BOOL IsLongWindowsPath(__in PCWSTR path)
{
return (wcslen(path) + 1) > MAX_PATH;
}
//----------------------------------------------------------------------------
// Function: IsPrefixedAlready
//
// Description:
// Checks if the given path is already prepended with \\?\.
//
// Returns:
// TRUE if yes
// FALSE otherwise
static BOOL IsPrefixedAlready(__in PCWSTR path)
{
static const PCWSTR LongPathPrefix = L"\\\\?\\";
size_t Prefixlen = wcslen(LongPathPrefix);
size_t i = 0;
if (path == NULL || wcslen(path) < Prefixlen)
{
return FALSE;
}
for (i = 0; i < Prefixlen; ++i)
{
if (path[i] != LongPathPrefix[i])
{
return FALSE;
}
}
return TRUE;
}
//----------------------------------------------------------------------------
// Function: ConvertToLongPath
//
// Description:
// Prepends the path with the \\?\ prefix if the path is longer than MAX_PATH.
// On success, newPath should be freed with LocalFree(). Given that relative
// paths cannot be longer than MAX_PATH, we will never prepend the prefix
// to relative paths.
//
// Returns:
// ERROR_SUCCESS on success
// error code on failure
DWORD ConvertToLongPath(__in PCWSTR path, __deref_out PWSTR *newPath)
{
DWORD dwErrorCode = ERROR_SUCCESS;
static const PCWSTR LongPathPrefix = L"\\\\?\\";
BOOL bAppendPrefix = IsLongWindowsPath(path) && !IsPrefixedAlready(path);
HRESULT hr = S_OK;
size_t newPathLen = wcslen(path) + (bAppendPrefix ? wcslen(LongPathPrefix) : 0);
// Allocate the buffer for the output path (+1 for terminating NULL char)
//
PWSTR newPathValue = (PWSTR)LocalAlloc(LPTR, (newPathLen + 1) * sizeof(WCHAR));
if (newPathValue == NULL)
{
dwErrorCode = GetLastError();
goto ConvertToLongPathExit;
}
if (bAppendPrefix)
{
// Append the prefix to the path
//
hr = StringCchPrintfW(newPathValue, newPathLen + 1, L"%s%s",
LongPathPrefix, path);
if (FAILED(hr))
{
dwErrorCode = HRESULT_CODE(hr);
goto ConvertToLongPathExit;
}
}
else
{
// Just copy the original value into the output path. In this scenario
// we are doing extra buffer copy. We decided to trade code simplicity
// on the call site for small performance impact (extra allocation and
// buffer copy). As paths are short, the impact is generally small.
//
hr = StringCchPrintfW(newPathValue, newPathLen + 1, L"%s", path);
if (FAILED(hr))
{
dwErrorCode = HRESULT_CODE(hr);
goto ConvertToLongPathExit;
}
}
*newPath = newPathValue;
ConvertToLongPathExit:
if (dwErrorCode != ERROR_SUCCESS)
{
LocalFree(newPathValue);
}
return dwErrorCode;
}
//----------------------------------------------------------------------------
// Function: IsDirFileInfo
//
// Description:
// Test if the given file information is a directory
//
// Returns:
// TRUE if it is a directory
// FALSE otherwise
//
// Notes:
//
BOOL IsDirFileInfo(const BY_HANDLE_FILE_INFORMATION *fileInformation)
{
if ((fileInformation->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
== FILE_ATTRIBUTE_DIRECTORY)
return TRUE;
return FALSE;
}
//----------------------------------------------------------------------------
// Function: CheckFileAttributes
//
// Description:
// Check if the given file has all the given attribute(s)
//
// Returns:
// ERROR_SUCCESS on success
// error code otherwise
//
// Notes:
//
static DWORD FileAttributesCheck(
__in LPCWSTR path, __in DWORD attr, __out PBOOL res)
{
DWORD attrs = INVALID_FILE_ATTRIBUTES;
*res = FALSE;
if ((attrs = GetFileAttributes(path)) != INVALID_FILE_ATTRIBUTES)
*res = ((attrs & attr) == attr);
else
return GetLastError();
return ERROR_SUCCESS;
}
//----------------------------------------------------------------------------
// Function: IsDirectory
//
// Description:
// Check if the given file is a directory
//
// Returns:
// ERROR_SUCCESS on success
// error code otherwise
//
// Notes:
//
DWORD DirectoryCheck(__in LPCWSTR pathName, __out PBOOL res)
{
return FileAttributesCheck(pathName, FILE_ATTRIBUTE_DIRECTORY, res);
}
//----------------------------------------------------------------------------
// Function: IsReparsePoint
//
// Description:
// Check if the given file is a reparse point
//
// Returns:
// ERROR_SUCCESS on success
// error code otherwise
//
// Notes:
//
static DWORD ReparsePointCheck(__in LPCWSTR pathName, __out PBOOL res)
{
return FileAttributesCheck(pathName, FILE_ATTRIBUTE_REPARSE_POINT, res);
}
//----------------------------------------------------------------------------
// Function: CheckReparseTag
//
// Description:
// Check if the given file is a reparse point of the given tag.
//
// Returns:
// ERROR_SUCCESS on success
// error code otherwise
//
// Notes:
//
static DWORD ReparseTagCheck(__in LPCWSTR path, __in DWORD tag, __out PBOOL res)
{
BOOL isReparsePoint = FALSE;
HANDLE hFind = INVALID_HANDLE_VALUE;
WIN32_FIND_DATA findData;
DWORD dwRtnCode;
if ((dwRtnCode = ReparsePointCheck(path, &isReparsePoint)) != ERROR_SUCCESS)
return dwRtnCode;
if (!isReparsePoint)
{
*res = FALSE;
}
else
{
if ((hFind = FindFirstFile(path, &findData)) == INVALID_HANDLE_VALUE)
{
return GetLastError();
}
else
{
*res = (findData.dwReserved0 == tag);
FindClose(hFind);
}
}
return ERROR_SUCCESS;
}
//----------------------------------------------------------------------------
// Function: IsSymbolicLink
//
// Description:
// Check if the given file is a symbolic link.
//
// Returns:
// ERROR_SUCCESS on success
// error code otherwise
//
// Notes:
//
DWORD SymbolicLinkCheck(__in LPCWSTR pathName, __out PBOOL res)
{
return ReparseTagCheck(pathName, IO_REPARSE_TAG_SYMLINK, res);
}
//----------------------------------------------------------------------------
// Function: IsJunctionPoint
//
// Description:
// Check if the given file is a junction point.
//
// Returns:
// ERROR_SUCCESS on success
// error code otherwise
//
// Notes:
//
DWORD JunctionPointCheck(__in LPCWSTR pathName, __out PBOOL res)
{
return ReparseTagCheck(pathName, IO_REPARSE_TAG_MOUNT_POINT, res);
}
//----------------------------------------------------------------------------
// Function: GetSidFromAcctNameW
//
// Description:
// To retrieve the SID for a user account
//
// Returns:
// ERROR_SUCCESS: on success
// Other error code: otherwise
//
// Notes:
// Caller needs to destroy the memory of Sid by calling LocalFree()
//
DWORD GetSidFromAcctNameW(__in PCWSTR acctName, __out PSID *ppSid)
{
DWORD dwSidSize = 0;
DWORD cchDomainName = 0;
DWORD dwDomainNameSize = 0;
LPWSTR domainName = NULL;
SID_NAME_USE eSidType;
DWORD dwErrorCode = ERROR_SUCCESS;
// Validate the input parameters.
//
assert (acctName != NULL && ppSid != NULL);
// Empty name is invalid. However, LookupAccountName() function will return a
// false Sid, i.e. Sid for 'BUILDIN', for an empty name instead failing. We
// report the error before calling LookupAccountName() function for this
// special case. The error code returned here is the same as the last error
// code set by LookupAccountName() function for an invalid name.
//
if (wcslen(acctName) == 0)
return ERROR_NONE_MAPPED;
// First pass to retrieve the buffer size.
//
LookupAccountName(
NULL, // Computer name. NULL for the local computer
acctName,
NULL, // pSid. NULL to retrieve buffer size
&dwSidSize,
NULL, // Domain Name. NULL to retrieve buffer size
&cchDomainName,
&eSidType);
if((dwErrorCode = GetLastError()) != ERROR_INSUFFICIENT_BUFFER)
{
return dwErrorCode;
}
else
{
// Reallocate memory for the buffers.
//
*ppSid = (PSID)LocalAlloc(LPTR, dwSidSize);
if (*ppSid == NULL)
{
return GetLastError();
}
dwDomainNameSize = (cchDomainName + 1) * sizeof(wchar_t);
domainName = (LPWSTR)LocalAlloc(LPTR, dwDomainNameSize);
if (domainName == NULL)
{
return GetLastError();
}
// Second pass to retrieve the SID and domain name.
//
if (!LookupAccountNameW(
NULL, // Computer name. NULL for the local computer
acctName,
*ppSid,
&dwSidSize,
domainName,
&cchDomainName,
&eSidType))
{
LocalFree(domainName);
return GetLastError();
}
assert(IsValidSid(*ppSid));
}
LocalFree(domainName);
return ERROR_SUCCESS;
}
//----------------------------------------------------------------------------
// Function: GetUnixAccessMask
//
// Description:
// Compute the 3 bit Unix mask for the owner, group, or, others
//
// Returns:
// The 3 bit Unix mask in INT
//
// Notes:
//
static INT GetUnixAccessMask(ACCESS_MASK Mask)
{
static const INT exe = 0x0001;
static const INT write = 0x0002;
static const INT read = 0x0004;
INT mask = 0;
if ((Mask & WinMasks[WIN_READ]) == WinMasks[WIN_READ])
mask |= read;
if ((Mask & WinMasks[WIN_WRITE]) == WinMasks[WIN_WRITE])
mask |= write;
if ((Mask & WinMasks[WIN_EXECUTE]) == WinMasks[WIN_EXECUTE])
mask |= exe;
return mask;
}
//----------------------------------------------------------------------------
// Function: GetAccess
//
// Description:
// Get Windows acces mask by AuthZ methods
//
// Returns:
// ERROR_SUCCESS: on success
//
// Notes:
//
static DWORD GetAccess(AUTHZ_CLIENT_CONTEXT_HANDLE hAuthzClient,
PSECURITY_DESCRIPTOR psd, PACCESS_MASK pAccessRights)
{
AUTHZ_ACCESS_REQUEST AccessRequest = {0};
AUTHZ_ACCESS_REPLY AccessReply = {0};
BYTE Buffer[1024];
assert (pAccessRights != NULL);
// Do AccessCheck
AccessRequest.DesiredAccess = MAXIMUM_ALLOWED;
AccessRequest.PrincipalSelfSid = NULL;
AccessRequest.ObjectTypeList = NULL;
AccessRequest.ObjectTypeListLength = 0;
AccessRequest.OptionalArguments = NULL;
RtlZeroMemory(Buffer, sizeof(Buffer));
AccessReply.ResultListLength = 1;
AccessReply.GrantedAccessMask = (PACCESS_MASK) (Buffer);
AccessReply.Error = (PDWORD) (Buffer + sizeof(ACCESS_MASK));
if (!AuthzAccessCheck(0,
hAuthzClient,
&AccessRequest,
NULL,
psd,
NULL,
0,
&AccessReply,
NULL))
{
return GetLastError();
}
*pAccessRights = (*(const ACCESS_MASK *)(AccessReply.GrantedAccessMask));
return ERROR_SUCCESS;
}
//----------------------------------------------------------------------------
// Function: GetEffectiveRightsForSid
//
// Description:
// Get Windows acces mask by AuthZ methods
//
// Returns:
// ERROR_SUCCESS: on success
//
// Notes:
// We run into problems for local user accounts when using the method
// GetEffectiveRightsFromAcl(). We resort to using AuthZ methods as
// an alternative way suggested on MSDN:
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa446637.aspx
//
static DWORD GetEffectiveRightsForSid(PSECURITY_DESCRIPTOR psd,
PSID pSid,
PACCESS_MASK pAccessRights)
{
AUTHZ_RESOURCE_MANAGER_HANDLE hManager = NULL;
LUID unusedId = { 0 };
AUTHZ_CLIENT_CONTEXT_HANDLE hAuthzClientContext = NULL;
DWORD dwRtnCode = ERROR_SUCCESS;
DWORD ret = ERROR_SUCCESS;
assert (pAccessRights != NULL);
if (!AuthzInitializeResourceManager(AUTHZ_RM_FLAG_NO_AUDIT,
NULL, NULL, NULL, NULL, &hManager))
{
return GetLastError();
}
// Pass AUTHZ_SKIP_TOKEN_GROUPS to the function to avoid querying user group
// information for access check. This allows us to model POSIX permissions
// on Windows, where a user can have less permissions than a group it
// belongs to.
if(!AuthzInitializeContextFromSid(AUTHZ_SKIP_TOKEN_GROUPS,
pSid, hManager, NULL, unusedId, NULL, &hAuthzClientContext))
{
ret = GetLastError();
goto GetEffectiveRightsForSidEnd;
}
if ((dwRtnCode = GetAccess(hAuthzClientContext, psd, pAccessRights))
!= ERROR_SUCCESS)
{
ret = dwRtnCode;
goto GetEffectiveRightsForSidEnd;
}
GetEffectiveRightsForSidEnd:
if (hManager != NULL)
{
(void)AuthzFreeResourceManager(hManager);
}
if (hAuthzClientContext != NULL)
{
(void)AuthzFreeContext(hAuthzClientContext);
}
return ret;
}
//----------------------------------------------------------------------------
// Function: CheckAccessForCurrentUser
//
// Description:
// Checks if the current process has the requested access rights on the given
// path. Based on the following MSDN article:
// http://msdn.microsoft.com/en-us/library/windows/desktop/ff394771(v=vs.85).aspx
//
// Returns:
// ERROR_SUCCESS: on success
//
DWORD CheckAccessForCurrentUser(
__in PCWSTR pathName,
__in ACCESS_MASK requestedAccess,
__out BOOL *allowed)
{
DWORD dwRtnCode = ERROR_SUCCESS;
LPWSTR longPathName = NULL;
HANDLE hProcessToken = NULL;
PSECURITY_DESCRIPTOR pSd = NULL;
AUTHZ_RESOURCE_MANAGER_HANDLE hManager = NULL;
AUTHZ_CLIENT_CONTEXT_HANDLE hAuthzClientContext = NULL;
LUID Luid = {0, 0};
ACCESS_MASK currentUserAccessRights = 0;
// Prepend the long path prefix if needed
dwRtnCode = ConvertToLongPath(pathName, &longPathName);
if (dwRtnCode != ERROR_SUCCESS)
{
goto CheckAccessEnd;
}
// Get SD of the given path. OWNER and DACL security info must be
// requested, otherwise, AuthzAccessCheck fails with invalid parameter
// error.
dwRtnCode = GetNamedSecurityInfo(longPathName, SE_FILE_OBJECT,
OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION,
NULL, NULL, NULL, NULL, &pSd);
if (dwRtnCode != ERROR_SUCCESS)
{
goto CheckAccessEnd;
}
// Get current process token
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hProcessToken))
{
dwRtnCode = GetLastError();
goto CheckAccessEnd;
}
if (!AuthzInitializeResourceManager(AUTHZ_RM_FLAG_NO_AUDIT, NULL, NULL,
NULL, NULL, &hManager))
{
dwRtnCode = GetLastError();
goto CheckAccessEnd;
}
if(!AuthzInitializeContextFromToken(0, hProcessToken, hManager, NULL,
Luid, NULL, &hAuthzClientContext))
{
dwRtnCode = GetLastError();
goto CheckAccessEnd;
}
dwRtnCode = GetAccess(hAuthzClientContext, pSd, ¤tUserAccessRights);
if (dwRtnCode != ERROR_SUCCESS)
{
goto CheckAccessEnd;
}
*allowed = ((currentUserAccessRights & requestedAccess) == requestedAccess);
CheckAccessEnd:
LocalFree(longPathName);
LocalFree(pSd);
if (hProcessToken != NULL)
{
CloseHandle(hProcessToken);
}
if (hManager != NULL)
{
(void)AuthzFreeResourceManager(hManager);
}
if (hAuthzClientContext != NULL)
{
(void)AuthzFreeContext(hAuthzClientContext);
}
return dwRtnCode;
}
//----------------------------------------------------------------------------
// Function: FindFileOwnerAndPermissionByHandle
//
// Description:
// Find the owner, primary group and permissions of a file object given the
// the file object handle. The function will always follow symbolic links.
//
// Returns:
// ERROR_SUCCESS: on success
// Error code otherwise
//
// Notes:
// - Caller needs to destroy the memeory of owner and group names by calling
// LocalFree() function.
//
// - If the user or group name does not exist, the user or group SID will be
// returned as the name.
//
DWORD FindFileOwnerAndPermissionByHandle(
__in HANDLE fileHandle,
__out_opt LPWSTR *pOwnerName,
__out_opt LPWSTR *pGroupName,
__out_opt PINT pMask)
{
LPWSTR path = NULL;
DWORD cchPathLen = 0;
DWORD dwRtnCode = ERROR_SUCCESS;
DWORD ret = ERROR_SUCCESS;
dwRtnCode = GetFinalPathNameByHandle(fileHandle, path, cchPathLen, 0);
if (dwRtnCode == 0)
{
ret = GetLastError();
goto FindFileOwnerAndPermissionByHandleEnd;
}
cchPathLen = dwRtnCode;
path = (LPWSTR) LocalAlloc(LPTR, cchPathLen * sizeof(WCHAR));
if (path == NULL)
{
ret = GetLastError();
goto FindFileOwnerAndPermissionByHandleEnd;
}
dwRtnCode = GetFinalPathNameByHandle(fileHandle, path, cchPathLen, 0);
if (dwRtnCode != cchPathLen - 1)
{
ret = GetLastError();
goto FindFileOwnerAndPermissionByHandleEnd;
}
dwRtnCode = FindFileOwnerAndPermission(path, TRUE, pOwnerName, pGroupName, pMask);
if (dwRtnCode != ERROR_SUCCESS)
{
ret = dwRtnCode;
goto FindFileOwnerAndPermissionByHandleEnd;
}
FindFileOwnerAndPermissionByHandleEnd:
LocalFree(path);
return ret;
}
//----------------------------------------------------------------------------
// Function: FindFileOwnerAndPermission
//
// Description:
// Find the owner, primary group and permissions of a file object
//
// Returns:
// ERROR_SUCCESS: on success
// Error code otherwise
//
// Notes:
// - Caller needs to destroy the memeory of owner and group names by calling
// LocalFree() function.
//
// - If the user or group name does not exist, the user or group SID will be
// returned as the name.
//
DWORD FindFileOwnerAndPermission(
__in LPCWSTR pathName,
__in BOOL followLink,
__out_opt LPWSTR *pOwnerName,
__out_opt LPWSTR *pGroupName,
__out_opt PINT pMask)
{
DWORD dwRtnCode = 0;
PSECURITY_DESCRIPTOR pSd = NULL;
PSID psidOwner = NULL;
PSID psidGroup = NULL;
PSID psidEveryone = NULL;
DWORD cbSid = SECURITY_MAX_SID_SIZE;
PACL pDacl = NULL;
BOOL isSymlink;
BY_HANDLE_FILE_INFORMATION fileInformation;
ACCESS_MASK ownerAccessRights = 0;
ACCESS_MASK groupAccessRights = 0;
ACCESS_MASK worldAccessRights = 0;
DWORD ret = ERROR_SUCCESS;
// Do nothing if the caller request nothing
//
if (pOwnerName == NULL && pGroupName == NULL && pMask == NULL)
{
return ret;
}
dwRtnCode = GetNamedSecurityInfo(pathName, SE_FILE_OBJECT,
OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION,
&psidOwner, &psidGroup, &pDacl, NULL, &pSd);
if (dwRtnCode != ERROR_SUCCESS)
{
ret = dwRtnCode;
goto FindFileOwnerAndPermissionEnd;
}
if (pOwnerName != NULL)
{
dwRtnCode = GetAccntNameFromSid(psidOwner, pOwnerName);
if (dwRtnCode == ERROR_NONE_MAPPED)
{
if (!ConvertSidToStringSid(psidOwner, pOwnerName))
{
ret = GetLastError();
goto FindFileOwnerAndPermissionEnd;
}
}
else if (dwRtnCode != ERROR_SUCCESS)
{
ret = dwRtnCode;
goto FindFileOwnerAndPermissionEnd;
}
}
if (pGroupName != NULL)
{
dwRtnCode = GetAccntNameFromSid(psidGroup, pGroupName);
if (dwRtnCode == ERROR_NONE_MAPPED)
{
if (!ConvertSidToStringSid(psidGroup, pGroupName))
{
ret = GetLastError();
goto FindFileOwnerAndPermissionEnd;
}
}
else if (dwRtnCode != ERROR_SUCCESS)
{
ret = dwRtnCode;
goto FindFileOwnerAndPermissionEnd;
}
}
if (pMask == NULL) goto FindFileOwnerAndPermissionEnd;
dwRtnCode = GetFileInformationByName(pathName,
followLink, &fileInformation);
if (dwRtnCode != ERROR_SUCCESS)
{
ret = dwRtnCode;
goto FindFileOwnerAndPermissionEnd;
}
dwRtnCode = SymbolicLinkCheck(pathName, &isSymlink);
if (dwRtnCode != ERROR_SUCCESS)
{
ret = dwRtnCode;
goto FindFileOwnerAndPermissionEnd;
}
if (isSymlink)
*pMask |= UX_SYMLINK;
else if (IsDirFileInfo(&fileInformation))
*pMask |= UX_DIRECTORY;
else
*pMask |= UX_REGULAR;
if ((dwRtnCode = GetEffectiveRightsForSid(pSd,
psidOwner, &ownerAccessRights)) != ERROR_SUCCESS)
{
ret = dwRtnCode;
goto FindFileOwnerAndPermissionEnd;
}
if ((dwRtnCode = GetEffectiveRightsForSid(pSd,
psidGroup, &groupAccessRights)) != ERROR_SUCCESS)
{
ret = dwRtnCode;
goto FindFileOwnerAndPermissionEnd;
}
if ((psidEveryone = LocalAlloc(LPTR, cbSid)) == NULL)
{
ret = GetLastError();
goto FindFileOwnerAndPermissionEnd;
}
if (!CreateWellKnownSid(WinWorldSid, NULL, psidEveryone, &cbSid))
{
ret = GetLastError();
goto FindFileOwnerAndPermissionEnd;
}
if ((dwRtnCode = GetEffectiveRightsForSid(pSd,
psidEveryone, &worldAccessRights)) != ERROR_SUCCESS)
{
ret = dwRtnCode;
goto FindFileOwnerAndPermissionEnd;
}
*pMask |= GetUnixAccessMask(ownerAccessRights) << 6;
*pMask |= GetUnixAccessMask(groupAccessRights) << 3;
*pMask |= GetUnixAccessMask(worldAccessRights);
FindFileOwnerAndPermissionEnd:
LocalFree(psidEveryone);
LocalFree(pSd);
return ret;
}
//----------------------------------------------------------------------------
// Function: GetWindowsAccessMask
//
// Description:
// Get the Windows AccessMask for user, group and everyone based on the Unix
// permission mask
//
// Returns:
// none
//
// Notes:
// none
//
static void GetWindowsAccessMask(INT unixMask,
ACCESS_MASK *userAllow,
ACCESS_MASK *userDeny,
ACCESS_MASK *groupAllow,
ACCESS_MASK *groupDeny,
ACCESS_MASK *otherAllow)
{
assert (userAllow != NULL && userDeny != NULL &&
groupAllow != NULL && groupDeny != NULL &&
otherAllow != NULL);
*userAllow = WinMasks[WIN_ALL] | WinMasks[WIN_OWNER_SE];
if ((unixMask & UX_U_READ) == UX_U_READ)
*userAllow |= WinMasks[WIN_READ];
if ((unixMask & UX_U_WRITE) == UX_U_WRITE)
*userAllow |= WinMasks[WIN_WRITE];
if ((unixMask & UX_U_EXECUTE) == UX_U_EXECUTE)
*userAllow |= WinMasks[WIN_EXECUTE];
*userDeny = 0;
if ((unixMask & UX_U_READ) != UX_U_READ &&
((unixMask & UX_G_READ) == UX_G_READ ||
(unixMask & UX_O_READ) == UX_O_READ))
*userDeny |= WinMasks[WIN_READ];
if ((unixMask & UX_U_WRITE) != UX_U_WRITE &&
((unixMask & UX_G_WRITE) == UX_G_WRITE ||
(unixMask & UX_O_WRITE) == UX_O_WRITE))
*userDeny |= WinMasks[WIN_WRITE];
if ((unixMask & UX_U_EXECUTE) != UX_U_EXECUTE &&
((unixMask & UX_G_EXECUTE) == UX_G_EXECUTE ||
(unixMask & UX_O_EXECUTE) == UX_O_EXECUTE))
*userDeny |= WinMasks[WIN_EXECUTE];
*groupAllow = WinMasks[WIN_ALL];
if ((unixMask & UX_G_READ) == UX_G_READ)
*groupAllow |= FILE_GENERIC_READ;
if ((unixMask & UX_G_WRITE) == UX_G_WRITE)
*groupAllow |= WinMasks[WIN_WRITE];
if ((unixMask & UX_G_EXECUTE) == UX_G_EXECUTE)
*groupAllow |= WinMasks[WIN_EXECUTE];
*groupDeny = 0;
if ((unixMask & UX_G_READ) != UX_G_READ &&
(unixMask & UX_O_READ) == UX_O_READ)
*groupDeny |= WinMasks[WIN_READ];
if ((unixMask & UX_G_WRITE) != UX_G_WRITE &&
(unixMask & UX_O_WRITE) == UX_O_WRITE)
*groupDeny |= WinMasks[WIN_WRITE];
if ((unixMask & UX_G_EXECUTE) != UX_G_EXECUTE &&
(unixMask & UX_O_EXECUTE) == UX_O_EXECUTE)
*groupDeny |= WinMasks[WIN_EXECUTE];
*otherAllow = WinMasks[WIN_ALL];
if ((unixMask & UX_O_READ) == UX_O_READ)
*otherAllow |= WinMasks[WIN_READ];
if ((unixMask & UX_O_WRITE) == UX_O_WRITE)
*otherAllow |= WinMasks[WIN_WRITE];
if ((unixMask & UX_O_EXECUTE) == UX_O_EXECUTE)
*otherAllow |= WinMasks[WIN_EXECUTE];
}
//----------------------------------------------------------------------------
// Function: GetWindowsDACLs
//
// Description:
// Get the Windows DACs based the Unix access mask
//
// Returns:
// ERROR_SUCCESS: on success
// Error code: otherwise
//
// Notes:
// - Administrators and SYSTEM are always given full permission to the file,
// unless Administrators or SYSTEM itself is the file owner and the user
// explictly set the permission to something else. For example, file 'foo'
// belongs to Administrators, 'chmod 000' on the file will not directly
// assign Administrators full permission on the file.
// - Only full permission for Administrators and SYSTEM are inheritable.
// - CREATOR OWNER is always given full permission and the permission is
// inheritable, more specifically OBJECT_INHERIT_ACE, CONTAINER_INHERIT_ACE
// flags are set. The reason is to give the creator of child file full
// permission, i.e., the child file will have permission mode 700 for
// a user other than Administrator or SYSTEM.
//
static DWORD GetWindowsDACLs(__in INT unixMask,
__in PSID pOwnerSid, __in PSID pGroupSid, __out PACL *ppNewDACL)
{
DWORD winUserAccessDenyMask;
DWORD winUserAccessAllowMask;
DWORD winGroupAccessDenyMask;
DWORD winGroupAccessAllowMask;
DWORD winOtherAccessAllowMask;
PSID pEveryoneSid = NULL;
DWORD cbEveryoneSidSize = SECURITY_MAX_SID_SIZE;
PSID pSystemSid = NULL;
DWORD cbSystemSidSize = SECURITY_MAX_SID_SIZE;
BOOL bAddSystemAcls = FALSE;
PSID pAdministratorsSid = NULL;
DWORD cbAdministratorsSidSize = SECURITY_MAX_SID_SIZE;
BOOL bAddAdministratorsAcls = FALSE;
PSID pCreatorOwnerSid = NULL;
DWORD cbCreatorOwnerSidSize = SECURITY_MAX_SID_SIZE;
PACL pNewDACL = NULL;
DWORD dwNewAclSize = 0;
DWORD ret = ERROR_SUCCESS;
GetWindowsAccessMask(unixMask,
&winUserAccessAllowMask, &winUserAccessDenyMask,
&winGroupAccessAllowMask, &winGroupAccessDenyMask,
&winOtherAccessAllowMask);
// Create a well-known SID for the Everyone group
//
if ((pEveryoneSid = LocalAlloc(LPTR, cbEveryoneSidSize)) == NULL)
{
ret = GetLastError();
goto GetWindowsDACLsEnd;
}
if (!CreateWellKnownSid(WinWorldSid, NULL, pEveryoneSid, &cbEveryoneSidSize))
{
ret = GetLastError();
goto GetWindowsDACLsEnd;
}
// Create a well-known SID for the Administrators group
//
if ((pAdministratorsSid = LocalAlloc(LPTR, cbAdministratorsSidSize)) == NULL)
{
ret = GetLastError();
goto GetWindowsDACLsEnd;
}
if (!CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL,
pAdministratorsSid, &cbAdministratorsSidSize))
{
ret = GetLastError();
goto GetWindowsDACLsEnd;
}
if (!EqualSid(pAdministratorsSid, pOwnerSid)
&& !EqualSid(pAdministratorsSid, pGroupSid))
bAddAdministratorsAcls = TRUE;
// Create a well-known SID for the SYSTEM
//
if ((pSystemSid = LocalAlloc(LPTR, cbSystemSidSize)) == NULL)
{
ret = GetLastError();
goto GetWindowsDACLsEnd;
}
if (!CreateWellKnownSid(WinLocalSystemSid, NULL,
pSystemSid, &cbSystemSidSize))
{
ret = GetLastError();
goto GetWindowsDACLsEnd;
}
if (!EqualSid(pSystemSid, pOwnerSid)
&& !EqualSid(pSystemSid, pGroupSid))
bAddSystemAcls = TRUE;
// Create a well-known SID for the Creator Owner
//
if ((pCreatorOwnerSid = LocalAlloc(LPTR, cbCreatorOwnerSidSize)) == NULL)
{
ret = GetLastError();
goto GetWindowsDACLsEnd;
}
if (!CreateWellKnownSid(WinCreatorOwnerSid, NULL,
pCreatorOwnerSid, &cbCreatorOwnerSidSize))
{
ret = GetLastError();
goto GetWindowsDACLsEnd;
}
// Create the new DACL
//
dwNewAclSize = sizeof(ACL);
dwNewAclSize += sizeof(ACCESS_ALLOWED_ACE) +
GetLengthSid(pOwnerSid) - sizeof(DWORD);
if (winUserAccessDenyMask)
dwNewAclSize += sizeof(ACCESS_DENIED_ACE) +
GetLengthSid(pOwnerSid) - sizeof(DWORD);
dwNewAclSize += sizeof(ACCESS_ALLOWED_ACE) +
GetLengthSid(pGroupSid) - sizeof(DWORD);
if (winGroupAccessDenyMask)
dwNewAclSize += sizeof(ACCESS_DENIED_ACE) +
GetLengthSid(pGroupSid) - sizeof(DWORD);
dwNewAclSize += sizeof(ACCESS_ALLOWED_ACE) +
GetLengthSid(pEveryoneSid) - sizeof(DWORD);
if (bAddSystemAcls)
{
dwNewAclSize += sizeof(ACCESS_ALLOWED_ACE) +
cbSystemSidSize - sizeof(DWORD);
}
if (bAddAdministratorsAcls)
{
dwNewAclSize += sizeof(ACCESS_ALLOWED_ACE) +
cbAdministratorsSidSize - sizeof(DWORD);
}
dwNewAclSize += sizeof(ACCESS_ALLOWED_ACE) +
cbCreatorOwnerSidSize - sizeof(DWORD);
pNewDACL = (PACL)LocalAlloc(LPTR, dwNewAclSize);
if (pNewDACL == NULL)
{
ret = GetLastError();
goto GetWindowsDACLsEnd;
}
if (!InitializeAcl(pNewDACL, dwNewAclSize, ACL_REVISION))
{
ret = GetLastError();
goto GetWindowsDACLsEnd;
}
if (!AddAccessAllowedAceEx(pNewDACL, ACL_REVISION,
CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
GENERIC_ALL, pCreatorOwnerSid))
{
ret = GetLastError();
goto GetWindowsDACLsEnd;
}
if (bAddSystemAcls &&
!AddAccessAllowedAceEx(pNewDACL, ACL_REVISION,
CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
GENERIC_ALL, pSystemSid))
{
ret = GetLastError();
goto GetWindowsDACLsEnd;
}
if (bAddAdministratorsAcls &&
!AddAccessAllowedAceEx(pNewDACL, ACL_REVISION,
CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
GENERIC_ALL, pAdministratorsSid))
{
ret = GetLastError();
goto GetWindowsDACLsEnd;
}
if (winUserAccessDenyMask &&
!AddAccessDeniedAceEx(pNewDACL, ACL_REVISION,
NO_PROPAGATE_INHERIT_ACE,
winUserAccessDenyMask, pOwnerSid))
{
ret = GetLastError();
goto GetWindowsDACLsEnd;
}
if (!AddAccessAllowedAceEx(pNewDACL, ACL_REVISION,
NO_PROPAGATE_INHERIT_ACE,
winUserAccessAllowMask, pOwnerSid))
{
ret = GetLastError();
goto GetWindowsDACLsEnd;
}
if (winGroupAccessDenyMask &&
!AddAccessDeniedAceEx(pNewDACL, ACL_REVISION,
NO_PROPAGATE_INHERIT_ACE,
winGroupAccessDenyMask, pGroupSid))
{
ret = GetLastError();
goto GetWindowsDACLsEnd;
}
if (!AddAccessAllowedAceEx(pNewDACL, ACL_REVISION,
NO_PROPAGATE_INHERIT_ACE,
winGroupAccessAllowMask, pGroupSid))
{
ret = GetLastError();
goto GetWindowsDACLsEnd;
}
if (!AddAccessAllowedAceEx(pNewDACL, ACL_REVISION,
NO_PROPAGATE_INHERIT_ACE,
winOtherAccessAllowMask, pEveryoneSid))
{
ret = GetLastError();
goto GetWindowsDACLsEnd;
}
*ppNewDACL = pNewDACL;
GetWindowsDACLsEnd:
LocalFree(pEveryoneSid);
LocalFree(pAdministratorsSid);
LocalFree(pSystemSid);
LocalFree(pCreatorOwnerSid);
if (ret != ERROR_SUCCESS) LocalFree(pNewDACL);
return ret;
}
//----------------------------------------------------------------------------
// Function: ChangeFileModeByMask
//
// Description:
// Change a file or direcotry at the path to Unix mode
//
// Returns:
// ERROR_SUCCESS: on success
// Error code: otherwise
//
// Notes:
// This function is long path safe, i.e. the path will be converted to long
// path format if not already converted. So the caller does not need to do
// the converstion before calling the method.
//
DWORD ChangeFileModeByMask(__in LPCWSTR path, INT mode)
{
LPWSTR longPathName = NULL;
PACL pNewDACL = NULL;
PSID pOwnerSid = NULL;
PSID pGroupSid = NULL;
PSECURITY_DESCRIPTOR pSD = NULL;
SECURITY_DESCRIPTOR_CONTROL control;
DWORD revision = 0;
PSECURITY_DESCRIPTOR pAbsSD = NULL;
PSECURITY_DESCRIPTOR pNonNullSD = NULL;
PACL pAbsDacl = NULL;
PACL pAbsSacl = NULL;
PSID pAbsOwner = NULL;
PSID pAbsGroup = NULL;
DWORD dwRtnCode = 0;
DWORD dwErrorCode = 0;
DWORD ret = ERROR_SUCCESS;
dwRtnCode = ConvertToLongPath(path, &longPathName);
if (dwRtnCode != ERROR_SUCCESS)
{
ret = dwRtnCode;
goto ChangeFileModeByMaskEnd;
}
// Get owner and group Sids
//
dwRtnCode = GetNamedSecurityInfoW(
longPathName,
SE_FILE_OBJECT,
OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION,
&pOwnerSid,
&pGroupSid,
NULL,
NULL,
&pSD);
if (ERROR_SUCCESS != dwRtnCode)
{
ret = dwRtnCode;
goto ChangeFileModeByMaskEnd;
}
// SetSecurityDescriptorDacl function used below only accepts security
// descriptor in absolute format, meaning that its members must be pointers to
// other structures, rather than offsets to contiguous data.
// To determine whether a security descriptor is self-relative or absolute,
// call the GetSecurityDescriptorControl function and check the
// SE_SELF_RELATIVE flag of the SECURITY_DESCRIPTOR_CONTROL parameter.
//
if (!GetSecurityDescriptorControl(pSD, &control, &revision))
{
ret = GetLastError();
goto ChangeFileModeByMaskEnd;
}
// If the security descriptor is self-relative, we use MakeAbsoluteSD function
// to convert it to absolute format.
//
if ((control & SE_SELF_RELATIVE) == SE_SELF_RELATIVE)
{
DWORD absSDSize = 0;
DWORD daclSize = 0;
DWORD saclSize = 0;
DWORD ownerSize = 0;
DWORD primaryGroupSize = 0;
MakeAbsoluteSD(pSD, NULL, &absSDSize, NULL, &daclSize, NULL,
&saclSize, NULL, &ownerSize, NULL, &primaryGroupSize);
if ((dwErrorCode = GetLastError()) != ERROR_INSUFFICIENT_BUFFER)
{
ret = dwErrorCode;
goto ChangeFileModeByMaskEnd;
}
if ((pAbsSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, absSDSize)) == NULL)
{
ret = GetLastError();
goto ChangeFileModeByMaskEnd;
}
if ((pAbsDacl = (PACL) LocalAlloc(LPTR, daclSize)) == NULL)
{
ret = GetLastError();
goto ChangeFileModeByMaskEnd;
}
if ((pAbsSacl = (PACL) LocalAlloc(LPTR, saclSize)) == NULL)
{
ret = GetLastError();
goto ChangeFileModeByMaskEnd;
}
if ((pAbsOwner = (PSID) LocalAlloc(LPTR, ownerSize)) == NULL)
{
ret = GetLastError();
goto ChangeFileModeByMaskEnd;
}
if ((pAbsGroup = (PSID) LocalAlloc(LPTR, primaryGroupSize)) == NULL)
{
ret = GetLastError();
goto ChangeFileModeByMaskEnd;
}
if (!MakeAbsoluteSD(pSD, pAbsSD, &absSDSize, pAbsDacl, &daclSize, pAbsSacl,
&saclSize, pAbsOwner, &ownerSize, pAbsGroup, &primaryGroupSize))
{
ret = GetLastError();
goto ChangeFileModeByMaskEnd;
}
}
// Get Windows DACLs based on Unix access mask
//
if ((dwRtnCode = GetWindowsDACLs(mode, pOwnerSid, pGroupSid, &pNewDACL))
!= ERROR_SUCCESS)
{
ret = dwRtnCode;
goto ChangeFileModeByMaskEnd;
}
// Set the DACL information in the security descriptor; if a DACL is already
// present in the security descriptor, the DACL is replaced. The security
// descriptor is then used to set the security of a file or directory.
//
pNonNullSD = (pAbsSD != NULL) ? pAbsSD : pSD;
if (!SetSecurityDescriptorDacl(pNonNullSD, TRUE, pNewDACL, FALSE))
{
ret = GetLastError();
goto ChangeFileModeByMaskEnd;
}
// MSDN states "This function is obsolete. Use the SetNamedSecurityInfo
// function instead." However we have the following problem when using
// SetNamedSecurityInfo:
// - When PROTECTED_DACL_SECURITY_INFORMATION is not passed in as part of
// security information, the object will include inheritable permissions
// from its parent.
// - When PROTECTED_DACL_SECURITY_INFORMATION is passsed in to set
// permissions on a directory, the child object of the directory will lose
// inheritable permissions from their parent (the current directory).
// By using SetFileSecurity, we have the nice property that the new
// permissions of the object does not include the inheritable permissions from
// its parent, and the child objects will not lose their inherited permissions
// from the current object.
//
if (!SetFileSecurity(longPathName, DACL_SECURITY_INFORMATION, pNonNullSD))
{
ret = GetLastError();
goto ChangeFileModeByMaskEnd;
}
ChangeFileModeByMaskEnd:
pNonNullSD = NULL;
LocalFree(longPathName);
LocalFree(pSD);
LocalFree(pNewDACL);
LocalFree(pAbsDacl);
LocalFree(pAbsSacl);
LocalFree(pAbsOwner);
LocalFree(pAbsGroup);
LocalFree(pAbsSD);
return ret;
}
//----------------------------------------------------------------------------
// Function: GetAccntNameFromSid
//
// Description:
// To retrieve an account name given the SID
//
// Returns:
// ERROR_SUCCESS: on success
// Other error code: otherwise
//
// Notes:
// Caller needs to destroy the memory of account name by calling LocalFree()
//
DWORD GetAccntNameFromSid(__in PSID pSid, __out PWSTR *ppAcctName)
{
LPWSTR lpName = NULL;
DWORD cchName = 0;
LPWSTR lpDomainName = NULL;
DWORD cchDomainName = 0;
SID_NAME_USE eUse = SidTypeUnknown;
DWORD cchAcctName = 0;
DWORD dwErrorCode = ERROR_SUCCESS;
HRESULT hr = S_OK;
DWORD ret = ERROR_SUCCESS;
assert(ppAcctName != NULL);
// NOTE:
// MSDN says the length returned for the buffer size including the terminating
// null character. However we found it is not true during debuging.
//
LookupAccountSid(NULL, pSid, NULL, &cchName, NULL, &cchDomainName, &eUse);
if ((dwErrorCode = GetLastError()) != ERROR_INSUFFICIENT_BUFFER)
return dwErrorCode;
lpName = (LPWSTR) LocalAlloc(LPTR, (cchName + 1) * sizeof(WCHAR));
if (lpName == NULL)
{
ret = GetLastError();
goto GetAccntNameFromSidEnd;
}
lpDomainName = (LPWSTR) LocalAlloc(LPTR, (cchDomainName + 1) * sizeof(WCHAR));
if (lpDomainName == NULL)
{
ret = GetLastError();
goto GetAccntNameFromSidEnd;
}
if (!LookupAccountSid(NULL, pSid,
lpName, &cchName, lpDomainName, &cchDomainName, &eUse))
{
ret = GetLastError();
goto GetAccntNameFromSidEnd;
}
// Buffer size = name length + 1 for '\' + domain length + 1 for NULL
cchAcctName = cchName + cchDomainName + 2;
*ppAcctName = (LPWSTR) LocalAlloc(LPTR, cchAcctName * sizeof(WCHAR));
if (*ppAcctName == NULL)
{
ret = GetLastError();
goto GetAccntNameFromSidEnd;
}
hr = StringCchCopyW(*ppAcctName, cchAcctName, lpDomainName);
if (FAILED(hr))
{
ret = HRESULT_CODE(hr);
goto GetAccntNameFromSidEnd;
}
hr = StringCchCatW(*ppAcctName, cchAcctName, L"\\");
if (FAILED(hr))
{
ret = HRESULT_CODE(hr);
goto GetAccntNameFromSidEnd;
}
hr = StringCchCatW(*ppAcctName, cchAcctName, lpName);
if (FAILED(hr))
{
ret = HRESULT_CODE(hr);
goto GetAccntNameFromSidEnd;
}
GetAccntNameFromSidEnd:
LocalFree(lpName);
LocalFree(lpDomainName);
if (ret != ERROR_SUCCESS)
{
LocalFree(*ppAcctName);
*ppAcctName = NULL;
}
return ret;
}
//----------------------------------------------------------------------------
// Function: GetLocalGroupsForUser
//
// Description:
// Get an array of groups for the given user.
//
// Returns:
// ERROR_SUCCESS on success
// Other error code on failure
//
// Notes:
// - NetUserGetLocalGroups() function only accepts full user name in the format
// [domain name]\[username]. The user input to this function can be only the
// username. In this case, NetUserGetLocalGroups() will fail on the first try,
// and we will try to find full user name using LookupAccountNameW() method,
// and call NetUserGetLocalGroups() function again with full user name.
// However, it is not always possible to find full user name given only user
// name. For example, a computer named 'win1' joined domain 'redmond' can have
// two different users, 'win1\alex' and 'redmond\alex'. Given only 'alex', we
// cannot tell which one is correct.
//
// - Caller needs to destroy the memory of groups by using the
// NetApiBufferFree() function
//
DWORD GetLocalGroupsForUser(
__in LPCWSTR user,
__out LPLOCALGROUP_USERS_INFO_0 *groups,
__out LPDWORD entries)
{
DWORD dwEntriesRead = 0;
DWORD dwTotalEntries = 0;
NET_API_STATUS nStatus = NERR_Success;
PSID pUserSid = NULL;
LPWSTR fullName = NULL;
DWORD dwRtnCode = ERROR_SUCCESS;
DWORD ret = ERROR_SUCCESS;
*groups = NULL;
*entries = 0;
nStatus = NetUserGetLocalGroups(NULL,
user,
0,
0,
(LPBYTE *) groups,
MAX_PREFERRED_LENGTH,
&dwEntriesRead,
&dwTotalEntries);
if (nStatus == NERR_Success)
{
*entries = dwEntriesRead;
return ERROR_SUCCESS;
}
else if (nStatus != NERR_UserNotFound)
{
return nStatus;
}
if ((dwRtnCode = GetSidFromAcctNameW(user, &pUserSid)) != ERROR_SUCCESS)
{
ret = dwRtnCode;
goto GetLocalGroupsForUserEnd;
}
if ((dwRtnCode = GetAccntNameFromSid(pUserSid, &fullName)) != ERROR_SUCCESS)
{
ret = dwRtnCode;
goto GetLocalGroupsForUserEnd;
}
nStatus = NetUserGetLocalGroups(NULL,
fullName,
0,
0,
(LPBYTE *) groups,
MAX_PREFERRED_LENGTH,
&dwEntriesRead,
&dwTotalEntries);
if (nStatus != NERR_Success)
{
// NERR_DCNotFound (2453) and NERR_UserNotFound (2221) are not published
// Windows System Error Code. All other error codes returned by
// NetUserGetLocalGroups() are valid System Error Codes according to MSDN.
ret = nStatus;
goto GetLocalGroupsForUserEnd;
}
*entries = dwEntriesRead;
GetLocalGroupsForUserEnd:
LocalFree(pUserSid);
LocalFree(fullName);
return ret;
}
//----------------------------------------------------------------------------
// Function: EnablePrivilege
//
// Description:
// Check if the process has the given privilege. If yes, enable the privilege
// to the process's access token.
//
// Returns:
// TRUE: on success
//
// Notes:
//
BOOL EnablePrivilege(__in LPCWSTR privilegeName)
{
HANDLE hToken = INVALID_HANDLE_VALUE;
TOKEN_PRIVILEGES tp = { 0 };
DWORD dwErrCode = ERROR_SUCCESS;
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
{
ReportErrorCode(L"OpenProcessToken", GetLastError());
return FALSE;
}
tp.PrivilegeCount = 1;
if (!LookupPrivilegeValueW(NULL,
privilegeName, &(tp.Privileges[0].Luid)))
{
ReportErrorCode(L"LookupPrivilegeValue", GetLastError());
CloseHandle(hToken);
return FALSE;
}
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// As stated on MSDN, we need to use GetLastError() to check if
// AdjustTokenPrivileges() adjusted all of the specified privileges.
//
AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);
dwErrCode = GetLastError();
CloseHandle(hToken);
return dwErrCode == ERROR_SUCCESS;
}
//----------------------------------------------------------------------------
// Function: ReportErrorCode
//
// Description:
// Report an error. Use FormatMessage function to get the system error message.
//
// Returns:
// None
//
// Notes:
//
//
void ReportErrorCode(LPCWSTR func, DWORD err)
{
DWORD len = 0;
LPWSTR msg = NULL;
assert(func != NULL);
len = FormatMessageW(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPWSTR)&msg, 0, NULL);
if (len > 0)
{
fwprintf(stderr, L"%s error (%d): %s\n", func, err, msg);
}
else
{
fwprintf(stderr, L"%s error code: %d.\n", func, err);
}
if (msg != NULL) LocalFree(msg);
}
//----------------------------------------------------------------------------
// Function: GetLibraryName
//
// Description:
// Given an address, get the file name of the library from which it was loaded.
//
// Returns:
// None
//
// Notes:
// - The function allocates heap memory and points the filename out parameter to
// the newly allocated memory, which will contain the name of the file.
//
// - If there is any failure, then the function frees the heap memory it
// allocated and sets the filename out parameter to NULL.
//
void GetLibraryName(LPCVOID lpAddress, LPWSTR *filename)
{
SIZE_T ret = 0;
DWORD size = MAX_PATH;
HMODULE mod = NULL;
DWORD err = ERROR_SUCCESS;
MEMORY_BASIC_INFORMATION mbi;
ret = VirtualQuery(lpAddress, &mbi, sizeof(mbi));
if (ret == 0) goto cleanup;
mod = mbi.AllocationBase;
do {
*filename = (LPWSTR) realloc(*filename, size * sizeof(WCHAR));
if (*filename == NULL) goto cleanup;
GetModuleFileName(mod, *filename, size);
size <<= 1;
err = GetLastError();
} while (err == ERROR_INSUFFICIENT_BUFFER);
if (err != ERROR_SUCCESS) goto cleanup;
return;
cleanup:
if (*filename != NULL)
{
free(*filename);
*filename = NULL;
}
}
Event Timeline
Log In to Comment