| /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
| /* ***** BEGIN LICENSE BLOCK ***** |
| * Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
| * |
| * The contents of this file are subject to the Mozilla Public License Version |
| * 1.1 (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.mozilla.org/MPL/ |
| * |
| * Software distributed under the License is distributed on an "AS IS" basis, |
| * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
| * for the specific language governing rights and limitations under the |
| * License. |
| * |
| * The Original Code is the Netscape Portable Runtime (NSPR). |
| * |
| * The Initial Developer of the Original Code is |
| * Netscape Communications Corporation. |
| * Portions created by the Initial Developer are Copyright (C) 2000 |
| * the Initial Developer. All Rights Reserved. |
| * |
| * Contributor(s): |
| * |
| * Alternatively, the contents of this file may be used under the terms of |
| * either the GNU General Public License Version 2 or later (the "GPL"), or |
| * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), |
| * in which case the provisions of the GPL or the LGPL are applicable instead |
| * of those above. If you wish to allow use of your version of this file only |
| * under the terms of either the GPL or the LGPL, and not to allow others to |
| * use your version of this file under the terms of the MPL, indicate your |
| * decision by deleting the provisions above and replace them with the notice |
| * and other provisions required by the GPL or the LGPL. If you do not delete |
| * the provisions above, a recipient may use your version of this file under |
| * the terms of any one of the MPL, the GPL or the LGPL. |
| * |
| * ***** END LICENSE BLOCK ***** */ |
| |
| #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); |
| } |
| } |