Przeglądaj źródła

SEC-1033: Added support for web IP ranges based on an address and netmask.

Luke Taylor 17 lat temu
rodzic
commit
7fe6a0fc0d

+ 48 - 16
core/src/main/java/org/springframework/security/expression/support/WebSecurityExpressionRoot.java

@@ -2,6 +2,7 @@ package org.springframework.security.expression.support;
 
 import java.net.InetAddress;
 import java.net.UnknownHostException;
+import java.util.Arrays;
 
 import org.springframework.security.Authentication;
 import org.springframework.security.intercept.web.FilterInvocation;
@@ -21,33 +22,64 @@ class WebSecurityExpressionRoot extends SecurityExpressionRoot {
         this.filterInvocation = fi;
     }
 
+    /**
+     * Takes a specific IP address or a range using the IP/Netmask (e.g. 192.168.1.0/24 or 202.24.0.0/14).
+     *
+     * @param ipAddress the address or range of addresses from which the request must come.
+     * @return true if the IP address of the current request is in the required range.
+     */
     public boolean hasIpAddress(String ipAddress) {
-        byte[] mask = null;
+        int nMaskBits = 0;
 
         if (ipAddress.indexOf('/') > 0) {
             String[] addressAndMask = StringUtils.split(ipAddress, "/");
             ipAddress = addressAndMask[0];
-            try {
-                mask = InetAddress.getByName(addressAndMask[1]).getAddress();
-            } catch (UnknownHostException e) {
-                throw new IllegalArgumentException("Failed to parse mask" + addressAndMask[1], e);
-            }
+            nMaskBits = Integer.parseInt(addressAndMask[1]);
         }
 
-        try {
-            InetAddress requiredAddress = InetAddress.getByName(ipAddress);
-            InetAddress remoteAddress = InetAddress.getByName(filterInvocation.getHttpRequest().getRemoteAddr());
+        InetAddress requiredAddress = parseAddress(ipAddress);
+        InetAddress remoteAddress = parseAddress(filterInvocation.getHttpRequest().getRemoteAddr());
 
-            if (mask == null) {
-                return remoteAddress.equals(requiredAddress);
-            } else {
+        if (!requiredAddress.getClass().equals(remoteAddress.getClass())) {
+            throw new IllegalArgumentException("IP Address in expression must be the same type as " +
+                    "version returned by request");
+        }
+
+        if (nMaskBits == 0) {
+            return remoteAddress.equals(requiredAddress);
+        }
 
+        byte[] remAddr = remoteAddress.getAddress();
+        byte[] reqAddr = requiredAddress.getAddress();
+
+        int oddBits = nMaskBits % 8;
+        int nMaskBytes = nMaskBits/8 + (oddBits == 0 ? 0 : 1);
+        byte[] mask = new byte[nMaskBytes];
+
+        Arrays.fill(mask, 0, oddBits == 0 ? mask.length : mask.length - 1, (byte)0xFF);
+
+        if (oddBits != 0) {
+            int finalByte = (1 << oddBits) - 1;
+            finalByte <<= 8-oddBits;
+            mask[mask.length - 1] = (byte) finalByte;
+        }
+
+ //       System.out.println("Mask is " + new sun.misc.HexDumpEncoder().encode(mask));
+
+        for (int i=0; i < mask.length; i++) {
+            if ((remAddr[i] & mask[i]) != (reqAddr[i] & mask[i])) {
+                return false;
             }
-//            byte[] remoteAddress = InetAddress.getByName(filterInvocation.getHttpRequest().getRemoteAddr()).getAddress();
-        } catch (UnknownHostException e) {
-            throw new IllegalArgumentException("Failed to parse " + ipAddress, e);
         }
 
-        return false;
+        return true;
+    }
+
+    private InetAddress parseAddress(String address) {
+        try {
+            return InetAddress.getByName(address);
+        } catch (UnknownHostException e) {
+            throw new IllegalArgumentException("Failed to parse address" + address, e);
+        }
     }
 }

+ 44 - 0
core/src/test/java/org/springframework/security/expression/support/WebSecurityExpressionRootTests.java

@@ -10,6 +10,13 @@ import org.springframework.security.Authentication;
 import org.springframework.security.intercept.web.FilterInvocation;
 import org.springframework.security.util.FilterInvocationUtils;
 
+/**
+ * Tests for {@link WebSecurityExpressionRoot}.
+ *
+ * @author Luke Taylor
+ * @version $Id$
+ * @since 2.5
+ */
 public class WebSecurityExpressionRootTests {
     Mockery jmock = new JUnit4Mockery();
 
@@ -17,9 +24,46 @@ public class WebSecurityExpressionRootTests {
     public void ipAddressMatchesForEqualIpAddresses() throws Exception {
         FilterInvocation fi = FilterInvocationUtils.create("/test");
         MockHttpServletRequest request = (MockHttpServletRequest) fi.getHttpRequest();
+        // IPv4
         request.setRemoteAddr("192.168.1.1");
         WebSecurityExpressionRoot root = new WebSecurityExpressionRoot(jmock.mock(Authentication.class), fi);
 
         assertTrue(root.hasIpAddress("192.168.1.1"));
+
+        // IPv6 Address
+        request.setRemoteAddr("fa:db8:85a3::8a2e:370:7334");
+        assertTrue(root.hasIpAddress("fa:db8:85a3::8a2e:370:7334"));
+    }
+
+    @Test
+    public void addressesInIpRangeMatch() throws Exception {
+        FilterInvocation fi = FilterInvocationUtils.create("/test");
+        MockHttpServletRequest request = (MockHttpServletRequest) fi.getHttpRequest();
+        WebSecurityExpressionRoot root = new WebSecurityExpressionRoot(jmock.mock(Authentication.class), fi);
+        for (int i=0; i < 255; i++) {
+            request.setRemoteAddr("192.168.1." + i);
+            assertTrue(root.hasIpAddress("192.168.1.0/24"));
+        }
+
+        request.setRemoteAddr("192.168.1.127");
+        // 25 = FF FF FF 80
+        assertTrue(root.hasIpAddress("192.168.1.0/25"));
+        // encroach on the mask
+        request.setRemoteAddr("192.168.1.128");
+        assertFalse(root.hasIpAddress("192.168.1.0/25"));
+        request.setRemoteAddr("192.168.1.255");
+        assertTrue(root.hasIpAddress("192.168.1.128/25"));
+        assertTrue(root.hasIpAddress("192.168.1.192/26"));
+        assertTrue(root.hasIpAddress("192.168.1.224/27"));
+        assertTrue(root.hasIpAddress("192.168.1.240/27"));
+        assertTrue(root.hasIpAddress("192.168.1.255/32"));
+
+        request.setRemoteAddr("202.24.199.127");
+        assertTrue(root.hasIpAddress("202.24.0.0/14"));
+        request.setRemoteAddr("202.25.179.135");
+        assertTrue(root.hasIpAddress("202.24.0.0/14"));
+        request.setRemoteAddr("202.26.179.135");
+        assertTrue(root.hasIpAddress("202.24.0.0/14"));
     }
+
 }