1. /*
  2. * @(#)MBeanServerFileAccessController.java 1.9 04/04/02
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.jmx.remote.security;
  8. import java.io.FileInputStream;
  9. import java.io.IOException;
  10. import java.security.AccessControlContext;
  11. import java.security.AccessController;
  12. import java.security.Principal;
  13. import java.security.PrivilegedAction;
  14. import java.util.Collection;
  15. import java.util.Iterator;
  16. import java.util.Properties;
  17. import java.util.Set;
  18. import javax.management.MBeanServer;
  19. import javax.security.auth.Subject;
  20. /**
  21. * <p>An object of this class implements the MBeanServerAccessController
  22. * interface and, for each of its methods, calls an appropriate checking
  23. * method and then forwards the request to a wrapped MBeanServer object.
  24. * The checking method may throw a SecurityException if the operation is
  25. * not allowed; in this case the request is not forwarded to the
  26. * wrapped object.</p>
  27. *
  28. * <p>This class implements the {@link #checkRead()} and {@link #checkWrite()}
  29. * methods based on an access level properties file containing username/access
  30. * level pairs. The set of username/access level pairs is passed either as a
  31. * filename which denotes a properties file on disk, or directly as an instance
  32. * of the {@link Properties} class. In both cases, the name of each property
  33. * represents a username, and the value of the property is the associated access
  34. * level. Thus, any given username either does not exist in the properties or
  35. * has exactly one access level. The same access level can be shared by several
  36. * usernames.</p>
  37. *
  38. * <p>The supported access level values are <i>readonly</i> and
  39. * <i>readwrite</i>.</p>
  40. */
  41. public class MBeanServerFileAccessController
  42. extends MBeanServerAccessController {
  43. public static final String READONLY = "readonly";
  44. public static final String READWRITE = "readwrite";
  45. /**
  46. * <p>Create a new MBeanServerAccessController that forwards all the
  47. * MBeanServer requests to the MBeanServer set by invoking the {@link
  48. * #setMBeanServer} method after doing access checks based on read and
  49. * write permissions.</p>
  50. *
  51. * <p>This instance is initialized from the specified properties file.</p>
  52. *
  53. * @param accessFileName name of the file which denotes a properties
  54. * file on disk containing the username/access level entries.
  55. *
  56. * @exception IOException if the file does not exist, is a
  57. * directory rather than a regular file, or for some other
  58. * reason cannot be opened for reading.
  59. *
  60. * @exception IllegalArgumentException if any of the supplied access
  61. * level values differs from "readonly" or "readwrite".
  62. */
  63. public MBeanServerFileAccessController(String accessFileName)
  64. throws IOException {
  65. super();
  66. this.accessFileName = accessFileName;
  67. props = propertiesFromFile(accessFileName);
  68. checkValues(props);
  69. }
  70. /**
  71. * <p>Create a new MBeanServerAccessController that forwards all the
  72. * MBeanServer requests to <code>mbs</code> after doing access checks
  73. * based on read and write permissions.</p>
  74. *
  75. * <p>This instance is initialized from the specified properties file.</p>
  76. *
  77. * @param accessFileName name of the file which denotes a properties
  78. * file on disk containing the username/access level entries.
  79. *
  80. * @param mbs the MBeanServer object to which requests will be forwarded.
  81. *
  82. * @exception IOException if the file does not exist, is a
  83. * directory rather than a regular file, or for some other
  84. * reason cannot be opened for reading.
  85. *
  86. * @exception IllegalArgumentException if any of the supplied access
  87. * level values differs from "readonly" or "readwrite".
  88. */
  89. public MBeanServerFileAccessController(String accessFileName,
  90. MBeanServer mbs)
  91. throws IOException {
  92. this(accessFileName);
  93. setMBeanServer(mbs);
  94. }
  95. /**
  96. * <p>Create a new MBeanServerAccessController that forwards all the
  97. * MBeanServer requests to the MBeanServer set by invoking the {@link
  98. * #setMBeanServer} method after doing access checks based on read and
  99. * write permissions.</p>
  100. *
  101. * <p>This instance is initialized from the specified properties instance.
  102. * This constructor makes a copy of the properties instance using its
  103. * <code>clone</code> method and it is the copy that is consulted to check
  104. * the username and access level of an incoming connection. The original
  105. * properties object can be modified without affecting the copy. If the
  106. * {@link #refresh} method is then called, the
  107. * <code>MBeanServerFileAccessController</code> will make a new copy of the
  108. * properties object at that time.</p>
  109. *
  110. * @param accessFileProps properties list containing the username/access
  111. * level entries.
  112. *
  113. * @exception IllegalArgumentException if <code>accessFileProps</code> is
  114. * <code>null</code> or if any of the supplied access level values differs
  115. * from "readonly" or "readwrite".
  116. */
  117. public MBeanServerFileAccessController(Properties accessFileProps)
  118. throws IOException {
  119. super();
  120. if (accessFileProps == null)
  121. throw new IllegalArgumentException("Null properties");
  122. originalProps = accessFileProps;
  123. props = (Properties) accessFileProps.clone();
  124. checkValues(props);
  125. }
  126. /**
  127. * <p>Create a new MBeanServerAccessController that forwards all the
  128. * MBeanServer requests to the MBeanServer set by invoking the {@link
  129. * #setMBeanServer} method after doing access checks based on read and
  130. * write permissions.</p>
  131. *
  132. * <p>This instance is initialized from the specified properties instance.
  133. * This constructor makes a copy of the properties instance using its
  134. * <code>clone</code> method and it is the copy that is consulted to check
  135. * the username and access level of an incoming connection. The original
  136. * properties object can be modified without affecting the copy. If the
  137. * {@link #refresh} method is then called, the
  138. * <code>MBeanServerFileAccessController</code> will make a new copy of the
  139. * properties object at that time.</p>
  140. *
  141. * @param accessFileProps properties list containing the username/access
  142. * level entries.
  143. *
  144. * @param mbs the MBeanServer object to which requests will be forwarded.
  145. *
  146. * @exception IllegalArgumentException if <code>accessFileProps</code> is
  147. * <code>null</code> or if any of the supplied access level values differs
  148. * from "readonly" or "readwrite".
  149. */
  150. public MBeanServerFileAccessController(Properties accessFileProps,
  151. MBeanServer mbs)
  152. throws IOException {
  153. this(accessFileProps);
  154. setMBeanServer(mbs);
  155. }
  156. /**
  157. * Check if the caller can do read operations. This method does
  158. * nothing if so, otherwise throws SecurityException.
  159. */
  160. public void checkRead() {
  161. checkAccessLevel(READONLY);
  162. }
  163. /**
  164. * Check if the caller can do write operations. This method does
  165. * nothing if so, otherwise throws SecurityException.
  166. */
  167. public void checkWrite() {
  168. checkAccessLevel(READWRITE);
  169. }
  170. /**
  171. * <p>Refresh the set of username/access level entries.</p>
  172. *
  173. * <p>If this instance was created using the
  174. * {@link #MBeanServerFileAccessController(String)} or
  175. * {@link #MBeanServerFileAccessController(String,MBeanServer)}
  176. * constructors to specify a file from which the entries are read,
  177. * the file is re-read.</p>
  178. *
  179. * <p>If this instance was created using the
  180. * {@link #MBeanServerFileAccessController(Properties)} or
  181. * {@link #MBeanServerFileAccessController(Properties,MBeanServer)}
  182. * constructors then a new copy of the <code>Properties</code> object
  183. * is made.</p>
  184. *
  185. * @exception IOException if the file does not exist, is a
  186. * directory rather than a regular file, or for some other
  187. * reason cannot be opened for reading.
  188. *
  189. * @exception IllegalArgumentException if any of the supplied access
  190. * level values differs from "readonly" or "readwrite".
  191. */
  192. public void refresh() throws IOException {
  193. synchronized (props) {
  194. if (accessFileName == null)
  195. props = (Properties) originalProps.clone();
  196. else
  197. props = propertiesFromFile(accessFileName);
  198. checkValues(props);
  199. }
  200. }
  201. private static Properties propertiesFromFile(String fname)
  202. throws IOException {
  203. FileInputStream fin = new FileInputStream(fname);
  204. Properties p = new Properties();
  205. p.load(fin);
  206. fin.close();
  207. return p;
  208. }
  209. private void checkAccessLevel(String accessLevel) {
  210. final AccessControlContext acc = AccessController.getContext();
  211. final Subject s = (Subject)
  212. AccessController.doPrivileged(new PrivilegedAction() {
  213. public Object run() {
  214. return Subject.getSubject(acc);
  215. }
  216. });
  217. if (s == null) return; /* security has not been enabled */
  218. final Set principals = s.getPrincipals();
  219. for (Iterator i = principals.iterator(); i.hasNext(); ) {
  220. final Principal p = (Principal) i.next();
  221. String grantedAccessLevel;
  222. synchronized (props) {
  223. grantedAccessLevel = props.getProperty(p.getName());
  224. }
  225. if (grantedAccessLevel != null) {
  226. if (accessLevel.equals(READONLY) &&
  227. (grantedAccessLevel.equals(READONLY) ||
  228. grantedAccessLevel.equals(READWRITE)))
  229. return;
  230. if (accessLevel.equals(READWRITE) &&
  231. grantedAccessLevel.equals(READWRITE))
  232. return;
  233. }
  234. }
  235. throw new SecurityException("Access denied! Invalid access level for " +
  236. "requested MBeanServer operation.");
  237. }
  238. private void checkValues(Properties props) {
  239. Collection c = props.values();
  240. for (Iterator i = c.iterator(); i.hasNext(); ) {
  241. final String accessLevel = (String) i.next();
  242. if (!accessLevel.equals(READONLY) &&
  243. !accessLevel.equals(READWRITE)) {
  244. throw new IllegalArgumentException(
  245. "Syntax error in access level entry [" + accessLevel + "]");
  246. }
  247. }
  248. }
  249. private Properties props;
  250. private Properties originalProps;
  251. private String accessFileName;
  252. }