blob: f466a6773effd94ac08bf58b37529658a5268fca [file] [log] [blame]
/*
* Copyright 2004 Sun Microsystems, Inc. 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. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
#include <windows.h>
#include <malloc.h>
#include <string.h>
#include "jni.h"
#include "jni_util.h"
#include "sun_management_FileSystemImpl.h"
/*
* Access mask to represent any file access
*/
#define ANY_ACCESS (FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE)
/*
* Function prototypes for security functions - we can't statically
* link because these functions aren't on Windows 9x.
*/
typedef BOOL (WINAPI *GetFileSecurityFunc)
(LPCTSTR lpFileName, SECURITY_INFORMATION RequestedInformation,
PSECURITY_DESCRIPTOR pSecurityDescriptor, DWORD nLength,
LPDWORD lpnLengthNeeded);
typedef BOOL (WINAPI *GetSecurityDescriptorOwnerFunc)
(PSECURITY_DESCRIPTOR pSecurityDescriptor, PSID *pOwner,
LPBOOL lpbOwnerDefaulted);
typedef BOOL (WINAPI *GetSecurityDescriptorDaclFunc)
(PSECURITY_DESCRIPTOR pSecurityDescriptor, LPBOOL lpbDaclPresent,
PACL *pDacl, LPBOOL lpbDaclDefaulted);
typedef BOOL (WINAPI *GetAclInformationFunc)
(PACL pAcl, LPVOID pAclInformation, DWORD nAclInformationLength,
ACL_INFORMATION_CLASS dwAclInformationClass);
typedef BOOL (WINAPI *GetAceFunc)
(PACL pAcl, DWORD dwAceIndex, LPVOID *pAce);
typedef BOOL (WINAPI *EqualSidFunc)(PSID pSid1, PSID pSid2);
/* Addresses of the security functions */
static GetFileSecurityFunc GetFileSecurity_func;
static GetSecurityDescriptorOwnerFunc GetSecurityDescriptorOwner_func;
static GetSecurityDescriptorDaclFunc GetSecurityDescriptorDacl_func;
static GetAclInformationFunc GetAclInformation_func;
static GetAceFunc GetAce_func;
static EqualSidFunc EqualSid_func;
/* True if this OS is NT kernel based (NT/2000/XP) */
static int isNT;
/*
* 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;
(*GetFileSecurity_func)(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 (!(*GetFileSecurity_func)(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_func)(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_func)(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_func)(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_func)(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: sun_management_FileSystemImpl
* Method: init0
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_sun_management_FileSystemImpl_init0
(JNIEnv *env, jclass ignored)
{
OSVERSIONINFO ver;
HINSTANCE hInst;
/*
* Get the OS version. If dwPlatformId is VER_PLATFORM_WIN32_NT
* it means we're running on a Windows NT, 2000, or XP machine.
*/
ver.dwOSVersionInfoSize = sizeof(ver);
GetVersionEx(&ver);
isNT = (ver.dwPlatformId == VER_PLATFORM_WIN32_NT);
if (!isNT) {
return;
}
/*
* On NT/2000/XP we need the addresses of the security functions
*/
hInst = LoadLibrary("ADVAPI32.DLL");
if (hInst == NULL) {
JNU_ThrowIOExceptionWithLastError(env, "Unable to load ADVAPI32.DLL");
return;
}
GetFileSecurity_func = (GetFileSecurityFunc)GetProcAddress(hInst, "GetFileSecurityA");
GetSecurityDescriptorOwner_func =
(GetSecurityDescriptorOwnerFunc)GetProcAddress(hInst, "GetSecurityDescriptorOwner");
GetSecurityDescriptorDacl_func =
(GetSecurityDescriptorDaclFunc)GetProcAddress(hInst, "GetSecurityDescriptorDacl");
GetAclInformation_func =
(GetAclInformationFunc)GetProcAddress(hInst, "GetAclInformation");
GetAce_func = (GetAceFunc)GetProcAddress(hInst, "GetAce");
EqualSid_func = (EqualSidFunc)GetProcAddress(hInst, "EqualSid");
if (GetFileSecurity_func == NULL ||
GetSecurityDescriptorDacl_func == NULL ||
GetSecurityDescriptorDacl_func == NULL ||
GetAclInformation_func == NULL ||
GetAce_func == NULL ||
EqualSid_func == NULL)
{
JNU_ThrowIOExceptionWithLastError(env,
"Unable to get address of security functions");
return;
}
}
/*
* Class: sun_management_FileSystemImpl
* Method: isSecuritySupported0
* Signature: (Ljava/lang/String;)Z
*/
JNIEXPORT jboolean JNICALL Java_sun_management_FileSystemImpl_isSecuritySupported0
(JNIEnv *env, jclass ignored, jstring str)
{
jboolean res;
jboolean isCopy;
const char* path;
if (!isNT) {
return JNI_FALSE;
}
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: sun_management_FileSystemImpl
* Method: isAccessUserOnly0
* Signature: (Ljava/lang/String;)Z
*/
JNIEXPORT jboolean JNICALL Java_sun_management_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;
}