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 34 JNIEXPORT 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 */ 56 static 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 */ 135 static 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 */ 163 static 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 */ 178 static 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 */ 197 static 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 */ 253 JNIEXPORT 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 */ 264 JNIEXPORT 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 */ 290 JNIEXPORT 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 }