diff -r 4ebc2e2fb97c -r 71c04702a3d5 src/jdk.management.agent/windows/native/libmanagement_agent/FileSystemImpl.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.management.agent/windows/native/libmanagement_agent/FileSystemImpl.c Tue Sep 12 19:03:39 2017 +0200 @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include +#include + +#include "jni.h" +#include "jni_util.h" +#include "jdk_internal_agent_FileSystemImpl.h" + +JNIEXPORT jint JNICALL DEF_JNI_OnLoad(JavaVM *vm, void *reserved) +{ + JNIEnv* env; + + if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_2) != JNI_OK) { + return JNI_EVERSION; /* JNI version not supported */ + } + + return JNI_VERSION_9; +} + + +/* + * Access mask to represent any file access + */ +#define ANY_ACCESS (FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE) + +/* + * Returns JNI_TRUE if the specified file is on a file system that supports + * persistent ACLs (On NTFS file systems returns true, on FAT32 file systems + * returns false). + */ +static jboolean isSecuritySupported(JNIEnv* env, const char* path) { + char* root; + char* p; + BOOL res; + DWORD dwMaxComponentLength; + DWORD dwFlags; + char fsName[128]; + DWORD fsNameLength; + + /* + * Get root directory. Assume that files are absolute paths. For UNCs + * the slash after the share name is required. + */ + root = strdup(path); + if (*root == '\\') { + /* + * \\server\share\file ==> \\server\share\ + */ + int slashskip = 3; + p = root; + while ((*p == '\\') && (slashskip > 0)) { + char* p2; + p++; + p2 = strchr(p, '\\'); + if ((p2 == NULL) || (*p2 != '\\')) { + free(root); + JNU_ThrowIOException(env, "Malformed UNC"); + return JNI_FALSE; + } + p = p2; + slashskip--; + } + if (slashskip != 0) { + free(root); + JNU_ThrowIOException(env, "Malformed UNC"); + return JNI_FALSE; + } + p++; + *p = '\0'; + + } else { + p = strchr(root, '\\'); + if (p == NULL) { + free(root); + JNU_ThrowIOException(env, "Absolute filename not specified"); + return JNI_FALSE; + } + p++; + *p = '\0'; + } + + + /* + * Get the volume information - this gives us the file system file and + * also tells us if the file system supports persistent ACLs. + */ + fsNameLength = sizeof(fsName)-1; + res = GetVolumeInformation(root, + NULL, // address of name of the volume, can be NULL + 0, // length of volume name + NULL, // address of volume serial number, can be NULL + &dwMaxComponentLength, + &dwFlags, + fsName, + fsNameLength); + if (res == 0) { + free(root); + JNU_ThrowIOExceptionWithLastError(env, "GetVolumeInformation failed"); + return JNI_FALSE; + } + + free(root); + return (dwFlags & FS_PERSISTENT_ACLS) ? JNI_TRUE : JNI_FALSE; +} + + +/* + * Returns the security descriptor for a file. + */ +static SECURITY_DESCRIPTOR* getFileSecurityDescriptor(JNIEnv* env, const char* path) { + SECURITY_DESCRIPTOR* sd; + DWORD len = 0; + SECURITY_INFORMATION info = + OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION; + + GetFileSecurityA(path, info , 0, 0, &len); + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + JNU_ThrowIOExceptionWithLastError(env, "GetFileSecurity failed"); + return NULL; + } + sd = (SECURITY_DESCRIPTOR *)malloc(len); + if (sd == NULL) { + JNU_ThrowOutOfMemoryError(env, 0); + } else { + if (!(*GetFileSecurityA)(path, info, sd, len, &len)) { + JNU_ThrowIOExceptionWithLastError(env, "GetFileSecurity failed"); + free(sd); + return NULL; + } + } + return sd; +} + +/* + * Returns pointer to the SID identifying the owner of the specified + * file. + */ +static SID* getFileOwner(JNIEnv* env, SECURITY_DESCRIPTOR* sd) { + SID* owner; + BOOL defaulted; + + if (!GetSecurityDescriptorOwner(sd, &owner, &defaulted)) { + JNU_ThrowIOExceptionWithLastError(env, "GetSecurityDescriptorOwner failed"); + return NULL; + } + return owner; +} + +/* + * Returns pointer discretionary access-control list (ACL) from the security + * descriptor of the specified file. + */ +static ACL* getFileDACL(JNIEnv* env, SECURITY_DESCRIPTOR* sd) { + ACL *acl; + int defaulted, present; + + if (!GetSecurityDescriptorDacl(sd, &present, &acl, &defaulted)) { + JNU_ThrowIOExceptionWithLastError(env, "GetSecurityDescriptorDacl failed"); + return NULL; + } + if (!present) { + JNU_ThrowInternalError(env, "Security descriptor does not contain a DACL"); + return NULL; + } + return acl; +} + +/* + * Returns JNI_TRUE if the specified owner is the only SID will access + * to the file. + */ +static jboolean isAccessUserOnly(JNIEnv* env, SID* owner, ACL* acl) { + ACL_SIZE_INFORMATION acl_size_info; + DWORD i; + + /* + * If there's no DACL then there's no access to the file + */ + if (acl == NULL) { + return JNI_TRUE; + } + + /* + * Get the ACE count + */ + if (!GetAclInformation(acl, (void *) &acl_size_info, sizeof(acl_size_info), + AclSizeInformation)) { + JNU_ThrowIOExceptionWithLastError(env, "GetAclInformation failed"); + return JNI_FALSE; + } + + /* + * Iterate over the ACEs. For each "allow" type check that the SID + * matches the owner, and check that the access is read only. + */ + for (i = 0; i < acl_size_info.AceCount; i++) { + void* ace; + ACCESS_ALLOWED_ACE *access; + SID* sid; + + if (!GetAce(acl, i, &ace)) { + JNU_ThrowIOExceptionWithLastError(env, "GetAce failed"); + return -1; + } + if (((ACCESS_ALLOWED_ACE *)ace)->Header.AceType != ACCESS_ALLOWED_ACE_TYPE) { + continue; + } + access = (ACCESS_ALLOWED_ACE *)ace; + sid = (SID *) &access->SidStart; + if (!EqualSid(owner, sid)) { + /* + * If the ACE allows any access then the file is not secure. + */ + if (access->Mask & ANY_ACCESS) { + return JNI_FALSE; + } + } + } + return JNI_TRUE; +} + + +/* + * Class: jdk_internal_agent_FileSystemImpl + * Method: init0 + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_jdk_internal_agent_FileSystemImpl_init0 + (JNIEnv *env, jclass ignored) +{ + /* nothing to do */ +} + +/* + * Class: jdk_internal_agent_FileSystemImpl + * Method: isSecuritySupported0 + * Signature: (Ljava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_jdk_internal_agent_FileSystemImpl_isSecuritySupported0 + (JNIEnv *env, jclass ignored, jstring str) +{ + jboolean res; + jboolean isCopy; + const char* path; + + path = JNU_GetStringPlatformChars(env, str, &isCopy); + if (path != NULL) { + res = isSecuritySupported(env, path); + if (isCopy) { + JNU_ReleaseStringPlatformChars(env, str, path); + } + return res; + } else { + /* exception thrown - doesn't matter what we return */ + return JNI_TRUE; + } +} + + +/* + * Class: jdk_internal_agent_FileSystemImpl + * Method: isAccessUserOnly0 + * Signature: (Ljava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_jdk_internal_agent_FileSystemImpl_isAccessUserOnly0 + (JNIEnv *env, jclass ignored, jstring str) +{ + jboolean res = JNI_FALSE; + jboolean isCopy; + const char* path; + + path = JNU_GetStringPlatformChars(env, str, &isCopy); + if (path != NULL) { + /* + * From the security descriptor get the file owner and + * DACL. Then check if anybody but the owner has access + * to the file. + */ + SECURITY_DESCRIPTOR* sd = getFileSecurityDescriptor(env, path); + if (sd != NULL) { + SID *owner = getFileOwner(env, sd); + if (owner != NULL) { + ACL* acl = getFileDACL(env, sd); + if (acl != NULL) { + res = isAccessUserOnly(env, owner, acl); + } else { + /* + * If acl is NULL it means that an exception was thrown + * or there is "all acess" to the file. + */ + res = JNI_FALSE; + } + } + free(sd); + } + if (isCopy) { + JNU_ReleaseStringPlatformChars(env, str, path); + } + } + return res; +}