Bläddra i källkod

improve cert import via httpclient/proxy

jrivard@gmail.com 6 år sedan
förälder
incheckning
b22ef1e8cd

+ 9 - 1
server/src/main/java/password/pwm/config/function/AbstractUriCertImportFunction.java

@@ -58,7 +58,15 @@ abstract class AbstractUriCertImportFunction implements SettingUIFunction
         final String urlString = getUri( storedConfiguration, setting, profile, extraData );
         try
         {
-            certs = X509Utils.readRemoteCertificates( URI.create( urlString ) );
+            final URI uri = URI.create( urlString );
+            if ( "https".equalsIgnoreCase( uri.getScheme() ) )
+            {
+                certs = X509Utils.readRemoteHttpCertificates( pwmRequest.getPwmApplication(), pwmSession.getLabel(), uri );
+            }
+            else
+            {
+                certs = X509Utils.readRemoteCertificates( URI.create( urlString ) );
+            }
         }
         catch ( Exception e )
         {

+ 2 - 1
server/src/main/java/password/pwm/health/ApplianceStatusChecker.java

@@ -38,6 +38,7 @@ import password.pwm.http.client.PwmHttpClientResponse;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.logging.PwmLogger;
+import password.pwm.util.secure.X509Utils;
 
 import java.io.File;
 import java.io.IOException;
@@ -90,7 +91,7 @@ public class ApplianceStatusChecker implements HealthChecker
         final Map<String, String> requestHeaders = Collections.singletonMap( "sspr-authorization-token", getApplianceAccessToken( pwmApplication ) );
 
         final PwmHttpClientConfiguration pwmHttpClientConfiguration = PwmHttpClientConfiguration.builder()
-                .promiscuous( true )
+                .trustManager( new X509Utils.PromiscuousTrustManager() )
                 .build();
 
         final PwmHttpClient pwmHttpClient = new PwmHttpClient( pwmApplication, SessionLabel.HEALTH_SESSION_LABEL, pwmHttpClientConfiguration );

+ 3 - 1
server/src/main/java/password/pwm/http/PwmRequest.java

@@ -49,6 +49,7 @@ import password.pwm.util.Validator;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.secure.PwmRandom;
+import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.ws.server.RestResultBean;
 
 import javax.servlet.ServletException;
@@ -494,7 +495,8 @@ public class PwmRequest extends PwmHttpRequestWrapper
 
         if ( strValue != null && !strValue.isEmpty() )
         {
-            return pwmApplication.getSecureService().decryptObject( strValue, returnClass );
+            final PwmSecurityKey pwmSecurityKey = pwmSession.getSecurityKey( this );
+            return pwmApplication.getSecureService().decryptObject( strValue, pwmSecurityKey, returnClass );
         }
 
         return null;

+ 3 - 1
server/src/main/java/password/pwm/http/PwmResponse.java

@@ -34,6 +34,7 @@ import password.pwm.i18n.Message;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.logging.PwmLogger;
+import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.ws.server.RestResultBean;
 
 import javax.servlet.ServletContext;
@@ -237,7 +238,8 @@ public class PwmResponse extends PwmHttpResponseWrapper
             throws PwmUnrecoverableException
     {
         final String jsonValue = JsonUtil.serialize( cookieValue );
-        final String encryptedValue = pwmRequest.getPwmApplication().getSecureService().encryptToString( jsonValue );
+        final PwmSecurityKey pwmSecurityKey = pwmRequest.getPwmSession().getSecurityKey( pwmRequest );
+        final String encryptedValue = pwmRequest.getPwmApplication().getSecureService().encryptToString( jsonValue, pwmSecurityKey );
         writeCookie( cookieName, encryptedValue, seconds, path, PwmHttpResponseWrapper.Flag.BypassSanitation );
     }
 

+ 19 - 0
server/src/main/java/password/pwm/http/PwmSession.java

@@ -29,6 +29,7 @@ import password.pwm.bean.LocalSessionStateBean;
 import password.pwm.bean.LoginInfoBean;
 import password.pwm.bean.SessionLabel;
 import password.pwm.bean.UserIdentity;
+import password.pwm.config.PwmSetting;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
@@ -40,11 +41,13 @@ import password.pwm.ldap.auth.AuthenticationType;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.StatisticsManager;
 import password.pwm.util.LocaleHelper;
+import password.pwm.util.PasswordData;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.secure.PwmRandom;
+import password.pwm.util.secure.PwmSecurityKey;
 
 import java.io.Serializable;
 import java.math.BigInteger;
@@ -72,6 +75,7 @@ public class PwmSession implements Serializable
     private static final Object CREATION_LOCK = new Object();
 
     private transient SessionManager sessionManager;
+    private transient PwmSecurityKey sessionSecurityKey;
 
     public static PwmSession createPwmSession( final PwmApplication pwmApplication )
             throws PwmUnrecoverableException
@@ -359,4 +363,19 @@ public class PwmSession implements Serializable
     {
         return ( int ) JavaHelper.sizeof( this );
     }
+
+    synchronized PwmSecurityKey getSecurityKey( final PwmRequest pwmRequest )
+            throws PwmUnrecoverableException
+    {
+        if ( sessionSecurityKey == null )
+        {
+            final PasswordData configSecret = pwmRequest.getConfig().readSettingAsPassword( PwmSetting.PWM_SECURITY_KEY );
+            final String sessionKey = pwmRequest.getHttpServletRequest().getSession().getId();
+            final String concatValue = configSecret.getStringValue() + sessionKey;
+            final String hashValue = pwmRequest.getPwmApplication().getSecureService().hash( concatValue );
+            sessionSecurityKey = new PwmSecurityKey( hashValue );
+        }
+
+        return sessionSecurityKey;
+    }
 }

