Skip to content

Commit

Permalink
Fix encryption of mail/sms passwords (MID-4941)
Browse files Browse the repository at this point in the history
CryptoUtil needed to be adapted after containerizing
NotificationConfigurationType.

(cherry picked from commit e39d7e8)
  • Loading branch information
mederly committed Oct 17, 2018
1 parent 8eb1e1e commit 0a863d2
Show file tree
Hide file tree
Showing 6 changed files with 290 additions and 43 deletions.
Expand Up @@ -40,11 +40,7 @@
import com.evolveum.midpoint.util.exception.TunnelException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.MailServerConfigurationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.NotificationConfigurationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.SmsConfigurationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.SmsGatewayConfigurationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType;
import org.jetbrains.annotations.NotNull;

Expand Down Expand Up @@ -105,26 +101,30 @@ private static void encryptValue(Protector protector, PrismPropertyValue<?> pval

if (itemDef.getTypeName().equals(ProtectedStringType.COMPLEX_TYPE)) {
QName propName = item.getElementName();
PrismPropertyValue<ProtectedStringType> psPval = (PrismPropertyValue<ProtectedStringType>)pval;
@SuppressWarnings("unchecked")
PrismPropertyValue<ProtectedStringType> psPval = (PrismPropertyValue<ProtectedStringType>) pval;
ProtectedStringType ps = psPval.getValue();
encryptProtectedStringType(protector, ps, propName.getLocalPart());
if (pval.getParent() == null){
pval.setParent(item);
if (pval.getParent() == null) {
pval.setParent(item); // todo ??? if the parent is null we wouldn't get here
}
} else if (itemDef.getTypeName().equals(NotificationConfigurationType.COMPLEX_TYPE)) {
// this is really ugly hack needed because currently it is not possible to break NotificationConfigurationType into prism item [pm]
NotificationConfigurationType ncfg = ((PrismPropertyValue<NotificationConfigurationType>) pval).getValue();
if (ncfg.getMail() != null) {
for (MailServerConfigurationType mscfg : ncfg.getMail().getServer()) {
encryptProtectedStringType(protector, mscfg.getPassword(), "mail server password");
}
}
if (ncfg.getSms() != null) {
for (SmsConfigurationType smscfg : ncfg.getSms()) {
for (SmsGatewayConfigurationType gwcfg : smscfg.getGateway()) {
encryptProtectedStringType(protector, gwcfg.getPassword(), "sms gateway password");
}
}
} else if (itemDef.getTypeName().equals(MailConfigurationType.COMPLEX_TYPE)) {
// todo fix this hack (it's because MailConfigurationType is not a container)
@SuppressWarnings("unchecked")
MailConfigurationType mailCfg = ((PrismPropertyValue<MailConfigurationType>) pval).getValue();
if (mailCfg != null) {
for (MailServerConfigurationType serverCfg : mailCfg.getServer()) {
encryptProtectedStringType(protector, serverCfg.getPassword(), "mail server password");
}
}
} else if (itemDef.getTypeName().equals(SmsConfigurationType.COMPLEX_TYPE)) {
// todo fix this hack (it's because SmsConfigurationType is not a container)
@SuppressWarnings("unchecked")
SmsConfigurationType smsCfg = ((PrismPropertyValue<SmsConfigurationType>) pval).getValue();
if (smsCfg != null) {
for (SmsGatewayConfigurationType gwCfg : smsCfg.getGateway()) {
encryptProtectedStringType(protector, gwCfg.getPassword(), "sms gateway password");
}
}
}
}
Expand Down Expand Up @@ -188,31 +188,35 @@ private static void checkEncrypted(PrismPropertyValue<?> pval) {
}
if (itemDef.getTypeName().equals(ProtectedStringType.COMPLEX_TYPE)) {
QName propName = item.getElementName();
PrismPropertyValue<ProtectedStringType> psPval = (PrismPropertyValue<ProtectedStringType>)pval;
@SuppressWarnings("unchecked")
PrismPropertyValue<ProtectedStringType> psPval = (PrismPropertyValue<ProtectedStringType>)pval;
ProtectedStringType ps = psPval.getValue();
if (ps.getClearValue() != null) {
throw new IllegalStateException("Unencrypted value in field " + propName);
}
} else if (itemDef.getTypeName().equals(NotificationConfigurationType.COMPLEX_TYPE)) {
// this is really ugly hack needed because currently it is not possible to break NotificationConfigurationType into prism item [pm]
NotificationConfigurationType ncfg = ((PrismPropertyValue<NotificationConfigurationType>) pval).getValue();
if (ncfg.getMail() != null) {
for (MailServerConfigurationType mscfg : ncfg.getMail().getServer()) {
if (mscfg.getPassword() != null && mscfg.getPassword().getClearValue() != null) {
throw new IllegalStateException("Unencrypted value in mail server config password entry");
}
}
}
if (ncfg.getSms() != null) {
for (SmsConfigurationType smscfg : ncfg.getSms()) {
for (SmsGatewayConfigurationType gwcfg : smscfg.getGateway()) {
if (gwcfg.getPassword() != null && gwcfg.getPassword().getClearValue() != null) {
throw new IllegalStateException("Unencrypted value in SMS gateway config password entry");
}
}
}
}
}
} else if (itemDef.getTypeName().equals(MailConfigurationType.COMPLEX_TYPE)) {
// todo fix this hack (it's because MailConfigurationType is not a container)
@SuppressWarnings("unchecked")
MailConfigurationType mailCfg = ((PrismPropertyValue<MailConfigurationType>) pval).getValue();
if (mailCfg != null) {
for (MailServerConfigurationType serverCfg : mailCfg.getServer()) {
if (serverCfg.getPassword() != null && serverCfg.getPassword().getClearValue() != null) {
throw new IllegalStateException("Unencrypted value in mail server config password entry");
}
}
}
} else if (itemDef.getTypeName().equals(SmsConfigurationType.COMPLEX_TYPE)) {
// todo fix this hack (it's because SmsConfigurationType is not a container)
@SuppressWarnings("unchecked")
SmsConfigurationType smsCfg = ((PrismPropertyValue<SmsConfigurationType>) pval).getValue();
if (smsCfg != null) {
for (SmsGatewayConfigurationType gwCfg : smsCfg.getGateway()) {
if (gwCfg.getPassword() != null && gwCfg.getPassword().getClearValue() != null) {
throw new IllegalStateException("Unencrypted value in SMS gateway config password entry");
}
}
}
}
}

