Search code examples
javajakarta-eejbossjmsjava-web-start

Java Web Start security warning despite using all-permissions and privileged action


I have set up JMS connections between my JBoss (4.2.2.GA) server and our J2SE client. The client runs via Java Web Start, and in the JNLP, I have the following:

<jnlp version="1.5+" codebase="$$codebase" href="start.jnlp">
    <!-- Information -->
    <security>
        <all-permissions />
    </security>
    <resources>
        <!-- Some other non-JMS resources -->
        <jar href="concurrent.jar" />
        <jar href="jboss-common.jar" />
        <jar href="jboss-j2ee.jar" />
        <jar href="jbossmq.jar" />
        <jar href="jnpserver.jar" />
        <jar href="log4j.jar" />
    <resources>
</jnlp>

The JMS connection is working fine. I have successfully configured, subscribed to, and tested a JMS topic. Here is that code:

final String topic = ... ;
final InitialContext context = new InitialContext();
PrivilegedAction<JmsConnectionContext> action = new PrivilegedAction<JmsConnectionContext>() {

    @Override
    public JmsConnectionContext run() {
        try {
            TopicConnectionFactory connectionFactory = (TopicConnectionFactory) context.lookup("TopicConnectionFactory");
            TopicConnection topicConnection = connectionFactory.createTopicConnection();
            int sessionId = Client.getSession().getSessionId();
            topicConnection.setClientID("UMClient-" + sessionId);
            TopicSession session = topicConnection.createTopicSession(false,
                    TopicSession.AUTO_ACKNOWLEDGE);

            Topic jmsTopic = (Topic) context.lookup(topic);
            TopicSubscriber subscriber = session.createDurableSubscriber(jmsTopic, topic + "-" +
                    sessionId);
            subscriber.setMessageListener(new JmsTopicMessageListener());
            topicConnection.start();
            return new JmsConnectionContext(topic, topicConnection, session);
        } catch(Exception e) {
            logger.log(Level.SEVERE, "Topic creation and connection failed.", e);
            return null;
        }
    }
};
return AccessController.doPrivileged(action);

And my JNDI properties configuration is very straightforward:

java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming
java.naming.provider.url=10.1.3.143:1099

Also, the jars have all been signed using our company certificate, which was issued via a CA (set to expire within the next few months, but it's still valid). I've also tried clearing the javaws cache to make sure it wasn't an outdated code issue.

So why is the warning still coming up? I'm running it as a privileged action which, as far as I understand, should just grant the code the all-permissions policy from the JNLP file, but yet when sun.rmi.transport.tcp.TCPChannel.checkConnectPermission() gets called during the JNDI lookup, the JWS security manager brings up the security warning. Is there something else I have to configure for the JNDI lookups to have elevated connection privileges, and can I configure that over Java Web Start?

Update

I'm still dealing with this problem. I narrowed down the dialog to coming up when it tries to access the RMI port on the server (it's checking permissionhost:1098). I tried setting the java.rmi.server.hostname property to host, going off of the workaround listed in this bug report. Since the first RMI lookup doesn't happen until after the client is already started, I assume that I don't need to find a way to add it to the JNLP file via a <property>.

Here's the stack trace from when I click "cancel" in the security warning:

java.security.AccessControlException: access denied (java.net.SocketPermission 10.1.3.143:1098 connect,resolve)
    at java.security.AccessControlContext.checkPermission(Unknown Source)
    at java.security.AccessController.checkPermission(Unknown Source)
    at java.lang.SecurityManager.checkPermission(Unknown Source)
    at java.lang.SecurityManager.checkConnect(Unknown Source)
    at com.sun.javaws.security.JavaWebStartSecurity.checkConnect(Unknown Source)
    at sun.rmi.transport.tcp.TCPChannel.checkConnectPermission(Unknown Source)
    at sun.rmi.transport.tcp.TCPChannel.newConnection(Unknown Source)
    at sun.rmi.server.UnicastRef.invoke(Unknown Source)
    at org.jnp.server.NamingServer_Stub.lookup(Unknown Source)
    at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:667)
    at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:627)
    at javax.naming.InitialContext.lookup(Unknown Source)
    at org.jboss.naming.LinkRefPairObjectFactory.getObjectInstance(LinkRefPairObjectFactory.java:85)
    at javax.naming.spi.NamingManager.getObjectInstance(Unknown Source)
    at org.jnp.interfaces.NamingContext.getObjectInstance(NamingContext.java:1273)
    at org.jnp.interfaces.NamingContext.getObjectInstanceWrapFailure(NamingContext.java:1290)
    at org.jnp.interfaces.NamingContext.getObjectInstanceWrapFailure(NamingContext.java:1298)
    at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:763)
    at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:627)
    at javax.naming.InitialContext.lookup(Unknown Source)
    at com.ultramain.uui.jms.JmsHelper.jndiLookup(JmsHelper.java:145)
    ...

So the problem is specific to RMI access. Why doesn't setting the hostname work to fix the dialog, as suggested in the workaround in the linked bug report and a few other resources I have found? Is there any other workaround to fix this?


Solution

  • The solution is a big hack, but it's all I could find.

    Going off of information here, the JWS security manager doesn't have the RMI code listed as trusted code because it was loaded under a separate class loader. Because the code didn't originate from the JNLP, JWS doesn't let it run with the all-permissions security.

    The hacky fix for this was simply wrapping each lookup with something like:

    Topic topic = null;
    SecurityManager prevSecurityManager = System.getSecurityManager();
    try {
        System.setSecurityManager(null);
        topic = (Topic) context.lookup(name);
    } finally {
        System.setSecurityManager(prevSecurityManager);
    }
    

    Setting the SecurityManager to null bypasses the JWS security manager, letting the default one take over.

    Eventually, I want to find a way to add the RMI code to the trusted codebase for the JNLP, but this solution will work in our development environments and demos until I can do that. This isn't a very secure way of doing things, especially in an application which can be multi-threaded like this. It gives a small window of opportunity where there is no security manager, so if anyone has a solution which will allow me to trust the RMI code in the JNLP configuration, post it and your answer will be accepted (provided it works of course).