Browse Source

removed network address restrictions setting and functionality

Jason Rivard 3 years ago
parent
commit
67d39cf2c2

+ 6 - 0
CHANGES.md

@@ -1,5 +1,11 @@
 # Changelog
 
+## [2.1.0] - Not yet released
+### New
+- Multi-Domain support (aka multi-tenant), each domain can server a specific host url with independent configuration and management 
+### Changed
+- Removed setting 'Security ⇨ Web Security ⇨ Permitted IP Network Addresses', this functionality is better provided by the web server itself.
+
 ## [2.0.1] - Released March 11, 2022
 ### Changed
 - Issue #573 - PWM 5081 at the end of user activation ( no profile assigned )

+ 3 - 2
server/src/main/java/password/pwm/config/PwmSetting.java

@@ -583,8 +583,6 @@ public enum PwmSetting
             "network.allowMultiIPSession", PwmSettingSyntax.BOOLEAN, PwmSettingCategory.WEB_SECURITY ),
     REQUIRED_HEADERS(
             "network.requiredHttpHeaders", PwmSettingSyntax.STRING_ARRAY, PwmSettingCategory.WEB_SECURITY ),
-    IP_PERMITTED_RANGE(
-            "network.ip.permittedRange", PwmSettingSyntax.STRING_ARRAY, PwmSettingCategory.WEB_SECURITY ),
     SECURITY_PAGE_LEAVE_NOTICE_TIMEOUT(
             "security.page.leaveNoticeTimeout", PwmSettingSyntax.NUMERIC, PwmSettingCategory.WEB_SECURITY ),
     SECURITY_PREVENT_FRAMING(
@@ -1268,6 +1266,9 @@ public enum PwmSetting
 
 
     // deprecated.
+    // deprecated 2022-04-20
+    IP_PERMITTED_RANGE(
+            "network.ip.permittedRange", PwmSettingSyntax.STRING_ARRAY, PwmSettingCategory.WEB_SECURITY ),
 
     // deprecated 2019-06-01
     PUBLIC_HEALTH_STATS_WEBSERVICES(

+ 2 - 0
server/src/main/java/password/pwm/config/stored/ConfigurationReader.java

@@ -164,6 +164,8 @@ public class ConfigurationReader
                 this.configMode = PwmApplicationMode.ERROR;
                 throw new PwmUnrecoverableException( errorInformation );
             }
+
+            ConfigurationVerifier.verifyConfiguration( storedConfiguration );
         }
         catch ( final Exception e )
         {

+ 86 - 0
server/src/main/java/password/pwm/config/stored/ConfigurationVerifier.java

@@ -0,0 +1,86 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2021 The PWM Project
+ *
+ * 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 password.pwm.config.stored;
+
+import password.pwm.PwmConstants;
+import password.pwm.bean.DomainID;
+import password.pwm.config.PwmSetting;
+import password.pwm.config.value.StoredValue;
+import password.pwm.config.value.ValueTypeConverter;
+import password.pwm.error.PwmError;
+import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.util.java.CollectionUtil;
+
+import java.util.List;
+import java.util.Optional;
+
+class ConfigurationVerifier
+{
+    private static final List<Verifier> VERIFIERS = List.of(
+            new BlockDeprecatedPermittedIpMaskSetting()
+    );
+
+    static void verifyConfiguration(
+            final StoredConfiguration storedConfiguration
+    )
+            throws PwmUnrecoverableException
+    {
+        for ( final Verifier verifier : VERIFIERS )
+        {
+            verifier.verify( storedConfiguration );
+        }
+    }
+
+    interface Verifier
+    {
+        void verify( StoredConfiguration storedConfiguration )
+                throws PwmUnrecoverableException;
+
+    }
+
+    private static class BlockDeprecatedPermittedIpMaskSetting implements Verifier
+    {
+        @Override
+        public void verify( final StoredConfiguration storedConfiguration )
+                throws PwmUnrecoverableException
+        {
+            final StoredConfigKey interestedKey = StoredConfigKey.forSetting(
+                    PwmSetting.IP_PERMITTED_RANGE,
+                    null,
+                    DomainID.systemId() );
+            final Optional<StoredValue> storedValue = storedConfiguration.readStoredValue( interestedKey );
+            if ( storedValue.isPresent() )
+            {
+                final List<String> oldValues = ValueTypeConverter.valueToStringArray( storedValue.get() );
+                if ( !CollectionUtil.isEmpty( oldValues ) )
+                {
+                    final String firstValue = oldValues.get( 0 );
+                    final String errorMsg = "Deprecated configuration setting '"
+                            + PwmSetting.IP_PERMITTED_RANGE.toMenuLocationDebug( null, PwmConstants.DEFAULT_LOCALE )
+                            + "' has a value of '" + firstValue
+                            + "'.  This setting is no longer used, and the configuration must not contain a value for this setting.";
+                    throw PwmUnrecoverableException.newException( PwmError.CONFIG_FORMAT_ERROR, errorMsg );
+                }
+            }
+        }
+    }
+
+}

+ 0 - 42
server/src/main/java/password/pwm/http/filter/RequestInitializationFilter.java

@@ -48,7 +48,6 @@ import password.pwm.svc.intruder.IntruderServiceClient;
 import password.pwm.svc.stats.EpsStatistic;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.StatisticsService;
-import password.pwm.util.IPMatcher;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.TimeDuration;
@@ -634,9 +633,6 @@ public class RequestInitializationFilter implements Filter
         // check headers
         checkRequiredHeaders( pwmRequest );
 
-        // check permitted source IP address
-        checkSourceNetworkAddress( pwmRequest );
-
         // csrf cross-site request forgery checks
         checkCsrfHeader( pwmRequest );
 
@@ -725,44 +721,6 @@ public class RequestInitializationFilter implements Filter
         }
     }
 
