Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 2FF62200BC5 for ; Mon, 7 Nov 2016 17:36:23 +0100 (CET) Received: by cust-asf.ponee.io (Postfix) id 2E90A160B18; Mon, 7 Nov 2016 16:36:23 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 2EE39160AE0 for ; Mon, 7 Nov 2016 17:36:22 +0100 (CET) Received: (qmail 23093 invoked by uid 500); 7 Nov 2016 16:36:21 -0000 Mailing-List: contact commits-help@activemq.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@activemq.apache.org Delivered-To: mailing list commits@activemq.apache.org Received: (qmail 23079 invoked by uid 99); 7 Nov 2016 16:36:21 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 07 Nov 2016 16:36:21 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 2C9CAEEE7B; Mon, 7 Nov 2016 16:36:21 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: clebertsuconic@apache.org To: commits@activemq.apache.org Date: Mon, 07 Nov 2016 16:36:21 -0000 Message-Id: X-Mailer: ASF-Git Admin Mailer Subject: [01/50] [abbrv] activemq-artemis git commit: ARTEMIS-786 Store user's password in hash form by default - user passwords for PropertiesLoginModule stored using PBKDF2 algothrim by default - implements cli user command to help create and manage use [Forced Update!] archived-at: Mon, 07 Nov 2016 16:36:23 -0000 Repository: activemq-artemis Updated Branches: refs/heads/ARTEMIS-780 5459cf76f -> 661ea2c4e (forced update) http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/cd7b8389/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/SecureHashProcessor.java ---------------------------------------------------------------------- diff --git a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/SecureHashProcessor.java b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/SecureHashProcessor.java new file mode 100644 index 0000000..81db051 --- /dev/null +++ b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/SecureHashProcessor.java @@ -0,0 +1,44 @@ +/* + * 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.activemq.artemis.utils; + +/** + * Hash function + */ +public class SecureHashProcessor implements HashProcessor { + + private static final String BEGIN_HASH = "ENC("; + private static final String END_HASH = ")"; + + private DefaultSensitiveStringCodec codec; + + public SecureHashProcessor(DefaultSensitiveStringCodec codec) { + this.codec = codec; + } + + @Override + public String hash(String plainText) throws Exception { + return BEGIN_HASH + codec.encode(plainText) + END_HASH; + } + + @Override + //storedValue must take form of ENC(...) + public boolean compare(char[] inputValue, String storedValue) { + String storedHash = storedValue.substring(4, storedValue.length() - 2); + return codec.verify(inputValue, storedHash); + } +} http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/cd7b8389/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/SensitiveDataCodec.java ---------------------------------------------------------------------- diff --git a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/SensitiveDataCodec.java b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/SensitiveDataCodec.java index b1bfd2b..d585976 100644 --- a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/SensitiveDataCodec.java +++ b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/SensitiveDataCodec.java @@ -29,5 +29,7 @@ public interface SensitiveDataCodec { T decode(Object mask) throws Exception; - void init(Map params); + T encode(Object secret) throws Exception; + + void init(Map params) throws Exception; } http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/cd7b8389/artemis-commons/src/test/java/org/apache/activemq/artemis/utils/DefaultSensitiveStringCodecTest.java ---------------------------------------------------------------------- diff --git a/artemis-commons/src/test/java/org/apache/activemq/artemis/utils/DefaultSensitiveStringCodecTest.java b/artemis-commons/src/test/java/org/apache/activemq/artemis/utils/DefaultSensitiveStringCodecTest.java new file mode 100644 index 0000000..393558d --- /dev/null +++ b/artemis-commons/src/test/java/org/apache/activemq/artemis/utils/DefaultSensitiveStringCodecTest.java @@ -0,0 +1,77 @@ +/* + * 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.activemq.artemis.utils; + +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +public class DefaultSensitiveStringCodecTest { + + @Test + public void testDefaultAlgorithm() throws Exception { + SensitiveDataCodec codec = PasswordMaskingUtil.getDefaultCodec(); + assertTrue(codec instanceof DefaultSensitiveStringCodec); + } + + @Test + public void testOnewayAlgorithm() throws Exception { + DefaultSensitiveStringCodec codec = new DefaultSensitiveStringCodec(); + Map params = new HashMap<>(); + params.put(DefaultSensitiveStringCodec.ALGORITHM, DefaultSensitiveStringCodec.ONE_WAY); + codec.init(params); + + String plainText = "some_password"; + String maskedText = codec.encode(plainText); + System.out.println("encoded value: " + maskedText); + + //one way can't decode + try { + codec.decode(maskedText); + fail("one way algorithm can't decode"); + } catch (IllegalArgumentException expected) { + } + + assertTrue(codec.verify(plainText.toCharArray(), maskedText)); + + String otherPassword = "some_other_password"; + assertFalse(codec.verify(otherPassword.toCharArray(), maskedText)); + } + + @Test + public void testTwowayAlgorithm() throws Exception { + DefaultSensitiveStringCodec codec = new DefaultSensitiveStringCodec(); + Map params = new HashMap<>(); + params.put(DefaultSensitiveStringCodec.ALGORITHM, DefaultSensitiveStringCodec.TWO_WAY); + codec.init(params); + + String plainText = "some_password"; + String maskedText = codec.encode(plainText); + System.out.println("encoded value: " + maskedText); + + String decoded = codec.decode(maskedText); + System.out.println("encoded value: " + maskedText); + + assertEquals("decoded result not match: " + decoded, decoded, plainText); + } +} http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/cd7b8389/artemis-commons/src/test/java/org/apache/activemq/artemis/utils/HashProcessorTest.java ---------------------------------------------------------------------- diff --git a/artemis-commons/src/test/java/org/apache/activemq/artemis/utils/HashProcessorTest.java b/artemis-commons/src/test/java/org/apache/activemq/artemis/utils/HashProcessorTest.java new file mode 100644 index 0000000..83199b5 --- /dev/null +++ b/artemis-commons/src/test/java/org/apache/activemq/artemis/utils/HashProcessorTest.java @@ -0,0 +1,66 @@ +/* + * 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.activemq.artemis.utils; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Arrays; +import java.util.Collection; + +import static org.junit.Assert.assertEquals; + +@RunWith(Parameterized.class) +public class HashProcessorTest { + + private static final String USER1_PASSWORD = "password"; + private static final String USER1_HASHED_PASSWORD = "ENC(1024:973A466A489ABFDED3D4B3D181DC77F410F2FC6E87432809A46B72B294147D76:C999ECA8A85387E1FFB14E4FE5CECD17948BA80BA04318A9BE4C3E34B7FE2925F43AB6BC9DFE0D9855DA67439AEEB9850351BC4D5D3AEC6A6903C42B8EB4ED1E)"; + + private static final String USER2_PASSWORD = "manager"; + private static final String USER2_HASHED_PASSWORD = "ENC(1024:48018CDB1B5925DA2CC51DBD6F7E8C5FF156C22C03C6C69720C56F8BE76A1D48:0A0F68C2C01F46D347C6C51D641291A4608EDA50A873ED122909D9134B7A757C14176F0C033F0BD3CE35B3C373D5B652650CDE5FFBBB0F286D4495CEFEEDB166)"; + + private static final String USER3_PASSWORD = "artemis000"; + + @Parameterized.Parameters(name = "{index}: testing password {0}") + public static Collection data() { + return Arrays.asList(new Object[][] { + {USER1_PASSWORD, USER1_HASHED_PASSWORD, true}, + {USER2_PASSWORD, USER2_HASHED_PASSWORD, true}, + {USER3_PASSWORD, USER3_PASSWORD, true}, + {USER1_PASSWORD, USER2_PASSWORD, false}, + {USER3_PASSWORD, USER2_HASHED_PASSWORD, false} + }); + } + + private String password; + private String storedPassword; + private boolean match; + + public HashProcessorTest(String password, String storedPassword, boolean match) { + this.password = password; + this.storedPassword = storedPassword; + this.match = match; + } + + @Test + public void testPasswordVerification() throws Exception { + HashProcessor processor = PasswordMaskingUtil.getHashProcessor(storedPassword); + boolean result = processor.compare(password.toCharArray(), storedPassword); + assertEquals(match, result); + } +} http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/cd7b8389/artemis-distribution/src/main/assembly/dep.xml ---------------------------------------------------------------------- diff --git a/artemis-distribution/src/main/assembly/dep.xml b/artemis-distribution/src/main/assembly/dep.xml index d21f1e0..aa3635e 100644 --- a/artemis-distribution/src/main/assembly/dep.xml +++ b/artemis-distribution/src/main/assembly/dep.xml @@ -89,6 +89,8 @@ commons-beanutils:commons-beanutils commons-logging:commons-logging commons-collections:commons-collections + org.apache.commons:commons-configuration2 + org.apache.commons:commons-lang3 org.fusesource.hawtbuf:hawtbuf org.jgroups:jgroups org.apache.geronimo.specs:geronimo-json_1.0_spec http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/cd7b8389/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PropertiesLoginModule.java ---------------------------------------------------------------------- diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PropertiesLoginModule.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PropertiesLoginModule.java index d120a98..957bb8a 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PropertiesLoginModule.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PropertiesLoginModule.java @@ -32,14 +32,16 @@ import java.util.Map; import java.util.Properties; import java.util.Set; +import org.apache.activemq.artemis.utils.HashProcessor; +import org.apache.activemq.artemis.utils.PasswordMaskingUtil; import org.jboss.logging.Logger; public class PropertiesLoginModule extends PropertiesLoader implements LoginModule { private static final Logger logger = Logger.getLogger(PropertiesLoginModule.class); - private static final String USER_FILE_PROP_NAME = "org.apache.activemq.jaas.properties.user"; - private static final String ROLE_FILE_PROP_NAME = "org.apache.activemq.jaas.properties.role"; + public static final String USER_FILE_PROP_NAME = "org.apache.activemq.jaas.properties.user"; + public static final String ROLE_FILE_PROP_NAME = "org.apache.activemq.jaas.properties.role"; private Subject subject; private CallbackHandler callbackHandler; @@ -49,6 +51,7 @@ public class PropertiesLoginModule extends PropertiesLoader implements LoginModu private String user; private final Set principals = new HashSet<>(); private boolean loginSucceeded; + private HashProcessor hashProcessor; @Override public void initialize(Subject subject, @@ -90,10 +93,21 @@ public class PropertiesLoginModule extends PropertiesLoader implements LoginModu if (password == null) { throw new FailedLoginException("User does exist"); } - if (!password.equals(new String(tmpPassword))) { - throw new FailedLoginException("Password does not match"); + + //password is hashed + try { + hashProcessor = PasswordMaskingUtil.getHashProcessor(password); + + if (!hashProcessor.compare(tmpPassword, password)) { + throw new FailedLoginException("Password does not match"); + } + loginSucceeded = true; + } catch (Exception e) { + if (debug) { + logger.debug("Exception getting a hash processor", e); + } + throw new FailedLoginException("Failed to get hash processor"); } - loginSucceeded = true; if (debug) { logger.debug("login " + user); http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/cd7b8389/docs/user-manual/en/configuration-index.md ---------------------------------------------------------------------- diff --git a/docs/user-manual/en/configuration-index.md b/docs/user-manual/en/configuration-index.md index 65ef931..240d6db 100644 --- a/docs/user-manual/en/configuration-index.md +++ b/docs/user-manual/en/configuration-index.md @@ -396,29 +396,14 @@ will have to be in masked form. ### Masking passwords in artemis-users.properties Apache ActiveMQ Artemis's built-in security manager uses plain properties files -where the user passwords are specified in plaintext forms by default. To -mask those parameters the following two properties need to be set -in the 'bootstrap.xml' file. +where the user passwords are specified in hash forms by default. -`mask-password` -- If set to "true" all the passwords are masked. -Default is false. - -`password-codec` -- Class name and its parameters for the Decoder used -to decode the masked password. Ignored if `mask-password` is false. The -format of this property is a full qualified class name optionally -followed by key/value pairs. It is the same format as that for JMS -Bridges. Example: +Please use Artemis CLI command to add a password. For example -```xml -true -org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec;key=hello world +```sh + ./artemis user add --username guest --password guest --role admin ``` -When so configured, the Apache ActiveMQ Artemis security manager will initialize a -DefaultSensitiveStringCodec with the parameters "key"-\>"hello world", -then use it to decode all the masked passwords in this configuration -file. - ### Choosing a decoder for password masking As described in the previous sections, all password masking requires a