xref: /NextBSD/contrib/ntp/lib/isc/win32/fsaccess.c (revision 287e3b14e9552995def1802ec9c5034f4adf28ec)
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 */
50 static char username[255] = "\0";
51 static 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 
58 BOOL
is_ntfs(const char * file)59 is_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  */
113 isc_result_t
FAT_fsaccess_set(const char * path,isc_fsaccess_t access)114 FAT_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 
153 isc_result_t
NTFS_Access_Control(const char * filename,const char * user,int access,isc_boolean_t isdir)154 NTFS_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 
282 isc_result_t
NTFS_fsaccess_set(const char * path,isc_fsaccess_t access,isc_boolean_t isdir)283 NTFS_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 
298 isc_result_t
isc_fsaccess_set(const char * path,isc_fsaccess_t access)299 isc_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 
326 isc_result_t
isc_fsaccess_changeowner(const char * filename,const char * user)327 isc_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