1 /*
   2  * Copyright (c) 2002, 2014, Oracle and/or its affiliates. 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.management;
  27 
  28 import java.io.IOException;
  29 import java.io.ObjectInputStream;
  30 import java.security.Permission;
  31 
  32 /**
  33  * <p>Permission controlling access to MBeanServer operations.  If a
  34  * security manager has been set using {@link
  35  * System#setSecurityManager}, most operations on the MBean Server
  36  * require that the caller's permissions imply an MBeanPermission
  37  * appropriate for the operation.  This is described in detail in the
  38  * documentation for the {@link MBeanServer} interface.</p>
  39  *
  40  * <p>As with other {@link Permission} objects, an MBeanPermission can
  41  * represent either a permission that you <em>have</em> or a
  42  * permission that you <em>need</em>.  When a sensitive operation is
  43  * being checked for permission, an MBeanPermission is constructed
  44  * representing the permission you need.  The operation is only
  45  * allowed if the permissions you have {@linkplain #implies imply} the
  46  * permission you need.</p>
  47  *
  48  * <p>An MBeanPermission contains four items of information:</p>
  49  *
  50  * <ul>
  51  *
  52  * <li><p>The <em>action</em>.  For a permission you need,
  53  * this is one of the actions in the list <a
  54  * href="#action-list">below</a>.  For a permission you have, this is
  55  * a comma-separated list of those actions, or <code>*</code>,
  56  * representing all actions.</p>
  57  *
  58  * <p>The action is returned by {@link #getActions()}.</p>
  59  *
  60  * <li><p>The <em>class name</em>.</p>
  61  *
  62  * <p>For a permission you need, this is the class name of an MBean
  63  * you are accessing, as returned by {@link
  64  * MBeanServer#getMBeanInfo(ObjectName)
  65  * MBeanServer.getMBeanInfo(name)}.{@link MBeanInfo#getClassName()
  66  * getClassName()}.  Certain operations do not reference a class name,
  67  * in which case the class name is null.</p>
  68  *
  69  * <p>For a permission you have, this is either empty or a <em>class
  70  * name pattern</em>.  A class name pattern is a string following the
  71  * Java conventions for dot-separated class names.  It may end with
  72  * "<code>.*</code>" meaning that the permission grants access to any
  73  * class that begins with the string preceding "<code>.*</code>".  For
  74  * instance, "<code>javax.management.*</code>" grants access to
  75  * <code>javax.management.MBeanServerDelegate</code> and
  76  * <code>javax.management.timer.Timer</code>, among other classes.</p>
  77  *
  78  * <p>A class name pattern can also be empty or the single character
  79  * "<code>*</code>", both of which grant access to any class.</p>
  80  *
  81  * <li><p>The <em>member</em>.</p>
  82  *
  83  * <p>For a permission you need, this is the name of the attribute or
  84  * operation you are accessing.  For operations that do not reference
  85  * an attribute or operation, the member is null.</p>
  86  *
  87  * <p>For a permission you have, this is either the name of an attribute
  88  * or operation you can access, or it is empty or the single character
  89  * "<code>*</code>", both of which grant access to any member.</p>
  90  *
  91  * <li id="MBeanName"><p>The <em>object name</em>.</p>
  92  *
  93  * <p>For a permission you need, this is the {@link ObjectName} of the
  94  * MBean you are accessing.  For operations that do not reference a
  95  * single MBean, it is null.  It is never an object name pattern.</p>
  96  *
  97  * <p>For a permission you have, this is the {@link ObjectName} of the
  98  * MBean or MBeans you can access.  It may be an object name pattern
  99  * to grant access to all MBeans whose names match the pattern.  It
 100  * may also be empty, which grants access to all MBeans whatever their
 101  * name.</p>
 102  *
 103  * </ul>
 104  *
 105  * <p>If you have an MBeanPermission, it allows operations only if all
 106  * four of the items match.</p>
 107  *
 108  * <p>The class name, member, and object name can be written together
 109  * as a single string, which is the <em>name</em> of this permission.
 110  * The name of the permission is the string returned by {@link
 111  * Permission#getName() getName()}.  The format of the string is:</p>
 112  *
 113  * <blockquote>
 114  * <code>className#member[objectName]</code>
 115  * </blockquote>
 116  *
 117  * <p>The object name is written using the usual syntax for {@link
 118  * ObjectName}.  It may contain any legal characters, including
 119  * <code>]</code>.  It is terminated by a <code>]</code> character
 120  * that is the last character in the string.</p>
 121  *
 122  * <p>One or more of the <code>className</code>, <code>member</code>,
 123  * or <code>objectName</code> may be omitted.  If the
 124  * <code>member</code> is omitted, the <code>#</code> may be too (but
 125  * does not have to be).  If the <code>objectName</code> is omitted,
 126  * the <code>[]</code> may be too (but does not have to be).  It is
 127  * not legal to omit all three items, that is to have a <em>name</em>
 128  * that is the empty string.</p>
 129  *
 130  * <p>One or more of the <code>className</code>, <code>member</code>,
 131  * or <code>objectName</code> may be the character "<code>-</code>",
 132  * which is equivalent to a null value.  A null value is implied by
 133  * any value (including another null value) but does not imply any
 134  * other value.</p>
 135  *
 136  * <p><a name="action-list">The possible actions are these:</a></p>
 137  *
 138  * <ul>
 139  * <li>addNotificationListener</li>
 140  * <li>getAttribute</li>
 141  * <li>getClassLoader</li>
 142  * <li>getClassLoaderFor</li>
 143  * <li>getClassLoaderRepository</li>
 144  * <li>getDomains</li>
 145  * <li>getMBeanInfo</li>
 146  * <li>getObjectInstance</li>
 147  * <li>instantiate</li>
 148  * <li>invoke</li>
 149  * <li>isInstanceOf</li>
 150  * <li>queryMBeans</li>
 151  * <li>queryNames</li>
 152  * <li>registerMBean</li>
 153  * <li>removeNotificationListener</li>
 154  * <li>setAttribute</li>
 155  * <li>unregisterMBean</li>
 156  * </ul>
 157  *
 158  * <p>In a comma-separated list of actions, spaces are allowed before
 159  * and after each action.
 160  *
 161  * @since 1.5
 162  */
 163 public class MBeanPermission extends Permission {
 164 
 165     private static final long serialVersionUID = -2416928705275160661L;
 166 
 167     /**
 168      * Actions list.
 169      */
 170     private static final int AddNotificationListener    = 0x00001;
 171     private static final int GetAttribute               = 0x00002;
 172     private static final int GetClassLoader             = 0x00004;
 173     private static final int GetClassLoaderFor          = 0x00008;
 174     private static final int GetClassLoaderRepository   = 0x00010;
 175     private static final int GetDomains                 = 0x00020;
 176     private static final int GetMBeanInfo               = 0x00040;
 177     private static final int GetObjectInstance          = 0x00080;
 178     private static final int Instantiate                = 0x00100;
 179     private static final int Invoke                     = 0x00200;
 180     private static final int IsInstanceOf               = 0x00400;
 181     private static final int QueryMBeans                = 0x00800;
 182     private static final int QueryNames                 = 0x01000;
 183     private static final int RegisterMBean              = 0x02000;
 184     private static final int RemoveNotificationListener = 0x04000;
 185     private static final int SetAttribute               = 0x08000;
 186     private static final int UnregisterMBean            = 0x10000;
 187 
 188     /**
 189      * No actions.
 190      */
 191     private static final int NONE = 0x00000;
 192 
 193     /**
 194      * All actions.
 195      */
 196     private static final int ALL =
 197         AddNotificationListener    |
 198         GetAttribute               |
 199         GetClassLoader             |
 200         GetClassLoaderFor          |
 201         GetClassLoaderRepository   |
 202         GetDomains                 |
 203         GetMBeanInfo               |
 204         GetObjectInstance          |
 205         Instantiate                |
 206         Invoke                     |
 207         IsInstanceOf               |
 208         QueryMBeans                |
 209         QueryNames                 |
 210         RegisterMBean              |
 211         RemoveNotificationListener |
 212         SetAttribute               |
 213         UnregisterMBean;
 214 
 215     /**
 216      * The actions string.
 217      */
 218     private String actions;
 219 
 220     /**
 221      * The actions mask.
 222      */
 223     private transient int mask;
 224 
 225     /**
 226      * The classname prefix that must match.  If null, is implied by any
 227      * classNamePrefix but does not imply any non-null classNamePrefix.
 228      */
 229     private transient String classNamePrefix;
 230 
 231     /**
 232      * True if classNamePrefix must match exactly.  Otherwise, the
 233      * className being matched must start with classNamePrefix.
 234      */
 235     private transient boolean classNameExactMatch;
 236 
 237     /**
 238      * The member that must match.  If null, is implied by any member
 239      * but does not imply any non-null member.
 240      */
 241     private transient String member;
 242 
 243     /**
 244      * The objectName that must match.  If null, is implied by any
 245      * objectName but does not imply any non-null objectName.
 246      */
 247     private transient ObjectName objectName;
 248 
 249     /**
 250      * Parse <code>actions</code> parameter.
 251      */
 252     private void parseActions() {
 253 
 254         int mask;
 255 
 256         if (actions == null)
 257             throw new IllegalArgumentException("MBeanPermission: " +
 258                                                "actions can't be null");
 259         if (actions.equals(""))
 260             throw new IllegalArgumentException("MBeanPermission: " +
 261                                                "actions can't be empty");
 262 
 263         mask = getMask(actions);
 264 
 265         if ((mask & ALL) != mask)
 266             throw new IllegalArgumentException("Invalid actions mask");
 267         if (mask == NONE)
 268             throw new IllegalArgumentException("Invalid actions mask");
 269         this.mask = mask;
 270     }
 271 
 272     /**
 273      * Parse <code>name</code> parameter.
 274      */
 275     private void parseName() {
 276         String name = getName();
 277 
 278         if (name == null)
 279             throw new IllegalArgumentException("MBeanPermission name " +
 280                                                "cannot be null");
 281 
 282         if (name.equals(""))
 283             throw new IllegalArgumentException("MBeanPermission name " +
 284                                                "cannot be empty");
 285 
 286         /* The name looks like "class#member[objectname]".  We subtract
 287            elements from the right as we parse, so after parsing the
 288            objectname we have "class#member" and after parsing the
 289            member we have "class".  Each element is optional.  */
 290 
 291         // Parse ObjectName
 292 
 293         int openingBracket = name.indexOf('[');
 294         if (openingBracket == -1) {
 295             // If "[on]" missing then ObjectName("*:*")
 296             //
 297             objectName = ObjectName.WILDCARD;
 298         } else {
 299             if (!name.endsWith("]")) {
 300                 throw new IllegalArgumentException("MBeanPermission: " +
 301                                                    "The ObjectName in the " +
 302                                                    "target name must be " +
 303                                                    "included in square " +
 304                                                    "brackets");
 305             } else {
 306                 // Create ObjectName
 307                 //
 308                 try {
 309                     // If "[]" then ObjectName("*:*")
 310                     //
 311                     String on = name.substring(openingBracket + 1,
 312                                                name.length() - 1);
 313                     if (on.equals(""))
 314                         objectName = ObjectName.WILDCARD;
 315                     else if (on.equals("-"))
 316                         objectName = null;
 317                     else
 318                         objectName = new ObjectName(on);
 319                 } catch (MalformedObjectNameException e) {
 320                     throw new IllegalArgumentException("MBeanPermission: " +
 321                                                        "The target name does " +
 322                                                        "not specify a valid " +
 323                                                        "ObjectName", e);
 324                 }
 325             }
 326 
 327             name = name.substring(0, openingBracket);
 328         }
 329 
 330         // Parse member
 331 
 332         int poundSign = name.indexOf('#');
 333 
 334         if (poundSign == -1)
 335             setMember("*");
 336         else {
 337             String memberName = name.substring(poundSign + 1);
 338             setMember(memberName);
 339             name = name.substring(0, poundSign);
 340         }
 341 
 342         // Parse className
 343 
 344         setClassName(name);
 345     }
 346 
 347     /**
 348      * Assign fields based on className, member, and objectName
 349      * parameters.
 350      */
 351     private void initName(String className, String member,
 352                           ObjectName objectName) {
 353         setClassName(className);
 354         setMember(member);
 355         this.objectName = objectName;
 356     }
 357 
 358     private void setClassName(String className) {
 359         if (className == null || className.equals("-")) {
 360             classNamePrefix = null;
 361             classNameExactMatch = false;
 362         } else if (className.equals("") || className.equals("*")) {
 363             classNamePrefix = "";
 364             classNameExactMatch = false;
 365         } else if (className.endsWith(".*")) {
 366             // Note that we include the "." in the required prefix
 367             classNamePrefix = className.substring(0, className.length() - 1);
 368             classNameExactMatch = false;
 369         } else {
 370             classNamePrefix = className;
 371             classNameExactMatch = true;
 372         }
 373     }
 374 
 375     private void setMember(String member) {
 376         if (member == null || member.equals("-"))
 377             this.member = null;
 378         else if (member.equals(""))
 379             this.member = "*";
 380         else
 381             this.member = member;
 382     }
 383 
 384     /**
 385      * <p>Create a new MBeanPermission object with the specified target name
 386      * and actions.</p>
 387      *
 388      * <p>The target name is of the form
 389      * "<code>className#member[objectName]</code>" where each part is
 390      * optional.  It must not be empty or null.</p>
 391      *
 392      * <p>The actions parameter contains a comma-separated list of the
 393      * desired actions granted on the target name.  It must not be
 394      * empty or null.</p>
 395      *
 396      * @param name the triplet "className#member[objectName]".
 397      * @param actions the action string.
 398      *
 399      * @exception IllegalArgumentException if the <code>name</code> or
 400      * <code>actions</code> is invalid.
 401      */
 402     public MBeanPermission(String name, String actions) {
 403         super(name);
 404 
 405         parseName();
 406 
 407         this.actions = actions;
 408         parseActions();
 409     }
 410 
 411     /**
 412      * <p>Create a new MBeanPermission object with the specified target name
 413      * (class name, member, object name) and actions.</p>
 414      *
 415      * <p>The class name, member and object name parameters define a
 416      * target name of the form
 417      * "<code>className#member[objectName]</code>" where each part is
 418      * optional.  This will be the result of {@link #getName()} on the
 419      * resultant MBeanPermission.</p>
 420      *
 421      * <p>The actions parameter contains a comma-separated list of the
 422      * desired actions granted on the target name.  It must not be
 423      * empty or null.</p>
 424      *
 425      * @param className the class name to which this permission applies.
 426      * May be null or <code>"-"</code>, which represents a class name
 427      * that is implied by any class name but does not imply any other
 428      * class name.
 429      * @param member the member to which this permission applies.  May
 430      * be null or <code>"-"</code>, which represents a member that is
 431      * implied by any member but does not imply any other member.
 432      * @param objectName the object name to which this permission
 433      * applies.  May be null, which represents an object name that is
 434      * implied by any object name but does not imply any other object
 435      * name.
 436      * @param actions the action string.
 437      */
 438     public MBeanPermission(String className,
 439                            String member,
 440                            ObjectName objectName,
 441                            String actions) {
 442 
 443         super(makeName(className, member, objectName));
 444         initName(className, member, objectName);
 445 
 446         this.actions = actions;
 447         parseActions();
 448     }
 449 
 450     private static String makeName(String className, String member,
 451                                    ObjectName objectName) {
 452         final StringBuilder name = new StringBuilder();
 453         if (className == null)
 454             className = "-";
 455         name.append(className);
 456         if (member == null)
 457             member = "-";
 458         name.append("#" + member);
 459         if (objectName == null)
 460             name.append("[-]");
 461         else
 462             name.append("[").append(objectName.getCanonicalName()).append("]");
 463 
 464         /* In the interests of legibility for Permission.toString(), we
 465            transform the empty string into "*".  */
 466         if (name.length() == 0)
 467             return "*";
 468         else
 469             return name.toString();
 470     }
 471 
 472     /**
 473      * Returns the "canonical string representation" of the actions. That is,
 474      * this method always returns present actions in alphabetical order.
 475      *
 476      * @return the canonical string representation of the actions.
 477      */
 478     public String getActions() {
 479 
 480         if (actions == null)
 481             actions = getActions(this.mask);
 482 
 483         return actions;
 484     }
 485 
 486     /**
 487      * Returns the "canonical string representation"
 488      * of the actions from the mask.
 489      */
 490     private static String getActions(int mask) {
 491         final StringBuilder sb = new StringBuilder();
 492         boolean comma = false;
 493 
 494         if ((mask & AddNotificationListener) == AddNotificationListener) {
 495             comma = true;
 496             sb.append("addNotificationListener");
 497         }
 498 
 499         if ((mask & GetAttribute) == GetAttribute) {
 500             if (comma) sb.append(',');
 501             else comma = true;
 502             sb.append("getAttribute");
 503         }
 504 
 505         if ((mask & GetClassLoader) == GetClassLoader) {
 506             if (comma) sb.append(',');
 507             else comma = true;
 508             sb.append("getClassLoader");
 509         }
 510 
 511         if ((mask & GetClassLoaderFor) == GetClassLoaderFor) {
 512             if (comma) sb.append(',');
 513             else comma = true;
 514             sb.append("getClassLoaderFor");
 515         }
 516 
 517         if ((mask & GetClassLoaderRepository) == GetClassLoaderRepository) {
 518             if (comma) sb.append(',');
 519             else comma = true;
 520             sb.append("getClassLoaderRepository");
 521         }
 522 
 523         if ((mask & GetDomains) == GetDomains) {
 524             if (comma) sb.append(',');
 525             else comma = true;
 526             sb.append("getDomains");
 527         }
 528 
 529         if ((mask & GetMBeanInfo) == GetMBeanInfo) {
 530             if (comma) sb.append(',');
 531             else comma = true;
 532             sb.append("getMBeanInfo");
 533         }
 534 
 535         if ((mask & GetObjectInstance) == GetObjectInstance) {
 536             if (comma) sb.append(',');
 537             else comma = true;
 538             sb.append("getObjectInstance");
 539         }
 540 
 541         if ((mask & Instantiate) == Instantiate) {
 542             if (comma) sb.append(',');
 543             else comma = true;
 544             sb.append("instantiate");
 545         }
 546 
 547         if ((mask & Invoke) == Invoke) {
 548             if (comma) sb.append(',');
 549             else comma = true;
 550             sb.append("invoke");
 551         }
 552 
 553         if ((mask & IsInstanceOf) == IsInstanceOf) {
 554             if (comma) sb.append(',');
 555             else comma = true;
 556             sb.append("isInstanceOf");
 557         }
 558 
 559         if ((mask & QueryMBeans) == QueryMBeans) {
 560             if (comma) sb.append(',');
 561             else comma = true;
 562             sb.append("queryMBeans");
 563         }
 564 
 565         if ((mask & QueryNames) == QueryNames) {
 566             if (comma) sb.append(',');
 567             else comma = true;
 568             sb.append("queryNames");
 569         }
 570 
 571         if ((mask & RegisterMBean) == RegisterMBean) {
 572             if (comma) sb.append(',');
 573             else comma = true;
 574             sb.append("registerMBean");
 575         }
 576 
 577         if ((mask & RemoveNotificationListener) == RemoveNotificationListener) {
 578             if (comma) sb.append(',');
 579             else comma = true;
 580             sb.append("removeNotificationListener");
 581         }
 582 
 583         if ((mask & SetAttribute) == SetAttribute) {
 584             if (comma) sb.append(',');
 585             else comma = true;
 586             sb.append("setAttribute");
 587         }
 588 
 589         if ((mask & UnregisterMBean) == UnregisterMBean) {
 590             if (comma) sb.append(',');
 591             else comma = true;
 592             sb.append("unregisterMBean");
 593         }
 594 
 595         return sb.toString();
 596     }
 597 
 598     /**
 599      * Returns the hash code value for this object.
 600      *
 601      * @return a hash code value for this object.
 602      */
 603     public int hashCode() {
 604         return this.getName().hashCode() + this.getActions().hashCode();
 605     }
 606 
 607     /**
 608      * Converts an action String to an integer action mask.
 609      *
 610      * @param action the action string.
 611      * @return the action mask.
 612      */
 613     private static int getMask(String action) {
 614 
 615         /*
 616          * BE CAREFUL HERE! PARSING ORDER IS IMPORTANT IN THIS ALGORITHM.
 617          *
 618          * The 'string length' test must be performed for the lengthiest
 619          * strings first.
 620          *
 621          * In this permission if the "unregisterMBean" string length test is
 622          * performed after the "registerMBean" string length test the algorithm
 623          * considers the 'unregisterMBean' action as being the 'registerMBean'
 624          * action and a parsing error is returned.
 625          */
 626 
 627         int mask = NONE;
 628 
 629         if (action == null) {
 630             return mask;
 631         }
 632 
 633         if (action.equals("*")) {
 634             return ALL;
 635         }
 636 
 637         char[] a = action.toCharArray();
 638 
 639         int i = a.length - 1;
 640         if (i < 0)
 641             return mask;
 642 
 643         while (i != -1) {
 644             char c;
 645 
 646             // skip whitespace
 647             while ((i!=-1) && ((c = a[i]) == ' ' ||
 648                                c == '\r' ||
 649                                c == '\n' ||
 650                                c == '\f' ||
 651                                c == '\t'))
 652                 i--;
 653 
 654             // check for the known strings
 655             int matchlen;
 656 
 657             if (i >= 25 && /* removeNotificationListener */
 658                 (a[i-25] == 'r') &&
 659                 (a[i-24] == 'e') &&
 660                 (a[i-23] == 'm') &&
 661                 (a[i-22] == 'o') &&
 662                 (a[i-21] == 'v') &&
 663                 (a[i-20] == 'e') &&
 664                 (a[i-19] == 'N') &&
 665                 (a[i-18] == 'o') &&
 666                 (a[i-17] == 't') &&
 667                 (a[i-16] == 'i') &&
 668                 (a[i-15] == 'f') &&
 669                 (a[i-14] == 'i') &&
 670                 (a[i-13] == 'c') &&
 671                 (a[i-12] == 'a') &&
 672                 (a[i-11] == 't') &&
 673                 (a[i-10] == 'i') &&
 674                 (a[i-9] == 'o') &&
 675                 (a[i-8] == 'n') &&
 676                 (a[i-7] == 'L') &&
 677                 (a[i-6] == 'i') &&
 678                 (a[i-5] == 's') &&
 679                 (a[i-4] == 't') &&
 680                 (a[i-3] == 'e') &&
 681                 (a[i-2] == 'n') &&
 682                 (a[i-1] == 'e') &&
 683                 (a[i] == 'r')) {
 684                 matchlen = 26;
 685                 mask |= RemoveNotificationListener;
 686             } else if (i >= 23 && /* getClassLoaderRepository */
 687                        (a[i-23] == 'g') &&
 688                        (a[i-22] == 'e') &&
 689                        (a[i-21] == 't') &&
 690                        (a[i-20] == 'C') &&
 691                        (a[i-19] == 'l') &&
 692                        (a[i-18] == 'a') &&
 693                        (a[i-17] == 's') &&
 694                        (a[i-16] == 's') &&
 695                        (a[i-15] == 'L') &&
 696                        (a[i-14] == 'o') &&
 697                        (a[i-13] == 'a') &&
 698                        (a[i-12] == 'd') &&
 699                        (a[i-11] == 'e') &&
 700                        (a[i-10] == 'r') &&
 701                        (a[i-9] == 'R') &&
 702                        (a[i-8] == 'e') &&
 703                        (a[i-7] == 'p') &&
 704                        (a[i-6] == 'o') &&
 705                        (a[i-5] == 's') &&
 706                        (a[i-4] == 'i') &&
 707                        (a[i-3] == 't') &&
 708                        (a[i-2] == 'o') &&
 709                        (a[i-1] == 'r') &&
 710                        (a[i] == 'y')) {
 711                 matchlen = 24;
 712                 mask |= GetClassLoaderRepository;
 713             } else if (i >= 22 && /* addNotificationListener */
 714                        (a[i-22] == 'a') &&
 715                        (a[i-21] == 'd') &&
 716                        (a[i-20] == 'd') &&
 717                        (a[i-19] == 'N') &&
 718                        (a[i-18] == 'o') &&
 719                        (a[i-17] == 't') &&
 720                        (a[i-16] == 'i') &&
 721                        (a[i-15] == 'f') &&
 722                        (a[i-14] == 'i') &&
 723                        (a[i-13] == 'c') &&
 724                        (a[i-12] == 'a') &&
 725                        (a[i-11] == 't') &&
 726                        (a[i-10] == 'i') &&
 727                        (a[i-9] == 'o') &&
 728                        (a[i-8] == 'n') &&
 729                        (a[i-7] == 'L') &&
 730                        (a[i-6] == 'i') &&
 731                        (a[i-5] == 's') &&
 732                        (a[i-4] == 't') &&
 733                        (a[i-3] == 'e') &&
 734                        (a[i-2] == 'n') &&
 735                        (a[i-1] == 'e') &&
 736                        (a[i] == 'r')) {
 737                 matchlen = 23;
 738                 mask |= AddNotificationListener;
 739             } else if (i >= 16 && /* getClassLoaderFor */
 740                        (a[i-16] == 'g') &&
 741                        (a[i-15] == 'e') &&
 742                        (a[i-14] == 't') &&
 743                        (a[i-13] == 'C') &&
 744                        (a[i-12] == 'l') &&
 745                        (a[i-11] == 'a') &&
 746                        (a[i-10] == 's') &&
 747                        (a[i-9] == 's') &&
 748                        (a[i-8] == 'L') &&
 749                        (a[i-7] == 'o') &&
 750                        (a[i-6] == 'a') &&
 751                        (a[i-5] == 'd') &&
 752                        (a[i-4] == 'e') &&
 753                        (a[i-3] == 'r') &&
 754                        (a[i-2] == 'F') &&
 755                        (a[i-1] == 'o') &&
 756                        (a[i] == 'r')) {
 757                 matchlen = 17;
 758                 mask |= GetClassLoaderFor;
 759             } else if (i >= 16 && /* getObjectInstance */
 760                        (a[i-16] == 'g') &&
 761                        (a[i-15] == 'e') &&
 762                        (a[i-14] == 't') &&
 763                        (a[i-13] == 'O') &&
 764                        (a[i-12] == 'b') &&
 765                        (a[i-11] == 'j') &&
 766                        (a[i-10] == 'e') &&
 767                        (a[i-9] == 'c') &&
 768                        (a[i-8] == 't') &&
 769                        (a[i-7] == 'I') &&
 770                        (a[i-6] == 'n') &&
 771                        (a[i-5] == 's') &&
 772                        (a[i-4] == 't') &&
 773                        (a[i-3] == 'a') &&
 774                        (a[i-2] == 'n') &&
 775                        (a[i-1] == 'c') &&
 776                        (a[i] == 'e')) {
 777                 matchlen = 17;
 778                 mask |= GetObjectInstance;
 779             } else if (i >= 14 && /* unregisterMBean */
 780                        (a[i-14] == 'u') &&
 781                        (a[i-13] == 'n') &&
 782                        (a[i-12] == 'r') &&
 783                        (a[i-11] == 'e') &&
 784                        (a[i-10] == 'g') &&
 785                        (a[i-9] == 'i') &&
 786                        (a[i-8] == 's') &&
 787                        (a[i-7] == 't') &&
 788                        (a[i-6] == 'e') &&
 789                        (a[i-5] == 'r') &&
 790                        (a[i-4] == 'M') &&
 791                        (a[i-3] == 'B') &&
 792                        (a[i-2] == 'e') &&
 793                        (a[i-1] == 'a') &&
 794                        (a[i] == 'n')) {
 795                 matchlen = 15;
 796                 mask |= UnregisterMBean;
 797             } else if (i >= 13 && /* getClassLoader */
 798                        (a[i-13] == 'g') &&
 799                        (a[i-12] == 'e') &&
 800                        (a[i-11] == 't') &&
 801                        (a[i-10] == 'C') &&
 802                        (a[i-9] == 'l') &&
 803                        (a[i-8] == 'a') &&
 804                        (a[i-7] == 's') &&
 805                        (a[i-6] == 's') &&
 806                        (a[i-5] == 'L') &&
 807                        (a[i-4] == 'o') &&
 808                        (a[i-3] == 'a') &&
 809                        (a[i-2] == 'd') &&
 810                        (a[i-1] == 'e') &&
 811                        (a[i] == 'r')) {
 812                 matchlen = 14;
 813                 mask |= GetClassLoader;
 814             } else if (i >= 12 && /* registerMBean */
 815                        (a[i-12] == 'r') &&
 816                        (a[i-11] == 'e') &&
 817                        (a[i-10] == 'g') &&
 818                        (a[i-9] == 'i') &&
 819                        (a[i-8] == 's') &&
 820                        (a[i-7] == 't') &&
 821                        (a[i-6] == 'e') &&
 822                        (a[i-5] == 'r') &&
 823                        (a[i-4] == 'M') &&
 824                        (a[i-3] == 'B') &&
 825                        (a[i-2] == 'e') &&
 826                        (a[i-1] == 'a') &&
 827                        (a[i] == 'n')) {
 828                 matchlen = 13;
 829                 mask |= RegisterMBean;
 830             } else if (i >= 11 && /* getAttribute */
 831                        (a[i-11] == 'g') &&
 832                        (a[i-10] == 'e') &&
 833                        (a[i-9] == 't') &&
 834                        (a[i-8] == 'A') &&
 835                        (a[i-7] == 't') &&
 836                        (a[i-6] == 't') &&
 837                        (a[i-5] == 'r') &&
 838                        (a[i-4] == 'i') &&
 839                        (a[i-3] == 'b') &&
 840                        (a[i-2] == 'u') &&
 841                        (a[i-1] == 't') &&
 842                        (a[i] == 'e')) {
 843                 matchlen = 12;
 844                 mask |= GetAttribute;
 845             } else if (i >= 11 && /* getMBeanInfo */
 846                        (a[i-11] == 'g') &&
 847                        (a[i-10] == 'e') &&
 848                        (a[i-9] == 't') &&
 849                        (a[i-8] == 'M') &&
 850                        (a[i-7] == 'B') &&
 851                        (a[i-6] == 'e') &&
 852                        (a[i-5] == 'a') &&
 853                        (a[i-4] == 'n') &&
 854                        (a[i-3] == 'I') &&
 855                        (a[i-2] == 'n') &&
 856                        (a[i-1] == 'f') &&
 857                        (a[i] == 'o')) {
 858                 matchlen = 12;
 859                 mask |= GetMBeanInfo;
 860             } else if (i >= 11 && /* isInstanceOf */
 861                        (a[i-11] == 'i') &&
 862                        (a[i-10] == 's') &&
 863                        (a[i-9] == 'I') &&
 864                        (a[i-8] == 'n') &&
 865                        (a[i-7] == 's') &&
 866                        (a[i-6] == 't') &&
 867                        (a[i-5] == 'a') &&
 868                        (a[i-4] == 'n') &&
 869                        (a[i-3] == 'c') &&
 870                        (a[i-2] == 'e') &&
 871                        (a[i-1] == 'O') &&
 872                        (a[i] == 'f')) {
 873                 matchlen = 12;
 874                 mask |= IsInstanceOf;
 875             } else if (i >= 11 && /* setAttribute */
 876                        (a[i-11] == 's') &&
 877                        (a[i-10] == 'e') &&
 878                        (a[i-9] == 't') &&
 879                        (a[i-8] == 'A') &&
 880                        (a[i-7] == 't') &&
 881                        (a[i-6] == 't') &&
 882                        (a[i-5] == 'r') &&
 883                        (a[i-4] == 'i') &&
 884                        (a[i-3] == 'b') &&
 885                        (a[i-2] == 'u') &&
 886                        (a[i-1] == 't') &&
 887                        (a[i] == 'e')) {
 888                 matchlen = 12;
 889                 mask |= SetAttribute;
 890             } else if (i >= 10 && /* instantiate */
 891                        (a[i-10] == 'i') &&
 892                        (a[i-9] == 'n') &&
 893                        (a[i-8] == 's') &&
 894                        (a[i-7] == 't') &&
 895                        (a[i-6] == 'a') &&
 896                        (a[i-5] == 'n') &&
 897                        (a[i-4] == 't') &&
 898                        (a[i-3] == 'i') &&
 899                        (a[i-2] == 'a') &&
 900                        (a[i-1] == 't') &&
 901                        (a[i] == 'e')) {
 902                 matchlen = 11;
 903                 mask |= Instantiate;
 904             } else if (i >= 10 && /* queryMBeans */
 905                        (a[i-10] == 'q') &&
 906                        (a[i-9] == 'u') &&
 907                        (a[i-8] == 'e') &&
 908                        (a[i-7] == 'r') &&
 909                        (a[i-6] == 'y') &&
 910                        (a[i-5] == 'M') &&
 911                        (a[i-4] == 'B') &&
 912                        (a[i-3] == 'e') &&
 913                        (a[i-2] == 'a') &&
 914                        (a[i-1] == 'n') &&
 915                        (a[i] == 's')) {
 916                 matchlen = 11;
 917                 mask |= QueryMBeans;
 918             } else if (i >= 9 && /* getDomains */
 919                        (a[i-9] == 'g') &&
 920                        (a[i-8] == 'e') &&
 921                        (a[i-7] == 't') &&
 922                        (a[i-6] == 'D') &&
 923                        (a[i-5] == 'o') &&
 924                        (a[i-4] == 'm') &&
 925                        (a[i-3] == 'a') &&
 926                        (a[i-2] == 'i') &&
 927                        (a[i-1] == 'n') &&
 928                        (a[i] == 's')) {
 929                 matchlen = 10;
 930                 mask |= GetDomains;
 931             } else if (i >= 9 && /* queryNames */
 932                        (a[i-9] == 'q') &&
 933                        (a[i-8] == 'u') &&
 934                        (a[i-7] == 'e') &&
 935                        (a[i-6] == 'r') &&
 936                        (a[i-5] == 'y') &&
 937                        (a[i-4] == 'N') &&
 938                        (a[i-3] == 'a') &&
 939                        (a[i-2] == 'm') &&
 940                        (a[i-1] == 'e') &&
 941                        (a[i] == 's')) {
 942                 matchlen = 10;
 943                 mask |= QueryNames;
 944             } else if (i >= 5 && /* invoke */
 945                        (a[i-5] == 'i') &&
 946                        (a[i-4] == 'n') &&
 947                        (a[i-3] == 'v') &&
 948                        (a[i-2] == 'o') &&
 949                        (a[i-1] == 'k') &&
 950                        (a[i] == 'e')) {
 951                 matchlen = 6;
 952                 mask |= Invoke;
 953             } else {
 954                 // parse error
 955                 throw new IllegalArgumentException("Invalid permission: " +
 956                                                    action);
 957             }
 958 
 959             // make sure we didn't just match the tail of a word
 960             // like "ackbarfaccept".  Also, skip to the comma.
 961             boolean seencomma = false;
 962             while (i >= matchlen && !seencomma) {
 963                 switch(a[i-matchlen]) {
 964                 case ',':
 965                     seencomma = true;
 966                     break;
 967                 case ' ': case '\r': case '\n':
 968                 case '\f': case '\t':
 969                     break;
 970                 default:
 971                     throw new IllegalArgumentException("Invalid permission: " +
 972                                                        action);
 973                 }
 974                 i--;
 975             }
 976 
 977             // point i at the location of the comma minus one (or -1).
 978             i -= matchlen;
 979         }
 980 
 981         return mask;
 982     }
 983 
 984     /**
 985      * <p>Checks if this MBeanPermission object "implies" the
 986      * specified permission.</p>
 987      *
 988      * <p>More specifically, this method returns true if:</p>
 989      *
 990      * <ul>
 991      *
 992      * <li> <i>p</i> is an instance of MBeanPermission; and</li>
 993      *
 994      * <li> <i>p</i> has a null className or <i>p</i>'s className
 995      * matches this object's className; and</li>
 996      *
 997      * <li> <i>p</i> has a null member or <i>p</i>'s member matches this
 998      * object's member; and</li>
 999      *
1000      * <li> <i>p</i> has a null object name or <i>p</i>'s
1001      * object name matches this object's object name; and</li>
1002      *
1003      * <li> <i>p</i>'s actions are a subset of this object's actions</li>
1004      *
1005      * </ul>
1006      *
1007      * <p>If this object's className is "<code>*</code>", <i>p</i>'s
1008      * className always matches it.  If it is "<code>a.*</code>", <i>p</i>'s
1009      * className matches it if it begins with "<code>a.</code>".</p>
1010      *
1011      * <p>If this object's member is "<code>*</code>", <i>p</i>'s
1012      * member always matches it.</p>
1013      *
1014      * <p>If this object's objectName <i>n1</i> is an object name pattern,
1015      * <i>p</i>'s objectName <i>n2</i> matches it if
1016      * {@link ObjectName#equals <i>n1</i>.equals(<i>n2</i>)} or if
1017      * {@link ObjectName#apply <i>n1</i>.apply(<i>n2</i>)}.</p>
1018      *
1019      * <p>A permission that includes the <code>queryMBeans</code> action
1020      * is considered to include <code>queryNames</code> as well.</p>
1021      *
1022      * @param p the permission to check against.
1023      * @return true if the specified permission is implied by this object,
1024      * false if not.
1025      */
1026     public boolean implies(Permission p) {
1027         if (!(p instanceof MBeanPermission))
1028             return false;
1029 
1030         MBeanPermission that = (MBeanPermission) p;
1031 
1032         // Actions
1033         //
1034         // The actions in 'this' permission must be a
1035         // superset of the actions in 'that' permission
1036         //
1037 
1038         /* "queryMBeans" implies "queryNames" */
1039         if ((this.mask & QueryMBeans) == QueryMBeans) {
1040             if (((this.mask | QueryNames) & that.mask) != that.mask) {
1041                 //System.out.println("action [with QueryNames] does not imply");
1042                 return false;
1043             }
1044         } else {
1045             if ((this.mask & that.mask) != that.mask) {
1046                 //System.out.println("action does not imply");
1047                 return false;
1048             }
1049         }
1050 
1051         // Target name
1052         //
1053         // The 'className' check is true iff:
1054         // 1) the className in 'this' permission is omitted or "*", or
1055         // 2) the className in 'that' permission is omitted or "*", or
1056         // 3) the className in 'this' permission does pattern
1057         //    matching with the className in 'that' permission.
1058         //
1059         // The 'member' check is true iff:
1060         // 1) the member in 'this' permission is omitted or "*", or
1061         // 2) the member in 'that' permission is omitted or "*", or
1062         // 3) the member in 'this' permission equals the member in
1063         //    'that' permission.
1064         //
1065         // The 'object name' check is true iff:
1066         // 1) the object name in 'this' permission is omitted or "*:*", or
1067         // 2) the object name in 'that' permission is omitted or "*:*", or
1068         // 3) the object name in 'this' permission does pattern
1069         //    matching with the object name in 'that' permission.
1070         //
1071 
1072         /* Check if this.className implies that.className.
1073 
1074            If that.classNamePrefix is empty that means the className is
1075            irrelevant for this permission check.  Otherwise, we do not
1076            expect that "that" contains a wildcard, since it is a
1077            needed permission.  So we assume that.classNameExactMatch.  */
1078 
1079         if (that.classNamePrefix == null) {
1080             // bottom is implied
1081         } else if (this.classNamePrefix == null) {
1082             // bottom implies nothing but itself
1083             return false;
1084         } else if (this.classNameExactMatch) {
1085             if (!that.classNameExactMatch)
1086                 return false; // exact never implies wildcard
1087             if (!that.classNamePrefix.equals(this.classNamePrefix))
1088                 return false; // exact match fails
1089         } else {
1090             // prefix match, works even if "that" is also a wildcard
1091             // e.g. a.* implies a.* and a.b.*
1092             if (!that.classNamePrefix.startsWith(this.classNamePrefix))
1093                 return false;
1094         }
1095 
1096         /* Check if this.member implies that.member */
1097 
1098         if (that.member == null) {
1099             // bottom is implied
1100         } else if (this.member == null) {
1101             // bottom implies nothing but itself
1102             return false;
1103         } else if (this.member.equals("*")) {
1104             // wildcard implies everything (including itself)
1105         } else if (!this.member.equals(that.member)) {
1106             return false;
1107         }
1108 
1109         /* Check if this.objectName implies that.objectName */
1110 
1111         if (that.objectName == null) {
1112             // bottom is implied
1113         } else if (this.objectName == null) {
1114             // bottom implies nothing but itself
1115             return false;
1116         } else if (!this.objectName.apply(that.objectName)) {
1117             /* ObjectName.apply returns false if that.objectName is a
1118                wildcard so we also allow equals for that case.  This
1119                never happens during real permission checks, but means
1120                the implies relation is reflexive.  */
1121             if (!this.objectName.equals(that.objectName))
1122                 return false;
1123         }
1124 
1125         return true;
1126     }
1127 
1128     /**
1129      * Checks two MBeanPermission objects for equality. Checks
1130      * that <i>obj</i> is an MBeanPermission, and has the same
1131      * name and actions as this object.
1132      *
1133      * @param obj the object we are testing for equality with this object.
1134      * @return true if obj is an MBeanPermission, and has the
1135      * same name and actions as this MBeanPermission object.
1136      */
1137     public boolean equals(Object obj) {
1138         if (obj == this)
1139             return true;
1140 
1141         if (! (obj instanceof MBeanPermission))
1142             return false;
1143 
1144         MBeanPermission that = (MBeanPermission) obj;
1145 
1146         return (this.mask == that.mask) &&
1147             (this.getName().equals(that.getName()));
1148     }
1149 
1150     /**
1151      * Deserialize this object based on its name and actions.
1152      */
1153     private void readObject(ObjectInputStream in)
1154             throws IOException, ClassNotFoundException {
1155         in.defaultReadObject();
1156         parseName();
1157         parseActions();
1158     }
1159 }