From 156ba853296fa6bcb688cdcaf249ec080d3746b0 Mon Sep 17 00:00:00 2001 From: Felix Marx <97086574+FelixMarxIBM@users.noreply.github.com> Date: Fri, 3 May 2024 18:48:47 +0200 Subject: [PATCH] J9 Allow attachment as root (#1631) * Allow attachment as root * Add javadoc for getUserId --- .../net/bytebuddy/agent/VirtualMachine.java | 102 +++++++++++------- 1 file changed, 66 insertions(+), 36 deletions(-) diff --git a/byte-buddy-agent/src/main/java/net/bytebuddy/agent/VirtualMachine.java b/byte-buddy-agent/src/main/java/net/bytebuddy/agent/VirtualMachine.java index e51b8c6468..e8bcc0a5e9 100644 --- a/byte-buddy-agent/src/main/java/net/bytebuddy/agent/VirtualMachine.java +++ b/byte-buddy-agent/src/main/java/net/bytebuddy/agent/VirtualMachine.java @@ -1660,49 +1660,23 @@ protected ForOpenJ9(Socket socket) { * @throws IOException If an IO exception occurs during establishing the connection. */ public static VirtualMachine attach(String processId) throws IOException { - return attach(processId, false); - } - - /** - * Attaches to the supplied process id using the default JNA implementation. - * - * @param processId The process id. - * @param ignoreUser {@code true} if VM processes that are owned by different users should be considered. - * @return A suitable virtual machine implementation. - * @throws IOException If an IO exception occurs during establishing the connection. - */ - public static VirtualMachine attach(String processId, boolean ignoreUser) throws IOException { return attach(processId, 5000, Platform.isWindows() ? new Dispatcher.ForJnaWindowsEnvironment() : new Dispatcher.ForJnaPosixEnvironment(15, 100, TimeUnit.MILLISECONDS)); } - /** - * Attaches to the supplied process id. This method will not consider attaching to VMs owned by - * different users than the current user. - * - * @param processId The process id. - * @param timeout The timeout for establishing the socket connection. - * @param dispatcher The connector to use to communicate with the target VM. - * @return A suitable virtual machine implementation. - * @throws IOException If an IO exception occurs during establishing the connection. - */ - public static VirtualMachine attach(String processId, int timeout, Dispatcher dispatcher) throws IOException { - return attach(processId, timeout, dispatcher, false); - } - /** * Attaches to the supplied process id. * * @param processId The process id. * @param timeout The timeout for establishing the socket connection. * @param dispatcher The connector to use to communicate with the target VM. - * @param ignoreUser {@code true} if VM processes that are owned by different users should be considered. * @return A suitable virtual machine implementation. * @throws IOException If an IO exception occurs during establishing the connection. */ - public static VirtualMachine attach(String processId, int timeout, Dispatcher dispatcher, boolean ignoreUser) throws IOException { + public static VirtualMachine attach(String processId, int timeout, Dispatcher dispatcher) throws IOException { File directory = new File(System.getProperty(IBM_TEMPORARY_FOLDER, dispatcher.getTemporaryFolder(processId)), ".com_ibm_tools_attach"); + long userId = dispatcher.userId(); RandomAccessFile attachLock = new RandomAccessFile(new File(directory, "_attachlock"), "rw"); try { FileLock attachLockLock = attachLock.getChannel().lock(); @@ -1716,10 +1690,9 @@ public static VirtualMachine attach(String processId, int timeout, Dispatcher di if (vmFolder == null) { throw new IllegalStateException("No descriptor files found in " + directory); } - long userId = dispatcher.userId(); virtualMachines = new ArrayList(); for (File aVmFolder : vmFolder) { - if (aVmFolder.isDirectory() && (ignoreUser || dispatcher.getOwnerIdOf(aVmFolder) == userId)) { + if (aVmFolder.isDirectory() && isFileOwnedByUid(dispatcher, aVmFolder, userId)) { File attachInfo = new File(aVmFolder, "attachInfo"); if (attachInfo.isFile()) { Properties virtualMachine = new Properties(); @@ -1730,12 +1703,7 @@ public static VirtualMachine attach(String processId, int timeout, Dispatcher di inputStream.close(); } int targetProcessId = Integer.parseInt(virtualMachine.getProperty("processId")); - long targetUserId; - try { - targetUserId = Long.parseLong(virtualMachine.getProperty("userUid")); - } catch (NumberFormatException ignored) { - targetUserId = 0L; - } + long targetUserId = getUserId(virtualMachine); if (userId != 0L && targetUserId == 0L) { targetUserId = dispatcher.getOwnerIdOf(attachInfo); } @@ -1782,10 +1750,14 @@ public static VirtualMachine attach(String processId, int timeout, Dispatcher di key = Long.toHexString(SECURE_RANDOM.nextLong()); } File reply = new File(receiver, "replyInfo"); + long targetUserId = getUserId(target); try { if (reply.createNewFile()) { dispatcher.setPermissions(reply, 0600); } + if (0 == userId && 0 != targetUserId) { + dispatcher.chownFileToTargetUid(reply, targetUserId); + } FileOutputStream outputStream = new FileOutputStream(reply); try { outputStream.write(key.getBytes("UTF-8")); @@ -1867,6 +1839,31 @@ public static VirtualMachine attach(String processId, int timeout, Dispatcher di } } + /** + * Returns the userUid present in the virtualMachine properties file + * @param virtualMachine Properties of the J9 attachInfo file + * @return the userUid if it can be parsed, 0L otherwise. + */ + private static long getUserId(Properties virtualMachine) { + long targetUserId; + try { + targetUserId = Long.parseLong(virtualMachine.getProperty("userUid")); + } catch (NumberFormatException ignored) { + targetUserId = 0L; + } + return targetUserId; + } + + /** + * Check if the file is owned by the UID. Note that UID 0 "owns" all files. + * @param aVmFolder File or directory + * @param userId user UID. + * @return true if the uid owns the file or uid == 0. + */ + private static boolean isFileOwnedByUid(Dispatcher dispatcher, File aVmFolder, long userId) { + return 0 == userId || dispatcher.getOwnerIdOf(aVmFolder) == userId; + } + /** * {@inheritDoc} */ @@ -2077,6 +2074,13 @@ public interface Dispatcher { */ void decrementSemaphore(File directory, String name, boolean global, int count); + /** + * change the ownership of a file. Can be called only if this process is owned by root. + * @param path path to the file + * @param targetUserId effective userid + */ + void chownFileToTargetUid(File path, long targetUserId); + /** * A connector implementation for a POSIX environment using JNA. */ @@ -2214,6 +2218,14 @@ public void decrementSemaphore(File directory, String name, boolean global, int notifySemaphore(directory, name, count, (short) -1, (short) (PosixLibrary.SEM_UNDO | PosixLibrary.IPC_NOWAIT), true); } + /** + * {@inheritDoc} + */ + @Override + public void chownFileToTargetUid(File file, long targetUserId) { + library.chown(file.getAbsolutePath(), targetUserId); + } + /** * Notifies a POSIX semaphore. * @@ -2319,6 +2331,16 @@ protected interface PosixLibrary extends Library { */ int chmod(String path, int mode) throws LastErrorException; + /** + * Runs the {@code chown} command. + * + * @param path The file path. + * @param uid The userid to set. + * @return The return code. + * @throws LastErrorException If an error occurred. + */ + int chown(String path, long uid) throws LastErrorException; + /** * Runs the {@code ftok} command. * @@ -2502,6 +2524,14 @@ public void decrementSemaphore(File directory, String name, boolean global, int } } + /** + * {@inheritDoc} + */ + @Override + public void chownFileToTargetUid(File path, long targetUserId) { + /* do nothing */ + } + /** * Opens a semaphore for signaling another process that an attachment is performed. *