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