changeset 1319:2bfdbd5a77f6

Agent Proxy server This patch includes the server component of the Agent Proxy. This component is not an OSGi bundle that is part of Thermostat, but a standalone program. The main class for this standalone program is AgentProxy, which makes a JNI call to seteuid and setegid in order to assume the credentials of the target VM. The native implementation is in AgentProxy.c. Once assuming the credentials of the target VM, AgentProxy instantiates an implementation of the AgentProxyLogin interface and exports it to the RMI registry. This allows the client to authenticate, and then perform necessary actions to attach and get the JMX connection URL. Then AgentProxy completes the handshake with the AgentProxyClient's listener, to notify the client that the server is up and running, or that it failed to do so. This class also has a shutdown mechanism, which is triggered when the client detaches from the target VM. AgentProxyLoginImpl provides the implementation for the client to authenticate with the server. It maintains a single AgentProxyControlImpl, which actually performs the attach operations moved from MXBeanConnector. The key addition in AgentProxyControlImpl, is that each method: attach, isAttached, detach and getConnectorAddress, take a Subject parameter to do an authorization check. The AgentProxyControlImpl is not exported to the RMI registry, but instead AgentProxyControlWrapper is exported, which simply wraps an authenticated Subject and the AgentProxyControlImpl. A wrapper is unique to each authenticated client, although practically speaking that should only be the Thermostat Agent. The act of logging in is done with AgentProxyLoginContext, which is a simple wrapper around two separate LoginContexts. The first context logs in using UnixLoginModule, which creates Principals corresponding to the caller's UID and GIDs. The second context uses our custom AgentProxyLoginModule, which compares the UID and GIDs from UnixLoginModule with the credentials of the target VM. If the credentials match, or the logged in user is root, then authentication succeeds. Our LoginModule then places an AgentProxyPrincipal in the caller's Subject, indicating the authentication was successful. Authorization checks for the methods in AgentProxyControlImpl search for this Principal. Reviewed-by: jerboaa Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-November/008713.html PR1460
author Elliott Baron <ebaron@redhat.com>
date Thu, 14 Nov 2013 11:32:26 -0500
parents 247cd7edff41
children 9732c67863d5
files agent/proxy/server/Makefile agent/proxy/server/pom.xml agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/AgentProxy.java agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/AgentProxyControlImpl.java agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/AgentProxyControlWrapper.java agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/AgentProxyLoginContext.java agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/AgentProxyLoginImpl.java agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/AgentProxyLoginModule.java agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/AgentProxyNativeUtils.java agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/AgentProxyPrincipal.java agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/ProcDataSource.java agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/ProcessUserInfoBuilder.java agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/RegistryUtils.java agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/ShutdownListener.java agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/UnixCredentials.java agent/proxy/server/src/main/native/AgentProxy.c
diffstat 16 files changed, 1555 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/proxy/server/Makefile	Thu Nov 14 11:32:26 2013 -0500
@@ -0,0 +1,42 @@
+CC         = gcc
+JAVAH      = javah
+MYCFLAGS   = -c -Wall -fPIC
+MYLDFLAGS  = -fPIC -shared
+COPY       = cp -a
+
+JAVA_HOME = /usr/lib/jvm/java-1.7.0-openjdk
+CLASSPATH  = target/classes
+TARGET_DIR = target
+
+INCLUDE    = -I $(TARGET_DIR) -I $(JAVA_HOME)/include/ -I $(JAVA_HOME)/include/linux
+SOURCES    = src/main/native/AgentProxy.c
+TARGET     = $(TARGET_DIR)/AgentProxy.c
+OBJECTS    = $(TARGET:.c=.o)
+
+EXECUTABLE = libAgentProxy.so
+
+.PHONY:
+JNI_LIST = com.redhat.thermostat.agent.proxy.server.AgentProxyNativeUtils
+
+$(JNI_LIST):
+	$(JAVAH) -force -classpath $(CLASSPATH) -d $(TARGET_DIR) $(JNI_LIST)
+
+all: $(JNI_LIST) init $(SOURCES) $(EXECUTABLE)
+
+.PHONY:
+init:
+	$(COPY) $(SOURCES) $(TARGET)
+
+$(EXECUTABLE): $(OBJECTS)
+	$(CC) $(OBJECTS) -o $(TARGET_DIR)/$@ $(MYLDFLAGS) $(LDFLAGS)
+	
+.c.o:
+	$(CC) $(MYCFLAGS) $(CFLAGS) $(INCLUDE) $< -o $@
+
+clean-lib:
+	rm -f $(TARGET_DIR)/$(EXECUTABLE)
+	
+clean-obj:
+	rm -f $(OBJECTS) $(TARGET)
+	
+clean: clean-obj clean-lib
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/proxy/server/pom.xml	Thu Nov 14 11:32:26 2013 -0500
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+
+ Copyright 2012, 2013 Red Hat, Inc.
+
+ This file is part of Thermostat.
+
+ Thermostat is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ Thermostat 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 for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Thermostat; see the file COPYING.  If not see
+ <http://www.gnu.org/licenses />.
+
+ Linking this code with other modules is making a combined work
+ based on this code.  Thus, the terms and conditions of the GNU
+ General Public License cover the whole combination.
+
+ As a special exception, the copyright holders of this code give
+ you permission to link this code with independent modules to
+ produce an executable, regardless of the license terms of these
+ independent modules, and to copy and distribute the resulting
+ executable under terms of your choice, provided that you also
+ meet, for each linked independent module, the terms and conditions
+ of the license of that module.  An independent module is a module
+ which is not derived from or based on this code.  If you modify
+ this code, you may extend this exception to your version of the
+ library, but you are not obligated to do so.  If you do not wish
+ to do so, delete this exception statement from your version.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.redhat.thermostat</groupId>
+    <artifactId>thermostat-agent-proxy</artifactId>
+    <version>0.16.0-SNAPSHOT</version>
+  </parent>
+  
+  <artifactId>thermostat-agent-proxy-server</artifactId>
+  <name>Thermostat Agent Proxy Server</name>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>exec-maven-plugin</artifactId>
+        <version>1.2.1</version>
+        <executions>  
+          <execution>
+            <phase>compile</phase>
+            <goals>
+              <goal>exec</goal>
+            </goals>
+          </execution>  
+        </executions>
+        <configuration>
+          <executable>make</executable>
+          <arguments>
+            <argument>all</argument>
+          </arguments>
+          <systemProperties>
+            <systemProperty>
+              <key>JAVA_HOME</key>
+              <value>${java.home}</value>
+            </systemProperty>
+          </systemProperties>
+        </configuration>
+      </plugin>
+    </plugins>
+    <pluginManagement>
+      <plugins>
+        <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
+        <plugin>
+          <groupId>org.eclipse.m2e</groupId>
+          <artifactId>lifecycle-mapping</artifactId>
+          <version>1.0.0</version>
+          <configuration>
+            <lifecycleMappingMetadata>
+              <pluginExecutions>
+                <pluginExecution>
+                  <pluginExecutionFilter>
+                    <groupId>org.codehaus.mojo</groupId>
+                    <artifactId>exec-maven-plugin</artifactId>
+                    <versionRange>[1.2.1,)</versionRange>
+                    <goals>
+                      <goal>exec</goal>
+                    </goals>
+                  </pluginExecutionFilter>
+                  <action>
+                    <ignore></ignore>
+                  </action>
+                </pluginExecution>
+              </pluginExecutions>
+            </lifecycleMappingMetadata>
+          </configuration>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+  </build>
+
+  <dependencies>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-agent-proxy-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.sun</groupId>
+      <artifactId>tools</artifactId>
+      <scope>system</scope>
+      <systemPath>${java.home}/../lib/tools.jar</systemPath>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/AgentProxy.java	Thu Nov 14 11:32:26 2013 -0500
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.agent.proxy.server;
+
+import java.io.IOException;
+import java.rmi.NotBoundException;
+import java.rmi.RemoteException;
+import java.rmi.registry.Registry;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.redhat.thermostat.agent.proxy.common.AgentProxyListener;
+import com.redhat.thermostat.agent.proxy.common.AgentProxyLogin;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+
+public class AgentProxy {
+    
+    private static final Logger logger = LoggingUtils.getLogger(AgentProxy.class);
+    private static final long TIMEOUT_MS = 300000L; // 5 minutes should be more than enough
+    private static final ShutdownListener shutdownListener = new ShutdownListener() {
+        @Override
+        public void shutdown() throws RemoteException {
+            shutdownProxy();
+        }
+    };
+    private static final TimerTask timeoutTask = new TimerTask() {
+        @Override
+        public void run() {
+            try {
+                shutdownProxy();
+                logger.warning("Server timed out");
+            } catch (RemoteException e) {
+                logger.log(Level.SEVERE, "Exception while shutting down "
+                        + "timed out server" , e);
+            }
+        }
+    };
+
+    private static String name = null;
+    private static int pid = -1;
+    private static Registry registry = null;
+    private static boolean bound = false;
+    private static AgentProxyLogin agent = null;
+    private static RegistryUtils registryUtils = new RegistryUtils();
+    private static AgentProxyNativeUtils nativeUtils = new AgentProxyNativeUtils();
+    private static ProcessUserInfoBuilder builder = new ProcessUserInfoBuilder(new ProcDataSource());
+    private static Timer timeoutTimer = new Timer(true);
+    
+    public static void main(String[] args) {
+        if (args.length < 1) {
+            usage();
+        }
+        
+        try {
+            // First argument is pid of target VM
+            pid = Integer.parseInt(args[0]);
+        } catch (NumberFormatException e) {
+            usage();
+        }
+        
+        // Schedule a timeout
+        timeoutTimer.schedule(timeoutTask, TIMEOUT_MS);
+        
+        // Load the native library
+        nativeUtils.loadLibrary();
+
+        // Look for registered status listener
+        AgentProxyListener listener;
+        try {
+            String listenerName = AgentProxyListener.REMOTE_PREFIX + String.valueOf(pid);
+            registry = registryUtils.getRegistry();
+            listener = (AgentProxyListener) registry.lookup(listenerName);
+        } catch (RemoteException e) {
+            throw new RuntimeException("Failed to locate registry", e);
+        } catch (NotBoundException e) {
+            throw new RuntimeException("No listener registered", e);
+        }
+
+        // Start proxy agent
+        Exception ex = null;
+        try {
+            setupProxy(pid);
+        } catch (Exception e) {
+            logger.log(Level.SEVERE, "Failed to setup agent proxy for " + pid, e);
+            ex = e;
+        }
+        
+        // Notify listener of result
+        try {
+            if (ex == null) {
+                // Success
+                listener.serverStarted();
+            }
+            else {
+                // Send exception to client
+                listener.serverFailedToStart(ex);
+            }
+        } catch (RemoteException e) {
+            throw new RuntimeException("Failed to notify listener", e);
+        }
+    }
+
+    private static void setupProxy(int pid) throws Exception {
+        try {
+            UnixCredentials creds;
+            try {
+                creds = builder.build(pid);
+            } catch (IOException e) {
+                throw new Exception("Failed to read credentials", e);
+            }
+            
+            try {
+                // Set UID/GID to owner of target VM
+                nativeUtils.setCredentials(creds.getUid(), creds.getGid());
+            } catch (Exception e) {
+                throw new Exception("Failed to set credentials to " + creds.getUid() 
+                        + ":" + creds.getGid() , e);
+            }
+
+            agent = new AgentProxyLoginImpl(creds, pid, shutdownListener);
+            name = AgentProxyLogin.REMOTE_PREFIX + String.valueOf(pid);
+            AgentProxyLogin stub = (AgentProxyLogin) registryUtils.exportObject(agent);
+            registry.rebind(name, stub);
+            bound = true;
+            logger.info(name + " bound to RMI registry");
+        } catch (RemoteException e) {
+            throw new Exception("Failed to create remote object", e);
+        }
+    }
+    
+    private static void shutdownProxy() throws RemoteException {
+        // Unbind from RMI registry
+        if (bound) {
+            try {
+                registry.unbind(name);
+                registryUtils.unexportObject(agent);
+                logger.info(name + " unbound from RMI registry");
+                bound = false;
+            } catch (NotBoundException e) {
+                throw new RemoteException("Object not bound", e);
+            }
+        }
+    }
+
+    private static void usage() {
+        throw new RuntimeException("usage: java " + AgentProxy.class.getName() + " <pidOfTargetJvm>");
+    }
+    
+    /*
+     * For testing purposes only.
+     */
+    static AgentProxyLogin getAgentProxyLogin() {
+        return agent;
+    }
+    
+    /*
+     * For testing purposes only.
+     */
+    static ShutdownListener getShutdownListener() {
+        return shutdownListener;
+    }
+    
+    /*
+     * For testing purposes only.
+     */
+    static boolean isBound() {
+        return bound;
+    }
+
+    /*
+     * For testing purposes only.
+     */
+    static void setRegistryUtils(RegistryUtils registryUtils) {
+        AgentProxy.registryUtils = registryUtils;
+    }
+    
+    /*
+     * For testing purposes only.
+     */
+    static void setNativeUtils(AgentProxyNativeUtils nativeUtils) {
+        AgentProxy.nativeUtils = nativeUtils;
+    }
+    
+    /*
+     * For testing purposes only.
+     */
+    static void setProcessUserInfoBuilder(ProcessUserInfoBuilder builder) {
+        AgentProxy.builder = builder;
+    }
+    
+    /*
+     * For testing purposes only.
+     */
+    static void setTimeoutTimer(Timer timeoutTimer) {
+        AgentProxy.timeoutTimer = timeoutTimer;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/AgentProxyControlImpl.java	Thu Nov 14 11:32:26 2013 -0500
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.agent.proxy.server;
+
+import java.io.File;
+import java.io.IOException;
+import java.rmi.RemoteException;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.security.auth.Subject;
+
+import com.sun.tools.attach.AgentInitializationException;
+import com.sun.tools.attach.AgentLoadException;
+import com.sun.tools.attach.AttachNotSupportedException;
+import com.sun.tools.attach.VirtualMachine;
+
+class AgentProxyControlImpl {
+    
+    private static final String CONNECTOR_ADDRESS_PROPERTY = "com.sun.management.jmxremote.localConnectorAddress";
+    
+    private final int pid;
+    private final VirtualMachineUtils vmUtils;
+    
+    private VirtualMachine vm;
+    private boolean attached;
+    private String connectorAddress;
+    
+    AgentProxyControlImpl(int pid) {
+        this(pid, new VirtualMachineUtils());
+    }
+    
+    AgentProxyControlImpl(int pid, VirtualMachineUtils vmUtils) {
+        this.pid = pid;
+        this.vmUtils = vmUtils;
+    }
+
+    void attach(Subject user) throws RemoteException, SecurityException {
+        authCheck(user);
+        try {
+            vm = vmUtils.attach(String.valueOf(pid));
+            attached = true;
+            
+            Properties props = vm.getAgentProperties();
+            connectorAddress = props.getProperty(CONNECTOR_ADDRESS_PROPERTY);
+            if (connectorAddress == null) {
+                props = vm.getSystemProperties();
+                String home = props.getProperty("java.home");
+                String agent = home + File.separator + "lib" + File.separator + "management-agent.jar";
+                vm.loadAgent(agent);
+                
+                props = vm.getAgentProperties();
+                connectorAddress = props.getProperty(CONNECTOR_ADDRESS_PROPERTY);
+            }
+        } catch (AttachNotSupportedException | IOException | AgentLoadException | AgentInitializationException e) {
+            throw new RemoteException("Failed to attach to VM", e);
+        }
+    }
+
+    boolean isAttached(Subject user) throws RemoteException, SecurityException {
+        authCheck(user);
+        return attached;
+    }
+
+    String getConnectorAddress(Subject user) throws RemoteException, SecurityException {
+        authCheck(user);
+        if (!attached) {
+            throw new RemoteException("Agent not attached to target VM");
+        }
+        return connectorAddress;
+    }
+
+    void detach(Subject user) throws RemoteException, SecurityException {
+        authCheck(user);
+        try {
+            if (attached) {
+                vm.detach();
+                attached = false;
+            }
+        } catch (IOException e) {
+            throw new RemoteException("Failed to detach from VM", e);
+        }
+    }
+    
+    private void authCheck(Subject user) throws SecurityException {
+        // If we've added our Principal, we've authenticated this user
+        Set<AgentProxyPrincipal> principals = user.getPrincipals(AgentProxyPrincipal.class);
+        if (principals.isEmpty()) {
+            throw new SecurityException("Access Denied");
+        }
+    }
+    
+    static class VirtualMachineUtils {
+        VirtualMachine attach(String pid) throws AttachNotSupportedException, IOException {
+            return VirtualMachine.attach(pid);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/AgentProxyControlWrapper.java	Thu Nov 14 11:32:26 2013 -0500
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.agent.proxy.server;
+
+import java.rmi.RemoteException;
+
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginException;
+
+import com.redhat.thermostat.agent.proxy.common.AgentProxyControl;
+
+class AgentProxyControlWrapper implements AgentProxyControl {
+    
+    private final Subject user;
+    private final AgentProxyLoginContext context;
+    private final AgentProxyControlImpl impl;
+    private final ShutdownListener listener;
+    private final RegistryUtils registryUtils;
+    
+    AgentProxyControlWrapper(Subject user, AgentProxyLoginContext context, AgentProxyControlImpl impl, 
+            ShutdownListener listener, RegistryUtils registryUtils) {
+        this.user = user;
+        this.context = context;
+        this.impl = impl;
+        this.listener = listener;
+        this.registryUtils = registryUtils;
+    }
+
+    @Override
+    public void attach() throws RemoteException, SecurityException {
+        impl.attach(user);
+    }
+
+    @Override
+    public boolean isAttached() throws RemoteException, SecurityException {
+        return impl.isAttached(user);
+    }
+
+    @Override
+    public String getConnectorAddress() throws RemoteException, SecurityException {
+        return impl.getConnectorAddress(user);
+    }
+
+    @Override
+    public void detach() throws RemoteException, SecurityException {
+        try {
+            impl.detach(user);
+        } finally {
+            try {
+                // Removes all Principals
+                context.logout();
+            } catch (LoginException e) {
+                throw new RemoteException("Failed to log out", e);
+            }
+            // Unexport this object
+            registryUtils.unexportObject(this);
+            
+            // Shutdown RMI server
+            listener.shutdown();
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/AgentProxyLoginContext.java	Thu Nov 14 11:32:26 2013 -0500
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.agent.proxy.server;
+
+import java.io.IOException;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+import com.redhat.thermostat.agent.proxy.server.AgentProxyLoginModule.AgentProxyCallback;
+
+/*
+ * Wraps both of our LoginModules.
+ */
+class AgentProxyLoginContext {
+    
+    private static final String UNIX_LOGIN_MODULE = "UnixLogin";
+    private static final String AGENT_PROXY_LOGIN_MODULE = "AgentProxyLogin";
+    
+    private final LoginContext unixContext;
+    private final LoginContext context;
+    
+    AgentProxyLoginContext(Subject user, UnixCredentials creds) throws LoginException {
+        this(user, creds, new ContextCreator());
+    }
+    
+    AgentProxyLoginContext(Subject user, final UnixCredentials creds, ContextCreator creator) throws LoginException {
+        unixContext = creator.createContext(UNIX_LOGIN_MODULE, user);
+        context = creator.createContext(AGENT_PROXY_LOGIN_MODULE, user, new CallbackHandler() {
+            
+            @Override
+            public void handle(Callback[] callbacks) throws IOException,
+                    UnsupportedCallbackException {
+                for (Callback callback : callbacks) {
+                    if (callback instanceof AgentProxyCallback) {
+                        ((AgentProxyCallback) callback).setTargetCredentials(creds);
+                    }
+                }
+            }
+        });
+    }
+    
+    void login() throws LoginException {
+        unixContext.login();
+        context.login();
+    }
+    
+    void logout() throws LoginException {
+        context.logout();
+        unixContext.logout();
+    }
+    
+    static class ContextCreator {
+        LoginContext createContext(String name, Subject subject) throws LoginException {
+            return new LoginContext(name, subject);
+        }
+        
+        LoginContext createContext(String name, Subject subject, CallbackHandler handler) throws LoginException {
+            return new LoginContext(name, subject, handler);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/AgentProxyLoginImpl.java	Thu Nov 14 11:32:26 2013 -0500
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.agent.proxy.server;
+
+import java.rmi.RemoteException;
+
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginException;
+
+import com.redhat.thermostat.agent.proxy.common.AgentProxyControl;
+import com.redhat.thermostat.agent.proxy.common.AgentProxyLogin;
+
+class AgentProxyLoginImpl implements AgentProxyLogin {
+    
+    private final UnixCredentials creds;
+    private final AgentProxyControlImpl impl;
+    private final ShutdownListener listener;
+    private final LoginContextCreator contextCreator;
+    private final RegistryUtils registryUtils;
+    
+    AgentProxyLoginImpl(UnixCredentials creds, int pid, ShutdownListener listener) throws RemoteException {
+        this(creds, pid, listener, new LoginContextCreator(), new RegistryUtils());
+    }
+    
+    AgentProxyLoginImpl(UnixCredentials creds, int pid, ShutdownListener listener, 
+            LoginContextCreator contextCreator, RegistryUtils registryUtils) throws RemoteException {
+        this.creds = creds;
+        this.impl = new AgentProxyControlImpl(pid);
+        this.listener = listener;
+        this.contextCreator = contextCreator;
+        this.registryUtils = registryUtils;
+    }
+
+    @Override
+    public AgentProxyControl login() throws RemoteException, SecurityException {
+        Subject user = new Subject();
+        try {
+            AgentProxyLoginContext context = contextCreator.createContext(user, creds);
+            context.login();
+            
+            AgentProxyControl control = new AgentProxyControlWrapper(user, context, impl, 
+                    listener, registryUtils);
+            AgentProxyControl stub = (AgentProxyControl) registryUtils.exportObject(control);
+            return stub;
+        } catch (LoginException e) {
+            throw new RemoteException("Failed to login", e);
+        }
+    }
+    
+    static class LoginContextCreator {
+        AgentProxyLoginContext createContext(Subject user, UnixCredentials creds) throws LoginException {
+            return new AgentProxyLoginContext(user, creds);
+        }
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/AgentProxyLoginModule.java	Thu Nov 14 11:32:26 2013 -0500
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.agent.proxy.server;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.spi.LoginModule;
+
+public class AgentProxyLoginModule implements LoginModule {
+    
+    private Subject subject;
+    private CallbackHandler callbackHandler;
+    private AgentProxyPrincipal principal;
+    private boolean loggedIn;
+    private boolean committed;
+    private boolean debug;
+    
+    interface AgentProxyCallback extends Callback {
+        
+        void setTargetCredentials(UnixCredentials creds);
+        
+    }
+
+    @Override
+    public void initialize(Subject subject, CallbackHandler callbackHandler,
+            Map<String, ?> sharedState, Map<String, ?> options) {
+        this.subject = subject;
+        this.callbackHandler = callbackHandler;
+        
+        // Check for debug option
+        debug = "true".equalsIgnoreCase((String) options.get("debug"));
+    }
+
+    @Override
+    public boolean login() throws LoginException {
+        loggedIn = false;
+        
+        // Get credentials of target process from callback
+        UnixCredentials creds = getTargetCredentials();
+
+        // Verify subject's credentials match those of target process
+        checkCredentials(creds);
+
+        // Add a custom principal to the subject to show we've authenticated
+        principal = createPrincipal();
+        if (debug) {
+            System.out.println("\t\t[AgentProxyLoginModule]: " +
+                    "created principal for user: " + principal.getName());
+        }
+        
+        loggedIn = true;
+        return true;
+    }
+    
+    private UnixCredentials getTargetCredentials() throws LoginException {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final UnixCredentials[] credsContainer = new UnixCredentials[1];
+        
+        try {
+            callbackHandler.handle(new Callback[] { new AgentProxyCallback() {
+
+                @Override
+                public void setTargetCredentials(UnixCredentials creds) {
+                    credsContainer[0] = creds;
+                    latch.countDown();
+                }
+
+            }});
+
+            latch.await();
+            
+            return credsContainer[0];
+        } catch (IOException e) {
+            throw new LoginException(e.getMessage());
+        } catch (UnsupportedCallbackException e) {
+            throw new LoginException(e.getMessage());
+        } catch (InterruptedException e) {
+            throw new LoginException("Interrupted");
+        }
+    }
+
+    @Override
+    public boolean commit() throws LoginException {
+        committed = false;
+        
+        if (loggedIn) {
+            subject.getPrincipals().add(principal);
+            if (debug) {
+                System.out.println("\t\t[AgentProxyLoginModule]: " +
+                        "adding AgentProxyPrincipal to Subject");
+            }
+            committed = true;
+        }
+        return committed;
+    }
+
+    @Override
+    public boolean abort() throws LoginException {
+        if (debug) {
+            System.out.println("\t\t[AgentProxyLoginModule]: " +
+                    "aborted authentication attempt");
+        }
+        if (!loggedIn) {
+            return false;
+        }
+        else if (loggedIn && !committed) {
+            // Clean up state
+            loggedIn = false;
+            principal = null;
+        }
+        else {
+            // Clean up state & remove principal
+            logout();
+        }
+        
+        return true;
+    }
+
+    @Override
+    public boolean logout() throws LoginException {
+        // Remove principal
+        subject.getPrincipals().remove(principal);
+        if (debug) {
+            System.out.println("\t\t[AgentProxyLoginModule]: " +
+                    "removed principal for user: " + principal.getName());
+        }
+        
+        // Clean up state
+        loggedIn = false;
+        committed = false;
+        principal = null;
+        
+        return true;
+    }
+    
+    @SuppressWarnings("restriction")
+    private void checkCredentials(UnixCredentials creds) throws LoginException {
+        boolean uidOkay = false, gidOkay = false;
+        
+        // Check UID
+        Set<com.sun.security.auth.UnixNumericUserPrincipal> userPrincipals = subject.getPrincipals(com.sun.security.auth.UnixNumericUserPrincipal.class);
+        if (!userPrincipals.isEmpty()) {
+            com.sun.security.auth.UnixNumericUserPrincipal userPrincipal = userPrincipals.iterator().next();
+            if (debug) {
+                System.out.println("UnixLoginModule UID: " + userPrincipal.longValue() + ", PID: " + creds.getPid() + ", Owner: " + creds.getUid());
+            }
+            if (userPrincipal.longValue() == creds.getUid() || userPrincipal.longValue() == 0) {
+                uidOkay = true;
+            }
+        }
+        
+        // Check GID
+        Set<com.sun.security.auth.UnixNumericGroupPrincipal> groupPrincipals = subject.getPrincipals(com.sun.security.auth.UnixNumericGroupPrincipal.class);
+        for (com.sun.security.auth.UnixNumericGroupPrincipal groupPrincipal : groupPrincipals) {
+            if (groupPrincipal.longValue() == creds.getGid() || groupPrincipal.longValue() == 0) {
+                gidOkay = true;
+            }
+        }
+        
+        if (!uidOkay || !gidOkay) {
+            throw new LoginException("Access Denied");
+        }
+    }
+    
+    @SuppressWarnings("restriction")
+    private AgentProxyPrincipal createPrincipal() throws LoginException {
+        Set<com.sun.security.auth.UnixPrincipal> userPrincipals = subject.getPrincipals(com.sun.security.auth.UnixPrincipal.class);
+        if (userPrincipals.isEmpty()) {
+            throw new LoginException("Unable to obtain user ID");
+        }
+        
+        com.sun.security.auth.UnixPrincipal userPrincipal = userPrincipals.iterator().next();
+        return new AgentProxyPrincipal(userPrincipal.getName());
+    }
+    
+    /*
+     * For testing purposes only.
+     */
+    AgentProxyPrincipal getPrincipal() {
+        return principal;
+    }
+    
+    /*
+     * For testing purposes only.
+     */
+    boolean isLoggedIn() {
+        return loggedIn;
+    }
+    
+    /*
+     * For testing purposes only.
+     */
+    boolean isCommitted() {
+        return committed;
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/AgentProxyNativeUtils.java	Thu Nov 14 11:32:26 2013 -0500
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.agent.proxy.server;
+
+import com.redhat.thermostat.shared.config.NativeLibraryResolver;
+
+class AgentProxyNativeUtils {
+    
+    void loadLibrary() {
+        String libPath = NativeLibraryResolver.getAbsoluteLibraryPath("AgentProxy");
+        System.load(libPath);
+    }
+    
+    native void setCredentials(long uid, long gid) throws Exception;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/AgentProxyPrincipal.java	Thu Nov 14 11:32:26 2013 -0500
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.agent.proxy.server;
+
+import java.security.Principal;
+
+class AgentProxyPrincipal implements Principal {
+    
+    private final String name;
+    
+    AgentProxyPrincipal(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/ProcDataSource.java	Thu Nov 14 11:32:26 2013 -0500
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.agent.proxy.server;
+
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * Wrapper for files under /proc. See proc(5) for details about this.
+ */
+class ProcDataSource {
+
+    private static final String PID_STATUS_FILE = "/proc/${pid}/status";
+
+    /**
+     * Returns a reader for /proc/$PID/status
+     */
+    Reader getStatusReader(int pid) throws IOException {
+        return new FileReader(getPidFile(PID_STATUS_FILE, pid));
+    }
+
+    private String getPidFile(String fileName, int pid) {
+        return fileName.replace("${pid}", Integer.toString(pid));
+    }
+
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/ProcessUserInfoBuilder.java	Thu Nov 14 11:32:26 2013 -0500
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.agent.proxy.server;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+
+class ProcessUserInfoBuilder {
+    
+    private static final String PROC_STATUS_UID = "Uid:";
+    private static final String PROC_STATUS_GID = "Gid:";
+    
+    private final ProcDataSource source;
+    
+    ProcessUserInfoBuilder(ProcDataSource source) {
+        this.source = source;
+    }
+    
+    UnixCredentials build(int pid) throws IOException {
+        Reader reader = source.getStatusReader(pid);
+        UnixCredentials creds = getUidGidFromProcfs(new BufferedReader(reader), pid);
+        return creds;
+    }
+
+    /*
+     * Look for the following lines:
+     * Uid:  <RealUid>   <EffectiveUid>   <SavedUid>   <FSUid>
+     * Gid:  <RealGid>   <EffectiveGid>   <SavedGid>   <FSGid>
+     */
+    private UnixCredentials getUidGidFromProcfs(BufferedReader br, int pid) throws IOException {
+        long uid = -1;
+        long gid = -1;
+        String line;
+        while ((line = br.readLine()) != null) {
+            line = line.trim();
+            if (line.startsWith(PROC_STATUS_UID)) {
+                uid = parseUidGid(line);
+            }
+            else if (line.startsWith(PROC_STATUS_GID)) {
+                gid = parseUidGid(line);
+            }
+        }
+        if (uid < 0) {
+            throw new IOException("Unable to determine UID from /proc/${pid}/status");
+        }
+        if (gid < 0) {
+            throw new IOException("Unable to determine GID from /proc/${pid}/status");
+        }
+        
+        return new UnixCredentials(uid, gid, pid);
+    }
+
+    private long parseUidGid(String line) throws IOException {
+        long result = -1;
+        String[] parts = line.split("\\s+");
+        if (parts.length == 5) {
+            try {
+                // Use Effective UID/GID
+                result = Long.parseLong(parts[2]);
+            } catch (NumberFormatException e) {
+                throw new IOException("Unexpected output from ps command", e);
+            }
+        }
+        else {
+            throw new IOException("Expected 5 parts from split /proc/${pid}/status output, got " + parts.length);
+        }
+        return result;
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/RegistryUtils.java	Thu Nov 14 11:32:26 2013 -0500
@@ -0,0 +1,26 @@
+package com.redhat.thermostat.agent.proxy.server;
+
+import java.net.InetAddress;
+import java.rmi.NoSuchObjectException;
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+import java.rmi.server.UnicastRemoteObject;
+
+class RegistryUtils {
+    
+    Registry getRegistry() throws RemoteException {
+        return LocateRegistry.getRegistry(InetAddress.getLoopbackAddress().getHostName());
+    }
+    
+    Remote exportObject(Remote obj) throws RemoteException {
+        // Single arg method exports stub instead of real object
+        return UnicastRemoteObject.exportObject(obj, 0);
+    }
+    
+    void unexportObject(Remote obj) throws NoSuchObjectException {
+        UnicastRemoteObject.unexportObject(obj, true);
+    }
+    
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/ShutdownListener.java	Thu Nov 14 11:32:26 2013 -0500
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.agent.proxy.server;
+
+import java.rmi.RemoteException;
+
+interface ShutdownListener {
+    
+    void shutdown() throws RemoteException;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/UnixCredentials.java	Thu Nov 14 11:32:26 2013 -0500
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.agent.proxy.server;
+
+class UnixCredentials {
+    
+    private final long uid;
+    private final long gid;
+    private final int pid;
+
+    UnixCredentials(long uid, long gid, int pid) {
+        this.uid = uid;
+        this.gid = gid;
+        this.pid = pid;
+    }
+    
+    long getUid() {
+        return uid;
+    }
+    
+    long getGid() {
+        return gid;
+    }
+    
+    int getPid() {
+        return pid;
+    }
+    
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/proxy/server/src/main/native/AgentProxy.c	Thu Nov 14 11:32:26 2013 -0500
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+#include "com_redhat_thermostat_agent_proxy_server_AgentProxyNativeUtils.h"
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+static jint throw_Exception(JNIEnv *, const char *, const char *);
+
+JNIEXPORT void JNICALL 
+Java_com_redhat_thermostat_agent_proxy_server_AgentProxyNativeUtils_setCredentials(
+        JNIEnv *env, jclass splitAgentKlass, jlong uid, jlong gid) {
+    int rc;
+    char *err;
+
+    // Need to setegid before seteuid, or otherwise we've dropped permissions
+    // needed to do so.
+    rc = setegid(gid);
+    if (rc < 0) {
+        err = strerror(errno);
+        throw_Exception(env, "java/lang/Exception", err);
+    }
+
+    rc = seteuid(uid);
+    if (rc < 0) {
+        err = strerror(errno);
+        throw_Exception(env, "java/lang/Exception", err);
+    }
+}
+
+static jint throw_Exception(JNIEnv *env, const char *class_name,
+        const char *message) {
+    jclass class;
+
+    class = (*env)->FindClass(env, class_name);
+    if (class == NULL) {
+        return -1;
+    }
+    return (*env)->ThrowNew(env, class, message);
+}