Skip to content

Commit

Permalink
Improve attach API access checking
Browse files Browse the repository at this point in the history
Ensure that files have the correct owner and access permissions,
delete suspect directories instead of re-using them.

Signed-off-by: Peter Bain <[email protected]>
  • Loading branch information
pdbain-ibm committed Jul 17, 2018
1 parent f497aa4 commit 82c9925
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1283,5 +1283,13 @@ K0800="Invalid Unicode code point - {0}"
K0801="Last character in replacement string can't be \, character to be escaped is required."
K0802="Last character in replacement string can't be $, group index is required."

# attach API
K0803="File {0} is owned by {1}, should be owned by current user"
K0804="Illegal file {0} found in target directory"
K0805="{0} has permissions {1}, should have owner access only"
K0806="Cannot verify permissions {0}"
K0807="Cannot delete file {0}"
K0808="Cannot create new file {0}"

#java.lang.ref.Reference
K0900="Create a new Reference, since a Reference cannot be cloned."
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*[INCLUDE-IF Sidecar16]*/
package com.ibm.tools.attach.target;
/*******************************************************************************
* Copyright (c) 2009, 2010 IBM Corp. and others
* Copyright (c) 2009, 2018 IBM Corp. and others
*
* This program and the accompanying materials are made available under
* the terms of the Eclipse Public License 2.0 which accompanies this
Expand Down Expand Up @@ -189,7 +189,7 @@ private static void encodeString(StringBuilder buffer, String originalString) {
* This is called during initialization or after successful initialization only.
* @param vmId ID of this VM
* @param displayName display name of this VM
* @throws IOException if cannot open the advertisement file
* @throws IOException if cannot open the advertisement file or cannot delete an existing one
*/
static void createAdvertisementFile(String vmId, String displayName) throws IOException {

Expand All @@ -198,22 +198,20 @@ static void createAdvertisementFile(String vmId, String displayName) throws IOEx
return;
}
File advertFile = TargetDirectory.getAdvertisementFileObject();
IPC.createFileWithPermissions(advertFile.getAbsolutePath(), TargetDirectory.ADVERTISEMENT_FILE_PERMISSIONS);
/* AttachHandler.terminate() will delete this file on shutdown */
FileOutputStream advertOutputStream = new FileOutputStream(advertFile);
try {
IPC.createNewFileWithPermissions(advertFile, TargetDirectory.ADVERTISEMENT_FILE_PERMISSIONS);
/* we have a brand new, empty file with correct ownership and permissions */
try (FileOutputStream advertOutputStream = new FileOutputStream(advertFile);){
StringBuilder advertContent = createAdvertContent(vmId, displayName);
if (null == advertContent) {
IPC.logMessage("createAdvertisementFile failed to create advertisement file : file objects null"); //$NON-NLS-1$
IPC.logMessage("createAdvertisementFile failed to create advertisement file : file object is null"); //$NON-NLS-1$
return;
}

advertOutputStream.write(advertContent.toString().getBytes("ISO8859_1")); //$NON-NLS-1$
if (IPC.loggingEnabled ) {
IPC.logMessage("createAdvertisementFile ", advertFile.getAbsolutePath()); //$NON-NLS-1$
}
} finally {
advertOutputStream.close(); /* cleanup from findbugs */
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,12 @@ static void initializeAttachAPI() {
/*[PR Jazz 59196 LIR: Disable attach API by default on z/OS (31972)]*/
boolean enableAttach = true;
String osName = com.ibm.oti.vm.VM.getVMLangAccess().internalGetProperties().getProperty("os.name"); //$NON-NLS-1$
if ((null != osName) && (osName.equalsIgnoreCase("z/OS"))) { //$NON-NLS-1$
enableAttach = false;
if (null != osName) {
if (osName.equalsIgnoreCase("z/OS")) { //$NON-NLS-1$
enableAttach = false;
} else if (osName.startsWith("Windows")) { //$NON-NLS-1$
IPC.isWindows = true;
}
}
/* the system property overrides the default */
String enableAttachProp = com.ibm.oti.vm.VM.getVMLangAccess().internalGetProperties().getProperty("com.ibm.tools.attach.enable"); //$NON-NLS-1$
Expand Down Expand Up @@ -335,7 +339,9 @@ private boolean initialize() throws IOException {
* @throws IOException if there is a problem reading the reply file.
*/
public Attachment connectToAttacher() throws IOException {
Reply attacherReply = Reply.readReply(TargetDirectory.getTargetDirectoryPath(AttachHandler.getVmId()));
String targetDirectoryPath = TargetDirectory.getTargetDirectoryPath(AttachHandler.getVmId());
IPC.checkOwnerAccessOnly(targetDirectoryPath);
Reply attacherReply = Reply.readReply(targetDirectoryPath);
Attachment at = null;
if (null != attacherReply) {
int portNumber = attacherReply.getPortNumber();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -328,10 +328,10 @@ static private boolean isCommonControlFile(String dirMemberName) {
|| MASTER_NOTIFIER.equalsIgnoreCase(dirMemberName));
}

/*
* look for leftover files and directories from previous VMs
/**
* Look for and delete leftover files and directories from previous VMs
* @param myId VMID of the current. Set to non-null to prevent deleting own directory.
*/
/*[PR Jazz 37778 deleteStaleDirectories was always called with checkProcess=true */
static void deleteStaleDirectories(String myId) {
long myUid = IPC.getUid();
File[] vmDirs = getCommonDirFileObject().listFiles(new DirectorySampler());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,22 @@
import java.io.OutputStream;
import java.io.PrintStream;
import java.security.SecureRandom;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Paths;
import java.nio.file.attribute.PosixFilePermission;
import java.util.EnumSet;
import java.util.Set;
import java.util.Objects;
import java.util.Properties;
import java.util.Random;

import static java.nio.file.attribute.PosixFilePermission.GROUP_READ;
import static java.nio.file.attribute.PosixFilePermission.GROUP_WRITE;
import static java.nio.file.attribute.PosixFilePermission.OTHERS_READ;
import static java.nio.file.attribute.PosixFilePermission.OTHERS_WRITE;

/**
* Utility class for operating system calls
*/
Expand All @@ -49,7 +61,13 @@ public class IPC {
static final int TRACEPOINT_STATUS_OOM_DURING_WAIT = -2;
static final int TRACEPOINT_STATUS_OOM_DURING_TERMINATE = -3;
static final String LOCAL_CONNECTOR_ADDRESS = "com.sun.management.jmxremote.localConnectorAddress"; //$NON-NLS-1$
private static final EnumSet<PosixFilePermission> NON_OWNER_READ_WRITE =
EnumSet.of(GROUP_READ, GROUP_WRITE, OTHERS_READ, OTHERS_WRITE);

/**
* True if operating system is Windows.
*/
public static boolean isWindows = false;

private static Random randomGen; /* Cleanup. this is used by multiple threads */
static PrintStream logStream; /* cleanup. Used by multiple threads */
Expand Down Expand Up @@ -90,6 +108,43 @@ static int mkdirWithPermissions(String absolutePath, int perms)

static native int mkdirWithPermissionsImpl(String absolutePath, int perms);

/**
* Ensure that a file or directory is not readable or writable by others
* and is owned by the current user.
* @param filePath File or directory path
* @throws IOException if the access or ownership is wrong
*/
public static void checkOwnerAccessOnly(String filePath) throws IOException {
final long myUid = getUid();
/* Ensure file is owned by current user, or current user is root */
final long fileOwner = CommonDirectory.getFileOwner(filePath);
if ((0 != myUid) && (fileOwner != myUid)) {
logMessage("Wrong permissions or ownership for ", filePath); //$NON-NLS-1$
/*[MSG "K0803", "File {0} is owned by {1}, should be owned by current user"]*/
throw new IOException(com.ibm.oti.util.Msg.getString("K0803", filePath, Long.valueOf(fileOwner)));//$NON-NLS-1$
}
if (!isWindows) {
try {
/* ensure that the directory is not readable or writable by others */
Set<PosixFilePermission> actualPermissions =
Files.getPosixFilePermissions(Paths.get(filePath), LinkOption.NOFOLLOW_LINKS);
actualPermissions.retainAll(NON_OWNER_READ_WRITE);
if (!actualPermissions.isEmpty()) {
final String permissionString = Files.getPosixFilePermissions(Paths.get(filePath), LinkOption.NOFOLLOW_LINKS).toString();
logMessage("Wrong permissions: " +permissionString + " for ", filePath); //$NON-NLS-1$ //$NON-NLS-2$
/*[MSG "K0805", "{0} has permissions {1}, should have owner access only"]*/
throw new IOException(com.ibm.oti.util.Msg.getString("K0805", filePath, permissionString));//$NON-NLS-1$
}
} catch (UnsupportedOperationException e) {
String osName = com.ibm.oti.vm.VM.getVMLangAccess().internalGetProperties().getProperty("os.name"); //$NON-NLS-1$
if ((null != osName) && !osName.startsWith("Windows")) { //$NON-NLS-1$
/*[MSG "K0806", "Cannot verify permissions {0}"]*/
throw new IOException(com.ibm.oti.util.Msg.getString("K0806", filePath), e); //$NON-NLS-1$
}
}
}
}

/*[PR Jazz 30075] setupSemaphore was re-doing what createDirectoryAndSemaphore (now called prepareCommonDirectory) did already */

/**
Expand Down Expand Up @@ -167,11 +222,29 @@ public static boolean processExists(long pid) {

private static native int processExistsImpl(long pid);

static void createFileWithPermissions(String path, int perms)
throws IOException {
int rc = createFileWithPermissionsImpl(path, perms);
/**
* Create a new file with specified the permissions (to override umask) and close it.
* If the file exists, delete it.
* @param path file system path
* @param mode file access permissions (posix format) for the new file
* @throws IOException if the file exists and cannot be removed, or
* a new file cannot be created with the specified permission
*/
static void createNewFileWithPermissions(File theFile, int perms) throws IOException {
final String filePathString = theFile.getAbsolutePath();
if (theFile.exists()) {
IPC.logMessage("Found existing file ", filePathString); //$NON-NLS-1$
if (!theFile.delete()) {
IPC.logMessage("Cannot delete existing file ", filePathString); //$NON-NLS-1$
/*[MSG "K0807", "Cannot delete file {0}"]*/
throw (new IOException(com.ibm.oti.util.Msg.getString("K0807", filePathString))); //$NON-NLS-1$
}
}
int rc = createFileWithPermissionsImpl(theFile.getAbsolutePath(), perms);
if (JNI_OK != rc) {
throw new IOException(path);
IPC.logMessage("Cannot create new file ", filePathString); //$NON-NLS-1$
/*[MSG "K0808", "Cannot create new file {0}"]*/
throw (new IOException(com.ibm.oti.util.Msg.getString("K0808", filePathString))); //$NON-NLS-1$
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ public final class Reply {
* Manages the file created by an attaching process
*
*/
private RandomAccessFile replyChannelRAF;
private String key;
private Integer portNumber;
private final File replyFile;
Expand Down Expand Up @@ -68,31 +67,30 @@ public Reply(Integer thePort, String theKey, String targetDirectory, long theUid

/**
* Write data to the reply file. Must be matched with an eraseReply()
* @param keyText Security key to validate transaction
* @param portNumberText to be written to file. Virtual machine ID, name, semaphore name etc.
* @throws IOException if files are not accessible
* keyText is a security key to validate transaction
* portNumberText is text to be written to file. Virtual machine ID, name, semaphore name etc.
* @throws IOException if file are not accessible or cannot be created with correct permissions
*/
public void writeReply() throws IOException {
/* replyFIle has been created by the constructor */
try {
IPC.logMessage("writing reply file port=", portNumber.intValue(), " file path=", replyFile.getAbsolutePath()); //$NON-NLS-1$//$NON-NLS-2$
replyChannelRAF = new RandomAccessFile(replyFile, "rw"); //$NON-NLS-1$
replyChannelRAF.setLength(0); /* delete whatever crud was there before */
/* replyFile has been created by the constructor */
final String replyFileAbsolutePath = replyFile.getAbsolutePath();
IPC.logMessage("writing reply file port=", portNumber.intValue(), " file path=", replyFileAbsolutePath); //$NON-NLS-1$//$NON-NLS-2$
IPC.createNewFileWithPermissions(replyFile, TargetDirectory.ADVERTISEMENT_FILE_PERMISSIONS);
/* we have a brand new, empty file with correct ownership and permissions */
try (RandomAccessFile replyChannelRAF = new RandomAccessFile(replyFile, "rw")) { //$NON-NLS-1$
replyChannelRAF.writeBytes(key);
replyChannelRAF.writeByte('\n');
replyChannelRAF.writeBytes(portNumber.toString());
replyChannelRAF.writeByte('\n');
replyChannelRAF.close();

IPC.chmod(replyFile.getAbsolutePath(), REPLY_PERMISSIONS);
long myUid = IPC.getUid();
if ((ROOT_UID == myUid) && (ROOT_UID != targetUid)) {
IPC.chownFileToTargetUid(replyFile.getAbsolutePath(), targetUid);
IPC.chownFileToTargetUid(replyFileAbsolutePath, targetUid);
}
} catch (FileNotFoundException e) {
/*[MSG "K0552", "File not found exception thrown in writeReply for file {0}"]*/
throw new IOException(com.ibm.oti.util.Msg.getString("K0552", replyFile.getAbsolutePath()), e); //$NON-NLS-1$
throw new IOException(com.ibm.oti.util.Msg.getString("K0552", replyFileAbsolutePath), e); //$NON-NLS-1$
}
}

Expand All @@ -102,12 +100,12 @@ public void writeReply() throws IOException {
*/
static Reply readReply(String path) throws IOException {
Reply rply = new Reply(path);
String replyFileAbsolutePath = rply.replyFile.getAbsolutePath();
if (rply.fileDoesNotExist()) {
return null;
}
BufferedReader replyStream;
try {
replyStream = new BufferedReader(new FileReader(rply.replyFile));
IPC.checkOwnerAccessOnly(replyFileAbsolutePath);
try (BufferedReader replyStream = new BufferedReader(new FileReader(rply.replyFile));) {
rply.key = replyStream.readLine();
String line = replyStream.readLine();
replyStream.close();
Expand All @@ -119,7 +117,7 @@ static Reply readReply(String path) throws IOException {
}
} catch (FileNotFoundException e) {
/*[MSG "K0548", "Cannot read reply file {0}"]*/
throw new IOException(com.ibm.oti.util.Msg.getString("K0548", rply.replyFile.getAbsolutePath()), e); //$NON-NLS-1$
throw new IOException(com.ibm.oti.util.Msg.getString("K0548", replyFileAbsolutePath), e); //$NON-NLS-1$
}
return rply;
}
Expand Down
Loading

0 comments on commit 82c9925

Please sign in to comment.