280 lines
8.1 KiB
C
280 lines
8.1 KiB
C
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "primpl.h"
|
|
|
|
/*
|
|
* ntsec.c
|
|
*
|
|
* Implement the POSIX-style mode bits (access permissions) for
|
|
* files and other securable objects in Windows NT using Windows
|
|
* NT's security descriptors with appropriate discretionary
|
|
* access-control lists.
|
|
*/
|
|
|
|
/*
|
|
* The security identifiers (SIDs) for owner, primary group,
|
|
* and the Everyone (World) group.
|
|
*
|
|
* These SIDs are looked up during NSPR initialization and
|
|
* saved in this global structure (see _PR_NT_InitSids) so
|
|
* that _PR_NT_MakeSecurityDescriptorACL doesn't need to
|
|
* look them up every time.
|
|
*/
|
|
static struct {
|
|
PSID owner;
|
|
PSID group;
|
|
PSID everyone;
|
|
} _pr_nt_sids;
|
|
|
|
/*
|
|
* Initialize the SIDs for owner, primary group, and the Everyone
|
|
* group in the _pr_nt_sids structure.
|
|
*
|
|
* This function needs to be called by NSPR initialization.
|
|
*/
|
|
void _PR_NT_InitSids(void)
|
|
{
|
|
#ifdef WINCE /* not supported */
|
|
return;
|
|
#else
|
|
SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
|
|
HANDLE hToken = NULL; /* initialized to an arbitrary value to
|
|
* silence a Purify UMR warning */
|
|
PSID infoBuffer[1024/sizeof(PSID)]; /* defined as an array of PSIDs
|
|
* to force proper alignment */
|
|
PTOKEN_OWNER pTokenOwner = (PTOKEN_OWNER) infoBuffer;
|
|
PTOKEN_PRIMARY_GROUP pTokenPrimaryGroup
|
|
= (PTOKEN_PRIMARY_GROUP) infoBuffer;
|
|
DWORD dwLength;
|
|
BOOL rv;
|
|
|
|
/*
|
|
* Look up and make a copy of the owner and primary group
|
|
* SIDs in the access token of the calling process.
|
|
*/
|
|
rv = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken);
|
|
if (rv == 0) {
|
|
/*
|
|
* On non-NT systems, this function is not implemented
|
|
* (error code ERROR_CALL_NOT_IMPLEMENTED), and neither are
|
|
* the other security functions. There is no point in
|
|
* going further.
|
|
*
|
|
* A process with insufficient access permissions may fail
|
|
* with the error code ERROR_ACCESS_DENIED.
|
|
*/
|
|
PR_LOG(_pr_io_lm, PR_LOG_DEBUG,
|
|
("_PR_NT_InitSids: OpenProcessToken() failed. Error: %d",
|
|
GetLastError()));
|
|
return;
|
|
}
|
|
|
|
rv = GetTokenInformation(hToken, TokenOwner, infoBuffer,
|
|
sizeof(infoBuffer), &dwLength);
|
|
PR_ASSERT(rv != 0);
|
|
dwLength = GetLengthSid(pTokenOwner->Owner);
|
|
_pr_nt_sids.owner = (PSID) PR_Malloc(dwLength);
|
|
PR_ASSERT(_pr_nt_sids.owner != NULL);
|
|
rv = CopySid(dwLength, _pr_nt_sids.owner, pTokenOwner->Owner);
|
|
PR_ASSERT(rv != 0);
|
|
|
|
rv = GetTokenInformation(hToken, TokenPrimaryGroup, infoBuffer,
|
|
sizeof(infoBuffer), &dwLength);
|
|
PR_ASSERT(rv != 0);
|
|
dwLength = GetLengthSid(pTokenPrimaryGroup->PrimaryGroup);
|
|
_pr_nt_sids.group = (PSID) PR_Malloc(dwLength);
|
|
PR_ASSERT(_pr_nt_sids.group != NULL);
|
|
rv = CopySid(dwLength, _pr_nt_sids.group,
|
|
pTokenPrimaryGroup->PrimaryGroup);
|
|
PR_ASSERT(rv != 0);
|
|
|
|
rv = CloseHandle(hToken);
|
|
PR_ASSERT(rv != 0);
|
|
|
|
/* Create a well-known SID for the Everyone group. */
|
|
rv = AllocateAndInitializeSid(&SIDAuthWorld, 1,
|
|
SECURITY_WORLD_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&_pr_nt_sids.everyone);
|
|
PR_ASSERT(rv != 0);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Free the SIDs for owner, primary group, and the Everyone group
|
|
* in the _pr_nt_sids structure.
|
|
*
|
|
* This function needs to be called by NSPR cleanup.
|
|
*/
|
|
void
|
|
_PR_NT_FreeSids(void)
|
|
{
|
|
#ifdef WINCE
|
|
return;
|
|
#else
|
|
if (_pr_nt_sids.owner) {
|
|
PR_Free(_pr_nt_sids.owner);
|
|
}
|
|
if (_pr_nt_sids.group) {
|
|
PR_Free(_pr_nt_sids.group);
|
|
}
|
|
if (_pr_nt_sids.everyone) {
|
|
FreeSid(_pr_nt_sids.everyone);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Construct a security descriptor whose discretionary access-control
|
|
* list implements the specified mode bits. The SIDs for owner, group,
|
|
* and everyone are obtained from the global _pr_nt_sids structure.
|
|
* Both the security descriptor and access-control list are returned
|
|
* and should be freed by a _PR_NT_FreeSecurityDescriptorACL call.
|
|
*
|
|
* The accessTable array maps NSPR's read, write, and execute access
|
|
* rights to the corresponding NT access rights for the securable
|
|
* object.
|
|
*/
|
|
PRStatus
|
|
_PR_NT_MakeSecurityDescriptorACL(
|
|
PRIntn mode,
|
|
DWORD accessTable[],
|
|
PSECURITY_DESCRIPTOR *resultSD,
|
|
PACL *resultACL)
|
|
{
|
|
#ifdef WINCE
|
|
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
|
|
return PR_FAILURE;
|
|
#else
|
|
PSECURITY_DESCRIPTOR pSD = NULL;
|
|
PACL pACL = NULL;
|
|
DWORD cbACL; /* size of ACL */
|
|
DWORD accessMask;
|
|
|
|
if (_pr_nt_sids.owner == NULL) {
|
|
PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
|
|
return PR_FAILURE;
|
|
}
|
|
|
|
pSD = (PSECURITY_DESCRIPTOR) PR_Malloc(SECURITY_DESCRIPTOR_MIN_LENGTH);
|
|
if (pSD == NULL) {
|
|
_PR_MD_MAP_DEFAULT_ERROR(GetLastError());
|
|
goto failed;
|
|
}
|
|
if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) {
|
|
_PR_MD_MAP_DEFAULT_ERROR(GetLastError());
|
|
goto failed;
|
|
}
|
|
if (!SetSecurityDescriptorOwner(pSD, _pr_nt_sids.owner, FALSE)) {
|
|
_PR_MD_MAP_DEFAULT_ERROR(GetLastError());
|
|
goto failed;
|
|
}
|
|
if (!SetSecurityDescriptorGroup(pSD, _pr_nt_sids.group, FALSE)) {
|
|
_PR_MD_MAP_DEFAULT_ERROR(GetLastError());
|
|
goto failed;
|
|
}
|
|
|
|
/*
|
|
* Construct a discretionary access-control list with three
|
|
* access-control entries, one each for owner, primary group,
|
|
* and Everyone.
|
|
*/
|
|
|
|
cbACL = sizeof(ACL)
|
|
+ 3 * (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD))
|
|
+ GetLengthSid(_pr_nt_sids.owner)
|
|
+ GetLengthSid(_pr_nt_sids.group)
|
|
+ GetLengthSid(_pr_nt_sids.everyone);
|
|
pACL = (PACL) PR_Malloc(cbACL);
|
|
if (pACL == NULL) {
|
|
_PR_MD_MAP_DEFAULT_ERROR(GetLastError());
|
|
goto failed;
|
|
}
|
|
if (!InitializeAcl(pACL, cbACL, ACL_REVISION)) {
|
|
_PR_MD_MAP_DEFAULT_ERROR(GetLastError());
|
|
goto failed;
|
|
}
|
|
accessMask = 0;
|
|
if (mode & 00400) {
|
|
accessMask |= accessTable[0];
|
|
}
|
|
if (mode & 00200) {
|
|
accessMask |= accessTable[1];
|
|
}
|
|
if (mode & 00100) {
|
|
accessMask |= accessTable[2];
|
|
}
|
|
if (accessMask && !AddAccessAllowedAce(pACL, ACL_REVISION, accessMask,
|
|
_pr_nt_sids.owner)) {
|
|
_PR_MD_MAP_DEFAULT_ERROR(GetLastError());
|
|
goto failed;
|
|
}
|
|
accessMask = 0;
|
|
if (mode & 00040) {
|
|
accessMask |= accessTable[0];
|
|
}
|
|
if (mode & 00020) {
|
|
accessMask |= accessTable[1];
|
|
}
|
|
if (mode & 00010) {
|
|
accessMask |= accessTable[2];
|
|
}
|
|
if (accessMask && !AddAccessAllowedAce(pACL, ACL_REVISION, accessMask,
|
|
_pr_nt_sids.group)) {
|
|
_PR_MD_MAP_DEFAULT_ERROR(GetLastError());
|
|
goto failed;
|
|
}
|
|
accessMask = 0;
|
|
if (mode & 00004) {
|
|
accessMask |= accessTable[0];
|
|
}
|
|
if (mode & 00002) {
|
|
accessMask |= accessTable[1];
|
|
}
|
|
if (mode & 00001) {
|
|
accessMask |= accessTable[2];
|
|
}
|
|
if (accessMask && !AddAccessAllowedAce(pACL, ACL_REVISION, accessMask,
|
|
_pr_nt_sids.everyone)) {
|
|
_PR_MD_MAP_DEFAULT_ERROR(GetLastError());
|
|
goto failed;
|
|
}
|
|
|
|
if (!SetSecurityDescriptorDacl(pSD, TRUE, pACL, FALSE)) {
|
|
_PR_MD_MAP_DEFAULT_ERROR(GetLastError());
|
|
goto failed;
|
|
}
|
|
|
|
*resultSD = pSD;
|
|
*resultACL = pACL;
|
|
return PR_SUCCESS;
|
|
|
|
failed:
|
|
if (pSD) {
|
|
PR_Free(pSD);
|
|
}
|
|
if (pACL) {
|
|
PR_Free(pACL);
|
|
}
|
|
return PR_FAILURE;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Free the specified security descriptor and access-control list
|
|
* previously created by _PR_NT_MakeSecurityDescriptorACL.
|
|
*/
|
|
void
|
|
_PR_NT_FreeSecurityDescriptorACL(PSECURITY_DESCRIPTOR pSD, PACL pACL)
|
|
{
|
|
if (pSD) {
|
|
PR_Free(pSD);
|
|
}
|
|
if (pACL) {
|
|
PR_Free(pACL);
|
|
}
|
|
}
|