From 7fe6a0fc0ddff71bfe78ab51d7773d8a3c782a4e Mon Sep 17 00:00:00 2001 From: Luke Taylor Date: Tue, 9 Dec 2008 23:14:44 +0000 Subject: [PATCH] SEC-1033: Added support for web IP ranges based on an address and netmask. --- .../support/WebSecurityExpressionRoot.java | 66 ++++++++++++++----- .../WebSecurityExpressionRootTests.java | 44 +++++++++++++ 2 files changed, 93 insertions(+), 17 deletions(-) diff --git a/core/src/main/java/org/springframework/security/expression/support/WebSecurityExpressionRoot.java b/core/src/main/java/org/springframework/security/expression/support/WebSecurityExpressionRoot.java index bffb905337..c32fdbc389 100644 --- a/core/src/main/java/org/springframework/security/expression/support/WebSecurityExpressionRoot.java +++ b/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]); + } + + InetAddress requiredAddress = parseAddress(ipAddress); + InetAddress remoteAddress = parseAddress(filterInvocation.getHttpRequest().getRemoteAddr()); + + 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; } } + return true; + } + + private InetAddress parseAddress(String address) { try { - InetAddress requiredAddress = InetAddress.getByName(ipAddress); - InetAddress remoteAddress = InetAddress.getByName(filterInvocation.getHttpRequest().getRemoteAddr()); - - if (mask == null) { - return remoteAddress.equals(requiredAddress); - } else { - - } -// byte[] remoteAddress = InetAddress.getByName(filterInvocation.getHttpRequest().getRemoteAddr()).getAddress(); + return InetAddress.getByName(address); } catch (UnknownHostException e) { - throw new IllegalArgumentException("Failed to parse " + ipAddress, e); + throw new IllegalArgumentException("Failed to parse address" + address, e); } - - return false; } } diff --git a/core/src/test/java/org/springframework/security/expression/support/WebSecurityExpressionRootTests.java b/core/src/test/java/org/springframework/security/expression/support/WebSecurityExpressionRootTests.java index 80d3691f84..a833459e87 100644 --- a/core/src/test/java/org/springframework/security/expression/support/WebSecurityExpressionRootTests.java +++ b/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")); + } + }