Ok just checked and we didnt expose the setPassword to JMX for dbcp so we
are fine, sorry for the noise!


Romain Manni-Bucau
@rmannibucau <https://twitter.com/rmannibucau> |  Blog
<http://rmannibucau.wordpress.com> | Github <https://github.com/rmannibucau> |
LinkedIn <https://www.linkedin.com/in/rmannibucau> | Tomitriber
<http://www.tomitribe.com>

2015-10-30 17:39 GMT-07:00 Jean-Louis Monteiro <jlmonte...@tomitribe.com>:

> Sure. Maybe the fix can be improved. Let's discuss
> Le 30 oct. 2015 17:37, "Romain Manni-Bucau" <rmannibu...@gmail.com> a
> écrit :
>
> > It prevents to reset the pwd at runtime if i read it correctly. It should
> > be fixed since it is used at least through jmx.
> > ---------- Message transféré ----------
> > De : <jlmonte...@apache.org>
> > Date : 30 oct. 2015 16:14
> > Objet : tomee git commit: TOMEE-1648: The password cipher is called each
> > time the datasource is built even if we already have the clear password
> > À : <comm...@tomee.apache.org>
> > Cc :
> >
> > Repository: tomee
> > Updated Branches:
> >   refs/heads/master 34c4cc7a2 -> 436089b65
> >
> >
> > TOMEE-1648: The password cipher is called each time the datasource is
> built
> > even if we already have the clear password
> >
> >
> > Project: http://git-wip-us.apache.org/repos/asf/tomee/repo
> > Commit: http://git-wip-us.apache.org/repos/asf/tomee/commit/436089b6
> > Tree: http://git-wip-us.apache.org/repos/asf/tomee/tree/436089b6
> > Diff: http://git-wip-us.apache.org/repos/asf/tomee/diff/436089b6
> >
> > Branch: refs/heads/master
> > Commit: 436089b658e239757e0a51b49be01974ca339627
> > Parents: 34c4cc7
> > Author: Jean-Louis Monteiro <jlmonte...@apache.org>
> > Authored: Fri Oct 30 16:13:56 2015 -0700
> > Committer: Jean-Louis Monteiro <jlmonte...@apache.org>
> > Committed: Fri Oct 30 16:13:56 2015 -0700
> >
> > ----------------------------------------------------------------------
> >  .../resource/jdbc/dbcp/BasicDataSource.java     |  20 +-
> >  .../jdbc/dbcp/BasicManagedDataSource.java       |  23 ++-
> >  .../jdbc/CipheredPasswordDataSourceTest.java    | 193
> +++++++++++++++++++
> >  3 files changed, 233 insertions(+), 3 deletions(-)
> > ----------------------------------------------------------------------
> >
> >
> >
> >
> http://git-wip-us.apache.org/repos/asf/tomee/blob/436089b6/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java
> > ----------------------------------------------------------------------
> > diff --git
> >
> >
> a/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java
> >
> >
> b/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java
> > index ffb5bba..995221c 100644
> > ---
> >
> >
> a/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java
> > +++
> >
> >
> b/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicDataSource.java
> > @@ -55,6 +55,10 @@ public class BasicDataSource extends
> > org.apache.commons.dbcp2.BasicDataSource im
> >       * not ciphered. The {@link
> > org.apache.openejb.cipher.PlainTextPasswordCipher} can also be used.
> >       */
> >      private String passwordCipher;
> > +
> > +    // keep tracking the user configured password in case we need it to
> be
> > decrypted again
> > +    private String initialPassword;
> > +
> >      private JMXBasicDataSource jmxDs;
> >      private CommonDataSource delegate;
> >      private String name;
> > @@ -128,6 +132,18 @@ public class BasicDataSource extends
> > org.apache.commons.dbcp2.BasicDataSource im
> >          }
> >      }
> >
> > +    @Override
> > +    public void setPassword(final String password) {
> > +        final ReentrantLock l = lock;
> > +        l.lock();
> > +        try {
> > +            // keep the encrypted value if it's encrypted
> > +            this.initialPassword = password;
> > +            super.setPassword(password);
> > +        } finally {
> > +            l.unlock();
> > +        }
> > +    }
> >
> >      public String getUserName() {
> >          final ReentrantLock l = lock;
> > @@ -226,7 +242,9 @@ public class BasicDataSource extends
> > org.apache.commons.dbcp2.BasicDataSource im
> >              // check password codec if available
> >              if (null != passwordCipher &&
> > !"PlainText".equals(passwordCipher)) {
> >                  final PasswordCipher cipher =
> > PasswordCipherFactory.getPasswordCipher(passwordCipher);
> > -                final String plainPwd =
> > cipher.decrypt(getPassword().toCharArray());
> > +
> > +                // always use the initial encrypted value
> > +                final String plainPwd =
> > cipher.decrypt(initialPassword.toCharArray());
> >
> >                  // override previous password value
> >                  super.setPassword(plainPwd);
> >
> >
> >
> http://git-wip-us.apache.org/repos/asf/tomee/blob/436089b6/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java
> > ----------------------------------------------------------------------
> > diff --git
> >
> >
> a/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java
> >
> >
> b/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java
> > index ec604b0..0846f21 100644
> > ---
> >
> >
> a/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java
> > +++
> >
> >
> b/container/openejb-core/src/main/java/org/apache/openejb/resource/jdbc/dbcp/BasicManagedDataSource.java
> > @@ -54,6 +54,10 @@ public class BasicManagedDataSource extends
> > org.apache.commons.dbcp2.managed.Bas
> >       * not ciphered. The {@link
> > org.apache.openejb.cipher.PlainTextPasswordCipher} can also be used.
> >       */
> >      private String passwordCipher;
> > +
> > +    // keep tracking the user configured password in case we need it to
> be
> > decrypted again
> > +    private String initialPassword;
> > +
> >      private JMXBasicDataSource jmxDs;
> >
> >      public BasicManagedDataSource(final String name) {
> > @@ -85,7 +89,7 @@ public class BasicManagedDataSource extends
> > org.apache.commons.dbcp2.managed.Bas
> >
> >      private void setJndiXaDataSource(final String xaDataSource) {
> >          setXaDataSourceInstance( // proxy cause we don't know if this
> > datasource was created before or not the delegate
> > -            XADataSourceResource.proxy(getDriverClassLoader() != null ?
> > getDriverClassLoader() : Thread.currentThread().getContextClassLoader(),
> > xaDataSource));
> > +                XADataSourceResource.proxy(getDriverClassLoader() !=
> null
> > ? getDriverClassLoader() :
> Thread.currentThread().getContextClassLoader(),
> > xaDataSource));
> >
> >          if (getTransactionManager() == null) {
> >              setTransactionManager(OpenEJB.getTransactionManager());
> > @@ -133,6 +137,19 @@ public class BasicManagedDataSource extends
> > org.apache.commons.dbcp2.managed.Bas
> >          }
> >      }
> >
> > +    @Override
> > +    public void setPassword(final String password) {
> > +        final ReentrantLock l = lock;
> > +        l.lock();
> > +        try {
> > +            // keep the encrypted value if it's encrypted
> > +            this.initialPassword = password;
> > +            super.setPassword(password);
> > +        } finally {
> > +            l.unlock();
> > +        }
> > +    }
> > +
> >      public String getUserName() {
> >          final ReentrantLock l = lock;
> >          l.lock();
> > @@ -229,7 +246,9 @@ public class BasicManagedDataSource extends
> > org.apache.commons.dbcp2.managed.Bas
> >              // check password codec if available
> >              if (null != passwordCipher) {
> >                  final PasswordCipher cipher =
> > PasswordCipherFactory.getPasswordCipher(passwordCipher);
> > -                final String plainPwd =
> > cipher.decrypt(getPassword().toCharArray());
> > +
> > +                // always use the initial encrypted value
> > +                final String plainPwd =
> > cipher.decrypt(initialPassword.toCharArray());
> >
> >                  // override previous password value
> >                  super.setPassword(plainPwd);
> >
> >
> >
> http://git-wip-us.apache.org/repos/asf/tomee/blob/436089b6/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/CipheredPasswordDataSourceTest.java
> > ----------------------------------------------------------------------
> > diff --git
> >
> >
> a/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/CipheredPasswordDataSourceTest.java
> >
> >
> b/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/CipheredPasswordDataSourceTest.java
> > new file mode 100644
> > index 0000000..0f1224f
> > --- /dev/null
> > +++
> >
> >
> b/container/openejb-core/src/test/java/org/apache/openejb/resource/jdbc/CipheredPasswordDataSourceTest.java
> > @@ -0,0 +1,193 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one or more
> > + * contributor license agreements.  See the NOTICE file distributed with
> > + * this work for additional information regarding copyright ownership.
> > + * The ASF licenses this file to You under the Apache License, Version
> 2.0
> > + * (the "License"); you may not use this file except in compliance with
> > + * the License.  You may obtain a copy of the License at
> > + *
> > + *     http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing, software
> > + * distributed under the License is distributed on an "AS IS" BASIS,
> > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> > implied.
> > + * See the License for the specific language governing permissions and
> > + * limitations under the License.
> > + */
> > +package org.apache.openejb.resource.jdbc;
> > +
> > +import org.apache.commons.dbcp.PoolableConnection;
> > +import org.apache.openejb.cipher.PasswordCipher;
> > +import org.apache.openejb.jee.EjbJar;
> > +import org.apache.openejb.jee.SingletonBean;
> > +import org.apache.openejb.junit.ApplicationComposer;
> > +import org.apache.openejb.resource.jdbc.dbcp.BasicManagedDataSource;
> > +import org.apache.openejb.testing.Configuration;
> > +import org.apache.openejb.testing.Module;
> > +import org.apache.openejb.util.Strings;
> > +import org.apache.openejb.util.reflection.Reflections;
> > +import org.junit.Before;
> > +import org.junit.BeforeClass;
> > +import org.junit.Test;
> > +import org.junit.runner.RunWith;
> > +
> > +import javax.annotation.Resource;
> > +import javax.ejb.EJB;
> > +import javax.ejb.EJBContext;
> > +import javax.ejb.LocalBean;
> > +import javax.ejb.Singleton;
> > +import javax.ejb.Startup;
> > +import javax.ejb.TransactionAttribute;
> > +import javax.ejb.TransactionAttributeType;
> > +import javax.sql.DataSource;
> > +import java.sql.Connection;
> > +import java.sql.DriverManager;
> > +import java.sql.DriverPropertyInfo;
> > +import java.sql.ResultSet;
> > +import java.sql.SQLException;
> > +import java.sql.SQLFeatureNotSupportedException;
> > +import java.sql.Statement;
> > +import java.util.Properties;
> > +import java.util.logging.Logger;
> > +
> > +import static org.junit.Assert.assertFalse;
> > +import static org.junit.Assert.assertTrue;
> > +
> > +@RunWith(ApplicationComposer.class)
> > +public class CipheredPasswordDataSourceTest {
> > +    private static final String URL = "jdbc:fake://sthg:3306";
> > +    private static final String USER = "pete";
> > +    private static final String ENCRYPTED_PASSWORD = "This is the
> > encrypted value.";
> > +
> > +    @EJB
> > +    private Persister persistManager;
> > +
> > +    @Resource
> > +    private DataSource ds;
> > +
> > +    @Configuration
> > +    public Properties config() {
> > +        final Properties p = new Properties();
> > +        p.put("openejb.jdbc.datasource-creator", "dbcp");
> > +
> > +        p.put("managed", "new://Resource?type=DataSource");
> > +        p.put("managed.JdbcDriver", FakeDriver.class.getName());
> > +        p.put("managed.JdbcUrl", URL);
> > +        p.put("managed.UserName", USER);
> > +        p.put("managed.Password", ENCRYPTED_PASSWORD);
> > +        p.put("managed.PasswordCipher",
> > EmptyPasswordCipher.class.getName());
> > +        p.put("managed.JtaManaged", "true");
> > +        p.put("managed.initialSize", "10");
> > +        p.put("managed.maxActive", "10");
> > +        p.put("managed.maxIdle", "10");
> > +        p.put("managed.minIdle", "10");
> > +        p.put("managed.maxWait", "200");
> > +        p.put("managed.defaultAutoCommit", "false");
> > +        p.put("managed.defaultReadOnly", "false");
> > +        p.put("managed.testOnBorrow", "true");
> > +        p.put("managed.testOnReturn", "true");
> > +        p.put("managed.testWhileIdle", "true");
> > +
> > +        return p;
> > +    }
> > +
> > +    @Module
> > +    public EjbJar app() throws Exception {
> > +        return new EjbJar()
> > +                .enterpriseBean(new
> > SingletonBean(Persister.class).localBean());
> > +
> > +    }
> > +
> > +    @Before
> > +    public void createDatasource(){
> > +        // This is to make sure TomEE created the data source.
> > +        persistManager.save();
> > +    }
> > +
> > +    @Test
> > +    public void rebuild() {
> > +        // because of the exception at startup, the pool creating
> aborted
> > +        // before fixing this bug, the password was already decrypted,
> but
> > the data source field
> > +        // wasn't yet initialized, so this.data source == null
> > +        // but the next time we try to initialize it, it tries to
> decrypt
> > and already decrypted password
> > +
> > +        try {
> > +            ds.getConnection();
> > +
> > +        } catch (final SQLException e) {
> > +            System.out.println(e.getMessage());
> > +            // success - it throws the SQLException from the connect
> > +            // but it does not fail with the IllegalArgumentException
> from
> > the Password Cipher
> > +        }
> > +    }
> > +
> > +    public static class EmptyPasswordCipher implements PasswordCipher {
> > +
> > +        @Override
> > +        public char[] encrypt(String plainPassword) {
> > +            throw new RuntimeException("Should never be called in this
> > test.");
> > +        }
> > +
> > +        @Override
> > +        public String decrypt(char[] encryptedPassword) {
> > +            System.out.println(String.format(">>> Decrypt password
> '%s'",
> > new String(encryptedPassword)));
> > +
> > +            // we want to know if the password as already been called
> > +            if (encryptedPassword.length == 0) {
> > +                throw new IllegalArgumentException("Can only decrypt a
> non
> > empty string.");
> > +            }
> > +
> > +            return "";
> > +        }
> > +    }
> > +
> > +    public static class FakeDriver implements java.sql.Driver {
> > +        public boolean acceptsURL(final String url) throws SQLException
> {
> > +            return false;
> > +        }
> > +
> > +        public Logger getParentLogger() throws
> > SQLFeatureNotSupportedException {
> > +            return null;
> > +        }
> > +
> > +        public Connection connect(final String url, final Properties
> info)
> > throws SQLException {
> > +            throw new SQLException("Pete said it first fails here!");
> > +        }
> > +
> > +        public int getMajorVersion() {
> > +            return 0;
> > +        }
> > +
> > +        public int getMinorVersion() {
> > +            return 0;
> > +        }
> > +
> > +        public DriverPropertyInfo[] getPropertyInfo(final String url,
> > final Properties info) throws SQLException {
> > +            return new DriverPropertyInfo[0];
> > +        }
> > +
> > +        public boolean jdbcCompliant() {
> > +            return false;
> > +        }
> > +    }
> > +
> > +    @LocalBean
> > +    @Singleton
> > +    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
> > +    public static class Persister {
> > +
> > +        @Resource(name = "managed")
> > +        private DataSource ds;
> > +
> > +        public void save() {
> > +            try {
> > +                ds.getConnection();
> > +
> > +            } catch (SQLException e) {
> > +                System.err.println("Generated SQL error > " +
> > e.getMessage());
> > +            }
> > +        }
> > +    }
> > +
> > +
> > +}
> >
>

Reply via email to