/* * Copyright (C) Internet Systems Consortium, Inc. ("ISC") * * SPDX-License-Identifier: MPL-2.0 * * 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 https://mozilla.org/MPL/2.0/. * * See the COPYRIGHT file distributed with this work for additional * information regarding copyright ownership. */ /* * Note that Win32 does not have the concept of files having access * and ownership bits. The FAT File system only has a readonly flag * for everyone and that's all. NTFS uses ACL's which is a totally * different concept of controlling access. * * This code needs to be revisited to set up proper access control for * NTFS file systems. Nothing can be done for FAT file systems. */ #include #include #include #include #include #include #include #include #include #include "errno2result.h" /* * The OS-independent part of the API is in lib/isc. */ #include "../fsaccess.c" /* Store the user account name locally */ static char username[255] = "\0"; static DWORD namelen = 0; /* * In order to set or retrieve access information, we need to obtain * the File System type. These could be UNC-type shares. */ BOOL is_ntfs(const char *file) { char drive[255]; char FSType[20]; char tmpbuf[256]; char *machinename; char *sharename; char filename[1024]; char *last; REQUIRE(filename != NULL); if (isc_file_absolutepath(file, filename, sizeof(filename)) != ISC_R_SUCCESS) { return (FALSE); } /* * Look for c:\path\... style, c:/path/... or \\computer\shar\path... * the UNC style file specs */ if (isalpha((unsigned char)filename[0]) && filename[1] == ':' && (filename[2] == '\\' || filename[2] == '/')) { /* Copy 'c:\' or 'c:/' and NUL terminate. */ strlcpy(drive, filename, ISC_MIN(3 + 1, sizeof(drive))); } else if ((filename[0] == '\\') && (filename[1] == '\\')) { /* Find the machine and share name and rebuild the UNC */ strlcpy(tmpbuf, filename, sizeof(tmpbuf)); machinename = strtok_r(tmpbuf, "\\", &last); sharename = strtok_r(NULL, "\\", &last); strlcpy(drive, "\\\\", sizeof(drive)); strlcat(drive, machinename, sizeof(drive)); strlcat(drive, "\\", sizeof(drive)); strlcat(drive, sharename, sizeof(drive)); strlcat(drive, "\\", sizeof(drive)); } else { /* Not determinable */ return (FALSE); } GetVolumeInformation(drive, NULL, 0, NULL, 0, NULL, FSType, sizeof(FSType)); if (strcmp(FSType, "NTFS") == 0) { return (TRUE); } else { return (FALSE); } } /* * If it's not NTFS, we assume that it is FAT and proceed * with almost nothing to do. Only the write flag can be set or * cleared. */ isc_result_t FAT_fsaccess_set(const char *path, isc_fsaccess_t access) { int mode; isc_fsaccess_t bits; /* * Done with checking bad bits. Set mode_t. */ mode = 0; #define SET_AND_CLEAR1(modebit) \ if ((access & bits) != 0) { \ mode |= modebit; \ access &= ~bits; \ } #define SET_AND_CLEAR(user, group, other) \ SET_AND_CLEAR1(user); \ bits <<= STEP; \ SET_AND_CLEAR1(group); \ bits <<= STEP; \ SET_AND_CLEAR1(other); bits = ISC_FSACCESS_READ | ISC_FSACCESS_LISTDIRECTORY; SET_AND_CLEAR(S_IRUSR, S_IRGRP, S_IROTH); bits = ISC_FSACCESS_WRITE | ISC_FSACCESS_CREATECHILD | ISC_FSACCESS_DELETECHILD; SET_AND_CLEAR(S_IWUSR, S_IWGRP, S_IWOTH); INSIST(access == 0); if (_chmod(path, mode) < 0) { return (isc__errno2result(errno)); } return (ISC_R_SUCCESS); } isc_result_t NTFS_Access_Control(const char *filename, const char *user, int access, bool isdir) { SECURITY_DESCRIPTOR sd; BYTE aclBuffer[1024]; PACL pacl = (PACL)&aclBuffer; BYTE sidBuffer[100]; PSID psid = (PSID)&sidBuffer; DWORD sidBufferSize = sizeof(sidBuffer); BYTE adminSidBuffer[100]; PSID padminsid = (PSID)&adminSidBuffer; DWORD adminSidBufferSize = sizeof(adminSidBuffer); BYTE otherSidBuffer[100]; PSID pothersid = (PSID)&otherSidBuffer; DWORD otherSidBufferSize = sizeof(otherSidBuffer); char domainBuffer[100]; DWORD domainBufferSize = sizeof(domainBuffer); SID_NAME_USE snu; DWORD NTFSbits; int caccess; /* Initialize an ACL */ if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) { return (ISC_R_NOPERM); } if (!InitializeAcl(pacl, sizeof(aclBuffer), ACL_REVISION)) { return (ISC_R_NOPERM); } if (!LookupAccountName(0, user, psid, &sidBufferSize, domainBuffer, &domainBufferSize, &snu)) { return (ISC_R_NOPERM); } domainBufferSize = sizeof(domainBuffer); if (!LookupAccountName(0, "Administrators", padminsid, &adminSidBufferSize, domainBuffer, &domainBufferSize, &snu)) { (void)GetLastError(); return (ISC_R_NOPERM); } domainBufferSize = sizeof(domainBuffer); if (!LookupAccountName(0, "Everyone", pothersid, &otherSidBufferSize, domainBuffer, &domainBufferSize, &snu)) { (void)GetLastError(); return (ISC_R_NOPERM); } caccess = access; /* Owner check */ NTFSbits = 0; if ((caccess & ISC_FSACCESS_READ) != 0) { NTFSbits |= FILE_GENERIC_READ; } if ((caccess & ISC_FSACCESS_WRITE) != 0) { NTFSbits |= FILE_GENERIC_WRITE; } if ((caccess & ISC_FSACCESS_EXECUTE) != 0) { NTFSbits |= FILE_GENERIC_EXECUTE; } /* For directories check the directory-specific bits */ if (isdir) { if ((caccess & ISC_FSACCESS_CREATECHILD) != 0) { NTFSbits |= FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE; } if ((caccess & ISC_FSACCESS_DELETECHILD) != 0) { NTFSbits |= FILE_DELETE_CHILD; } if ((caccess & ISC_FSACCESS_LISTDIRECTORY) != 0) { NTFSbits |= FILE_LIST_DIRECTORY; } if ((caccess & ISC_FSACCESS_ACCESSCHILD) != 0) { NTFSbits |= FILE_TRAVERSE; } } if (NTFSbits == (FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE)) { NTFSbits |= FILE_ALL_ACCESS; } /* * Owner and Administrator also get STANDARD_RIGHTS_ALL * to ensure that they have full control */ NTFSbits |= STANDARD_RIGHTS_ALL; /* Add the ACE to the ACL */ if (!AddAccessAllowedAce(pacl, ACL_REVISION, NTFSbits, psid)) { return (ISC_R_NOPERM); } if (!AddAccessAllowedAce(pacl, ACL_REVISION, NTFSbits, padminsid)) { return (ISC_R_NOPERM); } /* * Group is ignored since we can be in multiple groups or no group * and its meaning is not clear on Win32 */ caccess = caccess >> STEP; /* * Other check. We translate this to be the same as Everyone */ caccess = caccess >> STEP; NTFSbits = 0; if ((caccess & ISC_FSACCESS_READ) != 0) { NTFSbits |= FILE_GENERIC_READ; } if ((caccess & ISC_FSACCESS_WRITE) != 0) { NTFSbits |= FILE_GENERIC_WRITE; } if ((caccess & ISC_FSACCESS_EXECUTE) != 0) { NTFSbits |= FILE_GENERIC_EXECUTE; } /* For directories check the directory-specific bits */ if (isdir == TRUE) { if ((caccess & ISC_FSACCESS_CREATECHILD) != 0) { NTFSbits |= FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE; } if ((caccess & ISC_FSACCESS_DELETECHILD) != 0) { NTFSbits |= FILE_DELETE_CHILD; } if ((caccess & ISC_FSACCESS_LISTDIRECTORY) != 0) { NTFSbits |= FILE_LIST_DIRECTORY; } if ((caccess & ISC_FSACCESS_ACCESSCHILD) != 0) { NTFSbits |= FILE_TRAVERSE; } } /* Add the ACE to the ACL */ if (!AddAccessAllowedAce(pacl, ACL_REVISION, NTFSbits, pothersid)) { return (ISC_R_NOPERM); } if (!SetSecurityDescriptorDacl(&sd, TRUE, pacl, FALSE)) { return (ISC_R_NOPERM); } if (!SetFileSecurity(filename, DACL_SECURITY_INFORMATION, &sd)) { return (ISC_R_NOPERM); } return (ISC_R_SUCCESS); } isc_result_t NTFS_fsaccess_set(const char *path, isc_fsaccess_t access, bool isdir) { /* * For NTFS we first need to get the name of the account under * which BIND is running */ if (namelen == 0) { namelen = sizeof(username); if (GetUserName(username, &namelen) == 0) { return (ISC_R_FAILURE); } } return (NTFS_Access_Control(path, username, access, isdir)); } isc_result_t isc_fsaccess_set(const char *path, isc_fsaccess_t access) { struct stat statb; bool is_dir = false; isc_result_t result; if (stat(path, &statb) != 0) { return (isc__errno2result(errno)); } if ((statb.st_mode & S_IFDIR) != 0) { is_dir = true; } else if ((statb.st_mode & S_IFREG) == 0) { return (ISC_R_INVALIDFILE); } result = check_bad_bits(access, is_dir); if (result != ISC_R_SUCCESS) { return (result); } /* * Determine if this is a FAT or NTFS disk and * call the appropriate function to set the permissions */ if (is_ntfs(path)) { return (NTFS_fsaccess_set(path, access, is_dir)); } else { return (FAT_fsaccess_set(path, access)); } } isc_result_t isc_fsaccess_changeowner(const char *filename, const char *user) { SECURITY_DESCRIPTOR psd; BYTE sidBuffer[500]; BYTE groupBuffer[500]; PSID psid = (PSID)&sidBuffer; DWORD sidBufferSize = sizeof(sidBuffer); char domainBuffer[100]; DWORD domainBufferSize = sizeof(domainBuffer); SID_NAME_USE snu; PSID pSidGroup = (PSID)&groupBuffer; DWORD groupBufferSize = sizeof(groupBuffer); /* * Determine if this is a FAT or NTFS disk and * call the appropriate function to set the ownership * FAT disks do not have ownership attributes so it's * a noop. */ if (is_ntfs(filename) == FALSE) { return (ISC_R_SUCCESS); } if (!InitializeSecurityDescriptor(&psd, SECURITY_DESCRIPTOR_REVISION)) { return (ISC_R_NOPERM); } if (!LookupAccountName(0, user, psid, &sidBufferSize, domainBuffer, &domainBufferSize, &snu)) { return (ISC_R_NOPERM); } /* Make sure administrators can get to it */ domainBufferSize = sizeof(domainBuffer); if (!LookupAccountName(0, "Administrators", pSidGroup, &groupBufferSize, domainBuffer, &domainBufferSize, &snu)) { return (ISC_R_NOPERM); } if (!SetSecurityDescriptorOwner(&psd, psid, FALSE)) { return (ISC_R_NOPERM); } if (!SetSecurityDescriptorGroup(&psd, pSidGroup, FALSE)) { return (ISC_R_NOPERM); } if (!SetFileSecurity(filename, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION, &psd)) { return (ISC_R_NOPERM); } return (ISC_R_SUCCESS); }