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