diff options
| author | Keith Wall <kwall@apache.org> | 2012-09-28 12:46:06 +0000 |
|---|---|---|
| committer | Keith Wall <kwall@apache.org> | 2012-09-28 12:46:06 +0000 |
| commit | 58d9bbe8617e61e9cd503f18e914472a7ff5c43d (patch) | |
| tree | 12ed3399bf403feabb3b57a16c477a3ab67814ea /qpid/java/broker-plugins/access-control/src/main | |
| parent | 9d5ba686e83cb1b460e96ec6b0f248669e59342f (diff) | |
| download | qpid-python-58d9bbe8617e61e9cd503f18e914472a7ff5c43d.tar.gz | |
QPID-4334: removed the firewall plugin and moved its functionality into the Access Control plugin.
Applied patch from Philip Harvey <phil@philharveyonline.com>.
git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1391430 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'qpid/java/broker-plugins/access-control/src/main')
16 files changed, 974 insertions, 97 deletions
diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AbstractConfiguration.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AbstractConfiguration.java index f04dd38aca..44c48523e2 100644 --- a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AbstractConfiguration.java +++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AbstractConfiguration.java @@ -23,12 +23,9 @@ package org.apache.qpid.server.security.access.config; import java.io.File; import org.apache.commons.configuration.ConfigurationException; -import org.apache.log4j.Logger; public abstract class AbstractConfiguration implements ConfigurationFile { - private static final Logger _logger = Logger.getLogger(ConfigurationFile.class); - private File _file; private RuleSet _config; diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclAction.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclAction.java new file mode 100644 index 0000000000..e4bf21a082 --- /dev/null +++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclAction.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.qpid.server.security.access.config; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; +import org.apache.qpid.server.security.access.ObjectProperties; +import org.apache.qpid.server.security.access.ObjectType; +import org.apache.qpid.server.security.access.Operation; +import org.apache.qpid.server.security.access.firewall.FirewallRule; + +public class AclAction +{ + private Action _action; + private FirewallRule _firewallRule; + + public AclAction(Operation operation, ObjectType object, AclRulePredicates predicates) + { + _action = new Action(operation, object, predicates.getObjectProperties()); + _firewallRule = predicates.getFirewallRule(); + } + + public AclAction(Operation operation) + { + _action = new Action(operation); + } + + public AclAction(Operation operation, ObjectType object, ObjectProperties properties) + { + _action = new Action(operation, object, properties); + } + + public FirewallRule getFirewallRule() + { + return _firewallRule; + } + + public Action getAction() + { + return _action; + } + + public boolean isAllowed() + { + return _action.isAllowed(); + } + + @Override + public int hashCode() + { + return new HashCodeBuilder() + .append(_action) + .append(_firewallRule).toHashCode(); + } + + @Override + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + if (obj == this) + { + return true; + } + if (obj.getClass() != getClass()) + { + return false; + } + AclAction rhs = (AclAction) obj; + return new EqualsBuilder() + .append(_action, rhs._action) + .append(_firewallRule, rhs._firewallRule).isEquals(); + } + + @Override + public String toString() + { + return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) + .append(_action) + .append(_firewallRule).toString(); + } +} diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclRulePredicates.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclRulePredicates.java new file mode 100644 index 0000000000..0ea6b2fc9c --- /dev/null +++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/AclRulePredicates.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.qpid.server.security.access.config; + +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; +import org.apache.log4j.Logger; +import org.apache.qpid.server.security.access.ObjectProperties; +import org.apache.qpid.server.security.access.ObjectProperties.Property; +import org.apache.qpid.server.security.access.firewall.FirewallRule; +import org.apache.qpid.server.security.access.firewall.FirewallRuleFactory; + +/** + * Represents the predicates on an ACL rule by combining predicates relating to the object being operated on + * (e.g. name=foo) with firewall rules. + */ +public class AclRulePredicates +{ + private static final Logger _logger = Logger.getLogger(AclRulePredicates.class); + + private static final String SEPARATOR = ","; + + private ObjectProperties _properties = new ObjectProperties(); + + private FirewallRule _firewallRule; + + private FirewallRuleFactory _firewallRuleFactory = new FirewallRuleFactory(); + + public void parse(String key, String value) + { + ObjectProperties.Property property = ObjectProperties.Property.parse(key); + + if(property == Property.FROM_HOSTNAME) + { + checkFirewallRuleNotAlreadyDefined(key, value); + _firewallRule = _firewallRuleFactory.createForHostname(value.split(SEPARATOR)); + } + else if(property == Property.FROM_NETWORK) + { + checkFirewallRuleNotAlreadyDefined(key, value); + _firewallRule = _firewallRuleFactory.createForNetwork(value.split(SEPARATOR)); + } + else + { + _properties.put(property, value); + } + + _logger.debug("Parsed " + property + " with value " + value); + } + + private void checkFirewallRuleNotAlreadyDefined(String key, String value) + { + if(_firewallRule != null) + { + throw new IllegalStateException( + "Cannot parse " + key + "=" + value + + " because firewall rule " + _firewallRule + " has already been defined"); + } + } + + @Override + public String toString() + { + return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) + .append(_properties) + .append(_firewallRule).toString(); + } + + public FirewallRule getFirewallRule() + { + return _firewallRule; + } + + public ObjectProperties getObjectProperties() + { + return _properties; + } + + void setFirewallRuleFactory(FirewallRuleFactory firewallRuleFactory) + { + _firewallRuleFactory = firewallRuleFactory; + } +} diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/Action.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/Action.java index b887d1e079..d244af480a 100644 --- a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/Action.java +++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/Action.java @@ -20,8 +20,6 @@ */ package org.apache.qpid.server.security.access.config; -import java.util.Comparator; - import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import org.apache.commons.lang.builder.ToStringBuilder; @@ -32,7 +30,7 @@ import org.apache.qpid.server.security.access.Operation; /** * An access control v2 rule action. - * + * * An action consists of an {@link Operation} on an {@link ObjectType} with certain properties, stored in a {@link java.util.Map}. * The operation and object should be an allowable combination, based on the {@link ObjectType#isAllowed(Operation)} * method of the object, which is exposed as the {@link #isAllowed()} method here. The internal {@link #propertiesMatch(Map)} @@ -48,29 +46,29 @@ public class Action private Operation _operation; private ObjectType _object; private ObjectProperties _properties; - + public Action(Operation operation) { this(operation, ObjectType.ALL); } - + public Action(Operation operation, ObjectType object, String name) { this(operation, object, new ObjectProperties(name)); } - + public Action(Operation operation, ObjectType object) { this(operation, object, ObjectProperties.EMPTY); } - + public Action(Operation operation, ObjectType object, ObjectProperties properties) { setOperation(operation); setObjectType(object); setProperties(properties); } - + public Operation getOperation() { return _operation; @@ -95,12 +93,12 @@ public class Action { return _properties; } - + public void setProperties(ObjectProperties properties) { _properties = properties; } - + public boolean isAllowed() { return _object.isAllowed(_operation); @@ -109,40 +107,13 @@ public class Action /** @see Comparable#compareTo(Object) */ public boolean matches(Action a) { - return ((Operation.ALL == a.getOperation() || getOperation() == a.getOperation()) - && (ObjectType.ALL == a.getObjectType() || getObjectType() == a.getObjectType()) - && _properties.matches(a.getProperties())); - } + boolean operationMatches = Operation.ALL == a.getOperation() || getOperation() == a.getOperation(); + boolean objectTypeMatches = ObjectType.ALL == a.getObjectType() || getObjectType() == a.getObjectType(); + boolean propertiesMatch = _properties.matches(a.getProperties()); - /** - * An ordering based on specificity - * - * @see Comparator#compare(Object, Object) - */ - public class Specificity implements Comparator<Action> - { - public int compare(Action a, Action b) - { - if (a.getOperation() == Operation.ALL && b.getOperation() != Operation.ALL) - { - return 1; // B is more specific - } - else if (b.getOperation() == Operation.ALL && a.getOperation() != Operation.ALL) - { - return 1; // A is more specific - } - else if (a.getOperation() == b.getOperation()) - { - return 1; // b is more specific - } - else // Different operations - { - return a.getOperation().compareTo(b.getOperation()); // Arbitrary - } - } + return (operationMatches && objectTypeMatches && propertiesMatch); } - /** @see Object#equals(Object) */ @Override public boolean equals(Object o) { @@ -151,26 +122,24 @@ public class Action return false; } Action a = (Action) o; - + return new EqualsBuilder() .append(_operation, a.getOperation()) .append(_object, a.getObjectType()) - .appendSuper(_properties.equals(a.getProperties())) + .append(_properties, a.getProperties()) .isEquals(); } - /** @see Object#hashCode() */ @Override public int hashCode() { return new HashCodeBuilder() .append(_operation) - .append(_operation) + .append(_object) .append(_properties) .toHashCode(); } - /** @see Object#toString() */ @Override public String toString() { diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/ClientAction.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/ClientAction.java new file mode 100644 index 0000000000..fed20a56c8 --- /dev/null +++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/ClientAction.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.qpid.server.security.access.config; + +import java.net.InetAddress; + +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; +import org.apache.qpid.server.security.access.ObjectProperties; +import org.apache.qpid.server.security.access.ObjectType; +import org.apache.qpid.server.security.access.Operation; +import org.apache.qpid.server.security.access.firewall.FirewallRule; + +/** + * I represent an {@link Action} taken by a client from a known address. The address is used to + * determine if I match an {@link AclAction}, which may contain firewall rules. + */ +public class ClientAction +{ + private Action _clientAction; + + public ClientAction(Action clientAction) + { + _clientAction = clientAction; + } + + public ClientAction(Operation operation, ObjectType objectType, ObjectProperties properties) + { + _clientAction = new Action(operation, objectType, properties); + } + + public boolean matches(AclAction ruleAction, InetAddress addressOfClient) + { + return _clientAction.matches(ruleAction.getAction()) + && addressOfClientMatches(ruleAction, addressOfClient); + } + + private boolean addressOfClientMatches(AclAction ruleAction, InetAddress addressOfClient) + { + FirewallRule firewallRule = ruleAction.getFirewallRule(); + if(firewallRule == null || addressOfClient == null) + { + return true; + } + else + { + return firewallRule.matches(addressOfClient); + } + } + + public Operation getOperation() + { + return _clientAction.getOperation(); + } + + public ObjectType getObjectType() + { + return _clientAction.getObjectType(); + } + + public ObjectProperties getProperties() + { + return _clientAction.getProperties(); + } + + @Override + public String toString() + { + return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) + .append(_clientAction).toString(); + } +} diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/PlainConfiguration.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/PlainConfiguration.java index afaece6138..9f56b05e0f 100644 --- a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/PlainConfiguration.java +++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/PlainConfiguration.java @@ -34,13 +34,15 @@ import java.util.Stack; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.lang.StringUtils; -import org.apache.qpid.server.security.access.ObjectProperties; +import org.apache.log4j.Logger; import org.apache.qpid.server.security.access.ObjectType; import org.apache.qpid.server.security.access.Operation; import org.apache.qpid.server.security.access.Permission; public class PlainConfiguration extends AbstractConfiguration { + private static final Logger _logger = Logger.getLogger(PlainConfiguration.class); + public static final Character COMMENT = '#'; public static final Character CONTINUATION = '\\'; @@ -74,9 +76,16 @@ public class PlainConfiguration extends AbstractConfiguration { RuleSet ruleSet = super.load(); + File file = getFile(); + try { - _st = new StreamTokenizer(new BufferedReader(new FileReader(getFile()))); + if(_logger.isDebugEnabled()) + { + _logger.debug("About to load ACL file " + file); + } + + _st = new StreamTokenizer(new BufferedReader(new FileReader(file))); _st.resetSyntax(); // setup the tokenizer _st.commentChar(COMMENT); // single line comments @@ -195,11 +204,11 @@ public class PlainConfiguration extends AbstractConfiguration } catch (FileNotFoundException fnfe) { - throw new ConfigurationException(String.format(CONFIG_NOT_FOUND_MSG, getFile().getName()), fnfe); + throw new ConfigurationException(String.format(CONFIG_NOT_FOUND_MSG, file.getName()), fnfe); } catch (IOException ioe) { - throw new ConfigurationException(String.format(CANNOT_LOAD_MSG, getFile().getName()), ioe); + throw new ConfigurationException(String.format(CANNOT_LOAD_MSG, file.getName()), ioe); } return ruleSet; @@ -228,9 +237,9 @@ public class PlainConfiguration extends AbstractConfiguration else { ObjectType object = ObjectType.parse(args.get(3)); - ObjectProperties properties = toObjectProperties(args.subList(4, args.size())); + AclRulePredicates predicates = toRulePredicates(args.subList(4, args.size())); - getConfiguration().grant(number, identity, permission, operation, object, properties); + getConfiguration().grant(number, identity, permission, operation, object, predicates); } } @@ -246,10 +255,9 @@ public class PlainConfiguration extends AbstractConfiguration getConfiguration().configure(properties); } - /** Converts a {@link List} of "name", "=", "value" tokens into a {@link Map}. */ - protected ObjectProperties toObjectProperties(List<String> args) throws ConfigurationException + private AclRulePredicates toRulePredicates(List<String> args) throws ConfigurationException { - ObjectProperties properties = new ObjectProperties(); + AclRulePredicates predicates = new AclRulePredicates(); Iterator<String> i = args.iterator(); while (i.hasNext()) { @@ -268,11 +276,9 @@ public class PlainConfiguration extends AbstractConfiguration } String value = i.next(); - // parse property key - ObjectProperties.Property property = ObjectProperties.Property.parse(key); - properties.put(property, value); + predicates.parse(key, value); } - return properties; + return predicates; } /** Converts a {@link List} of "name", "=", "value" tokens into a {@link Map}. */ diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/Rule.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/Rule.java index 5e98e0bd1b..cef9a8696b 100644 --- a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/Rule.java +++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/Rule.java @@ -41,10 +41,10 @@ public class Rule implements Comparable<Rule> private Integer _number; private Boolean _enabled = Boolean.TRUE; private String _identity; - private Action _action; + private AclAction _action; private Permission _permission; - public Rule(Integer number, String identity, Action action, Permission permission) + public Rule(Integer number, String identity, AclAction action, Permission permission) { setNumber(number); setIdentity(identity); @@ -52,7 +52,7 @@ public class Rule implements Comparable<Rule> setPermission(permission); } - public Rule(String identity, Action action, Permission permission) + public Rule(String identity, AclAction action, Permission permission) { this(null, identity, action, permission); } @@ -99,10 +99,15 @@ public class Rule implements Comparable<Rule> public Action getAction() { + return _action.getAction(); + } + + public AclAction getAclAction() + { return _action; } - public void setAction(Action action) + public void setAction(AclAction action) { _action = action; } @@ -117,7 +122,7 @@ public class Rule implements Comparable<Rule> _permission = permission; } - /** @see Comparable#compareTo(Object) */ + @Override public int compareTo(Rule r) { return new CompareToBuilder() @@ -127,7 +132,6 @@ public class Rule implements Comparable<Rule> .toComparison(); } - /** @see Object#equals(Object) */ @Override public boolean equals(Object o) { @@ -139,30 +143,28 @@ public class Rule implements Comparable<Rule> return new EqualsBuilder() .append(getIdentity(), r.getIdentity()) - .append(getAction(), r.getAction()) + .append(getAclAction(), r.getAclAction()) .append(getPermission(), r.getPermission()) .isEquals(); } - /** @see Object#hashCode() */ @Override public int hashCode() { return new HashCodeBuilder() .append(getIdentity()) - .append(getAction()) + .append(getAclAction()) .append(getPermission()) .toHashCode(); } - /** @see Object#toString() */ @Override public String toString() { return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) .append("#", getNumber()) .append("identity", getIdentity()) - .append("action", getAction()) + .append("action", getAclAction()) .append("permission", getPermission()) .append("enabled", isEnabled()) .toString(); diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleSet.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleSet.java index 2477455de4..e61370fced 100644 --- a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleSet.java +++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleSet.java @@ -18,6 +18,7 @@ */ package org.apache.qpid.server.security.access.config; +import java.net.InetAddress; import java.security.Principal; import java.util.Arrays; import java.util.Collections; @@ -53,7 +54,7 @@ import org.apache.qpid.server.security.access.logging.AccessControlMessages; */ public class RuleSet { - public static final Logger _logger = Logger.getLogger(RuleSet.class); + private static final Logger _logger = Logger.getLogger(RuleSet.class); private static final String AT = "@"; private static final String SLASH = "/"; @@ -154,21 +155,27 @@ public class RuleSet public void grant(Integer number, String identity, Permission permission, Operation operation) { - Action action = new Action(operation); + AclAction action = new AclAction(operation); addRule(number, identity, permission, action); } public void grant(Integer number, String identity, Permission permission, Operation operation, ObjectType object, ObjectProperties properties) { - Action action = new Action(operation, object, properties); + AclAction action = new AclAction(operation, object, properties); addRule(number, identity, permission, action); } - public boolean ruleExists(String identity, Action action) + public void grant(Integer number, String identity, Permission permission, Operation operation, ObjectType object, AclRulePredicates predicates) + { + AclAction aclAction = new AclAction(operation, object, predicates); + addRule(number, identity, permission, aclAction); + } + + public boolean ruleExists(String identity, AclAction action) { for (Rule rule : _rules.values()) { - if (rule.getIdentity().equals(identity) && rule.getAction().equals(action)) + if (rule.getIdentity().equals(identity) && rule.getAclAction().equals(action)) { return true; } @@ -176,8 +183,7 @@ public class RuleSet return false; } - // TODO make this work when group membership is not known at file parse time - public void addRule(Integer number, String identity, Permission permission, Action action) + public void addRule(Integer number, String identity, Permission permission, AclAction action) { _cache.clear(); @@ -263,6 +269,16 @@ public class RuleSet } /** + * Checks for the case when the client's address is not known. + * + * @see #check(Subject, Operation, ObjectType, ObjectProperties, InetAddress) + */ + public Result check(Subject subject, Operation operation, ObjectType objectType, ObjectProperties properties) + { + return check(subject, operation, objectType, properties, null); + } + + /** * Check the authorisation granted to a particular identity for an operation on an object type with * specific properties. * @@ -271,10 +287,9 @@ public class RuleSet * the first match found, or denies access if there are no matching rules. Normally, it would be expected * to have a default deny or allow rule at the end of an access configuration however. */ - public Result check(Subject subject, Operation operation, ObjectType objectType, ObjectProperties properties) + public Result check(Subject subject, Operation operation, ObjectType objectType, ObjectProperties properties, InetAddress addressOfClient) { - // Create the action to check - Action action = new Action(operation, objectType, properties); + ClientAction action = new ClientAction(operation, objectType, properties); if(_logger.isDebugEnabled()) { @@ -293,27 +308,31 @@ public class RuleSet } // Iterate through a filtered set of rules dealing with this identity and operation - for (Rule current : rules) + for (Rule rule : rules) { if(_logger.isDebugEnabled()) { - _logger.debug("Checking against rule: " + current); + _logger.debug("Checking against rule: " + rule); } - // Check if action matches - if (action.matches(current.getAction())) + + if (action.matches(rule.getAclAction(), addressOfClient)) { - Permission permission = current.getPermission(); + Permission permission = rule.getPermission(); switch (permission) { case ALLOW_LOG: CurrentActor.get().message(AccessControlMessages.ALLOWED( - action.getOperation().toString(), action.getObjectType().toString(), action.getProperties().toString())); + action.getOperation().toString(), + action.getObjectType().toString(), + action.getProperties().toString())); case ALLOW: return Result.ALLOWED; case DENY_LOG: CurrentActor.get().message(AccessControlMessages.DENIED( - action.getOperation().toString(), action.getObjectType().toString(), action.getProperties().toString())); + action.getOperation().toString(), + action.getObjectType().toString(), + action.getProperties().toString())); case DENY: return Result.DENIED; } @@ -419,5 +438,4 @@ public class RuleSet } return objects; } - } diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/AccessControlFirewallException.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/AccessControlFirewallException.java new file mode 100644 index 0000000000..efae7f5653 --- /dev/null +++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/AccessControlFirewallException.java @@ -0,0 +1,43 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.qpid.server.security.access.firewall; + +public class AccessControlFirewallException extends RuntimeException +{ + /** serialVersionUID */ + private static final long serialVersionUID = 4526157149690917805L; + + public AccessControlFirewallException() { + super(); + } + + public AccessControlFirewallException(String message) { + super(message); + } + + public AccessControlFirewallException(String message, Throwable cause) { + super(message, cause); + } + + public AccessControlFirewallException(Throwable cause) { + super(cause); + } +}
\ No newline at end of file diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/FirewallRule.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/FirewallRule.java new file mode 100644 index 0000000000..482a795693 --- /dev/null +++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/FirewallRule.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.qpid.server.security.access.firewall; + +import java.net.InetAddress; + +public interface FirewallRule +{ + boolean matches(InetAddress addressOfClient); +} diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/FirewallRuleFactory.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/FirewallRuleFactory.java new file mode 100644 index 0000000000..64be26c209 --- /dev/null +++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/FirewallRuleFactory.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.qpid.server.security.access.firewall; + +public class FirewallRuleFactory +{ + public FirewallRule createForHostname(String[] hostnames) + { + return new HostnameFirewallRule(hostnames); + } + + public FirewallRule createForNetwork(String[] networks) + { + return new NetworkFirewallRule(networks); + } + +} diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/HostnameFirewallRule.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/HostnameFirewallRule.java new file mode 100644 index 0000000000..9d60c6d745 --- /dev/null +++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/HostnameFirewallRule.java @@ -0,0 +1,156 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.qpid.server.security.access.firewall; + +import java.net.InetAddress; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; +import org.apache.log4j.Logger; + +public class HostnameFirewallRule implements FirewallRule +{ + private static final Logger _logger = Logger.getLogger(HostnameFirewallRule.class); + + private static final long DNS_TIMEOUT = 30000; + private static final ExecutorService DNS_LOOKUP = Executors.newCachedThreadPool(); + + private Pattern[] _hostnamePatterns; + private String[] _hostnames; + + public HostnameFirewallRule(String... hostnames) + { + _hostnames = hostnames; + + int i = 0; + _hostnamePatterns = new Pattern[hostnames.length]; + for (String hostname : hostnames) + { + _hostnamePatterns[i++] = Pattern.compile(hostname); + } + + if(_logger.isDebugEnabled()) + { + _logger.debug("Created " + this); + } + } + + @Override + public boolean matches(InetAddress remote) + { + String hostname = getHostname(remote); + if (hostname == null) + { + throw new AccessControlFirewallException("DNS lookup failed"); + } + for (Pattern pattern : _hostnamePatterns) + { + boolean hostnameMatches = pattern.matcher(hostname).matches(); + + + if (hostnameMatches) + { + if(_logger.isDebugEnabled()) + { + _logger.debug("Hostname " + hostname + " matches rule " + pattern.toString()); + } + return true; + } + } + + if(_logger.isDebugEnabled()) + { + _logger.debug("Hostname " + hostname + " matches no configured hostname patterns"); + } + + return false; + } + + + /** + * @param remote + * the InetAddress to look up + * @return the hostname, null if not found, takes longer than + * {@value #DNS_LOOKUP} to find or otherwise fails + */ + private String getHostname(final InetAddress remote) throws AccessControlFirewallException + { + FutureTask<String> lookup = new FutureTask<String>(new Callable<String>() + { + public String call() + { + return remote.getCanonicalHostName(); + } + }); + DNS_LOOKUP.execute(lookup); + + try + { + return lookup.get(DNS_TIMEOUT, TimeUnit.MILLISECONDS); + } + catch (Exception e) + { + return null; + } + finally + { + lookup.cancel(true); + } + } + + @Override + public int hashCode() + { + return new HashCodeBuilder().append(_hostnames).toHashCode(); + } + + @Override + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + if (obj == this) + { + return true; + } + if (obj.getClass() != getClass()) + { + return false; + } + HostnameFirewallRule rhs = (HostnameFirewallRule) obj; + return new EqualsBuilder().append(_hostnames, rhs._hostnames).isEquals(); + } + + @Override + public String toString() + { + return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) + .append(_hostnames).toString(); + } +} diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/InetNetwork.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/InetNetwork.java new file mode 100644 index 0000000000..52516af84c --- /dev/null +++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/InetNetwork.java @@ -0,0 +1,193 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.qpid.server.security.access.firewall; + +import java.net.InetAddress; + +class InetNetwork +{ + /* + * Implements network masking, and is compatible with RFC 1518 and + * RFC 1519, which describe CIDR: Classless Inter-Domain Routing. + */ + + private InetAddress network; + private InetAddress netmask; + + public InetNetwork(InetAddress ip, InetAddress netmask) + { + network = maskIP(ip, netmask); + this.netmask = netmask; + } + + public boolean contains(final String name) throws java.net.UnknownHostException + { + return network.equals(maskIP(InetAddress.getByName(name), netmask)); + } + + public boolean contains(final InetAddress ip) + { + return network.equals(maskIP(ip, netmask)); + } + + public String toString() + { + return network.getHostAddress() + "/" + netmask.getHostAddress(); + } + + public int hashCode() + { + return maskIP(network, netmask).hashCode(); + } + + public boolean equals(Object obj) + { + return (obj != null) && (obj instanceof InetNetwork) && + ((((InetNetwork)obj).network.equals(network)) && (((InetNetwork)obj).netmask.equals(netmask))); + } + + public static InetNetwork getFromString(String netspec) throws java.net.UnknownHostException + { + if (netspec.endsWith("*")) + { + netspec = normalizeFromAsterisk(netspec); + } + else + { + int iSlash = netspec.indexOf('/'); + if (iSlash == -1) + { + netspec += "/255.255.255.255"; + } + else if (netspec.indexOf('.', iSlash) == -1) + { + netspec = normalizeFromCIDR(netspec); + } + } + + return new InetNetwork(InetAddress.getByName(netspec.substring(0, netspec.indexOf('/'))), + InetAddress.getByName(netspec.substring(netspec.indexOf('/') + 1))); + } + + public static InetAddress maskIP(final byte[] ip, final byte[] mask) + { + try + { + return getByAddress(new byte[] + { + (byte) (mask[0] & ip[0]), + (byte) (mask[1] & ip[1]), + (byte) (mask[2] & ip[2]), + (byte) (mask[3] & ip[3]) + }); + } + catch(Exception _) {} + { + return null; + } + } + + public static InetAddress maskIP(final InetAddress ip, final InetAddress mask) + { + return maskIP(ip.getAddress(), mask.getAddress()); + } + + /* + * This converts from an uncommon "wildcard" CIDR format + * to "address + mask" format: + * + * * => 000.000.000.0/000.000.000.0 + * xxx.* => xxx.000.000.0/255.000.000.0 + * xxx.xxx.* => xxx.xxx.000.0/255.255.000.0 + * xxx.xxx.xxx.* => xxx.xxx.xxx.0/255.255.255.0 + */ + static private String normalizeFromAsterisk(final String netspec) + { + String[] masks = { "0.0.0.0/0.0.0.0", "0.0.0/255.0.0.0", "0.0/255.255.0.0", "0/255.255.255.0" }; + char[] srcb = netspec.toCharArray(); + int octets = 0; + for (int i = 1; i < netspec.length(); i++) + { + if (srcb[i] == '.') + { + octets++; + } + } + return (octets == 0) ? masks[0] : netspec.substring(0, netspec.length() -1 ).concat(masks[octets]); + } + + /* + * RFC 1518, 1519 - Classless Inter-Domain Routing (CIDR) + * This converts from "prefix + prefix-length" format to + * "address + mask" format, e.g. from xxx.xxx.xxx.xxx/yy + * to xxx.xxx.xxx.xxx/yyy.yyy.yyy.yyy. + */ + static private String normalizeFromCIDR(final String netspec) + { + final int bits = 32 - Integer.parseInt(netspec.substring(netspec.indexOf('/')+1)); + final int mask = (bits == 32) ? 0 : 0xFFFFFFFF - ((1 << bits)-1); + + return netspec.substring(0, netspec.indexOf('/') + 1) + + Integer.toString(mask >> 24 & 0xFF, 10) + "." + + Integer.toString(mask >> 16 & 0xFF, 10) + "." + + Integer.toString(mask >> 8 & 0xFF, 10) + "." + + Integer.toString(mask >> 0 & 0xFF, 10); + } + + private static java.lang.reflect.Method getByAddress = null; + + static { + try { + Class<?> inetAddressClass = Class.forName("java.net.InetAddress"); + Class<?>[] parameterTypes = { byte[].class }; + getByAddress = inetAddressClass.getMethod("getByAddress", parameterTypes); + } catch (Exception e) { + getByAddress = null; + } + } + + private static InetAddress getByAddress(byte[] ip) throws java.net.UnknownHostException + { + InetAddress addr = null; + if (getByAddress != null) + { + try + { + addr = (InetAddress) getByAddress.invoke(null, new Object[] { ip }); + } + catch (IllegalAccessException e) + { + } + catch (java.lang.reflect.InvocationTargetException e) + { + } + } + + if (addr == null) { + addr = InetAddress.getByName + ( + Integer.toString(ip[0] & 0xFF, 10) + "." + + Integer.toString(ip[1] & 0xFF, 10) + "." + + Integer.toString(ip[2] & 0xFF, 10) + "." + + Integer.toString(ip[3] & 0xFF, 10) + ); + } + return addr; + } +}
\ No newline at end of file diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/NetworkFirewallRule.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/NetworkFirewallRule.java new file mode 100644 index 0000000000..ad619a0e0b --- /dev/null +++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/firewall/NetworkFirewallRule.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.qpid.server.security.access.firewall; + +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; +import org.apache.log4j.Logger; + +public class NetworkFirewallRule implements FirewallRule +{ + private static final Logger _logger = Logger.getLogger(NetworkFirewallRule.class); + + private List<InetNetwork> _networks; + + public NetworkFirewallRule(String... networks) + { + _networks = new ArrayList<InetNetwork>(); + for (int i = 0; i < networks.length; i++) + { + String network = networks[i]; + try + { + InetNetwork inetNetwork = InetNetwork.getFromString(network); + if (!_networks.contains(inetNetwork)) + { + _networks.add(inetNetwork); + } + } + catch (java.net.UnknownHostException uhe) + { + _logger.error("Cannot resolve address: " + network, uhe); + } + } + + if(_logger.isDebugEnabled()) + { + _logger.debug("Created " + this); + } + } + + @Override + public boolean matches(InetAddress ip) + { + for (InetNetwork network : _networks) + { + if (network.contains(ip)) + { + if(_logger.isDebugEnabled()) + { + _logger.debug("Client address " + ip + " matches configured network " + network); + } + return true; + } + } + + if(_logger.isDebugEnabled()) + { + _logger.debug("Client address " + ip + " does not match any configured networks"); + } + + return false; + } + + @Override + public int hashCode() + { + return new HashCodeBuilder().append(_networks).toHashCode(); + } + + @Override + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + if (obj == this) + { + return true; + } + if (obj.getClass() != getClass()) + { + return false; + } + NetworkFirewallRule rhs = (NetworkFirewallRule) obj; + return new EqualsBuilder().append(_networks, rhs._networks).isEquals(); + } + + @Override + public String toString() + { + return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) + .append(_networks).toString(); + } +} diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/logging/AccessControl_logmessages.properties b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/logging/AccessControl_logmessages.properties index bf80df3722..2a5eb7b3be 100644 --- a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/logging/AccessControl_logmessages.properties +++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/logging/AccessControl_logmessages.properties @@ -25,4 +25,4 @@ ALLOWED = ACL-1001 : Allowed : {0} {1} {2} # 'deny-log' rule message -DENIED = ACL-1002 : Denied : {0} {1} {2}
\ No newline at end of file +DENIED = ACL-1002 : Denied : {0} {1} {2} diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AccessControl.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AccessControl.java index d36ae810c6..d94948237f 100644 --- a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AccessControl.java +++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AccessControl.java @@ -20,9 +20,13 @@ */ package org.apache.qpid.server.security.access.plugins; +import java.net.InetAddress; +import java.net.InetSocketAddress; + import javax.security.auth.Subject; import org.apache.commons.configuration.ConfigurationException; +import org.apache.commons.lang.ObjectUtils; import org.apache.log4j.Logger; import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; import org.apache.qpid.server.security.AbstractPlugin; @@ -39,7 +43,7 @@ import org.apache.qpid.server.security.access.config.RuleSet; */ public class AccessControl extends AbstractPlugin { - public static final Logger _logger = Logger.getLogger(AccessControl.class); + private static final Logger _logger = Logger.getLogger(AccessControl.class); private RuleSet _ruleSet; @@ -82,9 +86,16 @@ public class AccessControl extends AbstractPlugin * Delegate to the {@link #authorise(Operation, ObjectType, ObjectProperties)} method, with * the operation set to ACCESS and no object properties. */ - public Result access(ObjectType objectType, Object instance) + public Result access(ObjectType objectType, Object inetSocketAddress) { - return authorise(Operation.ACCESS, objectType, ObjectProperties.EMPTY); + InetAddress addressOfClient = null; + + if(inetSocketAddress != null) + { + addressOfClient = ((InetSocketAddress) inetSocketAddress).getAddress(); + } + + return authoriseFromAddress(Operation.ACCESS, objectType, ObjectProperties.EMPTY, addressOfClient); } /** @@ -94,6 +105,11 @@ public class AccessControl extends AbstractPlugin */ public Result authorise(Operation operation, ObjectType objectType, ObjectProperties properties) { + return authoriseFromAddress(operation, objectType, properties, null); + } + + public Result authoriseFromAddress(Operation operation, ObjectType objectType, ObjectProperties properties, InetAddress addressOfClient) + { final Subject subject = SecurityManager.getThreadSubject(); // Abstain if there is no subject/principal associated with this thread if (subject == null || subject.getPrincipals().size() == 0) @@ -101,8 +117,20 @@ public class AccessControl extends AbstractPlugin return Result.ABSTAIN; } - _logger.debug("Checking " + operation + " " + objectType); - return _ruleSet.check(subject, operation, objectType, properties); + if(_logger.isDebugEnabled()) + { + _logger.debug("Checking " + operation + " " + objectType + " " + ObjectUtils.defaultIfNull(addressOfClient, "")); + } + + try + { + return _ruleSet.check(subject, operation, objectType, properties, addressOfClient); + } + catch(Exception e) + { + _logger.error("Unable to check " + operation + " " + objectType + " " + ObjectUtils.defaultIfNull(addressOfClient, ""), e); + return Result.DENIED; + } } public void configure(ConfigurationPlugin config) |
