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