blob: 66c78f2775eda63860b0033692b3051414dac47e [file] [log] [blame]
/*
* Copyright 2003-2008 Sun Microsystems, Inc. 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. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.jmx.remote.security;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.Principal;
import java.security.PrivilegedAction;
import java.util.Collection;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import javax.management.MBeanServer;
import javax.security.auth.Subject;
/**
* <p>An object of this class implements the MBeanServerAccessController
* interface and, for each of its methods, calls an appropriate checking
* method and then forwards the request to a wrapped MBeanServer object.
* The checking method may throw a SecurityException if the operation is
* not allowed; in this case the request is not forwarded to the
* wrapped object.</p>
*
* <p>This class implements the {@link #checkRead()} and {@link #checkWrite()}
* methods based on an access level properties file containing username/access
* level pairs. The set of username/access level pairs is passed either as a
* filename which denotes a properties file on disk, or directly as an instance
* of the {@link Properties} class. In both cases, the name of each property
* represents a username, and the value of the property is the associated access
* level. Thus, any given username either does not exist in the properties or
* has exactly one access level. The same access level can be shared by several
* usernames.</p>
*
* <p>The supported access level values are <i>readonly</i> and
* <i>readwrite</i>.</p>
*/
public class MBeanServerFileAccessController
extends MBeanServerAccessController {
public static final String READONLY = "readonly";
public static final String READWRITE = "readwrite";
/**
* <p>Create a new MBeanServerAccessController that forwards all the
* MBeanServer requests to the MBeanServer set by invoking the {@link
* #setMBeanServer} method after doing access checks based on read and
* write permissions.</p>
*
* <p>This instance is initialized from the specified properties file.</p>
*
* @param accessFileName name of the file which denotes a properties
* file on disk containing the username/access level entries.
*
* @exception IOException if the file does not exist, is a
* directory rather than a regular file, or for some other
* reason cannot be opened for reading.
*
* @exception IllegalArgumentException if any of the supplied access
* level values differs from "readonly" or "readwrite".
*/
public MBeanServerFileAccessController(String accessFileName)
throws IOException {
super();
this.accessFileName = accessFileName;
props = propertiesFromFile(accessFileName);
checkValues(props);
}
/**
* <p>Create a new MBeanServerAccessController that forwards all the
* MBeanServer requests to <code>mbs</code> after doing access checks
* based on read and write permissions.</p>
*
* <p>This instance is initialized from the specified properties file.</p>
*
* @param accessFileName name of the file which denotes a properties
* file on disk containing the username/access level entries.
*
* @param mbs the MBeanServer object to which requests will be forwarded.
*
* @exception IOException if the file does not exist, is a
* directory rather than a regular file, or for some other
* reason cannot be opened for reading.
*
* @exception IllegalArgumentException if any of the supplied access
* level values differs from "readonly" or "readwrite".
*/
public MBeanServerFileAccessController(String accessFileName,
MBeanServer mbs)
throws IOException {
this(accessFileName);
setMBeanServer(mbs);
}
/**
* <p>Create a new MBeanServerAccessController that forwards all the
* MBeanServer requests to the MBeanServer set by invoking the {@link
* #setMBeanServer} method after doing access checks based on read and
* write permissions.</p>
*
* <p>This instance is initialized from the specified properties instance.
* This constructor makes a copy of the properties instance using its
* <code>clone</code> method and it is the copy that is consulted to check
* the username and access level of an incoming connection. The original
* properties object can be modified without affecting the copy. If the
* {@link #refresh} method is then called, the
* <code>MBeanServerFileAccessController</code> will make a new copy of the
* properties object at that time.</p>
*
* @param accessFileProps properties list containing the username/access
* level entries.
*
* @exception IllegalArgumentException if <code>accessFileProps</code> is
* <code>null</code> or if any of the supplied access level values differs
* from "readonly" or "readwrite".
*/
public MBeanServerFileAccessController(Properties accessFileProps)
throws IOException {
super();
if (accessFileProps == null)
throw new IllegalArgumentException("Null properties");
originalProps = accessFileProps;
props = (Properties) accessFileProps.clone();
checkValues(props);
}
/**
* <p>Create a new MBeanServerAccessController that forwards all the
* MBeanServer requests to the MBeanServer set by invoking the {@link
* #setMBeanServer} method after doing access checks based on read and
* write permissions.</p>
*
* <p>This instance is initialized from the specified properties instance.
* This constructor makes a copy of the properties instance using its
* <code>clone</code> method and it is the copy that is consulted to check
* the username and access level of an incoming connection. The original
* properties object can be modified without affecting the copy. If the
* {@link #refresh} method is then called, the
* <code>MBeanServerFileAccessController</code> will make a new copy of the
* properties object at that time.</p>
*
* @param accessFileProps properties list containing the username/access
* level entries.
*
* @param mbs the MBeanServer object to which requests will be forwarded.
*
* @exception IllegalArgumentException if <code>accessFileProps</code> is
* <code>null</code> or if any of the supplied access level values differs
* from "readonly" or "readwrite".
*/
public MBeanServerFileAccessController(Properties accessFileProps,
MBeanServer mbs)
throws IOException {
this(accessFileProps);
setMBeanServer(mbs);
}
/**
* Check if the caller can do read operations. This method does
* nothing if so, otherwise throws SecurityException.
*/
public void checkRead() {
checkAccessLevel(READONLY);
}
/**
* Check if the caller can do write operations. This method does
* nothing if so, otherwise throws SecurityException.
*/
public void checkWrite() {
checkAccessLevel(READWRITE);
}
/**
* <p>Refresh the set of username/access level entries.</p>
*
* <p>If this instance was created using the
* {@link #MBeanServerFileAccessController(String)} or
* {@link #MBeanServerFileAccessController(String,MBeanServer)}
* constructors to specify a file from which the entries are read,
* the file is re-read.</p>
*
* <p>If this instance was created using the
* {@link #MBeanServerFileAccessController(Properties)} or
* {@link #MBeanServerFileAccessController(Properties,MBeanServer)}
* constructors then a new copy of the <code>Properties</code> object
* is made.</p>
*
* @exception IOException if the file does not exist, is a
* directory rather than a regular file, or for some other
* reason cannot be opened for reading.
*
* @exception IllegalArgumentException if any of the supplied access
* level values differs from "readonly" or "readwrite".
*/
public void refresh() throws IOException {
synchronized (props) {
if (accessFileName == null)
props = (Properties) originalProps.clone();
else
props = propertiesFromFile(accessFileName);
checkValues(props);
}
}
private static Properties propertiesFromFile(String fname)
throws IOException {
FileInputStream fin = new FileInputStream(fname);
try {
Properties p = new Properties();
p.load(fin);
return p;
} finally {
fin.close();
}
}
private void checkAccessLevel(String accessLevel) {
final AccessControlContext acc = AccessController.getContext();
final Subject s =
AccessController.doPrivileged(new PrivilegedAction<Subject>() {
public Subject run() {
return Subject.getSubject(acc);
}
});
if (s == null) return; /* security has not been enabled */
final Set<Principal> principals = s.getPrincipals();
for (Principal p : principals) {
String grantedAccessLevel;
synchronized (props) {
grantedAccessLevel = props.getProperty(p.getName());
}
if (grantedAccessLevel != null) {
if (accessLevel.equals(READONLY) &&
(grantedAccessLevel.equals(READONLY) ||
grantedAccessLevel.equals(READWRITE)))
return;
if (accessLevel.equals(READWRITE) &&
grantedAccessLevel.equals(READWRITE))
return;
}
}
throw new SecurityException("Access denied! Invalid access level for " +
"requested MBeanServer operation.");
}
private void checkValues(Properties props) {
Collection<?> c = props.values();
for (Iterator<?> i = c.iterator(); i.hasNext(); ) {
final String accessLevel = (String) i.next();
if (!accessLevel.equals(READONLY) &&
!accessLevel.equals(READWRITE)) {
throw new IllegalArgumentException(
"Syntax error in access level entry [" + accessLevel + "]");
}
}
}
private Properties props;
private Properties originalProps;
private String accessFileName;
}