1258945Sroberto/* 2258945Sroberto * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC") 3258945Sroberto * Copyright (C) 2000-2002 Internet Software Consortium. 4258945Sroberto * 5258945Sroberto * Permission to use, copy, modify, and/or distribute this software for any 6258945Sroberto * purpose with or without fee is hereby granted, provided that the above 7258945Sroberto * copyright notice and this permission notice appear in all copies. 8258945Sroberto * 9258945Sroberto * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10258945Sroberto * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11258945Sroberto * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12258945Sroberto * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13258945Sroberto * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14258945Sroberto * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15258945Sroberto * PERFORMANCE OF THIS SOFTWARE. 16258945Sroberto */ 17258945Sroberto 18258945Sroberto/* $Id: fsaccess.c,v 1.15 2007/06/19 23:47:19 tbox Exp $ */ 19258945Sroberto 20258945Sroberto/* 21258945Sroberto * Note that Win32 does not have the concept of files having access 22258945Sroberto * and ownership bits. The FAT File system only has a readonly flag 23258945Sroberto * for everyone and that's all. NTFS uses ACL's which is a totally 24258945Sroberto * different concept of controlling access. 25258945Sroberto * 26258945Sroberto * This code needs to be revisited to set up proper access control for 27258945Sroberto * NTFS file systems. Nothing can be done for FAT file systems. 28258945Sroberto */ 29258945Sroberto 30258945Sroberto#include <config.h> 31258945Sroberto 32258945Sroberto#include <aclapi.h> 33258945Sroberto 34258945Sroberto#include <sys/types.h> 35258945Sroberto#include <sys/stat.h> 36258945Sroberto#include <io.h> 37258945Sroberto#include <errno.h> 38258945Sroberto 39258945Sroberto#include <isc/file.h> 40258945Sroberto#include <isc/stat.h> 41258945Sroberto 42258945Sroberto#include "errno2result.h" 43258945Sroberto 44258945Sroberto/* 45258945Sroberto * The OS-independent part of the API is in lib/isc. 46258945Sroberto */ 47258945Sroberto#include "../fsaccess.c" 48258945Sroberto 49258945Sroberto/* Store the user account name locally */ 50258945Srobertostatic char username[255] = "\0"; 51258945Srobertostatic DWORD namelen = 0; 52258945Sroberto 53258945Sroberto/* 54258945Sroberto * In order to set or retrieve access information, we need to obtain 55258945Sroberto * the File System type. These could be UNC-type shares. 56258945Sroberto */ 57258945Sroberto 58258945SrobertoBOOL 59258945Srobertois_ntfs(const char * file) { 60258945Sroberto 61258945Sroberto char drive[255]; 62258945Sroberto char FSType[20]; 63258945Sroberto char tmpbuf[256]; 64258945Sroberto char *machinename; 65258945Sroberto char *sharename; 66258945Sroberto char filename[1024]; 67258945Sroberto 68258945Sroberto REQUIRE(filename != NULL); 69258945Sroberto 70258945Sroberto if (isc_file_absolutepath(file, filename, 71258945Sroberto sizeof(filename)) != ISC_R_SUCCESS) { 72258945Sroberto return (FALSE); 73258945Sroberto } 74258945Sroberto 75258945Sroberto /* 76258945Sroberto * Look for c:\path\... style, c:/path/... or \\computer\shar\path... 77258945Sroberto * the UNC style file specs 78258945Sroberto */ 79258945Sroberto if (isalpha(filename[0]) && filename[1] == ':' && 80258945Sroberto (filename[2] == '\\' || filename[2] == '/')) { 81258945Sroberto strncpy(drive, filename, 3); 82258945Sroberto drive[3] = '\0'; 83258945Sroberto } 84258945Sroberto 85258945Sroberto else if ((filename[0] == '\\') && (filename[1] == '\\')) { 86258945Sroberto /* Find the machine and share name and rebuild the UNC */ 87258945Sroberto strcpy(tmpbuf, filename); 88258945Sroberto machinename = strtok(tmpbuf, "\\"); 89258945Sroberto sharename = strtok(NULL, "\\"); 90258945Sroberto strcpy(drive, "\\\\"); 91258945Sroberto strcat(drive, machinename); 92258945Sroberto strcat(drive, "\\"); 93258945Sroberto strcat(drive, sharename); 94258945Sroberto strcat(drive, "\\"); 95258945Sroberto 96258945Sroberto } 97258945Sroberto else /* Not determinable */ 98258945Sroberto return (FALSE); 99258945Sroberto 100258945Sroberto GetVolumeInformation(drive, NULL, 0, NULL, 0, NULL, FSType, 101258945Sroberto sizeof(FSType)); 102258945Sroberto if(strcmp(FSType,"NTFS") == 0) 103258945Sroberto return (TRUE); 104258945Sroberto else 105258945Sroberto return (FALSE); 106258945Sroberto} 107258945Sroberto 108258945Sroberto/* 109258945Sroberto * If it's not NTFS, we assume that it is FAT and proceed 110258945Sroberto * with almost nothing to do. Only the write flag can be set or 111258945Sroberto * cleared. 112258945Sroberto */ 113258945Srobertoisc_result_t 114258945SrobertoFAT_fsaccess_set(const char *path, isc_fsaccess_t access) { 115258945Sroberto int mode; 116258945Sroberto isc_fsaccess_t bits; 117258945Sroberto 118258945Sroberto /* 119258945Sroberto * Done with checking bad bits. Set mode_t. 120258945Sroberto */ 121258945Sroberto mode = 0; 122258945Sroberto 123258945Sroberto#define SET_AND_CLEAR1(modebit) \ 124258945Sroberto if ((access & bits) != 0) { \ 125258945Sroberto mode |= modebit; \ 126258945Sroberto access &= ~bits; \ 127258945Sroberto } 128258945Sroberto#define SET_AND_CLEAR(user, group, other) \ 129258945Sroberto SET_AND_CLEAR1(user); \ 130258945Sroberto bits <<= STEP; \ 131258945Sroberto SET_AND_CLEAR1(group); \ 132258945Sroberto bits <<= STEP; \ 133258945Sroberto SET_AND_CLEAR1(other); 134258945Sroberto 135258945Sroberto bits = ISC_FSACCESS_READ | ISC_FSACCESS_LISTDIRECTORY; 136258945Sroberto 137258945Sroberto SET_AND_CLEAR(S_IRUSR, S_IRGRP, S_IROTH); 138258945Sroberto 139258945Sroberto bits = ISC_FSACCESS_WRITE | 140258945Sroberto ISC_FSACCESS_CREATECHILD | 141258945Sroberto ISC_FSACCESS_DELETECHILD; 142258945Sroberto 143258945Sroberto SET_AND_CLEAR(S_IWUSR, S_IWGRP, S_IWOTH); 144258945Sroberto 145258945Sroberto INSIST(access == 0); 146258945Sroberto 147258945Sroberto if (_chmod(path, mode) < 0) 148258945Sroberto return (isc__errno2result(errno)); 149258945Sroberto 150258945Sroberto return (ISC_R_SUCCESS); 151258945Sroberto} 152258945Sroberto 153258945Srobertoisc_result_t 154258945SrobertoNTFS_Access_Control(const char *filename, const char *user, int access, 155258945Sroberto isc_boolean_t isdir) { 156258945Sroberto SECURITY_DESCRIPTOR sd; 157258945Sroberto BYTE aclBuffer[1024]; 158258945Sroberto PACL pacl=(PACL)&aclBuffer; 159258945Sroberto BYTE sidBuffer[100]; 160258945Sroberto PSID psid=(PSID) &sidBuffer; 161258945Sroberto DWORD sidBufferSize = sizeof(sidBuffer); 162258945Sroberto BYTE adminSidBuffer[100]; 163258945Sroberto PSID padminsid=(PSID) &adminSidBuffer; 164258945Sroberto DWORD adminSidBufferSize = sizeof(adminSidBuffer); 165258945Sroberto BYTE otherSidBuffer[100]; 166258945Sroberto PSID pothersid=(PSID) &otherSidBuffer; 167258945Sroberto DWORD otherSidBufferSize = sizeof(otherSidBuffer); 168258945Sroberto char domainBuffer[100]; 169258945Sroberto DWORD domainBufferSize = sizeof(domainBuffer); 170258945Sroberto SID_NAME_USE snu; 171258945Sroberto int errval; 172258945Sroberto DWORD NTFSbits; 173258945Sroberto int caccess; 174258945Sroberto 175258945Sroberto 176258945Sroberto /* Initialize an ACL */ 177258945Sroberto if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) 178258945Sroberto return (ISC_R_NOPERM); 179258945Sroberto if (!InitializeAcl(pacl, sizeof(aclBuffer), ACL_REVISION)) 180258945Sroberto return (ISC_R_NOPERM); 181258945Sroberto if (!LookupAccountName(0, user, psid, &sidBufferSize, domainBuffer, 182258945Sroberto &domainBufferSize, &snu)) 183258945Sroberto return (ISC_R_NOPERM); 184258945Sroberto domainBufferSize = sizeof(domainBuffer); 185258945Sroberto if (!LookupAccountName(0, "Administrators", padminsid, 186258945Sroberto &adminSidBufferSize, domainBuffer, &domainBufferSize, &snu)) { 187258945Sroberto errval = GetLastError(); 188258945Sroberto return (ISC_R_NOPERM); 189258945Sroberto } 190258945Sroberto domainBufferSize = sizeof(domainBuffer); 191258945Sroberto if (!LookupAccountName(0, "Everyone", pothersid, 192258945Sroberto &otherSidBufferSize, domainBuffer, &domainBufferSize, &snu)) { 193258945Sroberto errval = GetLastError(); 194258945Sroberto return (ISC_R_NOPERM); 195258945Sroberto } 196258945Sroberto 197258945Sroberto caccess = access; 198258945Sroberto /* Owner check */ 199258945Sroberto 200258945Sroberto NTFSbits = 0; 201258945Sroberto if (caccess & ISC_FSACCESS_READ) 202258945Sroberto NTFSbits |= FILE_GENERIC_READ; 203258945Sroberto if (caccess & ISC_FSACCESS_WRITE) 204258945Sroberto NTFSbits |= FILE_GENERIC_WRITE; 205258945Sroberto if (caccess & ISC_FSACCESS_EXECUTE) 206258945Sroberto NTFSbits |= FILE_GENERIC_EXECUTE; 207258945Sroberto 208258945Sroberto /* For directories check the directory-specific bits */ 209258945Sroberto if (isdir == ISC_TRUE) { 210258945Sroberto if (caccess & ISC_FSACCESS_CREATECHILD) 211258945Sroberto NTFSbits |= FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE; 212258945Sroberto if (caccess & ISC_FSACCESS_DELETECHILD) 213258945Sroberto NTFSbits |= FILE_DELETE_CHILD; 214258945Sroberto if (caccess & ISC_FSACCESS_LISTDIRECTORY) 215258945Sroberto NTFSbits |= FILE_LIST_DIRECTORY; 216258945Sroberto if (caccess & ISC_FSACCESS_ACCESSCHILD) 217258945Sroberto NTFSbits |= FILE_TRAVERSE; 218258945Sroberto } 219258945Sroberto 220258945Sroberto if (NTFSbits == (FILE_GENERIC_READ | FILE_GENERIC_WRITE 221258945Sroberto | FILE_GENERIC_EXECUTE)) 222258945Sroberto NTFSbits |= FILE_ALL_ACCESS; 223258945Sroberto /* 224258945Sroberto * Owner and Administrator also get STANDARD_RIGHTS_ALL 225258945Sroberto * to ensure that they have full control 226258945Sroberto */ 227258945Sroberto 228258945Sroberto NTFSbits |= STANDARD_RIGHTS_ALL; 229258945Sroberto 230258945Sroberto /* Add the ACE to the ACL */ 231258945Sroberto if (!AddAccessAllowedAce(pacl, ACL_REVISION, NTFSbits, psid)) 232258945Sroberto return (ISC_R_NOPERM); 233258945Sroberto if (!AddAccessAllowedAce(pacl, ACL_REVISION, NTFSbits, padminsid)) 234258945Sroberto return (ISC_R_NOPERM); 235258945Sroberto 236258945Sroberto /* 237258945Sroberto * Group is ignored since we can be in multiple groups or no group 238258945Sroberto * and its meaning is not clear on Win32 239258945Sroberto */ 240258945Sroberto 241258945Sroberto caccess = caccess >> STEP; 242258945Sroberto 243258945Sroberto /* 244258945Sroberto * Other check. We translate this to be the same as Everyone 245258945Sroberto */ 246258945Sroberto 247258945Sroberto caccess = caccess >> STEP; 248258945Sroberto 249258945Sroberto NTFSbits = 0; 250258945Sroberto if (caccess & ISC_FSACCESS_READ) 251258945Sroberto NTFSbits |= FILE_GENERIC_READ; 252258945Sroberto if (caccess & ISC_FSACCESS_WRITE) 253258945Sroberto NTFSbits |= FILE_GENERIC_WRITE; 254258945Sroberto if (caccess & ISC_FSACCESS_EXECUTE) 255258945Sroberto NTFSbits |= FILE_GENERIC_EXECUTE; 256258945Sroberto 257258945Sroberto /* For directories check the directory-specific bits */ 258258945Sroberto if (isdir == TRUE) { 259258945Sroberto if (caccess & ISC_FSACCESS_CREATECHILD) 260258945Sroberto NTFSbits |= FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE; 261258945Sroberto if (caccess & ISC_FSACCESS_DELETECHILD) 262258945Sroberto NTFSbits |= FILE_DELETE_CHILD; 263258945Sroberto if (caccess & ISC_FSACCESS_LISTDIRECTORY) 264258945Sroberto NTFSbits |= FILE_LIST_DIRECTORY; 265258945Sroberto if (caccess & ISC_FSACCESS_ACCESSCHILD) 266258945Sroberto NTFSbits |= FILE_TRAVERSE; 267258945Sroberto } 268258945Sroberto /* Add the ACE to the ACL */ 269258945Sroberto if (!AddAccessAllowedAce(pacl, ACL_REVISION, NTFSbits, 270258945Sroberto pothersid)) 271258945Sroberto return (ISC_R_NOPERM); 272258945Sroberto 273258945Sroberto if (!SetSecurityDescriptorDacl(&sd, TRUE, pacl, FALSE)) 274258945Sroberto return (ISC_R_NOPERM); 275258945Sroberto if (!SetFileSecurity(filename, DACL_SECURITY_INFORMATION, &sd)) { 276258945Sroberto return (ISC_R_NOPERM); 277258945Sroberto } 278258945Sroberto 279258945Sroberto return(ISC_R_SUCCESS); 280258945Sroberto} 281258945Sroberto 282258945Srobertoisc_result_t 283258945SrobertoNTFS_fsaccess_set(const char *path, isc_fsaccess_t access, 284258945Sroberto isc_boolean_t isdir){ 285258945Sroberto 286258945Sroberto /* 287258945Sroberto * For NTFS we first need to get the name of the account under 288258945Sroberto * which BIND is running 289258945Sroberto */ 290258945Sroberto if (namelen <= 0) { 291258945Sroberto namelen = sizeof(username); 292258945Sroberto if (GetUserName(username, &namelen) == 0) 293258945Sroberto return (ISC_R_FAILURE); 294258945Sroberto } 295258945Sroberto return (NTFS_Access_Control(path, username, access, isdir)); 296258945Sroberto} 297258945Sroberto 298258945Srobertoisc_result_t 299258945Srobertoisc_fsaccess_set(const char *path, isc_fsaccess_t access) { 300258945Sroberto struct stat statb; 301258945Sroberto isc_boolean_t is_dir = ISC_FALSE; 302258945Sroberto isc_result_t result; 303258945Sroberto 304258945Sroberto if (stat(path, &statb) != 0) 305258945Sroberto return (isc__errno2result(errno)); 306258945Sroberto 307258945Sroberto if ((statb.st_mode & S_IFDIR) != 0) 308258945Sroberto is_dir = ISC_TRUE; 309258945Sroberto else if ((statb.st_mode & S_IFREG) == 0) 310258945Sroberto return (ISC_R_INVALIDFILE); 311258945Sroberto 312258945Sroberto result = check_bad_bits(access, is_dir); 313258945Sroberto if (result != ISC_R_SUCCESS) 314258945Sroberto return (result); 315258945Sroberto 316258945Sroberto /* 317258945Sroberto * Determine if this is a FAT or NTFS disk and 318258945Sroberto * call the appropriate function to set the permissions 319258945Sroberto */ 320258945Sroberto if (is_ntfs(path)) 321258945Sroberto return (NTFS_fsaccess_set(path, access, is_dir)); 322258945Sroberto else 323258945Sroberto return (FAT_fsaccess_set(path, access)); 324258945Sroberto} 325258945Sroberto 326258945Srobertoisc_result_t 327258945Srobertoisc_fsaccess_changeowner(const char *filename, const char *user) { 328258945Sroberto SECURITY_DESCRIPTOR psd; 329258945Sroberto BYTE sidBuffer[500]; 330258945Sroberto BYTE groupBuffer[500]; 331258945Sroberto PSID psid=(PSID) &sidBuffer; 332258945Sroberto DWORD sidBufferSize = sizeof(sidBuffer); 333258945Sroberto char domainBuffer[100]; 334258945Sroberto DWORD domainBufferSize = sizeof(domainBuffer); 335258945Sroberto SID_NAME_USE snu; 336258945Sroberto PSID pSidGroup = (PSID) &groupBuffer; 337258945Sroberto DWORD groupBufferSize = sizeof(groupBuffer); 338258945Sroberto 339258945Sroberto 340258945Sroberto /* 341258945Sroberto * Determine if this is a FAT or NTFS disk and 342258945Sroberto * call the appropriate function to set the ownership 343258945Sroberto * FAT disks do not have ownership attributes so it's 344258945Sroberto * a noop. 345258945Sroberto */ 346258945Sroberto if (is_ntfs(filename) == FALSE) 347258945Sroberto return (ISC_R_SUCCESS); 348258945Sroberto 349258945Sroberto if (!InitializeSecurityDescriptor(&psd, SECURITY_DESCRIPTOR_REVISION)) 350258945Sroberto return (ISC_R_NOPERM); 351258945Sroberto 352258945Sroberto if (!LookupAccountName(0, user, psid, &sidBufferSize, domainBuffer, 353258945Sroberto &domainBufferSize, &snu)) 354258945Sroberto return (ISC_R_NOPERM); 355258945Sroberto 356258945Sroberto /* Make sure administrators can get to it */ 357258945Sroberto domainBufferSize = sizeof(domainBuffer); 358258945Sroberto if (!LookupAccountName(0, "Administrators", pSidGroup, 359258945Sroberto &groupBufferSize, domainBuffer, &domainBufferSize, &snu)) 360258945Sroberto return (ISC_R_NOPERM); 361258945Sroberto 362258945Sroberto if (!SetSecurityDescriptorOwner(&psd, psid, FALSE)) 363258945Sroberto return (ISC_R_NOPERM); 364258945Sroberto 365258945Sroberto if (!SetSecurityDescriptorGroup(&psd, pSidGroup, FALSE)) 366258945Sroberto return (ISC_R_NOPERM); 367258945Sroberto 368258945Sroberto if (!SetFileSecurity(filename, 369258945Sroberto OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION, 370258945Sroberto &psd)) 371258945Sroberto return (ISC_R_NOPERM); 372258945Sroberto 373258945Sroberto return (ISC_R_SUCCESS); 374258945Sroberto} 375258945Sroberto 376