1 /*
2  * Copyright (c) 2004, 2007, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 #include <stdio.h>
25 #include <windows.h>
26 #include <malloc.h>
27 #include <string.h>
28 
29 /*
30  * Simple Windows utility to remove all non-owner access to a given
31  * file - suitable for NT/2000/XP only.
32  */
33 
34 
35 /*
36  * Access mask to represent any file access
37  */
38 #define ANY_ACCESS (FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE)
39 
40 
41 /*
42  * Print error message to stderr
43  */
printLastError(const char * msg)44 static void printLastError(const char* msg) {
45     int len;
46     char buf[128];
47     DWORD errval;
48 
49     buf[0] = '\0';
50     len = sizeof(buf);
51 
52     errval = GetLastError();
53     if (errval != 0) {
54         int n = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
55                               NULL, errval,
56                               0, buf, len, NULL);
57         if (n > 3) {
58             /* Drop final '.', CR, LF */
59             if (buf[n - 1] == '\n') n--;
60             if (buf[n - 1] == '\r') n--;
61             if (buf[n - 1] == '.') n--;
62             buf[n] = '\0';
63         }
64     }
65 
66     if (strlen(buf) > 0) {
67         fprintf(stderr, "revokeall %s: %s\n", msg, buf);
68     } else {
69         fprintf(stderr, "revokeall %s\n", msg);
70     }
71 }
72 
73 
74 
75 /*
76  * Return a string that includes all the components of a given SID.
77  * See here for a description of the SID components :-
78  * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/security/security/sid_components.asp
79  */
getTextualSid(SID * sid)80 static char *getTextualSid(SID* sid) {
81     SID_IDENTIFIER_AUTHORITY* sia;
82     DWORD i, count;
83     DWORD len;
84     char* name;
85 
86     /*
87      * Get the identifier authority and the number of sub-authorities
88      */
89     sia = GetSidIdentifierAuthority(sid);
90     count = *GetSidSubAuthorityCount(sid);
91 
92     /*
93      * Allocate buffer for the string - buffer is :-
94      * S-SID_REVISION- + identifierAuthority- + subauthorities- + NULL
95      */
96     len=(15 + 12 + (12 * count) + 1) * sizeof(char);
97     name = (char*)malloc(len);
98     if (name == NULL) {
99         return NULL;
100     }
101 
102     // S-SID_REVISION
103     sprintf(name, "S-%lu-", SID_REVISION );
104 
105     // Identifier authority
106     if ((sia->Value[0] != 0) || (sia->Value[1] != 0))
107     {
108         sprintf(name + strlen(name), "0x%02hx%02hx%02hx%02hx%02hx%02hx",
109                 (USHORT)sia->Value[0],
110                 (USHORT)sia->Value[1],
111                 (USHORT)sia->Value[2],
112                 (USHORT)sia->Value[3],
113                 (USHORT)sia->Value[4],
114                 (USHORT)sia->Value[5]);
115     }
116     else
117     {
118         sprintf(name + strlen(name), "%lu",
119                 (ULONG)(sia->Value[5]      )   +
120                 (ULONG)(sia->Value[4] <<  8)   +
121                 (ULONG)(sia->Value[3] << 16)   +
122                 (ULONG)(sia->Value[2] << 24)   );
123     }
124 
125     // finally, the sub-authorities
126     for (i=0 ; i<count; i++) {
127         sprintf(name + strlen(name), "-%lu",
128                 *GetSidSubAuthority(sid, i) );
129     }
130 
131     return name;
132 }
133 
134 /*
135  * Returns a string to represent the given security identifier (SID).
136  * If the account is known to the local computer then the account
137  * domain is returned. The format will be \\name or domain\\name depending
138  * on if the computer belongs to a domain.
139  * If the account name is not known then the textual representation of
140  * SID is returned -- eg: S-1-5-21-2818032319-470147023-1036452850-13037.
141  */
getSIDString(SID * sid)142 static char *getSIDString(SID* sid) {
143     char domain[255];
144     char name[255];
145     DWORD domainLen = sizeof(domain);
146     DWORD nameLen = sizeof(name);
147     SID_NAME_USE use;
148 
149     if(!IsValidSid(sid)) {
150         return strdup("<Invalid SID>");
151     }
152 
153     if (LookupAccountSid(NULL, sid, name, &nameLen, domain, &domainLen, &use)) {
154         int len = strlen(name) + strlen(domain) + 3;
155         char* s = (char*)malloc(len);
156         if (s != NULL) {
157             strcpy(s, domain);
158             strcat(s, "\\\\");
159             strcat(s, name);
160         }
161         return s;
162     } else {
163         return getTextualSid(sid);
164     }
165 }
166 
167 
168 
169 /*
170  * Returns 1 if the specified file is on a file system that supports
171  * persistent ACLs (On NTFS file systems returns true, on FAT32 file systems
172  * returns false), otherwise 0. Returns -1 if error.
173  */
isSecuritySupported(const char * path)174 static int isSecuritySupported(const char* path) {
175     char* root;
176     char* p;
177     BOOL res;
178     DWORD dwMaxComponentLength;
179     DWORD dwFlags;
180     char fsName[128];
181     DWORD fsNameLength;
182 
183     /*
184      * Get root directory. For UNCs the slash after the share name is required.
185      */
186     root = strdup(path);
187     if (*root == '\\') {
188         /*
189          * \\server\share\file ==> \\server\share\
190          */
191         int slashskip = 3;
192         p = root;
193         while ((*p == '\\') && (slashskip > 0)) {
194             char* p2;
195             p++;
196             p2 = strchr(p, '\\');
197             if ((p2 == NULL) || (*p2 != '\\')) {
198                 free(root);
199                 fprintf(stderr, "Malformed UNC");
200                 return -1;
201             }
202             p = p2;
203             slashskip--;
204         }
205         if (slashskip != 0) {
206             free(root);
207             fprintf(stderr, "Malformed UNC");
208             return -1;
209         }
210         p++;
211         *p = '\0';
212 
213     } else {
214         p = strchr(root, '\\');
215 
216         /*
217          * Relative path so use current directory
218          */
219         if (p == NULL) {
220             free(root);
221             root = malloc(255);
222             if (GetCurrentDirectory(255, root) == 0) {
223                 printLastError("GetCurrentDirectory failed");
224                 return -1;
225             }
226             p = strchr(root, '\\');
227             if (p == NULL) {
228                 fprintf(stderr, "GetCurrentDirectory doesn't include drive letter!!!!\n");
229                 return -1;
230             }
231         }
232         p++;
233         *p = '\0';
234     }
235 
236     /*
237      * Get the volume information - this gives us the file system file and
238      * also tells us if the file system supports persistent ACLs.
239      */
240     fsNameLength = sizeof(fsName)-1;
241     res = GetVolumeInformation(root,
242                                NULL,        // address of name of the volume, can be NULL
243                                0,           // length of volume name
244                                NULL,        // address of volume serial number, can be NULL
245                                &dwMaxComponentLength,
246                                &dwFlags,
247                                fsName,
248                                fsNameLength);
249     if (res == 0) {
250         printLastError("GetVolumeInformation failed");
251         free(root);
252         return -1;
253     }
254 
255     free(root);
256     return (dwFlags & FS_PERSISTENT_ACLS) ? 1 : 0;
257 }
258 
259 
260 /*
261  * Returns the security descriptor for a file.
262  */
getFileSecurityDescriptor(const char * path)263 static SECURITY_DESCRIPTOR* getFileSecurityDescriptor(const char* path) {
264     SECURITY_DESCRIPTOR* sd;
265     DWORD len = 0;
266     SECURITY_INFORMATION info =
267         OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
268 
269     GetFileSecurity(path, info , 0, 0, &len);
270     if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
271         printLastError("GetFileSecurity failed");
272         return NULL;
273     }
274     sd = (SECURITY_DESCRIPTOR *)malloc(len);
275     if (sd == NULL) {
276         fprintf(stderr, "Out of memory");
277     } else {
278         if (!GetFileSecurity(path, info, sd, len, &len)) {
279             printLastError("GetFileSecurity failed");
280             free(sd);
281             return NULL;
282         }
283     }
284     return sd;
285 }
286 
287 
288 /*
289  * Revoke all access to the specific file
290  */
revokeAll(const char * path)291 static int revokeAll(const char* path) {
292     SECURITY_DESCRIPTOR* sd;
293     SID* owner;
294     ACL *acl;
295     BOOL defaulted, present;
296     ACL_SIZE_INFORMATION acl_size_info;
297     DWORD i, count;
298     char* str;
299 
300     /*
301      * Get security descriptor for file; From security descriptor get the
302      * owner SID, and the DACL.
303      */
304     sd = getFileSecurityDescriptor(path);
305     if (sd == NULL) {
306         return -1;      /* error already reported */
307     }
308     if (!GetSecurityDescriptorOwner(sd, &owner, &defaulted)) {
309         printLastError("GetSecurityDescriptorOwner failed");
310         return -1;
311     }
312     str = getSIDString(owner);
313     if (str != NULL) {
314         printf("owner: %s\n", str);
315         free(str);
316     }
317     if (!GetSecurityDescriptorDacl(sd, &present, &acl, &defaulted)) {
318         printLastError("GetSecurityDescriptorDacl failed");
319         return -1;
320     }
321     if (!present) {
322         fprintf(stderr, "Security descriptor does not contain a DACL");
323         return -1;
324     }
325 
326     /*
327      * If DACL is NULL there is no access to the file - we are done
328      */
329     if (acl == NULL) {
330         return 1;
331     }
332 
333     /*
334      * Iterate over the ACEs. For each "allow" type check that the SID
335      * matches the owner - if not we remove the ACE from the ACL
336      */
337     if (!GetAclInformation(acl, (void *) &acl_size_info, sizeof(acl_size_info),
338                                   AclSizeInformation)) {
339         printLastError("GetAclInformation failed");
340         return -1;
341     }
342     count = acl_size_info.AceCount;
343     i = 0;
344     while (count > 0) {
345         void* ace;
346         ACCESS_ALLOWED_ACE *access;
347         SID* sid;
348         BOOL deleted;
349 
350         if (!GetAce(acl, i, &ace)) {
351             printLastError("GetAce failed");
352             return -1;
353         }
354         if (((ACCESS_ALLOWED_ACE *)ace)->Header.AceType != ACCESS_ALLOWED_ACE_TYPE) {
355             continue;
356         }
357         access = (ACCESS_ALLOWED_ACE *)ace;
358         sid = (SID *) &access->SidStart;
359 
360 
361         deleted = FALSE;
362         if (!EqualSid(owner, sid)) {
363             /*
364              * If the ACE allows any access then the file then we
365              * delete it.
366              */
367             if (access->Mask & ANY_ACCESS) {
368                 str = getSIDString(sid);
369                 if (str != NULL) {
370                     printf("remove ALLOW %s\n", str);
371                     free(str);
372                 }
373                 if (DeleteAce(acl, i) == 0) {
374                     printLastError("DeleteAce failed");
375                     return -1;
376                 }
377                 deleted = TRUE;
378             }
379         }
380 
381         if (!deleted) {
382             str = getSIDString(sid);
383             if (str != NULL) {
384                 printf("ALLOW %s (access mask=%x)\n", str, access->Mask);
385                 free(str);
386             }
387 
388             /* onto the next ACE */
389             i++;
390         }
391         count--;
392     }
393 
394     /*
395      * No changes - only owner has access
396      */
397     if (i == acl_size_info.AceCount) {
398         printf("No changes.\n");
399         return 1;
400     }
401 
402     /*
403      * Create security descriptor and set its DACL to the version
404      * that we just edited
405      */
406     if (!InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION)) {
407         printLastError("InitializeSecurityDescriptor failed");
408         return -1;
409     }
410     if (!SetSecurityDescriptorDacl(sd, present, acl, defaulted)) {
411         printLastError("SetSecurityDescriptorDacl failed");
412         return -1;
413     }
414     if (!SetFileSecurity(path, DACL_SECURITY_INFORMATION, sd)) {
415         printLastError("SetFileSecurity failed");
416         return -1;
417     }
418 
419     printf("File updated.\n");
420 
421     return 1;
422 }
423 
424 /*
425  * Convert slashes in the pathname to backslashes if needed.
426  */
convert_path(const char * p)427 static char* convert_path(const char* p) {
428    int i = 0;
429    char* path = strdup(p);
430    while (p[i] != '\0') {
431        if (p[i] == '/') {
432            path[i] = '\\';
433        }
434        i++;
435    }
436    return path;
437 }
438 
439 /*
440  * Usage: revokeall file
441  */
main(int argc,char * argv[])442 int main( int argc, char *argv[])
443 {
444     int rc;
445     const char* path;
446 
447     if (argc != 2) {
448         fprintf(stderr, "Usage: %s file\n", argv[0]);
449         return -1;
450     }
451     path = convert_path(argv[1]);
452     printf("Revoking all non-owner access to %s\n", path);
453     rc = isSecuritySupported(path);
454     if (rc != 1) {
455         if (rc == 0) {
456             printf("File security not supported on this file system\n");
457         }
458         return rc;
459     } else {
460         return revokeAll(path);
461     }
462 }
463