fsaccess.c revision 290001
1/*
2 * Copyright (C) 2004, 2007  Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2000-2002  Internet Software Consortium.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/* $Id: fsaccess.c,v 1.15 2007/06/19 23:47:19 tbox Exp $ */
19
20/*
21 * Note that Win32 does not have the concept of files having access
22 * and ownership bits.  The FAT File system only has a readonly flag
23 * for everyone and that's all. NTFS uses ACL's which is a totally
24 * different concept of controlling access.
25 *
26 * This code needs to be revisited to set up proper access control for
27 * NTFS file systems.  Nothing can be done for FAT file systems.
28 */
29
30#include <config.h>
31
32#include <aclapi.h>
33
34#include <sys/types.h>
35#include <sys/stat.h>
36#include <io.h>
37#include <errno.h>
38
39#include <isc/file.h>
40#include <isc/stat.h>
41
42#include "errno2result.h"
43
44/*
45 * The OS-independent part of the API is in lib/isc.
46 */
47#include "../fsaccess.c"
48
49/* Store the user account name locally */
50static char username[255] = "\0";
51static DWORD namelen = 0;
52
53/*
54 * In order to set or retrieve access information, we need to obtain
55 * the File System type.  These could be UNC-type shares.
56 */
57
58BOOL
59is_ntfs(const char * file) {
60
61	char drive[255];
62	char FSType[20];
63	char tmpbuf[256];
64	char *machinename;
65	char *sharename;
66	char filename[1024];
67
68	REQUIRE(filename != NULL);
69
70	if (isc_file_absolutepath(file, filename,
71		sizeof(filename)) != ISC_R_SUCCESS) {
72		return (FALSE);
73	}
74
75	/*
76	 * Look for c:\path\... style, c:/path/... or \\computer\shar\path...
77	 * the UNC style file specs
78	 */
79	if (isalpha(filename[0]) && filename[1] == ':' &&
80		(filename[2] == '\\' || filename[2] == '/')) {
81		strncpy(drive, filename, 3);
82		drive[3] = '\0';
83	}
84
85	else if ((filename[0] == '\\') && (filename[1] == '\\')) {
86		/* Find the machine and share name and rebuild the UNC */
87		strcpy(tmpbuf, filename);
88		machinename = strtok(tmpbuf, "\\");
89		sharename = strtok(NULL, "\\");
90		strcpy(drive, "\\\\");
91		strcat(drive, machinename);
92		strcat(drive, "\\");
93		strcat(drive, sharename);
94		strcat(drive, "\\");
95
96	}
97	else /* Not determinable */
98		return (FALSE);
99
100	GetVolumeInformation(drive, NULL, 0, NULL, 0, NULL, FSType,
101			     sizeof(FSType));
102	if(strcmp(FSType,"NTFS") == 0)
103		return (TRUE);
104	else
105		return (FALSE);
106}
107
108/*
109 * If it's not NTFS, we assume that it is FAT and proceed
110 * with almost nothing to do. Only the write flag can be set or
111 * cleared.
112 */
113isc_result_t
114FAT_fsaccess_set(const char *path, isc_fsaccess_t access) {
115	int mode;
116	isc_fsaccess_t bits;
117
118	/*
119	 * Done with checking bad bits.  Set mode_t.
120	 */
121	mode = 0;
122
123#define SET_AND_CLEAR1(modebit) \
124	if ((access & bits) != 0) { \
125		mode |= modebit; \
126		access &= ~bits; \
127	}
128#define SET_AND_CLEAR(user, group, other) \
129	SET_AND_CLEAR1(user); \
130	bits <<= STEP; \
131	SET_AND_CLEAR1(group); \
132	bits <<= STEP; \
133	SET_AND_CLEAR1(other);
134
135	bits = ISC_FSACCESS_READ | ISC_FSACCESS_LISTDIRECTORY;
136
137	SET_AND_CLEAR(S_IRUSR, S_IRGRP, S_IROTH);
138
139	bits = ISC_FSACCESS_WRITE |
140	       ISC_FSACCESS_CREATECHILD |
141	       ISC_FSACCESS_DELETECHILD;
142
143	SET_AND_CLEAR(S_IWUSR, S_IWGRP, S_IWOTH);
144
145	INSIST(access == 0);
146
147	if (_chmod(path, mode) < 0)
148		return (isc__errno2result(errno));
149
150	return (ISC_R_SUCCESS);
151}
152
153isc_result_t
154NTFS_Access_Control(const char *filename, const char *user, int access,
155		    isc_boolean_t isdir) {
156	SECURITY_DESCRIPTOR sd;
157	BYTE aclBuffer[1024];
158	PACL pacl=(PACL)&aclBuffer;
159	BYTE sidBuffer[100];
160	PSID psid=(PSID) &sidBuffer;
161	DWORD sidBufferSize = sizeof(sidBuffer);
162	BYTE adminSidBuffer[100];
163	PSID padminsid=(PSID) &adminSidBuffer;
164	DWORD adminSidBufferSize = sizeof(adminSidBuffer);
165	BYTE otherSidBuffer[100];
166	PSID pothersid=(PSID) &otherSidBuffer;
167	DWORD otherSidBufferSize = sizeof(otherSidBuffer);
168	char domainBuffer[100];
169	DWORD domainBufferSize = sizeof(domainBuffer);
170	SID_NAME_USE snu;
171	int errval;
172	DWORD NTFSbits;
173	int caccess;
174
175
176	/* Initialize an ACL */
177	if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION))
178		return (ISC_R_NOPERM);
179	if (!InitializeAcl(pacl, sizeof(aclBuffer), ACL_REVISION))
180		return (ISC_R_NOPERM);
181	if (!LookupAccountName(0, user, psid, &sidBufferSize, domainBuffer,
182			  &domainBufferSize, &snu))
183		return (ISC_R_NOPERM);
184	domainBufferSize = sizeof(domainBuffer);
185	if (!LookupAccountName(0, "Administrators", padminsid,
186		&adminSidBufferSize, domainBuffer, &domainBufferSize, &snu)) {
187		errval = GetLastError();
188		return (ISC_R_NOPERM);
189	}
190	domainBufferSize = sizeof(domainBuffer);
191	if (!LookupAccountName(0, "Everyone", pothersid,
192		&otherSidBufferSize, domainBuffer, &domainBufferSize, &snu)) {
193		errval = GetLastError();
194		return (ISC_R_NOPERM);
195	}
196
197	caccess = access;
198	/* Owner check */
199
200	NTFSbits = 0;
201	if (caccess & ISC_FSACCESS_READ)
202		NTFSbits |= FILE_GENERIC_READ;
203	if (caccess & ISC_FSACCESS_WRITE)
204		NTFSbits |= FILE_GENERIC_WRITE;
205	if (caccess & ISC_FSACCESS_EXECUTE)
206		NTFSbits |= FILE_GENERIC_EXECUTE;
207
208	/* For directories check the directory-specific bits */
209	if (isdir == ISC_TRUE) {
210		if (caccess & ISC_FSACCESS_CREATECHILD)
211			NTFSbits |= FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE;
212		if (caccess & ISC_FSACCESS_DELETECHILD)
213			NTFSbits |= FILE_DELETE_CHILD;
214		if (caccess & ISC_FSACCESS_LISTDIRECTORY)
215			NTFSbits |= FILE_LIST_DIRECTORY;
216		if (caccess & ISC_FSACCESS_ACCESSCHILD)
217			NTFSbits |= FILE_TRAVERSE;
218	}
219
220	if (NTFSbits == (FILE_GENERIC_READ | FILE_GENERIC_WRITE
221		     | FILE_GENERIC_EXECUTE))
222		     NTFSbits |= FILE_ALL_ACCESS;
223	/*
224	 * Owner and Administrator also get STANDARD_RIGHTS_ALL
225	 * to ensure that they have full control
226	 */
227
228	NTFSbits |= STANDARD_RIGHTS_ALL;
229
230	/* Add the ACE to the ACL */
231	if (!AddAccessAllowedAce(pacl, ACL_REVISION, NTFSbits, psid))
232		return (ISC_R_NOPERM);
233	if (!AddAccessAllowedAce(pacl, ACL_REVISION, NTFSbits, padminsid))
234		return (ISC_R_NOPERM);
235
236	/*
237	 * Group is ignored since we can be in multiple groups or no group
238	 * and its meaning is not clear on Win32
239	 */
240
241	caccess = caccess >> STEP;
242
243	/*
244	 * Other check.  We translate this to be the same as Everyone
245	 */
246
247	caccess = caccess >> STEP;
248
249	NTFSbits = 0;
250	if (caccess & ISC_FSACCESS_READ)
251		NTFSbits |= FILE_GENERIC_READ;
252	if (caccess & ISC_FSACCESS_WRITE)
253		NTFSbits |= FILE_GENERIC_WRITE;
254	if (caccess & ISC_FSACCESS_EXECUTE)
255		NTFSbits |= FILE_GENERIC_EXECUTE;
256
257	/* For directories check the directory-specific bits */
258	if (isdir == TRUE) {
259		if (caccess & ISC_FSACCESS_CREATECHILD)
260			NTFSbits |= FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE;
261		if (caccess & ISC_FSACCESS_DELETECHILD)
262			NTFSbits |= FILE_DELETE_CHILD;
263		if (caccess & ISC_FSACCESS_LISTDIRECTORY)
264			NTFSbits |= FILE_LIST_DIRECTORY;
265		if (caccess & ISC_FSACCESS_ACCESSCHILD)
266			NTFSbits |= FILE_TRAVERSE;
267	}
268	/* Add the ACE to the ACL */
269	if (!AddAccessAllowedAce(pacl, ACL_REVISION, NTFSbits,
270				 pothersid))
271		return (ISC_R_NOPERM);
272
273	if (!SetSecurityDescriptorDacl(&sd, TRUE, pacl, FALSE))
274		return (ISC_R_NOPERM);
275	if (!SetFileSecurity(filename, DACL_SECURITY_INFORMATION, &sd)) {
276		return (ISC_R_NOPERM);
277	}
278
279	return(ISC_R_SUCCESS);
280}
281
282isc_result_t
283NTFS_fsaccess_set(const char *path, isc_fsaccess_t access,
284		  isc_boolean_t isdir){
285
286	/*
287	 * For NTFS we first need to get the name of the account under
288	 * which BIND is running
289	 */
290	if (namelen <= 0) {
291		namelen = sizeof(username);
292		if (GetUserName(username, &namelen) == 0)
293			return (ISC_R_FAILURE);
294	}
295	return (NTFS_Access_Control(path, username, access, isdir));
296}
297
298isc_result_t
299isc_fsaccess_set(const char *path, isc_fsaccess_t access) {
300	struct stat statb;
301	isc_boolean_t is_dir = ISC_FALSE;
302	isc_result_t result;
303
304	if (stat(path, &statb) != 0)
305		return (isc__errno2result(errno));
306
307	if ((statb.st_mode & S_IFDIR) != 0)
308		is_dir = ISC_TRUE;
309	else if ((statb.st_mode & S_IFREG) == 0)
310		return (ISC_R_INVALIDFILE);
311
312	result = check_bad_bits(access, is_dir);
313	if (result != ISC_R_SUCCESS)
314		return (result);
315
316	/*
317	 * Determine if this is a FAT or NTFS disk and
318	 * call the appropriate function to set the permissions
319	 */
320	if (is_ntfs(path))
321		return (NTFS_fsaccess_set(path, access, is_dir));
322	else
323		return (FAT_fsaccess_set(path, access));
324}
325
326isc_result_t
327isc_fsaccess_changeowner(const char *filename, const char *user) {
328	SECURITY_DESCRIPTOR psd;
329	BYTE sidBuffer[500];
330	BYTE groupBuffer[500];
331	PSID psid=(PSID) &sidBuffer;
332	DWORD sidBufferSize = sizeof(sidBuffer);
333	char domainBuffer[100];
334	DWORD domainBufferSize = sizeof(domainBuffer);
335	SID_NAME_USE snu;
336	PSID pSidGroup = (PSID) &groupBuffer;
337	DWORD groupBufferSize = sizeof(groupBuffer);
338
339
340	/*
341	 * Determine if this is a FAT or NTFS disk and
342	 * call the appropriate function to set the ownership
343	 * FAT disks do not have ownership attributes so it's
344	 * a noop.
345	 */
346	if (is_ntfs(filename) == FALSE)
347		return (ISC_R_SUCCESS);
348
349	if (!InitializeSecurityDescriptor(&psd, SECURITY_DESCRIPTOR_REVISION))
350		return (ISC_R_NOPERM);
351
352	if (!LookupAccountName(0, user, psid, &sidBufferSize, domainBuffer,
353		&domainBufferSize, &snu))
354		return (ISC_R_NOPERM);
355
356	/* Make sure administrators can get to it */
357	domainBufferSize = sizeof(domainBuffer);
358	if (!LookupAccountName(0, "Administrators", pSidGroup,
359		&groupBufferSize, domainBuffer, &domainBufferSize, &snu))
360		return (ISC_R_NOPERM);
361
362	if (!SetSecurityDescriptorOwner(&psd, psid, FALSE))
363		return (ISC_R_NOPERM);
364
365	if (!SetSecurityDescriptorGroup(&psd, pSidGroup, FALSE))
366		return (ISC_R_NOPERM);
367
368	if (!SetFileSecurity(filename,
369		OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION,
370		&psd))
371		return (ISC_R_NOPERM);
372
373	return (ISC_R_SUCCESS);
374}
375
376