001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    package org.apache.geronimo.jmxremoting;
018    
019    import java.io.IOException;
020    import java.net.ServerSocket;
021    import java.rmi.server.RMIClientSocketFactory;
022    import java.rmi.server.RMIServerSocketFactory;
023    import java.util.HashMap;
024    import java.util.Map;
025    
026    import javax.management.MBeanServer;
027    import javax.management.NotificationFilterSupport;
028    import javax.management.remote.JMXConnectionNotification;
029    import javax.management.remote.JMXConnectorServer;
030    import javax.management.remote.JMXConnectorServerFactory;
031    import javax.management.remote.JMXServiceURL;
032    import javax.management.remote.rmi.RMIConnectorServer;
033    import javax.net.ssl.KeyManagerFactory;
034    import javax.net.ssl.SSLServerSocket;
035    import javax.net.ssl.SSLServerSocketFactory;
036    import javax.rmi.ssl.SslRMIClientSocketFactory;
037    
038    import org.apache.geronimo.gbean.GBeanInfo;
039    import org.apache.geronimo.gbean.GBeanInfoBuilder;
040    import org.apache.geronimo.management.geronimo.KeystoreManager;
041    import org.apache.geronimo.system.jmx.MBeanServerReference;
042    
043    /**
044     * A secure (SSL/TLS) connector that supports the server side of JSR 160 JMX Remoting.
045     *
046     * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
047     */
048    public class JMXSecureConnector extends JMXConnector {
049        
050        private KeystoreManager keystoreManager;
051        private String algorithm;
052        private String secureProtocol;
053        private String keyStore;
054        private String trustStore;
055        private String keyAlias;
056        private boolean clientAuth;
057        
058        public JMXSecureConnector(MBeanServerReference mbeanServerReference, String objectName, ClassLoader classLoader) {
059            this(mbeanServerReference.getMBeanServer(), objectName, classLoader);
060        }
061    
062        public JMXSecureConnector(MBeanServer mbeanServer, String objectName, ClassLoader classLoader) {
063            super(mbeanServer, objectName, classLoader);
064        }
065    
066        public void setKeystoreManager(KeystoreManager keystoreManager) {
067            this.keystoreManager = keystoreManager;
068        }
069                
070        public KeystoreManager getKeystoreManager() {
071            return this.keystoreManager;
072        }
073                
074        public String getKeyStore() {
075            return this.keyStore;
076        }
077        
078        public void setKeyStore(String keyStore) {
079            this.keyStore = keyStore;
080        }
081            
082        public String getTrustStore() {
083            return this.trustStore;
084        }
085            
086        public void setTrustStore(String trustStore) {
087            this.trustStore = trustStore;
088        }
089            
090        public String getKeyAlias() {
091            return this.keyAlias;
092        }
093            
094        public void setKeyAlias(String keyAlias) {
095            this.keyAlias = keyAlias;
096        }
097            
098        public String getAlgorithm() {
099            return this.algorithm;
100        }
101            
102        /**
103         * Algorithm to use.
104         * As different JVMs have different implementations available, the default algorithm can be used by supplying the value "Default".
105         *
106         * @param algorithm the algorithm to use, or "Default" to use the default from {@link javax.net.ssl.KeyManagerFactory#getDefaultAlgorithm()}
107         */
108        public void setAlgorithm(String algorithm) {                
109            if ("default".equalsIgnoreCase(algorithm)) {
110                this.algorithm = KeyManagerFactory.getDefaultAlgorithm();
111            } else {
112                this.algorithm = algorithm;
113            }
114        }
115                
116        public String getSecureProtocol() {
117            return this.secureProtocol;
118        }
119            
120        public void setSecureProtocol(String secureProtocol) {
121            this.secureProtocol = secureProtocol;
122        }
123            
124        public void setClientAuth(boolean clientAuth) {
125            this.clientAuth = clientAuth;
126        }
127            
128        public boolean isClientAuth() {
129            return this.clientAuth;
130        }
131                   
132        public void doStart() throws Exception {
133            jmxServiceURL = new JMXServiceURL(protocol, host, port, urlPath);
134            Map env = new HashMap();
135            Authenticator authenticator = null;
136            if (applicationConfigName != null) {
137                authenticator = new Authenticator(applicationConfigName, classLoader);
138                env.put(JMXConnectorServer.AUTHENTICATOR, authenticator);
139            } else {
140                log.warn("Starting unauthenticating JMXConnector for " + jmxServiceURL);
141            }
142            
143            SSLServerSocketFactory sssf = keystoreManager.createSSLServerFactory(null, secureProtocol, algorithm, keyStore, keyAlias, trustStore, classLoader);
144            RMIServerSocketFactory rssf = new GeronimoSslRMIServerSocketFactory(sssf, clientAuth);
145            RMIClientSocketFactory rcsf = new SslRMIClientSocketFactory();
146            env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, rssf);
147            env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, rcsf);
148            
149            server = JMXConnectorServerFactory.newJMXConnectorServer(jmxServiceURL, env, mbeanServer);
150            NotificationFilterSupport filter = new NotificationFilterSupport();
151            filter.enableType(JMXConnectionNotification.OPENED);
152            filter.enableType(JMXConnectionNotification.CLOSED);
153            filter.enableType(JMXConnectionNotification.FAILED);
154            server.addNotificationListener(authenticator, filter, null);
155            server.start();
156            log.debug("Started JMXConnector " + server.getAddress());
157        }
158    
159        private static class GeronimoSslRMIServerSocketFactory implements RMIServerSocketFactory {
160            private SSLServerSocketFactory sssf;
161            private boolean clientAuth;
162            
163            public GeronimoSslRMIServerSocketFactory(SSLServerSocketFactory sssf, boolean clientAuth) {
164                this.sssf = sssf;
165                this.clientAuth = clientAuth;
166            }
167            
168            public ServerSocket createServerSocket(int port) throws IOException {
169                SSLServerSocket ss = (SSLServerSocket) sssf.createServerSocket(port);
170                ss.setNeedClientAuth(clientAuth);
171                return ss;
172            }
173        }
174        
175        public static final GBeanInfo GBEAN_INFO;
176    
177        static {
178            GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic("JMX Secure Remoting Connector", JMXSecureConnector.class);
179            infoFactory.addReference("MBeanServerReference", MBeanServerReference.class);
180            infoFactory.addAttribute("objectName", String.class, false);
181            infoFactory.addAttribute("classLoader", ClassLoader.class, false);
182    
183            infoFactory.addAttribute("protocol", String.class, true, true);
184            infoFactory.addAttribute("host", String.class, true, true);
185            infoFactory.addAttribute("port", int.class, true, true);
186            infoFactory.addAttribute("urlPath", String.class, true, true);
187            infoFactory.addAttribute("applicationConfigName", String.class, true, true);
188    
189            infoFactory.addInterface(JMXConnectorInfo.class);
190            
191            infoFactory.addReference("KeystoreManager", KeystoreManager.class);
192            infoFactory.addAttribute("algorithm", String.class, true, true);
193            infoFactory.addAttribute("secureProtocol", String.class, true, true);
194            infoFactory.addAttribute("keyStore", String.class, true, true);
195            infoFactory.addAttribute("keyAlias", String.class, true, true);
196            infoFactory.addAttribute("trustStore", String.class, true, true);
197            infoFactory.addAttribute("clientAuth", boolean.class, true, true);
198            
199            infoFactory.setConstructor(new String[]{"MBeanServerReference", "objectName", "classLoader"});
200            GBEAN_INFO = infoFactory.getBeanInfo();
201        }
202    
203        public static GBeanInfo getGBeanInfo() {
204            return GBEAN_INFO;
205        }
206    }