1 /*
2  * Copyright (c) 2004, 2011, 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.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 #include <windows.h>
27 #include <malloc.h>
28 #include <string.h>
29 
30 #include "jni.h"
31 #include "jni_util.h"
32 #include "sun_management_FileSystemImpl.h"
33 
34 /*
35  * Access mask to represent any file access
36  */
37 #define ANY_ACCESS (FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE)
38 
39 /*
40  * Returns JNI_TRUE if the specified file is on a file system that supports
41  * persistent ACLs (On NTFS file systems returns true, on FAT32 file systems
42  * returns false).
43  */
isSecuritySupported(JNIEnv * env,const char * path)44 static jboolean isSecuritySupported(JNIEnv* env, const char* path) {
45     char* root;
46     char* p;
47     BOOL res;
48     DWORD dwMaxComponentLength;
49     DWORD dwFlags;
50     char fsName[128];
51     DWORD fsNameLength;
52 
53     /*
54      * Get root directory. Assume that files are absolute paths. For UNCs
55      * the slash after the share name is required.
56      */
57     root = strdup(path);
58     if (*root == '\\') {
59         /*
60          * \\server\share\file ==> \\server\share\
61          */
62         int slashskip = 3;
63         p = root;
64         while ((*p == '\\') && (slashskip > 0)) {
65             char* p2;
66             p++;
67             p2 = strchr(p, '\\');
68             if ((p2 == NULL) || (*p2 != '\\')) {
69                 free(root);
70                 JNU_ThrowIOException(env, "Malformed UNC");
71                 return JNI_FALSE;
72             }
73             p = p2;
74             slashskip--;
75         }
76         if (slashskip != 0) {
77             free(root);
78             JNU_ThrowIOException(env, "Malformed UNC");
79             return JNI_FALSE;
80         }
81         p++;
82         *p = '\0';
83 
84     } else {
85         p = strchr(root, '\\');
86         if (p == NULL) {
87             free(root);
88             JNU_ThrowIOException(env, "Absolute filename not specified");
89             return JNI_FALSE;
90         }
91         p++;
92         *p = '\0';
93     }
94 
95 
96     /*
97      * Get the volume information - this gives us the file system file and
98      * also tells us if the file system supports persistent ACLs.
99      */
100     fsNameLength = sizeof(fsName)-1;
101     res = GetVolumeInformation(root,
102                                NULL,        // address of name of the volume, can be NULL
103                                0,           // length of volume name
104                                NULL,        // address of volume serial number, can be NULL
105                                &dwMaxComponentLength,
106                                &dwFlags,
107                                fsName,
108                                fsNameLength);
109     if (res == 0) {
110         free(root);
111         JNU_ThrowIOExceptionWithLastError(env, "GetVolumeInformation failed");
112         return JNI_FALSE;
113     }
114 
115     free(root);
116     return (dwFlags & FS_PERSISTENT_ACLS) ? JNI_TRUE : JNI_FALSE;
117 }
118 
119 
120 /*
121  * Returns the security descriptor for a file.
122  */
getFileSecurityDescriptor(JNIEnv * env,const char * path)123 static SECURITY_DESCRIPTOR* getFileSecurityDescriptor(JNIEnv* env, const char* path) {
124     SECURITY_DESCRIPTOR* sd;
125     DWORD len = 0;
126     SECURITY_INFORMATION info =
127         OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
128 
129     GetFileSecurityA(path, info , 0, 0, &len);
130     if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
131         JNU_ThrowIOExceptionWithLastError(env, "GetFileSecurity failed");
132         return NULL;
133     }
134     sd = (SECURITY_DESCRIPTOR *)malloc(len);
135     if (sd == NULL) {
136         JNU_ThrowOutOfMemoryError(env, 0);
137     } else {
138         if (!(*GetFileSecurityA)(path, info, sd, len, &len)) {
139             JNU_ThrowIOExceptionWithLastError(env, "GetFileSecurity failed");
140             free(sd);
141             return NULL;
142         }
143     }
144     return sd;
145 }
146 
147 /*
148  * Returns pointer to the SID identifying the owner of the specified
149  * file.
150  */
getFileOwner(JNIEnv * env,SECURITY_DESCRIPTOR * sd)151 static SID* getFileOwner(JNIEnv* env, SECURITY_DESCRIPTOR* sd) {
152     SID* owner;
153     BOOL defaulted;
154 
155     if (!GetSecurityDescriptorOwner(sd, &owner, &defaulted)) {
156         JNU_ThrowIOExceptionWithLastError(env, "GetSecurityDescriptorOwner failed");
157         return NULL;
158     }
159     return owner;
160 }
161 
162 /*
163  * Returns pointer discretionary access-control list (ACL) from the security
164  * descriptor of the specified file.
165  */
getFileDACL(JNIEnv * env,SECURITY_DESCRIPTOR * sd)166 static ACL* getFileDACL(JNIEnv* env, SECURITY_DESCRIPTOR* sd) {
167     ACL *acl;
168     int defaulted, present;
169 
170     if (!GetSecurityDescriptorDacl(sd, &present, &acl, &defaulted)) {
171         JNU_ThrowIOExceptionWithLastError(env, "GetSecurityDescriptorDacl failed");
172         return NULL;
173     }
174     if (!present) {
175         JNU_ThrowInternalError(env, "Security descriptor does not contain a DACL");
176         return NULL;
177     }
178     return acl;
179 }
180 
181 /*
182  * Returns JNI_TRUE if the specified owner is the only SID will access
183  * to the file.
184  */
isAccessUserOnly(JNIEnv * env,SID * owner,ACL * acl)185 static jboolean isAccessUserOnly(JNIEnv* env, SID* owner, ACL* acl) {
186     ACL_SIZE_INFORMATION acl_size_info;
187     DWORD i;
188 
189     /*
190      * If there's no DACL then there's no access to the file
191      */
192     if (acl == NULL) {
193         return JNI_TRUE;
194     }
195 
196     /*
197      * Get the ACE count
198      */
199     if (!GetAclInformation(acl, (void *) &acl_size_info, sizeof(acl_size_info),
200                            AclSizeInformation)) {
201         JNU_ThrowIOExceptionWithLastError(env, "GetAclInformation failed");
202         return JNI_FALSE;
203     }
204 
205     /*
206      * Iterate over the ACEs. For each "allow" type check that the SID
207      * matches the owner, and check that the access is read only.
208      */
209     for (i = 0; i < acl_size_info.AceCount; i++) {
210         void* ace;
211         ACCESS_ALLOWED_ACE *access;
212         SID* sid;
213 
214         if (!GetAce(acl, i, &ace)) {
215             JNU_ThrowIOExceptionWithLastError(env, "GetAce failed");
216             return -1;
217         }
218         if (((ACCESS_ALLOWED_ACE *)ace)->Header.AceType != ACCESS_ALLOWED_ACE_TYPE) {
219             continue;
220         }
221         access = (ACCESS_ALLOWED_ACE *)ace;
222         sid = (SID *) &access->SidStart;
223         if (!EqualSid(owner, sid)) {
224             /*
225              * If the ACE allows any access then the file is not secure.
226              */
227             if (access->Mask & ANY_ACCESS) {
228                 return JNI_FALSE;
229             }
230         }
231     }
232     return JNI_TRUE;
233 }
234 
235 
236 /*
237  * Class:     sun_management_FileSystemImpl
238  * Method:    init0
239  * Signature: ()V
240  */
Java_sun_management_FileSystemImpl_init0(JNIEnv * env,jclass ignored)241 JNIEXPORT void JNICALL Java_sun_management_FileSystemImpl_init0
242   (JNIEnv *env, jclass ignored)
243 {
244         /* nothing to do */
245 }
246 
247 /*
248  * Class:     sun_management_FileSystemImpl
249  * Method:    isSecuritySupported0
250  * Signature: (Ljava/lang/String;)Z
251  */
Java_sun_management_FileSystemImpl_isSecuritySupported0(JNIEnv * env,jclass ignored,jstring str)252 JNIEXPORT jboolean JNICALL Java_sun_management_FileSystemImpl_isSecuritySupported0
253   (JNIEnv *env, jclass ignored, jstring str)
254 {
255     jboolean res;
256     jboolean isCopy;
257     const char* path;
258 
259     path = JNU_GetStringPlatformChars(env, str, &isCopy);
260     if (path != NULL) {
261         res = isSecuritySupported(env, path);
262         if (isCopy) {
263             JNU_ReleaseStringPlatformChars(env, str, path);
264         }
265         return res;
266     } else {
267         /* exception thrown - doesn't matter what we return */
268         return JNI_TRUE;
269     }
270 }
271 
272 
273 /*
274  * Class:     sun_management_FileSystemImpl
275  * Method:    isAccessUserOnly0
276  * Signature: (Ljava/lang/String;)Z
277  */
Java_sun_management_FileSystemImpl_isAccessUserOnly0(JNIEnv * env,jclass ignored,jstring str)278 JNIEXPORT jboolean JNICALL Java_sun_management_FileSystemImpl_isAccessUserOnly0
279   (JNIEnv *env, jclass ignored, jstring str)
280 {
281     jboolean res = JNI_FALSE;
282     jboolean isCopy;
283     const char* path;
284 
285     path = JNU_GetStringPlatformChars(env, str, &isCopy);
286     if (path != NULL) {
287         /*
288          * From the security descriptor get the file owner and
289          * DACL. Then check if anybody but the owner has access
290          * to the file.
291          */
292         SECURITY_DESCRIPTOR* sd = getFileSecurityDescriptor(env, path);
293         if (sd != NULL) {
294             SID *owner = getFileOwner(env, sd);
295             if (owner != NULL) {
296                 ACL* acl = getFileDACL(env, sd);
297                 if (acl != NULL) {
298                     res = isAccessUserOnly(env, owner, acl);
299                 } else {
300                     /*
301                      * If acl is NULL it means that an exception was thrown
302                      * or there is "all acess" to the file.
303                      */
304                     res = JNI_FALSE;
305                 }
306             }
307             free(sd);
308         }
309         if (isCopy) {
310             JNU_ReleaseStringPlatformChars(env, str, path);
311         }
312     }
313     return res;
314 }
315