1290001Sglebius/*
2290001Sglebius * Copyright (C) 2004, 2007  Internet Systems Consortium, Inc. ("ISC")
3290001Sglebius * Copyright (C) 2000-2002  Internet Software Consortium.
4290001Sglebius *
5290001Sglebius * Permission to use, copy, modify, and/or distribute this software for any
6290001Sglebius * purpose with or without fee is hereby granted, provided that the above
7290001Sglebius * copyright notice and this permission notice appear in all copies.
8290001Sglebius *
9290001Sglebius * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10290001Sglebius * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11290001Sglebius * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12290001Sglebius * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13290001Sglebius * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14290001Sglebius * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15290001Sglebius * PERFORMANCE OF THIS SOFTWARE.
16290001Sglebius */
17290001Sglebius
18290001Sglebius/* $Id: fsaccess.c,v 1.15 2007/06/19 23:47:19 tbox Exp $ */
19290001Sglebius
20290001Sglebius/*
21290001Sglebius * Note that Win32 does not have the concept of files having access
22290001Sglebius * and ownership bits.  The FAT File system only has a readonly flag
23290001Sglebius * for everyone and that's all. NTFS uses ACL's which is a totally
24290001Sglebius * different concept of controlling access.
25290001Sglebius *
26290001Sglebius * This code needs to be revisited to set up proper access control for
27290001Sglebius * NTFS file systems.  Nothing can be done for FAT file systems.
28290001Sglebius */
29290001Sglebius
30290001Sglebius#include <config.h>
31290001Sglebius
32290001Sglebius#include <aclapi.h>
33290001Sglebius
34290001Sglebius#include <sys/types.h>
35290001Sglebius#include <sys/stat.h>
36290001Sglebius#include <io.h>
37290001Sglebius#include <errno.h>
38290001Sglebius
39290001Sglebius#include <isc/file.h>
40290001Sglebius#include <isc/stat.h>
41290001Sglebius
42290001Sglebius#include "errno2result.h"
43290001Sglebius
44290001Sglebius/*
45290001Sglebius * The OS-independent part of the API is in lib/isc.
46290001Sglebius */
47290001Sglebius#include "../fsaccess.c"
48290001Sglebius
49290001Sglebius/* Store the user account name locally */
50290001Sglebiusstatic char username[255] = "\0";
51290001Sglebiusstatic DWORD namelen = 0;
52290001Sglebius
53290001Sglebius/*
54290001Sglebius * In order to set or retrieve access information, we need to obtain
55290001Sglebius * the File System type.  These could be UNC-type shares.
56290001Sglebius */
57290001Sglebius
58290001SglebiusBOOL
59290001Sglebiusis_ntfs(const char * file) {
60290001Sglebius
61290001Sglebius	char drive[255];
62290001Sglebius	char FSType[20];
63290001Sglebius	char tmpbuf[256];
64290001Sglebius	char *machinename;
65290001Sglebius	char *sharename;
66290001Sglebius	char filename[1024];
67290001Sglebius
68290001Sglebius	REQUIRE(filename != NULL);
69290001Sglebius
70290001Sglebius	if (isc_file_absolutepath(file, filename,
71290001Sglebius		sizeof(filename)) != ISC_R_SUCCESS) {
72290001Sglebius		return (FALSE);
73290001Sglebius	}
74290001Sglebius
75290001Sglebius	/*
76290001Sglebius	 * Look for c:\path\... style, c:/path/... or \\computer\shar\path...
77290001Sglebius	 * the UNC style file specs
78290001Sglebius	 */
79290001Sglebius	if (isalpha(filename[0]) && filename[1] == ':' &&
80290001Sglebius		(filename[2] == '\\' || filename[2] == '/')) {
81290001Sglebius		strncpy(drive, filename, 3);
82290001Sglebius		drive[3] = '\0';
83290001Sglebius	}
84290001Sglebius
85290001Sglebius	else if ((filename[0] == '\\') && (filename[1] == '\\')) {
86290001Sglebius		/* Find the machine and share name and rebuild the UNC */
87290001Sglebius		strcpy(tmpbuf, filename);
88290001Sglebius		machinename = strtok(tmpbuf, "\\");
89290001Sglebius		sharename = strtok(NULL, "\\");
90290001Sglebius		strcpy(drive, "\\\\");
91290001Sglebius		strcat(drive, machinename);
92290001Sglebius		strcat(drive, "\\");
93290001Sglebius		strcat(drive, sharename);
94290001Sglebius		strcat(drive, "\\");
95290001Sglebius
96290001Sglebius	}
97290001Sglebius	else /* Not determinable */
98290001Sglebius		return (FALSE);
99290001Sglebius
100290001Sglebius	GetVolumeInformation(drive, NULL, 0, NULL, 0, NULL, FSType,
101290001Sglebius			     sizeof(FSType));
102290001Sglebius	if(strcmp(FSType,"NTFS") == 0)
103290001Sglebius		return (TRUE);
104290001Sglebius	else
105290001Sglebius		return (FALSE);
106290001Sglebius}
107290001Sglebius
108290001Sglebius/*
109290001Sglebius * If it's not NTFS, we assume that it is FAT and proceed
110290001Sglebius * with almost nothing to do. Only the write flag can be set or
111290001Sglebius * cleared.
112290001Sglebius */
113290001Sglebiusisc_result_t
114290001SglebiusFAT_fsaccess_set(const char *path, isc_fsaccess_t access) {
115290001Sglebius	int mode;
116290001Sglebius	isc_fsaccess_t bits;
117290001Sglebius
118290001Sglebius	/*
119290001Sglebius	 * Done with checking bad bits.  Set mode_t.
120290001Sglebius	 */
121290001Sglebius	mode = 0;
122290001Sglebius
123290001Sglebius#define SET_AND_CLEAR1(modebit) \
124290001Sglebius	if ((access & bits) != 0) { \
125290001Sglebius		mode |= modebit; \
126290001Sglebius		access &= ~bits; \
127290001Sglebius	}
128290001Sglebius#define SET_AND_CLEAR(user, group, other) \
129290001Sglebius	SET_AND_CLEAR1(user); \
130290001Sglebius	bits <<= STEP; \
131290001Sglebius	SET_AND_CLEAR1(group); \
132290001Sglebius	bits <<= STEP; \
133290001Sglebius	SET_AND_CLEAR1(other);
134290001Sglebius
135290001Sglebius	bits = ISC_FSACCESS_READ | ISC_FSACCESS_LISTDIRECTORY;
136290001Sglebius
137290001Sglebius	SET_AND_CLEAR(S_IRUSR, S_IRGRP, S_IROTH);
138290001Sglebius
139290001Sglebius	bits = ISC_FSACCESS_WRITE |
140290001Sglebius	       ISC_FSACCESS_CREATECHILD |
141290001Sglebius	       ISC_FSACCESS_DELETECHILD;
142290001Sglebius
143290001Sglebius	SET_AND_CLEAR(S_IWUSR, S_IWGRP, S_IWOTH);
144290001Sglebius
145290001Sglebius	INSIST(access == 0);
146290001Sglebius
147290001Sglebius	if (_chmod(path, mode) < 0)
148290001Sglebius		return (isc__errno2result(errno));
149290001Sglebius
150290001Sglebius	return (ISC_R_SUCCESS);
151290001Sglebius}
152290001Sglebius
153290001Sglebiusisc_result_t
154290001SglebiusNTFS_Access_Control(const char *filename, const char *user, int access,
155290001Sglebius		    isc_boolean_t isdir) {
156290001Sglebius	SECURITY_DESCRIPTOR sd;
157290001Sglebius	BYTE aclBuffer[1024];
158290001Sglebius	PACL pacl=(PACL)&aclBuffer;
159290001Sglebius	BYTE sidBuffer[100];
160290001Sglebius	PSID psid=(PSID) &sidBuffer;
161290001Sglebius	DWORD sidBufferSize = sizeof(sidBuffer);
162290001Sglebius	BYTE adminSidBuffer[100];
163290001Sglebius	PSID padminsid=(PSID) &adminSidBuffer;
164290001Sglebius	DWORD adminSidBufferSize = sizeof(adminSidBuffer);
165290001Sglebius	BYTE otherSidBuffer[100];
166290001Sglebius	PSID pothersid=(PSID) &otherSidBuffer;
167290001Sglebius	DWORD otherSidBufferSize = sizeof(otherSidBuffer);
168290001Sglebius	char domainBuffer[100];
169290001Sglebius	DWORD domainBufferSize = sizeof(domainBuffer);
170290001Sglebius	SID_NAME_USE snu;
171290001Sglebius	int errval;
172290001Sglebius	DWORD NTFSbits;
173290001Sglebius	int caccess;
174290001Sglebius
175290001Sglebius
176290001Sglebius	/* Initialize an ACL */
177290001Sglebius	if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION))
178290001Sglebius		return (ISC_R_NOPERM);
179290001Sglebius	if (!InitializeAcl(pacl, sizeof(aclBuffer), ACL_REVISION))
180290001Sglebius		return (ISC_R_NOPERM);
181290001Sglebius	if (!LookupAccountName(0, user, psid, &sidBufferSize, domainBuffer,
182290001Sglebius			  &domainBufferSize, &snu))
183290001Sglebius		return (ISC_R_NOPERM);
184290001Sglebius	domainBufferSize = sizeof(domainBuffer);
185290001Sglebius	if (!LookupAccountName(0, "Administrators", padminsid,
186290001Sglebius		&adminSidBufferSize, domainBuffer, &domainBufferSize, &snu)) {
187290001Sglebius		errval = GetLastError();
188290001Sglebius		return (ISC_R_NOPERM);
189290001Sglebius	}
190290001Sglebius	domainBufferSize = sizeof(domainBuffer);
191290001Sglebius	if (!LookupAccountName(0, "Everyone", pothersid,
192290001Sglebius		&otherSidBufferSize, domainBuffer, &domainBufferSize, &snu)) {
193290001Sglebius		errval = GetLastError();
194290001Sglebius		return (ISC_R_NOPERM);
195290001Sglebius	}
196290001Sglebius
197290001Sglebius	caccess = access;
198290001Sglebius	/* Owner check */
199290001Sglebius
200290001Sglebius	NTFSbits = 0;
201290001Sglebius	if (caccess & ISC_FSACCESS_READ)
202290001Sglebius		NTFSbits |= FILE_GENERIC_READ;
203290001Sglebius	if (caccess & ISC_FSACCESS_WRITE)
204290001Sglebius		NTFSbits |= FILE_GENERIC_WRITE;
205290001Sglebius	if (caccess & ISC_FSACCESS_EXECUTE)
206290001Sglebius		NTFSbits |= FILE_GENERIC_EXECUTE;
207290001Sglebius
208290001Sglebius	/* For directories check the directory-specific bits */
209290001Sglebius	if (isdir == ISC_TRUE) {
210290001Sglebius		if (caccess & ISC_FSACCESS_CREATECHILD)
211290001Sglebius			NTFSbits |= FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE;
212290001Sglebius		if (caccess & ISC_FSACCESS_DELETECHILD)
213290001Sglebius			NTFSbits |= FILE_DELETE_CHILD;
214290001Sglebius		if (caccess & ISC_FSACCESS_LISTDIRECTORY)
215290001Sglebius			NTFSbits |= FILE_LIST_DIRECTORY;
216290001Sglebius		if (caccess & ISC_FSACCESS_ACCESSCHILD)
217290001Sglebius			NTFSbits |= FILE_TRAVERSE;
218290001Sglebius	}
219290001Sglebius
220290001Sglebius	if (NTFSbits == (FILE_GENERIC_READ | FILE_GENERIC_WRITE
221290001Sglebius		     | FILE_GENERIC_EXECUTE))
222290001Sglebius		     NTFSbits |= FILE_ALL_ACCESS;
223290001Sglebius	/*
224290001Sglebius	 * Owner and Administrator also get STANDARD_RIGHTS_ALL
225290001Sglebius	 * to ensure that they have full control
226290001Sglebius	 */
227290001Sglebius
228290001Sglebius	NTFSbits |= STANDARD_RIGHTS_ALL;
229290001Sglebius
230290001Sglebius	/* Add the ACE to the ACL */
231290001Sglebius	if (!AddAccessAllowedAce(pacl, ACL_REVISION, NTFSbits, psid))
232290001Sglebius		return (ISC_R_NOPERM);
233290001Sglebius	if (!AddAccessAllowedAce(pacl, ACL_REVISION, NTFSbits, padminsid))
234290001Sglebius		return (ISC_R_NOPERM);
235290001Sglebius
236290001Sglebius	/*
237290001Sglebius	 * Group is ignored since we can be in multiple groups or no group
238290001Sglebius	 * and its meaning is not clear on Win32
239290001Sglebius	 */
240290001Sglebius
241290001Sglebius	caccess = caccess >> STEP;
242290001Sglebius
243290001Sglebius	/*
244290001Sglebius	 * Other check.  We translate this to be the same as Everyone
245290001Sglebius	 */
246290001Sglebius
247290001Sglebius	caccess = caccess >> STEP;
248290001Sglebius
249290001Sglebius	NTFSbits = 0;
250290001Sglebius	if (caccess & ISC_FSACCESS_READ)
251290001Sglebius		NTFSbits |= FILE_GENERIC_READ;
252290001Sglebius	if (caccess & ISC_FSACCESS_WRITE)
253290001Sglebius		NTFSbits |= FILE_GENERIC_WRITE;
254290001Sglebius	if (caccess & ISC_FSACCESS_EXECUTE)
255290001Sglebius		NTFSbits |= FILE_GENERIC_EXECUTE;
256290001Sglebius
257290001Sglebius	/* For directories check the directory-specific bits */
258290001Sglebius	if (isdir == TRUE) {
259290001Sglebius		if (caccess & ISC_FSACCESS_CREATECHILD)
260290001Sglebius			NTFSbits |= FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE;
261290001Sglebius		if (caccess & ISC_FSACCESS_DELETECHILD)
262290001Sglebius			NTFSbits |= FILE_DELETE_CHILD;
263290001Sglebius		if (caccess & ISC_FSACCESS_LISTDIRECTORY)
264290001Sglebius			NTFSbits |= FILE_LIST_DIRECTORY;
265290001Sglebius		if (caccess & ISC_FSACCESS_ACCESSCHILD)
266290001Sglebius			NTFSbits |= FILE_TRAVERSE;
267290001Sglebius	}
268290001Sglebius	/* Add the ACE to the ACL */
269290001Sglebius	if (!AddAccessAllowedAce(pacl, ACL_REVISION, NTFSbits,
270290001Sglebius				 pothersid))
271290001Sglebius		return (ISC_R_NOPERM);
272290001Sglebius
273290001Sglebius	if (!SetSecurityDescriptorDacl(&sd, TRUE, pacl, FALSE))
274290001Sglebius		return (ISC_R_NOPERM);
275290001Sglebius	if (!SetFileSecurity(filename, DACL_SECURITY_INFORMATION, &sd)) {
276290001Sglebius		return (ISC_R_NOPERM);
277290001Sglebius	}
278290001Sglebius
279290001Sglebius	return(ISC_R_SUCCESS);
280290001Sglebius}
281290001Sglebius
282290001Sglebiusisc_result_t
283290001SglebiusNTFS_fsaccess_set(const char *path, isc_fsaccess_t access,
284290001Sglebius		  isc_boolean_t isdir){
285290001Sglebius
286290001Sglebius	/*
287290001Sglebius	 * For NTFS we first need to get the name of the account under
288290001Sglebius	 * which BIND is running
289290001Sglebius	 */
290290001Sglebius	if (namelen <= 0) {
291290001Sglebius		namelen = sizeof(username);
292290001Sglebius		if (GetUserName(username, &namelen) == 0)
293290001Sglebius			return (ISC_R_FAILURE);
294290001Sglebius	}
295290001Sglebius	return (NTFS_Access_Control(path, username, access, isdir));
296290001Sglebius}
297290001Sglebius
298290001Sglebiusisc_result_t
299290001Sglebiusisc_fsaccess_set(const char *path, isc_fsaccess_t access) {
300290001Sglebius	struct stat statb;
301290001Sglebius	isc_boolean_t is_dir = ISC_FALSE;
302290001Sglebius	isc_result_t result;
303290001Sglebius
304290001Sglebius	if (stat(path, &statb) != 0)
305290001Sglebius		return (isc__errno2result(errno));
306290001Sglebius
307290001Sglebius	if ((statb.st_mode & S_IFDIR) != 0)
308290001Sglebius		is_dir = ISC_TRUE;
309290001Sglebius	else if ((statb.st_mode & S_IFREG) == 0)
310290001Sglebius		return (ISC_R_INVALIDFILE);
311290001Sglebius
312290001Sglebius	result = check_bad_bits(access, is_dir);
313290001Sglebius	if (result != ISC_R_SUCCESS)
314290001Sglebius		return (result);
315290001Sglebius
316290001Sglebius	/*
317290001Sglebius	 * Determine if this is a FAT or NTFS disk and
318290001Sglebius	 * call the appropriate function to set the permissions
319290001Sglebius	 */
320290001Sglebius	if (is_ntfs(path))
321290001Sglebius		return (NTFS_fsaccess_set(path, access, is_dir));
322290001Sglebius	else
323290001Sglebius		return (FAT_fsaccess_set(path, access));
324290001Sglebius}
325290001Sglebius
326290001Sglebiusisc_result_t
327290001Sglebiusisc_fsaccess_changeowner(const char *filename, const char *user) {
328290001Sglebius	SECURITY_DESCRIPTOR psd;
329290001Sglebius	BYTE sidBuffer[500];
330290001Sglebius	BYTE groupBuffer[500];
331290001Sglebius	PSID psid=(PSID) &sidBuffer;
332290001Sglebius	DWORD sidBufferSize = sizeof(sidBuffer);
333290001Sglebius	char domainBuffer[100];
334290001Sglebius	DWORD domainBufferSize = sizeof(domainBuffer);
335290001Sglebius	SID_NAME_USE snu;
336290001Sglebius	PSID pSidGroup = (PSID) &groupBuffer;
337290001Sglebius	DWORD groupBufferSize = sizeof(groupBuffer);
338290001Sglebius
339290001Sglebius
340290001Sglebius	/*
341290001Sglebius	 * Determine if this is a FAT or NTFS disk and
342290001Sglebius	 * call the appropriate function to set the ownership
343290001Sglebius	 * FAT disks do not have ownership attributes so it's
344290001Sglebius	 * a noop.
345290001Sglebius	 */
346290001Sglebius	if (is_ntfs(filename) == FALSE)
347290001Sglebius		return (ISC_R_SUCCESS);
348290001Sglebius
349290001Sglebius	if (!InitializeSecurityDescriptor(&psd, SECURITY_DESCRIPTOR_REVISION))
350290001Sglebius		return (ISC_R_NOPERM);
351290001Sglebius
352290001Sglebius	if (!LookupAccountName(0, user, psid, &sidBufferSize, domainBuffer,
353290001Sglebius		&domainBufferSize, &snu))
354290001Sglebius		return (ISC_R_NOPERM);
355290001Sglebius
356290001Sglebius	/* Make sure administrators can get to it */
357290001Sglebius	domainBufferSize = sizeof(domainBuffer);
358290001Sglebius	if (!LookupAccountName(0, "Administrators", pSidGroup,
359290001Sglebius		&groupBufferSize, domainBuffer, &domainBufferSize, &snu))
360290001Sglebius		return (ISC_R_NOPERM);
361290001Sglebius
362290001Sglebius	if (!SetSecurityDescriptorOwner(&psd, psid, FALSE))
363290001Sglebius		return (ISC_R_NOPERM);
364290001Sglebius
365290001Sglebius	if (!SetSecurityDescriptorGroup(&psd, pSidGroup, FALSE))
366290001Sglebius		return (ISC_R_NOPERM);
367290001Sglebius
368290001Sglebius	if (!SetFileSecurity(filename,
369290001Sglebius		OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION,
370290001Sglebius		&psd))
371290001Sglebius		return (ISC_R_NOPERM);
372290001Sglebius
373290001Sglebius	return (ISC_R_SUCCESS);
374290001Sglebius}
375290001Sglebius
376