blob: b8b012acbc194c871519d926bef029fcac802c19 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package com.sun.jmx.remote.security;
27
28import java.io.FileInputStream;
29import java.io.IOException;
30import java.security.AccessControlContext;
31import java.security.AccessController;
32import java.security.Principal;
33import java.security.PrivilegedAction;
34import java.util.Collection;
35import java.util.Iterator;
36import java.util.Properties;
37import java.util.Set;
38import javax.management.MBeanServer;
39import javax.security.auth.Subject;
40
41/**
42 * <p>An object of this class implements the MBeanServerAccessController
43 * interface and, for each of its methods, calls an appropriate checking
44 * method and then forwards the request to a wrapped MBeanServer object.
45 * The checking method may throw a SecurityException if the operation is
46 * not allowed; in this case the request is not forwarded to the
47 * wrapped object.</p>
48 *
49 * <p>This class implements the {@link #checkRead()} and {@link #checkWrite()}
50 * methods based on an access level properties file containing username/access
51 * level pairs. The set of username/access level pairs is passed either as a
52 * filename which denotes a properties file on disk, or directly as an instance
53 * of the {@link Properties} class. In both cases, the name of each property
54 * represents a username, and the value of the property is the associated access
55 * level. Thus, any given username either does not exist in the properties or
56 * has exactly one access level. The same access level can be shared by several
57 * usernames.</p>
58 *
59 * <p>The supported access level values are <i>readonly</i> and
60 * <i>readwrite</i>.</p>
61 */
62public class MBeanServerFileAccessController
63 extends MBeanServerAccessController {
64
65 public static final String READONLY = "readonly";
66 public static final String READWRITE = "readwrite";
67
68 /**
69 * <p>Create a new MBeanServerAccessController that forwards all the
70 * MBeanServer requests to the MBeanServer set by invoking the {@link
71 * #setMBeanServer} method after doing access checks based on read and
72 * write permissions.</p>
73 *
74 * <p>This instance is initialized from the specified properties file.</p>
75 *
76 * @param accessFileName name of the file which denotes a properties
77 * file on disk containing the username/access level entries.
78 *
79 * @exception IOException if the file does not exist, is a
80 * directory rather than a regular file, or for some other
81 * reason cannot be opened for reading.
82 *
83 * @exception IllegalArgumentException if any of the supplied access
84 * level values differs from "readonly" or "readwrite".
85 */
86 public MBeanServerFileAccessController(String accessFileName)
87 throws IOException {
88 super();
89 this.accessFileName = accessFileName;
90 props = propertiesFromFile(accessFileName);
91 checkValues(props);
92 }
93
94 /**
95 * <p>Create a new MBeanServerAccessController that forwards all the
96 * MBeanServer requests to <code>mbs</code> after doing access checks
97 * based on read and write permissions.</p>
98 *
99 * <p>This instance is initialized from the specified properties file.</p>
100 *
101 * @param accessFileName name of the file which denotes a properties
102 * file on disk containing the username/access level entries.
103 *
104 * @param mbs the MBeanServer object to which requests will be forwarded.
105 *
106 * @exception IOException if the file does not exist, is a
107 * directory rather than a regular file, or for some other
108 * reason cannot be opened for reading.
109 *
110 * @exception IllegalArgumentException if any of the supplied access
111 * level values differs from "readonly" or "readwrite".
112 */
113 public MBeanServerFileAccessController(String accessFileName,
114 MBeanServer mbs)
115 throws IOException {
116 this(accessFileName);
117 setMBeanServer(mbs);
118 }
119
120 /**
121 * <p>Create a new MBeanServerAccessController that forwards all the
122 * MBeanServer requests to the MBeanServer set by invoking the {@link
123 * #setMBeanServer} method after doing access checks based on read and
124 * write permissions.</p>
125 *
126 * <p>This instance is initialized from the specified properties instance.
127 * This constructor makes a copy of the properties instance using its
128 * <code>clone</code> method and it is the copy that is consulted to check
129 * the username and access level of an incoming connection. The original
130 * properties object can be modified without affecting the copy. If the
131 * {@link #refresh} method is then called, the
132 * <code>MBeanServerFileAccessController</code> will make a new copy of the
133 * properties object at that time.</p>
134 *
135 * @param accessFileProps properties list containing the username/access
136 * level entries.
137 *
138 * @exception IllegalArgumentException if <code>accessFileProps</code> is
139 * <code>null</code> or if any of the supplied access level values differs
140 * from "readonly" or "readwrite".
141 */
142 public MBeanServerFileAccessController(Properties accessFileProps)
143 throws IOException {
144 super();
145 if (accessFileProps == null)
146 throw new IllegalArgumentException("Null properties");
147 originalProps = accessFileProps;
148 props = (Properties) accessFileProps.clone();
149 checkValues(props);
150 }
151
152 /**
153 * <p>Create a new MBeanServerAccessController that forwards all the
154 * MBeanServer requests to the MBeanServer set by invoking the {@link
155 * #setMBeanServer} method after doing access checks based on read and
156 * write permissions.</p>
157 *
158 * <p>This instance is initialized from the specified properties instance.
159 * This constructor makes a copy of the properties instance using its
160 * <code>clone</code> method and it is the copy that is consulted to check
161 * the username and access level of an incoming connection. The original
162 * properties object can be modified without affecting the copy. If the
163 * {@link #refresh} method is then called, the
164 * <code>MBeanServerFileAccessController</code> will make a new copy of the
165 * properties object at that time.</p>
166 *
167 * @param accessFileProps properties list containing the username/access
168 * level entries.
169 *
170 * @param mbs the MBeanServer object to which requests will be forwarded.
171 *
172 * @exception IllegalArgumentException if <code>accessFileProps</code> is
173 * <code>null</code> or if any of the supplied access level values differs
174 * from "readonly" or "readwrite".
175 */
176 public MBeanServerFileAccessController(Properties accessFileProps,
177 MBeanServer mbs)
178 throws IOException {
179 this(accessFileProps);
180 setMBeanServer(mbs);
181 }
182
183 /**
184 * Check if the caller can do read operations. This method does
185 * nothing if so, otherwise throws SecurityException.
186 */
187 public void checkRead() {
188 checkAccessLevel(READONLY);
189 }
190
191 /**
192 * Check if the caller can do write operations. This method does
193 * nothing if so, otherwise throws SecurityException.
194 */
195 public void checkWrite() {
196 checkAccessLevel(READWRITE);
197 }
198
199 /**
200 * <p>Refresh the set of username/access level entries.</p>
201 *
202 * <p>If this instance was created using the
203 * {@link #MBeanServerFileAccessController(String)} or
204 * {@link #MBeanServerFileAccessController(String,MBeanServer)}
205 * constructors to specify a file from which the entries are read,
206 * the file is re-read.</p>
207 *
208 * <p>If this instance was created using the
209 * {@link #MBeanServerFileAccessController(Properties)} or
210 * {@link #MBeanServerFileAccessController(Properties,MBeanServer)}
211 * constructors then a new copy of the <code>Properties</code> object
212 * is made.</p>
213 *
214 * @exception IOException if the file does not exist, is a
215 * directory rather than a regular file, or for some other
216 * reason cannot be opened for reading.
217 *
218 * @exception IllegalArgumentException if any of the supplied access
219 * level values differs from "readonly" or "readwrite".
220 */
221 public void refresh() throws IOException {
222 synchronized (props) {
223 if (accessFileName == null)
224 props = (Properties) originalProps.clone();
225 else
226 props = propertiesFromFile(accessFileName);
227 checkValues(props);
228 }
229 }
230
231 private static Properties propertiesFromFile(String fname)
232 throws IOException {
233 FileInputStream fin = new FileInputStream(fname);
234 Properties p = new Properties();
235 p.load(fin);
236 fin.close();
237 return p;
238 }
239
240 private void checkAccessLevel(String accessLevel) {
241 final AccessControlContext acc = AccessController.getContext();
242 final Subject s =
243 AccessController.doPrivileged(new PrivilegedAction<Subject>() {
244 public Subject run() {
245 return Subject.getSubject(acc);
246 }
247 });
248 if (s == null) return; /* security has not been enabled */
249 final Set principals = s.getPrincipals();
250 for (Iterator i = principals.iterator(); i.hasNext(); ) {
251 final Principal p = (Principal) i.next();
252 String grantedAccessLevel;
253 synchronized (props) {
254 grantedAccessLevel = props.getProperty(p.getName());
255 }
256 if (grantedAccessLevel != null) {
257 if (accessLevel.equals(READONLY) &&
258 (grantedAccessLevel.equals(READONLY) ||
259 grantedAccessLevel.equals(READWRITE)))
260 return;
261 if (accessLevel.equals(READWRITE) &&
262 grantedAccessLevel.equals(READWRITE))
263 return;
264 }
265 }
266 throw new SecurityException("Access denied! Invalid access level for " +
267 "requested MBeanServer operation.");
268 }
269
270 private void checkValues(Properties props) {
271 Collection c = props.values();
272 for (Iterator i = c.iterator(); i.hasNext(); ) {
273 final String accessLevel = (String) i.next();
274 if (!accessLevel.equals(READONLY) &&
275 !accessLevel.equals(READWRITE)) {
276 throw new IllegalArgumentException(
277 "Syntax error in access level entry [" + accessLevel + "]");
278 }
279 }
280 }
281
282 private Properties props;
283 private Properties originalProps;
284 private String accessFileName;
285}