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 "jdk_internal_agent_FileSystemImpl.h"
33
34JNIEXPORT jint JNICALL DEF_JNI_OnLoad(JavaVM *vm, void *reserved)
35{
36    JNIEnv* env;
37
38    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_2) != JNI_OK) {
39        return JNI_EVERSION; /* JNI version not supported */
40    }
41
42    return JNI_VERSION_9;
43}
44
45
46/*
47 * Access mask to represent any file access
48 */
49#define ANY_ACCESS (FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE)
50
51/*
52 * Returns JNI_TRUE if the specified file is on a file system that supports
53 * persistent ACLs (On NTFS file systems returns true, on FAT32 file systems
54 * returns false).
55 */
56static jboolean isSecuritySupported(JNIEnv* env, const char* path) {
57    char* root;
58    char* p;
59    BOOL res;
60    DWORD dwMaxComponentLength;
61    DWORD dwFlags;
62    char fsName[128];
63    DWORD fsNameLength;
64
65    /*
66     * Get root directory. Assume that files are absolute paths. For UNCs
67     * the slash after the share name is required.
68     */
69    root = strdup(path);
70    if (*root == '\\') {
71        /*
72         * \\server\share\file ==> \\server\share\
73         */
74        int slashskip = 3;
75        p = root;
76        while ((*p == '\\') && (slashskip > 0)) {
77            char* p2;
78            p++;
79            p2 = strchr(p, '\\');
80            if ((p2 == NULL) || (*p2 != '\\')) {
81                free(root);
82                JNU_ThrowIOException(env, "Malformed UNC");
83                return JNI_FALSE;
84            }
85            p = p2;
86            slashskip--;
87        }
88        if (slashskip != 0) {
89            free(root);
90            JNU_ThrowIOException(env, "Malformed UNC");
91            return JNI_FALSE;
92        }
93        p++;
94        *p = '\0';
95
96    } else {
97        p = strchr(root, '\\');
98        if (p == NULL) {
99            free(root);
100            JNU_ThrowIOException(env, "Absolute filename not specified");
101            return JNI_FALSE;
102        }
103        p++;
104        *p = '\0';
105    }
106
107
108    /*
109     * Get the volume information - this gives us the file system file and
110     * also tells us if the file system supports persistent ACLs.
111     */
112    fsNameLength = sizeof(fsName)-1;
113    res = GetVolumeInformation(root,
114                               NULL,        // address of name of the volume, can be NULL
115                               0,           // length of volume name
116                               NULL,        // address of volume serial number, can be NULL
117                               &dwMaxComponentLength,
118                               &dwFlags,
119                               fsName,
120                               fsNameLength);
121    if (res == 0) {
122        free(root);
123        JNU_ThrowIOExceptionWithLastError(env, "GetVolumeInformation failed");
124        return JNI_FALSE;
125    }
126
127    free(root);
128    return (dwFlags & FS_PERSISTENT_ACLS) ? JNI_TRUE : JNI_FALSE;
129}
130
131
132/*
133 * Returns the security descriptor for a file.
134 */
135static SECURITY_DESCRIPTOR* getFileSecurityDescriptor(JNIEnv* env, const char* path) {
136    SECURITY_DESCRIPTOR* sd;
137    DWORD len = 0;
138    SECURITY_INFORMATION info =
139        OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
140
141    GetFileSecurityA(path, info , 0, 0, &len);
142    if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
143        JNU_ThrowIOExceptionWithLastError(env, "GetFileSecurity failed");
144        return NULL;
145    }
146    sd = (SECURITY_DESCRIPTOR *)malloc(len);
147    if (sd == NULL) {
148        JNU_ThrowOutOfMemoryError(env, 0);
149    } else {
150        if (!(*GetFileSecurityA)(path, info, sd, len, &len)) {
151            JNU_ThrowIOExceptionWithLastError(env, "GetFileSecurity failed");
152            free(sd);
153            return NULL;
154        }
155    }
156    return sd;
157}
158
159/*
160 * Returns pointer to the SID identifying the owner of the specified
161 * file.
162 */
163static SID* getFileOwner(JNIEnv* env, SECURITY_DESCRIPTOR* sd) {
164    SID* owner;
165    BOOL defaulted;
166
167    if (!GetSecurityDescriptorOwner(sd, &owner, &defaulted)) {
168        JNU_ThrowIOExceptionWithLastError(env, "GetSecurityDescriptorOwner failed");
169        return NULL;
170    }
171    return owner;
172}
173
174/*
175 * Returns pointer discretionary access-control list (ACL) from the security
176 * descriptor of the specified file.
177 */
178static ACL* getFileDACL(JNIEnv* env, SECURITY_DESCRIPTOR* sd) {
179    ACL *acl;
180    int defaulted, present;
181
182    if (!GetSecurityDescriptorDacl(sd, &present, &acl, &defaulted)) {
183        JNU_ThrowIOExceptionWithLastError(env, "GetSecurityDescriptorDacl failed");
184        return NULL;
185    }
186    if (!present) {
187        JNU_ThrowInternalError(env, "Security descriptor does not contain a DACL");
188        return NULL;
189    }
190    return acl;
191}
192
193/*
194 * Returns JNI_TRUE if the specified owner is the only SID will access
195 * to the file.
196 */
197static jboolean isAccessUserOnly(JNIEnv* env, SID* owner, ACL* acl) {
198    ACL_SIZE_INFORMATION acl_size_info;
199    DWORD i;
200
201    /*
202     * If there's no DACL then there's no access to the file
203     */
204    if (acl == NULL) {
205        return JNI_TRUE;
206    }
207
208    /*
209     * Get the ACE count
210     */
211    if (!GetAclInformation(acl, (void *) &acl_size_info, sizeof(acl_size_info),
212                           AclSizeInformation)) {
213        JNU_ThrowIOExceptionWithLastError(env, "GetAclInformation failed");
214        return JNI_FALSE;
215    }
216
217    /*
218     * Iterate over the ACEs. For each "allow" type check that the SID
219     * matches the owner, and check that the access is read only.
220     */
221    for (i = 0; i < acl_size_info.AceCount; i++) {
222        void* ace;
223        ACCESS_ALLOWED_ACE *access;
224        SID* sid;
225
226        if (!GetAce(acl, i, &ace)) {
227            JNU_ThrowIOExceptionWithLastError(env, "GetAce failed");
228            return -1;
229        }
230        if (((ACCESS_ALLOWED_ACE *)ace)->Header.AceType != ACCESS_ALLOWED_ACE_TYPE) {
231            continue;
232        }
233        access = (ACCESS_ALLOWED_ACE *)ace;
234        sid = (SID *) &access->SidStart;
235        if (!EqualSid(owner, sid)) {
236            /*
237             * If the ACE allows any access then the file is not secure.
238             */
239            if (access->Mask & ANY_ACCESS) {
240                return JNI_FALSE;
241            }
242        }
243    }
244    return JNI_TRUE;
245}
246
247
248/*
249 * Class:     jdk_internal_agent_FileSystemImpl
250 * Method:    init0
251 * Signature: ()V
252 */
253JNIEXPORT void JNICALL Java_jdk_internal_agent_FileSystemImpl_init0
254  (JNIEnv *env, jclass ignored)
255{
256        /* nothing to do */
257}
258
259/*
260 * Class:     jdk_internal_agent_FileSystemImpl
261 * Method:    isSecuritySupported0
262 * Signature: (Ljava/lang/String;)Z
263 */
264JNIEXPORT jboolean JNICALL Java_jdk_internal_agent_FileSystemImpl_isSecuritySupported0
265  (JNIEnv *env, jclass ignored, jstring str)
266{
267    jboolean res;
268    jboolean isCopy;
269    const char* path;
270
271    path = JNU_GetStringPlatformChars(env, str, &isCopy);
272    if (path != NULL) {
273        res = isSecuritySupported(env, path);
274        if (isCopy) {
275            JNU_ReleaseStringPlatformChars(env, str, path);
276        }
277        return res;
278    } else {
279        /* exception thrown - doesn't matter what we return */
280        return JNI_TRUE;
281    }
282}
283
284
285/*
286 * Class:     jdk_internal_agent_FileSystemImpl
287 * Method:    isAccessUserOnly0
288 * Signature: (Ljava/lang/String;)Z
289 */
290JNIEXPORT jboolean JNICALL Java_jdk_internal_agent_FileSystemImpl_isAccessUserOnly0
291  (JNIEnv *env, jclass ignored, jstring str)
292{
293    jboolean res = JNI_FALSE;
294    jboolean isCopy;
295    const char* path;
296
297    path = JNU_GetStringPlatformChars(env, str, &isCopy);
298    if (path != NULL) {
299        /*
300         * From the security descriptor get the file owner and
301         * DACL. Then check if anybody but the owner has access
302         * to the file.
303         */
304        SECURITY_DESCRIPTOR* sd = getFileSecurityDescriptor(env, path);
305        if (sd != NULL) {
306            SID *owner = getFileOwner(env, sd);
307            if (owner != NULL) {
308                ACL* acl = getFileDACL(env, sd);
309                if (acl != NULL) {
310                    res = isAccessUserOnly(env, owner, acl);
311                } else {
312                    /*
313                     * If acl is NULL it means that an exception was thrown
314                     * or there is "all acess" to the file.
315                     */
316                    res = JNI_FALSE;
317                }
318            }
319            free(sd);
320        }
321        if (isCopy) {
322            JNU_ReleaseStringPlatformChars(env, str, path);
323        }
324    }
325    return res;
326}
327