-    private static void checkSourceNetworkAddress( final PwmRequest pwmRequest )
-            throws PwmUnrecoverableException
-    {
-        final List<String> requiredHeaders = pwmRequest.getAppConfig().readSettingAsStringArray( PwmSetting.IP_PERMITTED_RANGE );
-        if ( requiredHeaders != null && !requiredHeaders.isEmpty() )
-        {
-            final String requestAddress = pwmRequest.getHttpServletRequest().getRemoteAddr();
-            for ( final String ipMatchString : requiredHeaders )
-            {
-                try
-                {
-                    final IPMatcher ipMatcher = new IPMatcher( ipMatchString );
-                    try
-                    {
-                        if ( ipMatcher.match( requestAddress ) )
-                        {
-                            return;
-
-                        }
-                    }
-                    catch ( final IPMatcher.IPMatcherException e )
-                    {
-                        LOGGER.error( () -> "error while attempting to match permitted address range '" + ipMatchString + "', error: " + e );
-                    }
-                }
-                catch ( final IPMatcher.IPMatcherException e )
-                {
-                    LOGGER.error( () -> "error parsing permitted address range '" + ipMatchString + "', error: " + e );
-                }
-            }
-
-            final String errorMsg = "request network address '" + requestAddress + "' does not match any configured permitted source address";
-            final ErrorInformation errorInformation = new ErrorInformation( PwmError.ERROR_SECURITY_VIOLATION, errorMsg );
-            throw new PwmUnrecoverableException( errorInformation );
-        }
-    }
-
-
     private static void checkCsrfHeader( final PwmRequest pwmRequest )
             throws PwmUnrecoverableException
     {

+ 0 - 352
server/src/main/java/password/pwm/util/IPMatcher.java

@@ -1,352 +0,0 @@
-/*
- * Password Management Servlets (PWM)
- * http://www.pwm-project.org
- *
- * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2021 The PWM Project
- *
- * 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 password.pwm.util;
-
-import java.net.Inet6Address;
-import java.net.UnknownHostException;
-
-public class IPMatcher
-{
-    /**
-     * Network to match.
-     */
-    private byte[] network;
-
-    /**
-     * Network mask.
-     */
-    private byte[] netmask;
-
-    /**
-     * Construct an IPMatcher that will test for the given IP specification.
-     *
-     * @param ipSpec IP specification (full or partial address, network/netmask,
-     *               network/cidr)
-     * @throws IPMatcherException if there is an error parsing the specification (i.e. it is
-     *                            somehow malformed)
-     */
-    public IPMatcher( final String ipSpec ) throws IPMatcherException
-    {
-        // Boil all specs down to network + mask
-
-        String ipPart = ipSpec;
-        final String[] parts = ipSpec.split( "/" );
-
-        if ( parts[ 0 ].indexOf( ':' ) >= 0 )
-        {
-            // looks like IPv6
-            try
-            {
-                network = Inet6Address.getByName( parts[ 0 ] ).getAddress();
-            }
-            catch ( final UnknownHostException e )
-            {
-                throw new IPMatcherException(
-                        "Malformed IP range specification " + ipSpec, e );
-            }
-
-            netmask = new byte[ 16 ];
-            switch ( parts.length )
-            {
-                case 2:
-                    // CIDR notation:  calculate the mask
-                    final int maskBits;
-                    try
-                    {
-                        maskBits = Integer.parseInt( parts[ 1 ] );
-                    }
-                    catch ( final NumberFormatException nfe )
-                    {
-                        throw new IPMatcherException(
-                                "Malformed IP range specification " + ipSpec, nfe );
-                    }
-                    if ( maskBits < 0 || maskBits > 128 )
-                    {
-                        throw new IPMatcherException( "Mask bits out of range 0-128 "
-                                + ipSpec );
-                    }
-
-                    final int maskBytes = maskBits / 8;
-                    for ( int i = 0; i < maskBytes; i++ )
-                    {
-                        netmask[ i ] = ( byte ) 0Xff;
-                    }
-                    netmask[ maskBytes ] = ( byte ) ( ( byte ) 0Xff << 8 - ( maskBits % 8 ) );
-                    for ( int i = maskBytes + 1; i < ( 128 / 8 ); i++ )
-                    {
-                        netmask[ i ] = 0;
-                    }
-                    break;
-                case 1:
-                    // No explicit mask:  fill the mask with 1s
-                    for ( int i = 0; i < netmask.length; i++ )
-                    {
-                        netmask[ i ] = ( byte ) 0Xff;
-                    }
-                    break;
-                default:
-                    throw new IPMatcherException( "Malformed IP range specification "
-                            + ipSpec );
-            }
-        }
-        else
-        {
-            // assume IPv4
-            // Allow partial IP
-            boolean mustHave4 = false;
-
-            network = new byte[ 4 ];
-            netmask = new byte[ 4 ];
-            switch ( parts.length )
-            {
-                case 2:
-                    // Some kind of slash notation -- we'll need a full network IP
-                    ipPart = parts[ 0 ];
-                    mustHave4 = true;
-
-                    final String[] maskParts = parts[ 1 ].split( "\\." );
-                    if ( maskParts.length == 1 )
-                    {
-                        // CIDR slash notation
-                        final int x;
-
-                        try
-                        {
-                            x = Integer.parseInt( maskParts[ 0 ] );
-                        }
-                        catch ( final NumberFormatException nfe )
-                        {
-                            throw new IPMatcherException(
-                                    "Malformed IP range specification " + ipSpec, nfe );
-                        }
-
-                        if ( x < 0 || x > 32 )
-                        {
-                            throw new IPMatcherException();
-                        }
-
-                        final int fullMask = -1 << ( 32 - x );
-                        netmask[ 0 ] = ( byte ) ( ( fullMask & 0xFF000000 ) >>> 24 );
-                        netmask[ 1 ] = ( byte ) ( ( fullMask & 0x00FF0000 ) >>> 16 );
-                        netmask[ 2 ] = ( byte ) ( ( fullMask & 0x0000FF00 ) >>> 8 );
-                        netmask[ 3 ] = ( byte ) ( fullMask & 0x000000FF );
-                        ipToBytes( ipPart, network, mustHave4 );
-                    }
-                    else
-                    {
-                        // full subnet specified
-                        ipToBytes( parts[ 0 ], network, true );
-                        ipToBytes( parts[ 1 ], netmask, true );
-                    }
-                    break;
-
-                case 1:
-                    // Get IP
-                    for ( int i = 0; i < netmask.length; i++ )
-                    {
-                        netmask[ i ] = -1;
-                    }
-                    final int partCount = ipToBytes( ipPart, network, mustHave4 );
-
-                    // If partial IP, set mask for remaining bytes
-                    for ( int i = 3; i >= partCount; i-- )
-                    {
-                        netmask[ i ] = 0;
-                    }
-
-                    break;
-
-                default:
-                    throw new IPMatcherException( "Malformed IP range specification "
-                            + ipSpec );
-            }
-            network = ip4ToIp6( network );
-            netmask = ip4MaskToIp6( netmask );
-        }
-    }
-
-    /**
-     * Fill out a given four-byte array with the IPv4 address specified in the
-     * given String.
-     *
-     * @param ip        IPv4 address as a dot-delimited String
-     * @param bytes     4-byte array to fill out
-     * @param mustHave4 if true, will require that the given IP string specify all
-     *                  four bytes
-     * @return the number of actual IP bytes found in the given IP address
-     *         String
-     * @throws IPMatcherException if there is a problem parsing the IP string -- e.g. number
-     *                            outside of range 0-255, too many numbers, less than 4 numbers
-     *                            if {@code mustHave4} is true
-     */
-    private static int ipToBytes( final String ip, final byte[] bytes, final boolean mustHave4 )
-            throws IPMatcherException
-    {
-        final String[] parts = ip.split( "\\." );
-
-        if ( parts.length > 4 || mustHave4 && parts.length != 4 )
-        {
-            throw new IPMatcherException( "Malformed IP specification " + ip );
-        }
-
-        try
-        {
-
-            for ( int i = 0; i < parts.length; i++ )
-            {
-                final int p = Integer.parseInt( parts[ i ] );
-                if ( p < 0 || p > 255 )
-                {
-                    throw new IPMatcherException( "Malformed IP specification "
-                            + ip );
-
-                }
-
-                bytes[ i ] = ( byte ) ( p < 128 ? p : p - 256 );
-            }
-        }
-        catch ( final NumberFormatException nfe )
-        {
-            throw new IPMatcherException( "Malformed IP specification " + ip,
-                    nfe );
-        }
-
-        return parts.length;
-    }
-
-    /**
-     * Determine whether the given full IP falls within the range this
-     * {@code IPMatcher} was initialized with.
-     *
-     * @param ipIn IP address as dot-delimited String
-     * @return {@code true} if the IP matches the range of this
-     *         {@code IPMatcher}; {@code false} otherwise
-     * @throws IPMatcherException if the IP passed in cannot be parsed correctly (i.e. is
-     *                            malformed)
-     */
-    public boolean match( final String ipIn ) throws IPMatcherException
-    {
-        byte[] candidate;
-
-        if ( ipIn.indexOf( ':' ) < 0 )
-        {
-            candidate = new byte[ 4 ];
-            ipToBytes( ipIn, candidate, true );
-            candidate = ip4ToIp6( candidate );
-        }
-        else
-        {
-            try
-            {
-                candidate = Inet6Address.getByName( ipIn ).getAddress();
-            }
-            catch ( final UnknownHostException e )
-            {
-                throw new IPMatcherException( "Malformed IPv6 address ", e );
-            }
-        }
-
-        for ( int i = 0; i < netmask.length; i++ )
-        {
-            if ( ( candidate[ i ] & netmask[ i ] ) != ( network[ i ] & netmask[ i ] ) )
-            {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     * Convert an IPv4 address to an IPv6 IPv4-compatible address.
-     *
-     * @param ip4 an IPv4 address
-     * @return the corresponding IPv6 address
-     * @throws IllegalArgumentException if ip4 is not exactly four octets long.
-     */
-    private static byte[] ip4ToIp6( final byte[] ip4 )
-    {
-        if ( ip4.length != 4 )
-        {
-            throw new IllegalArgumentException( "IPv4 address must be four octets" );
-        }
-
-        final byte[] ip6 = new byte[ 16 ];
-        for ( int i = 0; i < 16 - 4; i++ )
-        {
-            ip6[ i ] = 0;
-        }
-        for ( int i = 0; i < 4; i++ )
-        {
-            ip6[ 12 + i ] = ip4[ i ];
-        }
-        return ip6;
-    }
-
-    /**
-     * Convert an IPv4 mask to the equivalent IPv6 mask.
-     *
-     * @param ip4 an IPv4 mask
-     * @return the corresponding IPv6 mask
-     * @throws IllegalArgumentException if ip4 is not exactly four octets long.
-     */
-    private static byte[] ip4MaskToIp6( final byte[] ip4 )
-    {
-        if ( ip4.length != 4 )
-        {
-            throw new IllegalArgumentException( "IPv4 mask must be four octets" );
-        }
-
-        final byte[] ip6 = new byte[ 16 ];
-        for ( int i = 0; i < 16 - 4; i++ )
-        {
-            ip6[ i ] = ( byte ) 0Xff;
-        }
-        for ( int i = 0; i < 4; i++ )
-        {
-            ip6[ 12 + i ] = ip4[ i ];
-        }
-        return ip6;
-    }
-
-    public static class IPMatcherException extends Exception
-    {
-        public IPMatcherException( )
-        {
-            super();
-        }
-
-        public IPMatcherException( final String message )
-        {
-            super( message );
-        }
-
-        public IPMatcherException( final Throwable cause )
-        {
-            super( cause );
-        }
-
-        public IPMatcherException( final String message, final Throwable cause )
-        {
-            super( message, cause );
-        }
-    }
-}

+ 1 - 1
server/src/main/resources/password/pwm/config/PwmSetting.xml

@@ -1611,7 +1611,7 @@
         <default/>
         <regex>^[a-zA-Z0-9.-]{1,1024}=.{1,1024}$</regex>
     </setting>
-    <setting hidden="false" key="network.ip.permittedRange" level="2" required="false">
+    <setting hidden="true" key="network.ip.permittedRange" level="2" required="false">
         <default>
             <value/>
         </default>

+ 1 - 1
server/src/main/resources/password/pwm/i18n/PwmSetting.properties

@@ -527,7 +527,7 @@ Setting_Description_ldap.wireTrace.enable=Enable this option to have @PwmAppName
 Setting_Description_locale.cookie.age=Specify the duration of time to remember a user's locale preferences.  Anytime @PwmAppName@ overrides a browser's default locale setting, it stores a cookie in the browser remembering that setting for the duration of this setting.
 Setting_Description_logoutAfterPasswordChange=Enable this option to force users to log out (and send them to the logoutURL) after a password change.<br/><br/>In most cases, leave this option enabled (default), especially if you are using some type of single sign-on service.
 Setting_Description_network.allowMultiIPSession=Enable this option to allow @PwmAppName@ to access a single HTTP session from different source IP addresses.  Some load balancing or proxy network infrastructures might require this, but in most cases disable it.  Especially since typical sessions are very short, there is not a practical reason for a user to access the same session from multiple client addresses.
-Setting_Description_network.ip.permittedRange=Enable this option to have @PwmAppName@ only permit connections originating from the specified IP address ranges.  If disabled (default), @PwmAppName@ permits any source IP address. <p>Supported range specifications are\:<p><ul><li>Full IPv4 address, such as <b>12.34.56.78</b></li><li>Full IPv6 address, such as <b>2001\:18e8\:3\:171\:218\:8bff\:fe2a\:56a4</b></li><li>Partial IPv4 address, such as <b>12.34</b> (which matches any IP addres starting <b>12.34</b></li><li>IPv4 network/netmask, such as <b>18.25.0.0/255.255.0.0</b></li><li>IPv4 or IPv6 CIDR slash notation, such as <b>18.25.0.0/16</b> or <b>2001\:18e8\:3\:171\:\:/64</b></li></ul>
+Setting_Description_network.ip.permittedRange=DEPRECATED.  Enable this option to have @PwmAppName@ only permit connections originating from the specified IP address ranges.  If disabled (default), @PwmAppName@ permits any source IP address. <p>Supported range specifications are\:<p><ul><li>Full IPv4 address, such as <b>12.34.56.78</b></li><li>Full IPv6 address, such as <b>2001\:18e8\:3\:171\:218\:8bff\:fe2a\:56a4</b></li><li>Partial IPv4 address, such as <b>12.34</b> (which matches any IP addres starting <b>12.34</b></li><li>IPv4 network/netmask, such as <b>18.25.0.0/255.255.0.0</b></li><li>IPv4 or IPv6 CIDR slash notation, such as <b>18.25.0.0/16</b> or <b>2001\:18e8\:3\:171\:\:/64</b></li></ul>
 Setting_Description_network.requiredHttpHeaders=<p>If specified, any HTTP/S request sent to this @PwmAppName@ application server must include these headers.  This feature is useful if you have an upstream security gateway, proxy or web server and wish to only allow sessions from the gateway, and deny direct access to this @PwmAppName@ application server from clients.</p><p>The settings must be in <code>name\=value</code> format.  If the upstream security gateway, proxy or web server is not setting these name/value headers, you will no longer be able to access this @PwmAppName@ application server.</p><p><b>WARNING:</b>If the client you are using to access this server is not setting the headers configured here, this @PwmAppName@ server will become inaccessible.</p>
 Setting_Description_network.reverseDNS.enable=Enable this option to have @PwmAppName@ use its reverse DNS system to record the hostname of the client.  In some cases this can cause performance issues so you can disable it if you do not requrie it.
 Setting_Description_newUser.createContext=Specify the LDAP context where you would like @PwmAppName@ to create new users. You can use macros in this setting.