summaryrefslogtreecommitdiffstats
path: root/lib/isc/win32/fsaccess.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/isc/win32/fsaccess.c')
-rw-r--r--lib/isc/win32/fsaccess.c404
1 files changed, 404 insertions, 0 deletions
diff --git a/lib/isc/win32/fsaccess.c b/lib/isc/win32/fsaccess.c
new file mode 100644
index 0000000..909f9e5
--- /dev/null
+++ b/lib/isc/win32/fsaccess.c
@@ -0,0 +1,404 @@
+/*
+ * 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 <aclapi.h>
+#include <errno.h>
+#include <io.h>
+#include <stdbool.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <isc/file.h>
+#include <isc/stat.h>
+#include <isc/string.h>
+
+#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(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);
+}