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