public static void checkEncrypted(Collection<? extends ItemDelta> modifications) {
Expand Down
@@ -0,0 +1,148 @@
/*
* Copyright (c) 2010-2018 Evolveum
*
* Licensed 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 com.evolveum.midpoint.common;

import com.evolveum.midpoint.common.crypto.CryptoUtil;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.crypto.Protector;
import com.evolveum.midpoint.prism.crypto.ProtectorImpl;
import com.evolveum.midpoint.prism.util.PrismTestUtil;
import com.evolveum.midpoint.schema.MidPointPrismContextFactory;
import com.evolveum.midpoint.schema.constants.MidPointConstants;
import com.evolveum.midpoint.test.util.TestUtil;
import com.evolveum.midpoint.util.PrettyPrinter;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemConfigurationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType;
import org.apache.xml.security.encryption.XMLCipher;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import org.xml.sax.SAXException;

import java.io.File;
import java.io.IOException;

import static com.evolveum.midpoint.test.util.MidPointTestConstants.TEST_RESOURCES_DIR;
import static com.evolveum.midpoint.test.util.MidPointTestConstants.TEST_RESOURCES_PATH;
import static org.testng.Assert.fail;

/**
* @author mederly
*/
@Listeners({com.evolveum.midpoint.tools.testng.AlphabeticalMethodInterceptor.class})
public class TestCryptoUtil {

private static final File TEST_DIR = new File(TEST_RESOURCES_DIR, "crypto");
private static final File FILE_USER_JACK = new File(TEST_DIR, "user-jack.xml");
private static final File FILE_SYSTEM_CONFIGURATION = new File(TEST_DIR, "system-configuration.xml");

private static final String KEYSTORE_PATH = TEST_RESOURCES_PATH + "/keystore.jceks";
private static final String KEYSTORE_PASSWORD = "changeit";

private Protector protector;

@BeforeSuite
public void setup() throws SchemaException, SAXException, IOException {
PrettyPrinter.setDefaultNamespacePrefix(MidPointConstants.NS_MIDPOINT_PUBLIC_PREFIX);
PrismTestUtil.resetPrismContext(MidPointPrismContextFactory.FACTORY);
protector = createProtector();
}

@Test
public void test100CheckEncryptedUser() throws Exception {
final String TEST_NAME = "test100CheckEncryptedUser";
TestUtil.displayTestTitle(TEST_NAME);

// GIVEN
PrismContext prismContext = PrismTestUtil.getPrismContext();
PrismObject<UserType> jack = prismContext.parserFor(FILE_USER_JACK).xml().parse();

// WHEN+THEN
checkEncryptedObject(jack);
}

@Test
public void test110EncryptUser() throws Exception {
final String TEST_NAME = "test110EncryptUser";
TestUtil.displayTestTitle(TEST_NAME);

// GIVEN
PrismContext prismContext = PrismTestUtil.getPrismContext();
PrismObject<UserType> jack = prismContext.parserFor(FILE_USER_JACK).xml().parse();

// WHEN
CryptoUtil.encryptValues(protector, jack);

// THEN
System.out.println("After encryption:\n" + jack.debugDump());
CryptoUtil.checkEncrypted(jack);
}

// MID-4941
@Test
public void test200CheckEncryptedSystemConfiguration() throws Exception {
final String TEST_NAME = "test200CheckEncryptedSystemConfiguration";
TestUtil.displayTestTitle(TEST_NAME);

// GIVEN
PrismContext prismContext = PrismTestUtil.getPrismContext();
PrismObject<SystemConfigurationType> config = prismContext.parserFor(FILE_SYSTEM_CONFIGURATION).xml().parse();

// WHEN+THEN
checkEncryptedObject(config);
}

// MID-4941
@Test
public void test210EncryptSystemConfiguration() throws Exception {
final String TEST_NAME = "test210EncryptSystemConfiguration";
TestUtil.displayTestTitle(TEST_NAME);

// GIVEN
PrismContext prismContext = PrismTestUtil.getPrismContext();
PrismObject<SystemConfigurationType> config = prismContext.parserFor(FILE_SYSTEM_CONFIGURATION).xml().parse();

// WHEN
CryptoUtil.encryptValues(protector, config);

// THEN
System.out.println("After encryption:\n" + config.debugDump());
CryptoUtil.checkEncrypted(config);
}

private void checkEncryptedObject(PrismObject<? extends ObjectType> object) {
try {
CryptoUtil.checkEncrypted(object);
fail("Unexpected success");
} catch (IllegalStateException e) {
System.out.println("Got expected exception: " + e.getMessage());
e.printStackTrace(System.out);
}
}

private Protector createProtector() {
ProtectorImpl protector = new ProtectorImpl();
protector.setKeyStorePassword(KEYSTORE_PASSWORD);
protector.setKeyStorePath(KEYSTORE_PATH);
protector.setEncryptionAlgorithm(XMLCipher.AES_256);
protector.init();
return protector;
}

}
51 changes: 51 additions & 0 deletions infra/common/src/test/resources/crypto/system-configuration.xml
@@ -0,0 +1,51 @@
<?xml version="1.0"?>
<!--
~ Copyright (c) 2010-2018 Evolveum
~
~ Licensed 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.
-->

<systemConfiguration xmlns="http://midpoint.evolveum.com/xml/ns/public/common/common-3"
xmlns:t="http://prism.evolveum.com/xml/ns/public/types-3">

<name>dummy</name>

<notificationConfiguration>
<mail>
<server>
<host>smtp.gmail.com</host>
<port>587</port>
<username>user</username>
<password>
<t:clearValue>password</t:clearValue>
</password>
<transportSecurity>starttlsEnabled</transportSecurity>
</server>
</mail>
<sms name="sms1">
<gateway>
<password>
<t:clearValue>password</t:clearValue>
</password>
</gateway>
</sms>
<sms name="sms2">
<gateway>
<password>
<t:clearValue>password</t:clearValue>
</password>
</gateway>
</sms>
</notificationConfiguration>

</systemConfiguration>
43 changes: 43 additions & 0 deletions infra/common/src/test/resources/crypto/user-jack.xml
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2010-2018 Evolveum
~
~ Licensed 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.
-->
<user oid="c0c010c0-d34d-b33f-f00d-111111111111"
xmlns="http://midpoint.evolveum.com/xml/ns/public/common/common-3"
xmlns:t="http://prism.evolveum.com/xml/ns/public/types-3">
<name>jack</name>
<description>Where's the rum?</description>
<activation>
<administrativeStatus>enabled</administrativeStatus>
</activation>
<locality>Caribbean</locality>
<emailAddress>jack.sparrow@evolveum.com</emailAddress>
<telephoneNumber>555-1234</telephoneNumber>
<fullName>Jack Sparrow</fullName>
<givenName>Jack</givenName>
<familyName>Sparrow</familyName>
<additionalName>Jackie</additionalName>
<honorificPrefix>Cpt.</honorificPrefix>
<honorificSuffix>PhD.</honorificSuffix>
<employeeNumber>emp1234</employeeNumber>
<employeeType>CAPTAIN</employeeType>
<credentials>
<password>
<value>
<t:clearValue>deadmentellnotales</t:clearValue>
</value>
</password>
</credentials>
</user>
Binary file added infra/common/src/test/resources/keystore.jceks
Binary file not shown.
1 change: 1 addition & 0 deletions infra/common/testng-unit.xml
Expand Up @@ -27,6 +27,7 @@
<class name="com.evolveum.midpoint.common.TestActivationComputerDefault" />
<class name="com.evolveum.midpoint.common.TestActivationComputerLifecycle" />
<class name="com.evolveum.midpoint.common.TestStaticValues" />
<class name="com.evolveum.midpoint.common.TestCryptoUtil" />
</classes>
</test>
<test name="Schema" preserve-order="false">
Expand Down

0 comments on commit 0a863d2

Please sign in to comment.