+ 14 - 13
server/src/main/java/password/pwm/http/client/PwmHttpClient.java

@@ -122,7 +122,7 @@ public class PwmHttpClient
         return getHttpClient( configuration, PwmHttpClientConfiguration.builder().certificates( null ).build(), null );
     }
 
-    public static HttpClient getHttpClient(
+    static HttpClient getHttpClient(
             final Configuration configuration,
             final PwmHttpClientConfiguration pwmHttpClientConfiguration,
             final SessionLabel sessionLabel
@@ -130,21 +130,25 @@ public class PwmHttpClient
             throws PwmUnrecoverableException
     {
         final HttpClientBuilder clientBuilder = HttpClientBuilder.create();
-        clientBuilder.useSystemProperties();
         clientBuilder.setUserAgent( PwmConstants.PWM_APP_NAME + " " + PwmConstants.SERVLET_VERSION );
-        final boolean httpClientPromiscuousEnable = Boolean.parseBoolean( configuration.readAppProperty( AppProperty.SECURITY_HTTP_PROMISCUOUS_ENABLE ) );
+        final boolean configPromiscuousEnabled = Boolean.parseBoolean( configuration.readAppProperty( AppProperty.SECURITY_HTTP_PROMISCUOUS_ENABLE ) );
+        final boolean promiscuousTrustMgrSet = pwmHttpClientConfiguration != null
+                && pwmHttpClientConfiguration.getTrustManager() != null
+                && X509Utils.PromiscuousTrustManager.class.equals( pwmHttpClientConfiguration.getTrustManager().getClass() );
 
         try
         {
-            if ( httpClientPromiscuousEnable || ( pwmHttpClientConfiguration != null && pwmHttpClientConfiguration.isPromiscuous() ) )
+            if ( configPromiscuousEnabled || promiscuousTrustMgrSet )
             {
                 clientBuilder.setSSLContext( promiscuousSSLContext() );
                 clientBuilder.setSSLHostnameVerifier( NoopHostnameVerifier.INSTANCE );
             }
-            else if ( pwmHttpClientConfiguration != null && pwmHttpClientConfiguration.getCertificates() != null )
+            else if ( pwmHttpClientConfiguration != null && ( pwmHttpClientConfiguration.getCertificates() != null || pwmHttpClientConfiguration.getTrustManager() != null ) )
             {
-                final SSLContext sslContext = SSLContext.getInstance( "SSL" );
-                final TrustManager trustManager = new X509Utils.CertMatchingTrustManager( configuration, pwmHttpClientConfiguration.getCertificates() );
+                final SSLContext sslContext = SSLContext.getInstance( "TLS" );
+                final TrustManager trustManager = pwmHttpClientConfiguration.getTrustManager() != null
+                        ? pwmHttpClientConfiguration.getTrustManager()
+                        : new X509Utils.CertMatchingTrustManager( configuration, pwmHttpClientConfiguration.getCertificates() );
                 sslContext.init( null, new TrustManager[]
                                 {
                                         trustManager,
@@ -157,7 +161,7 @@ public class PwmHttpClient
                         .register( "http", PlainConnectionSocketFactory.INSTANCE )
                         .build();
                 final HttpClientConnectionManager ccm = new BasicHttpClientConnectionManager( registry );
-
+                clientBuilder.setSSLContext( sslContext );
                 clientBuilder.setSSLSocketFactory( sslConnectionFactory );
                 clientBuilder.setConnectionManager( ccm );
             }
@@ -176,8 +180,6 @@ public class PwmHttpClient
             final int port = proxyURI.getPort();
             final HttpHost proxyHost = new HttpHost( host, port );
 
-            clientBuilder.setProxy( proxyHost );
-
             final String userInfo = proxyURI.getUserInfo();
             if ( userInfo != null && userInfo.length() > 0 )
             {
@@ -416,8 +418,6 @@ public class PwmHttpClient
         throw new IllegalArgumentException( "unknown protocol type: " + url.getProtocol() );
     }
 
-
-
     private static class ProxyRoutePlanner implements HttpRoutePlanner
     {
         private final HttpHost proxyServer;
@@ -447,7 +447,8 @@ public class PwmHttpClient
                 return new HttpRoute( target );
             }
 
-            return new HttpRoute( target, proxyServer );
+            final boolean secure = "https".equalsIgnoreCase( target.getSchemeName() );
+            return new HttpRoute( target, null, proxyServer, secure );
         }
     }
 }

+ 2 - 1
server/src/main/java/password/pwm/http/client/PwmHttpClientConfiguration.java

@@ -25,6 +25,7 @@ package password.pwm.http.client;
 import lombok.Builder;
 import lombok.Value;
 
+import javax.net.ssl.X509TrustManager;
 import java.security.cert.X509Certificate;
 import java.util.List;
 
@@ -33,6 +34,6 @@ import java.util.List;
 public class PwmHttpClientConfiguration
 {
     private List<X509Certificate> certificates;
-    private boolean promiscuous;
+    private X509TrustManager trustManager;
     private boolean maskBodyDebugOutput;
 }

+ 1 - 1
server/src/main/java/password/pwm/http/servlet/admin/AppDashboardData.java

@@ -298,7 +298,7 @@ public class AppDashboardData implements Serializable
             localDbInfo.add( new DisplayElement(
                     "oldestSharedHistory",
                     DisplayElement.Type.string,
-                    "OldestShared Password Entry",
+                    "Oldest Shared Password Entry",
                     display
             ) );
         }

+ 2 - 2
server/src/main/java/password/pwm/http/state/CryptoCookieLoginImpl.java

@@ -101,8 +101,8 @@ class CryptoCookieLoginImpl implements SessionLoginProvider
         catch ( PwmUnrecoverableException e )
         {
             final String errorMsg = "unexpected error reading login cookie, will clear and ignore; error: " + e.getMessage();
-            final ErrorInformation errorInformation = new ErrorInformation( PwmError.ERROR_UNKNOWN, errorMsg );
-            LOGGER.error( pwmRequest, errorInformation );
+            final ErrorInformation errorInformation = new ErrorInformation( PwmError.ERROR_CRYPT_ERROR, errorMsg );
+            LOGGER.trace( pwmRequest, errorInformation.toDebugStr() );
             clearLoginSession( pwmRequest );
             return;
         }

+ 2 - 1
server/src/main/java/password/pwm/svc/telemetry/HttpTelemetrySender.java

@@ -37,6 +37,7 @@ import password.pwm.http.client.PwmHttpClientConfiguration;
 import password.pwm.http.client.PwmHttpClientRequest;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.logging.PwmLogger;
+import password.pwm.util.secure.X509Utils;
 
 import java.io.Serializable;
 import java.util.HashMap;
@@ -62,7 +63,7 @@ public class HttpTelemetrySender implements TelemetrySender
             throws PwmUnrecoverableException
     {
         final PwmHttpClientConfiguration pwmHttpClientConfiguration = PwmHttpClientConfiguration.builder()
-                .promiscuous( true )
+                .trustManager( new X509Utils.PromiscuousTrustManager() )
                 .build();
         final PwmHttpClient pwmHttpClient = new PwmHttpClient( pwmApplication, SessionLabel.TELEMETRY_SESSION_LABEL, pwmHttpClientConfiguration );
         final String body = JsonUtil.serialize( statsPublishBean );

+ 2 - 1
server/src/main/java/password/pwm/svc/wordlist/AbstractWordlist.java

@@ -50,6 +50,7 @@ import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.secure.ChecksumInputStream;
 import password.pwm.util.secure.PwmHashAlgorithm;
 import password.pwm.util.secure.SecureEngine;
+import password.pwm.util.secure.X509Utils;
 
 import java.io.FileNotFoundException;
 import java.io.IOException;
@@ -669,7 +670,7 @@ abstract class AbstractWordlist implements Wordlist, PwmService
         {
             final boolean promiscuous = Boolean.parseBoolean( pwmApplication.getConfig().readAppProperty( AppProperty.HTTP_CLIENT_PROMISCUOUS_WORDLIST_ENABLE ) );
             final PwmHttpClientConfiguration pwmHttpClientConfiguration = PwmHttpClientConfiguration.builder()
-                    .promiscuous( promiscuous )
+                    .trustManager( promiscuous ? new X509Utils.PromiscuousTrustManager() : null )
                     .build();
             final PwmHttpClient client = new PwmHttpClient( pwmApplication, null, pwmHttpClientConfiguration );
             return client.streamForUrl( wordlistConfiguration.getAutoImportUrl() );

+ 52 - 0
server/src/main/java/password/pwm/util/secure/X509Utils.java

@@ -23,14 +23,22 @@
 package password.pwm.util.secure;
 
 import password.pwm.AppProperty;
+import password.pwm.PwmApplication;
 import password.pwm.PwmConstants;
+import password.pwm.bean.SessionLabel;
 import password.pwm.config.Configuration;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
+import password.pwm.error.PwmException;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.http.HttpMethod;
 import password.pwm.http.PwmURL;
+import password.pwm.http.client.PwmHttpClient;
+import password.pwm.http.client.PwmHttpClientConfiguration;
+import password.pwm.http.client.PwmHttpClientRequest;
 import password.pwm.util.java.JavaHelper;
+import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.logging.PwmLogger;
 
@@ -69,6 +77,46 @@ public abstract class X509Utils
         return readRemoteCertificates( host, port );
     }
 
+    public static List<X509Certificate> readRemoteHttpCertificates(
+            final PwmApplication pwmApplication,
+            final SessionLabel sessionLabel,
+            final URI uri
+    )
+            throws PwmUnrecoverableException
+    {
+        final CertReaderTrustManager certReaderTrustManager = new CertReaderTrustManager();
+        final PwmHttpClientConfiguration pwmHttpClientConfiguration = PwmHttpClientConfiguration.builder()
+                .trustManager( certReaderTrustManager )
+                .build();
+        final PwmHttpClient pwmHttpClient = new PwmHttpClient( pwmApplication, sessionLabel, pwmHttpClientConfiguration );
+        final PwmHttpClientRequest request = new PwmHttpClientRequest( HttpMethod.GET, uri.toString(), "", Collections.emptyMap() );
+
+        LOGGER.debug( sessionLabel, "beginning attempt to import certificates via httpclient" );
+
+        ErrorInformation requestError = null;
+        try
+        {
+            pwmHttpClient.makeRequest( request );
+        }
+        catch ( PwmException e )
+        {
+            requestError = e.getErrorInformation();
+        }
+
+        if ( certReaderTrustManager.getCertificates() != null )
+        {
+            return Arrays.asList( certReaderTrustManager.getCertificates() );
+        }
+        LOGGER.debug( sessionLabel, "unable to read certificates from remote server via httpclient, error: " + requestError );
+
+        if ( requestError == null )
+        {
+            final String msg = "unable to read certificates via httpclient; check log files for more details";
+            throw PwmUnrecoverableException.newException( PwmError.ERROR_CERTIFICATE_ERROR, msg );
+        }
+
+        throw new PwmUnrecoverableException( requestError );
+    }
 
     public static List<X509Certificate> readRemoteCertificates( final String host, final int port )
             throws PwmOperationalException
@@ -164,6 +212,7 @@ public abstract class X509Utils
         public void checkClientTrusted( final X509Certificate[] chain, final String authType )
                 throws CertificateException
         {
+            LOGGER.debug( "clientCheckTrusted invoked in CertReaderTrustManager" );
         }
 
         public X509Certificate[] getAcceptedIssuers( )
@@ -175,6 +224,9 @@ public abstract class X509Utils
                 throws CertificateException
         {
             certificates = chain;
+            final List<Map<String, String>> certDebugInfo = X509Utils.makeDebugInfoMap( Arrays.asList( certificates ) );
+            LOGGER.debug( "read certificates from remote server via httpclient: "
+                    + JsonUtil.serialize( new ArrayList<>( certDebugInfo ) ) );
         }
 
         public X509Certificate[] getCertificates( )