001: package org.tanukisoftware.wrapper.security;
002:
003: /*
004: * Copyright (c) 1999, 2006 Tanuki Software Inc.
005: *
006: * Permission is hereby granted, free of charge, to any person
007: * obtaining a copy of the Java Service Wrapper and associated
008: * documentation files (the "Software"), to deal in the Software
009: * without restriction, including without limitation the rights
010: * to use, copy, modify, merge, publish, distribute, sub-license,
011: * and/or sell copies of the Software, and to permit persons to
012: * whom the Software is furnished to do so, subject to the
013: * following conditions:
014: *
015: * The above copyright notice and this permission notice shall be
016: * included in all copies or substantial portions of the Software.
017: *
018: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
019: * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
020: * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
021: * NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
022: * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
023: * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
024: * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
025: * OTHER DEALINGS IN THE SOFTWARE.
026: */
027:
028: import java.security.Permission;
029: import java.security.PermissionCollection;
030: import java.util.Enumeration;
031: import java.util.Vector;
032: import java.util.StringTokenizer;
033:
034: /**
035: * WrapperServicePermissions are used to grant the right to start, stop,
036: * pause, continue, interrogate, or send custom codes to other services
037: * running on a Windows system.
038: * <p>
039: * These permissions are inherently quite dangerous so great care should be
040: * taken when granting them. When doing so, try to only grant permission to
041: * those services which really need to be controlled.
042: * <p>
043: * The following are examples of how to specify the permission within a policy
044: * file.
045: * <pre>
046: * grant codeBase "file:../lib/-" {
047: * // Grant various permissions to a specific service.
048: * permission org.tanukisoftware.wrapper.security.WrapperServicePermission "myservice", "interrogate";
049: * permission org.tanukisoftware.wrapper.security.WrapperServicePermission "myservice", "interrogate,start,stop";
050: * permission org.tanukisoftware.wrapper.security.WrapperServicePermission "myservice", "userCode";
051: * permission org.tanukisoftware.wrapper.security.WrapperServicePermission "myservice", "*";
052: *
053: * // Grant various permissions to any service starting with "my".
054: * permission org.tanukisoftware.wrapper.security.WrapperServicePermission "my*", "*";
055: *
056: * // Let the calling code do anything to any service on the system
057: * permission org.tanukisoftware.wrapper.security.WrapperServicePermission "*", "*";
058: * permission org.tanukisoftware.wrapper.security.WrapperServicePermission "*";
059: * };
060: * </pre>
061: * <p>
062: * Possible actions include the following:
063: * <table border='1' cellpadding='2' cellspacing='0'>
064: * <tr>
065: * <th>Permission Action Name</th>
066: * <th>What the Permission Allows</th>
067: * <th>Risks of Allowing this Permission</th>
068: * </tr>
069: *
070: * <tr>
071: * <td>start</td>
072: * <td>Start a service which is installed but has not been started.</td>
073: * <td>Malicious code could potentially start any service that is not currently running.
074: * This includes services which were previously stopped or that are configured to be
075: * started manually. Many Windows systems have several services stopped by default
076: * because of the security hazards that they pose. Starting such services could open
077: * the system up to attacks related to that service.</td>
078: * </tr>
079: *
080: * <tr>
081: * <td>stop</td>
082: * <td>Stop a service which is currently running.</td>
083: * <td>Malicious code could potentially stop running service. This could result in a
084: * denial of service attack if the service is a web or database server. Or it
085: * result in more dangerous attacks if the service is a firewall or virus scanner.
086: * </td>
087: * </tr>
088: *
089: * <tr>
090: * <td>pause</td>
091: * <td>Pause a service which is currently running.</td>
092: * <td>Malicious code could potentially pause running service. This could result in a
093: * denial of service attack if the service is a web or database server. Or it
094: * result in more dangerous attacks if the service is a firewall or virus scanner.
095: * </td>
096: * </tr>
097: *
098: * <tr>
099: * <td>continue</td>
100: * <td>Continue a service which was previously paused.</td>
101: * <td>Malicious code could resume services which had been paused for a good reason.</td>
102: * </tr>
103: *
104: * <tr>
105: * <td>interrogate</td>
106: * <td>Interrogate a service as to its current state.</td>
107: * <td>Malicious code learn a lot about a system and its weakness by probing which
108: * services are currently running.</td>
109: * </tr>
110: *
111: * <tr>
112: * <td>userCode</td>
113: * <td>Send any custom user code to a service.</td>
114: * <td>The danger of this action depends on whether or not the service understands
115: * custom user codes, and what it does with them. This could potentially be a
116: * very dangerous permission to grant.</td>
117: * </tr>
118: * </table>
119: *
120: * @author Leif Mortenson <leif@tanukisoftware.com>
121: */
122: public class WrapperServicePermission extends Permission {
123: public static String ACTION_START = "start";
124: public static String ACTION_STOP = "stop";
125: public static String ACTION_PAUSE = "pause";
126: public static String ACTION_CONTINUE = "continue";
127: public static String ACTION_INTERROGATE = "interrogate";
128: public static String ACTION_USER_CODE = "userCode";
129:
130: private static int MASK_START = 1;
131: private static int MASK_STOP = 2;
132: private static int MASK_PAUSE = 4;
133: private static int MASK_CONTINUE = 8;
134: private static int MASK_INTERROGATE = 16;
135: private static int MASK_USER_CODE = 32;
136: private static int MASK_ALL = MASK_START | MASK_STOP | MASK_PAUSE
137: | MASK_CONTINUE | MASK_INTERROGATE | MASK_USER_CODE;
138:
139: private int m_actionMask;
140:
141: /*---------------------------------------------------------------
142: * Constructors
143: *-------------------------------------------------------------*/
144: /**
145: * Creates a new WrapperServicePermission for the specified service.
146: *
147: * @param serviceName The name of the service whose access is being
148: * controlled.
149: * @param actions The action or actions to be performed.
150: */
151: public WrapperServicePermission(String serviceName, String actions) {
152: super (serviceName);
153: m_actionMask = buildActionMask(actions);
154: }
155:
156: /**
157: * Creates a new WrapperServicePermission for the specified service.
158: * This version of the constructor grants all actions.
159: *
160: * @param serviceName The name of the service whose access is being
161: * controlled.
162: */
163: public WrapperServicePermission(String serviceName) {
164: this (serviceName, "*");
165: }
166:
167: /*---------------------------------------------------------------
168: * Permission Methods
169: *-------------------------------------------------------------*/
170: /**
171: * Checks two Permission objects for equality.
172: * <p>
173: * Do not use the equals method for making access control decisions; use
174: * the implies method.
175: *
176: * @param obj The object we are testing for equality with this object.
177: *
178: * @return True if both Permission objects are equivalent.
179: */
180: public boolean equals(Object obj) {
181: if (obj == this ) {
182: return true;
183: }
184:
185: if (!(obj instanceof WrapperServicePermission)) {
186: return false;
187: }
188:
189: WrapperServicePermission wsp = (WrapperServicePermission) obj;
190:
191: return (m_actionMask == wsp.m_actionMask)
192: && getName().equals(wsp.getName());
193: }
194:
195: /**
196: * Return the canonical string representation of the actions.
197: * Always returns present actions in the following order:
198: * start, stop, pause, continue, interrogate. userCode.
199: *
200: * @return the canonical string representation of the actions.
201: */
202: public String getActions() {
203: StringBuffer sb = new StringBuffer();
204: boolean first = true;
205:
206: if ((m_actionMask & MASK_START) != 0) {
207: if (first) {
208: sb.append(',');
209: } else {
210: first = false;
211: }
212: sb.append(ACTION_START);
213: }
214: if ((m_actionMask & MASK_STOP) != 0) {
215: if (first) {
216: sb.append(',');
217: } else {
218: first = false;
219: }
220: sb.append(ACTION_STOP);
221: }
222: if ((m_actionMask & MASK_PAUSE) != 0) {
223: if (first) {
224: sb.append(',');
225: } else {
226: first = false;
227: }
228: sb.append(ACTION_CONTINUE);
229: }
230: if ((m_actionMask & MASK_CONTINUE) != 0) {
231: if (first) {
232: sb.append(',');
233: } else {
234: first = false;
235: }
236: sb.append(ACTION_CONTINUE);
237: }
238: if ((m_actionMask & MASK_INTERROGATE) != 0) {
239: if (first) {
240: sb.append(',');
241: } else {
242: first = false;
243: }
244: sb.append(ACTION_INTERROGATE);
245: }
246: if ((m_actionMask & MASK_USER_CODE) != 0) {
247: if (first) {
248: sb.append(',');
249: } else {
250: first = false;
251: }
252: sb.append(ACTION_USER_CODE);
253: }
254:
255: return sb.toString();
256: }
257:
258: /**
259: * Checks if this WrapperServicePermission object "implies" the
260: * specified permission.
261: * <P>
262: * More specifically, this method returns true if:<p>
263: * <ul>
264: * <li><i>p2</i> is an instanceof FilePermission,<p>
265: * <li><i>p2</i>'s actions are a proper subset of this object's actions,
266: * and<p>
267: * <li><i>p2</i>'s service name is implied by this object's service name.
268: * For example, "MyApp*" implies "MyApp".
269: * </ul>
270: *
271: * @param p2 the permission to check against.
272: *
273: * @return true if the specified permission is implied by this object,
274: */
275: public boolean implies(Permission p2) {
276: if (!(p2 instanceof WrapperServicePermission)) {
277: return false;
278: }
279:
280: WrapperServicePermission wsp = (WrapperServicePermission) p2;
281:
282: // we get the effective mask. i.e., the "and" of this and that.
283: // They must be equal to that.mask for implies to return true.
284:
285: return ((m_actionMask & wsp.m_actionMask) == wsp.m_actionMask)
286: && impliesIgnoreActionMask(wsp);
287: }
288:
289: /**
290: * Returns an custom WSCollection implementation of a PermissionCollection.
291: */
292: public PermissionCollection newPermissionCollection() {
293: return new WSCollection();
294: }
295:
296: /**
297: * Returns the hash code value for this object.
298: *
299: * @return A hash code value for this object.
300: */
301: public int hashCode() {
302: return getName().hashCode();
303: }
304:
305: /*---------------------------------------------------------------
306: * Methods
307: *-------------------------------------------------------------*/
308: /**
309: * Returns the action mask of the Permission.
310: */
311: int getActionMask() {
312: return m_actionMask;
313: }
314:
315: /**
316: * Tests whether this permissions implies another without taking the
317: * action mask into account.
318: */
319: boolean impliesIgnoreActionMask(WrapperServicePermission p2) {
320: if (getName().equals(p2.getName())) {
321: return true;
322: }
323:
324: if (p2.getName().endsWith("*")) {
325: if (getName().startsWith(
326: p2.getName()
327: .substring(0, p2.getName().length() - 1))) {
328: return true;
329: }
330: }
331: return false;
332: }
333:
334: /**
335: * Builds an action mask given a comma separated list of actions.
336: */
337: private int buildActionMask(String actions) {
338: // Check for the constants first as they are used internally.
339: if (actions == ACTION_START) {
340: return MASK_START;
341: } else if (actions == ACTION_STOP) {
342: return MASK_STOP;
343: } else if (actions == ACTION_PAUSE) {
344: return MASK_PAUSE;
345: } else if (actions == ACTION_CONTINUE) {
346: return MASK_CONTINUE;
347: } else if (actions == ACTION_INTERROGATE) {
348: return MASK_INTERROGATE;
349: } else if (actions == ACTION_USER_CODE) {
350: return MASK_USER_CODE;
351: } else if (actions.equals("*")) {
352: return MASK_ALL;
353: }
354:
355: int mask = 0;
356: StringTokenizer st = new StringTokenizer(actions, ",");
357: while (st.hasMoreTokens()) {
358: String action = st.nextToken();
359: if (action.equals(ACTION_START)) {
360: mask |= MASK_START;
361: } else if (action.equals(ACTION_STOP)) {
362: mask |= MASK_STOP;
363: } else if (action.equals(ACTION_PAUSE)) {
364: mask |= MASK_PAUSE;
365: } else if (action.equals(ACTION_CONTINUE)) {
366: mask |= MASK_CONTINUE;
367: } else if (action.equals(ACTION_INTERROGATE)) {
368: mask |= MASK_INTERROGATE;
369: } else if (action.equals(ACTION_USER_CODE)) {
370: mask |= MASK_USER_CODE;
371: } else {
372: throw new IllegalArgumentException(
373: "Invalid permission action: \"" + action + "\"");
374: }
375: }
376:
377: return mask;
378: }
379: }
380:
381: final class WSCollection extends PermissionCollection {
382: private Vector m_permissions = new Vector();
383:
384: /*---------------------------------------------------------------
385: * Constructors
386: *-------------------------------------------------------------*/
387: /**
388: * Creates an empty WSCollection.
389: */
390: public WSCollection() {
391: }
392:
393: /*---------------------------------------------------------------
394: * Methods
395: *-------------------------------------------------------------*/
396: /**
397: * Adds a permission to the FilePermissions. The key for the hash is
398: * permission.path.
399: *
400: * @param permission the Permission object to add.
401: *
402: * @exception IllegalArgumentException - if the permission is not a
403: * FilePermission
404: *
405: * @exception SecurityException - if this FilePermissionCollection object
406: * has been marked readonly
407: */
408: public void add(Permission permission) {
409: if (!(permission instanceof WrapperServicePermission)) {
410: throw new IllegalArgumentException("invalid permission: "
411: + permission);
412: }
413:
414: if (isReadOnly()) {
415: throw new SecurityException("Collection is read-only.");
416: }
417:
418: m_permissions.add(permission);
419: }
420:
421: /**
422: * Check and see if this set of permissions implies the permissions
423: * expressed in "permission".
424: *
425: * @param permission the Permission object to compare
426: *
427: * @return true if "permission" is a proper subset of a permission in
428: * the set, false if not.
429: */
430: public boolean implies(Permission permission) {
431: if (!(permission instanceof WrapperServicePermission)) {
432: return false;
433: }
434:
435: WrapperServicePermission wsp = (WrapperServicePermission) permission;
436:
437: int desiredMask = wsp.getActionMask();
438: int pendingMask = desiredMask;
439: int foundMask = 0;
440:
441: for (Enumeration en = m_permissions.elements(); en
442: .hasMoreElements();) {
443: WrapperServicePermission p2 = (WrapperServicePermission) en
444: .nextElement();
445: if ((pendingMask & p2.getActionMask()) != 0) {
446: // This permission has one or more actions that we need.
447: if (wsp.impliesIgnoreActionMask(p2)) {
448: foundMask |= desiredMask & p2.getActionMask();
449: if (foundMask == desiredMask) {
450: return true;
451: }
452: pendingMask = desiredMask ^ foundMask;
453: }
454: }
455: }
456:
457: return false;
458: }
459:
460: /**
461: * Returns an enumeration of all the WrapperServicePermission
462: * objects in the container.
463: *
464: * @return An enumeration of all the WrapperServicePermission
465: * objects.
466: */
467: public Enumeration elements() {
468: return m_permissions.elements();
469: }
470: }
|