blob: 92bd84d1017bfbeb3640bd1f55e473d0eee7cd0f [file] [log] [blame]
/*
* Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.acl;
import java.io.*;
import java.util.*;
import java.security.Principal;
import java.security.acl.*;
/**
* An Access Control List (ACL) is encapsulated by this class.
* @author Satish Dharmaraj
*/
public class AclImpl extends OwnerImpl implements Acl {
//
// Maintain four tables. one each for positive and negative
// ACLs. One each depending on whether the entity is a group
// or principal.
//
private Hashtable<Principal, AclEntry> allowedUsersTable =
new Hashtable<>(23);
private Hashtable<Principal, AclEntry> allowedGroupsTable =
new Hashtable<>(23);
private Hashtable<Principal, AclEntry> deniedUsersTable =
new Hashtable<>(23);
private Hashtable<Principal, AclEntry> deniedGroupsTable =
new Hashtable<>(23);
private String aclName = null;
private Vector<Permission> zeroSet = new Vector<>(1,1);
/**
* Constructor for creating an empty ACL.
*/
public AclImpl(Principal owner, String name) {
super(owner);
try {
setName(owner, name);
} catch (Exception e) {}
}
/**
* Sets the name of the ACL.
* @param caller the principal who is invoking this method.
* @param name the name of the ACL.
* @exception NotOwnerException if the caller principal is
* not on the owners list of the Acl.
*/
public void setName(Principal caller, String name)
throws NotOwnerException
{
if (!isOwner(caller))
throw new NotOwnerException();
aclName = name;
}
/**
* Returns the name of the ACL.
* @return the name of the ACL.
*/
public String getName() {
return aclName;
}
/**
* Adds an ACL entry to this ACL. An entry associates a
* group or a principal with a set of permissions. Each
* user or group can have one positive ACL entry and one
* negative ACL entry. If there is one of the type (negative
* or positive) already in the table, a false value is returned.
* The caller principal must be a part of the owners list of
* the ACL in order to invoke this method.
* @param caller the principal who is invoking this method.
* @param entry the ACL entry that must be added to the ACL.
* @return true on success, false if the entry is already present.
* @exception NotOwnerException if the caller principal
* is not on the owners list of the Acl.
*/
public synchronized boolean addEntry(Principal caller, AclEntry entry)
throws NotOwnerException
{
if (!isOwner(caller))
throw new NotOwnerException();
Hashtable<Principal, AclEntry> aclTable = findTable(entry);
Principal key = entry.getPrincipal();
if (aclTable.get(key) != null)
return false;
aclTable.put(key, entry);
return true;
}
/**
* Removes an ACL entry from this ACL.
* The caller principal must be a part of the owners list of the ACL
* in order to invoke this method.
* @param caller the principal who is invoking this method.
* @param entry the ACL entry that must be removed from the ACL.
* @return true on success, false if the entry is not part of the ACL.
* @exception NotOwnerException if the caller principal is not
* the owners list of the Acl.
*/
public synchronized boolean removeEntry(Principal caller, AclEntry entry)
throws NotOwnerException
{
if (!isOwner(caller))
throw new NotOwnerException();
Hashtable<Principal, AclEntry> aclTable = findTable(entry);
Principal key = entry.getPrincipal();
AclEntry o = aclTable.remove(key);
return (o != null);
}
/**
* This method returns the set of allowed permissions for the
* specified principal. This set of allowed permissions is calculated
* as follows:
*
* If there is no entry for a group or a principal an empty permission
* set is assumed.
*
* The group positive permission set is the union of all
* the positive permissions of each group that the individual belongs to.
* The group negative permission set is the union of all
* the negative permissions of each group that the individual belongs to.
* If there is a specific permission that occurs in both
* the postive permission set and the negative permission set,
* it is removed from both. The group positive and negatoive permission
* sets are calculated.
*
* The individial positive permission set and the individual negative
* permission set is then calculated. Again abscence of an entry means
* the empty set.
*
* The set of permissions granted to the principal is then calculated using
* the simple rule: Individual permissions always override the Group permissions.
* Specifically, individual negative permission set (specific
* denial of permissions) overrides the group positive permission set.
* And the individual positive permission set override the group negative
* permission set.
*
* @param user the principal for which the ACL entry is returned.
* @return The resulting permission set that the principal is allowed.
*/
public synchronized Enumeration<Permission> getPermissions(Principal user) {
Enumeration<Permission> individualPositive;
Enumeration<Permission> individualNegative;
Enumeration<Permission> groupPositive;
Enumeration<Permission> groupNegative;
//
// canonicalize the sets. That is remove common permissions from
// positive and negative sets.
//
groupPositive =
subtract(getGroupPositive(user), getGroupNegative(user));
groupNegative =
subtract(getGroupNegative(user), getGroupPositive(user));
individualPositive =
subtract(getIndividualPositive(user), getIndividualNegative(user));
individualNegative =
subtract(getIndividualNegative(user), getIndividualPositive(user));
//
// net positive permissions is individual positive permissions
// plus (group positive - individual negative).
//
Enumeration<Permission> temp1 =
subtract(groupPositive, individualNegative);
Enumeration<Permission> netPositive =
union(individualPositive, temp1);
// recalculate the enumeration since we lost it in performing the
// subtraction
//
individualPositive =
subtract(getIndividualPositive(user), getIndividualNegative(user));
individualNegative =
subtract(getIndividualNegative(user), getIndividualPositive(user));
//
// net negative permissions is individual negative permissions
// plus (group negative - individual positive).
//
temp1 = subtract(groupNegative, individualPositive);
Enumeration<Permission> netNegative = union(individualNegative, temp1);
return subtract(netPositive, netNegative);
}
/**
* This method checks whether or not the specified principal
* has the required permission. If permission is denied
* permission false is returned, a true value is returned otherwise.
* This method does not authenticate the principal. It presumes that
* the principal is a valid authenticated principal.
* @param principal the name of the authenticated principal
* @param permission the permission that the principal must have.
* @return true of the principal has the permission desired, false
* otherwise.
*/
public boolean checkPermission(Principal principal, Permission permission)
{
Enumeration<Permission> permSet = getPermissions(principal);
while (permSet.hasMoreElements()) {
Permission p = permSet.nextElement();
if (p.equals(permission))
return true;
}
return false;
}
/**
* returns an enumeration of the entries in this ACL.
*/
public synchronized Enumeration<AclEntry> entries() {
return new AclEnumerator(this,
allowedUsersTable, allowedGroupsTable,
deniedUsersTable, deniedGroupsTable);
}
/**
* return a stringified version of the
* ACL.
*/
public String toString() {
StringBuffer sb = new StringBuffer();
Enumeration<AclEntry> entries = entries();
while (entries.hasMoreElements()) {
AclEntry entry = entries.nextElement();
sb.append(entry.toString().trim());
sb.append("\n");
}
return sb.toString();
}
//
// Find the table that this entry belongs to. There are 4
// tables that are maintained. One each for postive and
// negative ACLs and one each for groups and users.
// This method figures out which
// table is the one that this AclEntry belongs to.
//
private Hashtable<Principal, AclEntry> findTable(AclEntry entry) {
Hashtable<Principal, AclEntry> aclTable = null;
Principal p = entry.getPrincipal();
if (p instanceof Group) {
if (entry.isNegative())
aclTable = deniedGroupsTable;
else
aclTable = allowedGroupsTable;
} else {
if (entry.isNegative())
aclTable = deniedUsersTable;
else
aclTable = allowedUsersTable;
}
return aclTable;
}
//
// returns the set e1 U e2.
//
private static Enumeration<Permission> union(Enumeration<Permission> e1,
Enumeration<Permission> e2) {
Vector<Permission> v = new Vector<>(20, 20);
while (e1.hasMoreElements())
v.addElement(e1.nextElement());
while (e2.hasMoreElements()) {
Permission o = e2.nextElement();
if (!v.contains(o))
v.addElement(o);
}
return v.elements();
}
//
// returns the set e1 - e2.
//
private Enumeration<Permission> subtract(Enumeration<Permission> e1,
Enumeration<Permission> e2) {
Vector<Permission> v = new Vector<>(20, 20);
while (e1.hasMoreElements())
v.addElement(e1.nextElement());
while (e2.hasMoreElements()) {
Permission o = e2.nextElement();
if (v.contains(o))
v.removeElement(o);
}
return v.elements();
}
private Enumeration<Permission> getGroupPositive(Principal user) {
Enumeration<Permission> groupPositive = zeroSet.elements();
Enumeration<Principal> e = allowedGroupsTable.keys();
while (e.hasMoreElements()) {
Group g = (Group)e.nextElement();
if (g.isMember(user)) {
AclEntry ae = allowedGroupsTable.get(g);
groupPositive = union(ae.permissions(), groupPositive);
}
}
return groupPositive;
}
private Enumeration<Permission> getGroupNegative(Principal user) {
Enumeration<Permission> groupNegative = zeroSet.elements();
Enumeration<Principal> e = deniedGroupsTable.keys();
while (e.hasMoreElements()) {
Group g = (Group)e.nextElement();
if (g.isMember(user)) {
AclEntry ae = deniedGroupsTable.get(g);
groupNegative = union(ae.permissions(), groupNegative);
}
}
return groupNegative;
}
private Enumeration<Permission> getIndividualPositive(Principal user) {
Enumeration<Permission> individualPositive = zeroSet.elements();
AclEntry ae = allowedUsersTable.get(user);
if (ae != null)
individualPositive = ae.permissions();
return individualPositive;
}
private Enumeration<Permission> getIndividualNegative(Principal user) {
Enumeration<Permission> individualNegative = zeroSet.elements();
AclEntry ae = deniedUsersTable.get(user);
if (ae != null)
individualNegative = ae.permissions();
return individualNegative;
}
}
final class AclEnumerator implements Enumeration<AclEntry> {
Acl acl;
Enumeration<AclEntry> u1, u2, g1, g2;
AclEnumerator(Acl acl, Hashtable<?,AclEntry> u1, Hashtable<?,AclEntry> g1,
Hashtable<?,AclEntry> u2, Hashtable<?,AclEntry> g2) {
this.acl = acl;
this.u1 = u1.elements();
this.u2 = u2.elements();
this.g1 = g1.elements();
this.g2 = g2.elements();
}
public boolean hasMoreElements() {
return (u1.hasMoreElements() ||
u2.hasMoreElements() ||
g1.hasMoreElements() ||
g2.hasMoreElements());
}
public AclEntry nextElement()
{
AclEntry o;
synchronized (acl) {
if (u1.hasMoreElements())
return u1.nextElement();
if (u2.hasMoreElements())
return u2.nextElement();
if (g1.hasMoreElements())
return g1.nextElement();
if (g2.hasMoreElements())
return g2.nextElement();
}
throw new NoSuchElementException("Acl Enumerator");
}
}