src/java.security.jgss/share/classes/javax/security/auth/kerberos/ServicePermission.java
changeset 47216 71c04702a3d5
parent 33282 00f3c40fd3af
child 58831 b026a43e1809
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 2000, 2015, 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.security.auth.kerberos;
       
    27 
       
    28 import java.io.IOException;
       
    29 import java.io.ObjectInputStream;
       
    30 import java.io.ObjectOutputStream;
       
    31 import java.io.ObjectStreamField;
       
    32 import java.security.Permission;
       
    33 import java.security.PermissionCollection;
       
    34 import java.util.*;
       
    35 import java.util.concurrent.ConcurrentHashMap;
       
    36 
       
    37 /**
       
    38  * This class is used to protect Kerberos services and the
       
    39  * credentials necessary to access those services. There is a one to
       
    40  * one mapping of a service principal and the credentials necessary
       
    41  * to access the service. Therefore granting access to a service
       
    42  * principal implicitly grants access to the credential necessary to
       
    43  * establish a security context with the service principal. This
       
    44  * applies regardless of whether the credentials are in a cache
       
    45  * or acquired via an exchange with the KDC. The credential can
       
    46  * be either a ticket granting ticket, a service ticket or a secret
       
    47  * key from a key table.
       
    48  * <p>
       
    49  * A ServicePermission contains a service principal name and
       
    50  * a list of actions which specify the context the credential can be
       
    51  * used within.
       
    52  * <p>
       
    53  * The service principal name is the canonical name of the
       
    54  * {@code KerberosPrincipal} supplying the service, that is
       
    55  * the KerberosPrincipal represents a Kerberos service
       
    56  * principal. This name is treated in a case sensitive manner.
       
    57  * An asterisk may appear by itself, to signify any service principal.
       
    58  * <p>
       
    59  * Granting this permission implies that the caller can use a cached
       
    60  * credential (TGT, service ticket or secret key) within the context
       
    61  * designated by the action. In the case of the TGT, granting this
       
    62  * permission also implies that the TGT can be obtained by an
       
    63  * Authentication Service exchange.
       
    64  * <p>
       
    65  * Granting this permission also implies creating {@link KerberosPrincipal}
       
    66  * or {@link org.ietf.jgss.GSSName GSSName} without providing a Kerberos
       
    67  * realm, as long as the permission's service principal is in this realm.
       
    68  * <p>
       
    69  * The possible actions are:
       
    70  *
       
    71  * <pre>
       
    72  *    initiate -              allow the caller to use the credential to
       
    73  *                            initiate a security context with a service
       
    74  *                            principal.
       
    75  *
       
    76  *    accept -                allow the caller to use the credential to
       
    77  *                            accept security context as a particular
       
    78  *                            principal.
       
    79  * </pre>
       
    80  *
       
    81  * For example, to specify the permission to access to the TGT to
       
    82  * initiate a security context the permission is constructed as follows:
       
    83  *
       
    84  * <pre>
       
    85  *     ServicePermission("krbtgt/EXAMPLE.COM@EXAMPLE.COM", "initiate");
       
    86  * </pre>
       
    87  * <p>
       
    88  * To obtain a service ticket to initiate a context with the "host"
       
    89  * service the permission is constructed as follows:
       
    90  * <pre>
       
    91  *     ServicePermission("host/foo.example.com@EXAMPLE.COM", "initiate");
       
    92  * </pre>
       
    93  * <p>
       
    94  * For a Kerberized server the action is "accept". For example, the permission
       
    95  * necessary to access and use the secret key of the  Kerberized "host"
       
    96  * service (telnet and the likes)  would be constructed as follows:
       
    97  *
       
    98  * <pre>
       
    99  *     ServicePermission("host/foo.example.com@EXAMPLE.COM", "accept");
       
   100  * </pre>
       
   101  *
       
   102  * @since 1.4
       
   103  */
       
   104 
       
   105 public final class ServicePermission extends Permission
       
   106     implements java.io.Serializable {
       
   107 
       
   108     private static final long serialVersionUID = -1227585031618624935L;
       
   109 
       
   110     /**
       
   111      * Initiate a security context to the specified service
       
   112      */
       
   113     private final static int INITIATE   = 0x1;
       
   114 
       
   115     /**
       
   116      * Accept a security context
       
   117      */
       
   118     private final static int ACCEPT     = 0x2;
       
   119 
       
   120     /**
       
   121      * All actions
       
   122      */
       
   123     private final static int ALL        = INITIATE|ACCEPT;
       
   124 
       
   125     /**
       
   126      * No actions.
       
   127      */
       
   128     private final static int NONE    = 0x0;
       
   129 
       
   130     // the actions mask
       
   131     private transient int mask;
       
   132 
       
   133     /**
       
   134      * the actions string.
       
   135      *
       
   136      * @serial
       
   137      */
       
   138 
       
   139     private String actions; // Left null as long as possible, then
       
   140                             // created and re-used in the getAction function.
       
   141 
       
   142     /**
       
   143      * Create a new {@code ServicePermission}
       
   144      * with the specified {@code servicePrincipal}
       
   145      * and {@code action}.
       
   146      *
       
   147      * @param servicePrincipal the name of the service principal.
       
   148      * An asterisk may appear by itself, to signify any service principal.
       
   149      *
       
   150      * @param action the action string
       
   151      */
       
   152     public ServicePermission(String servicePrincipal, String action) {
       
   153         // Note: servicePrincipal can be "@REALM" which means any principal in
       
   154         // this realm implies it. action can be "-" which means any
       
   155         // action implies it.
       
   156         super(servicePrincipal);
       
   157         init(servicePrincipal, getMask(action));
       
   158     }
       
   159 
       
   160     /**
       
   161      * Creates a ServicePermission object with the specified servicePrincipal
       
   162      * and a pre-calculated mask. Avoids the overhead of re-computing the mask.
       
   163      * Called by ServicePermissionCollection.
       
   164      */
       
   165     ServicePermission(String servicePrincipal, int mask) {
       
   166         super(servicePrincipal);
       
   167         init(servicePrincipal, mask);
       
   168     }
       
   169 
       
   170     /**
       
   171      * Initialize the ServicePermission object.
       
   172      */
       
   173     private void init(String servicePrincipal, int mask) {
       
   174 
       
   175         if (servicePrincipal == null)
       
   176                 throw new NullPointerException("service principal can't be null");
       
   177 
       
   178         if ((mask & ALL) != mask)
       
   179             throw new IllegalArgumentException("invalid actions mask");
       
   180 
       
   181         this.mask = mask;
       
   182     }
       
   183 
       
   184 
       
   185     /**
       
   186      * Checks if this Kerberos service permission object "implies" the
       
   187      * specified permission.
       
   188      * <P>
       
   189      * More specifically, this method returns true if all of the following
       
   190      * are true (and returns false if any of them are not):
       
   191      * <ul>
       
   192      * <li> <i>p</i> is an instanceof {@code ServicePermission},
       
   193      * <li> <i>p</i>'s actions are a proper subset of this
       
   194      * {@code ServicePermission}'s actions,
       
   195      * <li> <i>p</i>'s name is equal to this {@code ServicePermission}'s name
       
   196      * or this {@code ServicePermission}'s name is "*".
       
   197      * </ul>
       
   198      *
       
   199      * @param p the permission to check against.
       
   200      *
       
   201      * @return true if the specified permission is implied by this object,
       
   202      * false if not.
       
   203      */
       
   204     @Override
       
   205     public boolean implies(Permission p) {
       
   206         if (!(p instanceof ServicePermission))
       
   207             return false;
       
   208 
       
   209         ServicePermission that = (ServicePermission) p;
       
   210 
       
   211         return ((this.mask & that.mask) == that.mask) &&
       
   212             impliesIgnoreMask(that);
       
   213     }
       
   214 
       
   215 
       
   216     boolean impliesIgnoreMask(ServicePermission p) {
       
   217         return ((this.getName().equals("*")) ||
       
   218                 this.getName().equals(p.getName()) ||
       
   219                 (p.getName().startsWith("@") &&
       
   220                         this.getName().endsWith(p.getName())));
       
   221     }
       
   222 
       
   223     /**
       
   224      * Checks two ServicePermission objects for equality.
       
   225      *
       
   226      * @param obj the object to test for equality with this object.
       
   227      *
       
   228      * @return true if {@code obj} is a ServicePermission, and has the
       
   229      *  same service principal, and actions as this
       
   230      * ServicePermission object.
       
   231      */
       
   232     @Override
       
   233     public boolean equals(Object obj) {
       
   234         if (obj == this)
       
   235             return true;
       
   236 
       
   237         if (! (obj instanceof ServicePermission))
       
   238             return false;
       
   239 
       
   240         ServicePermission that = (ServicePermission) obj;
       
   241         return ((this.mask & that.mask) == that.mask) &&
       
   242             this.getName().equals(that.getName());
       
   243 
       
   244 
       
   245     }
       
   246 
       
   247     /**
       
   248      * Returns the hash code value for this object.
       
   249      *
       
   250      * @return a hash code value for this object.
       
   251      */
       
   252     @Override
       
   253     public int hashCode() {
       
   254         return (getName().hashCode() ^ mask);
       
   255     }
       
   256 
       
   257 
       
   258     /**
       
   259      * Returns the "canonical string representation" of the actions in the
       
   260      * specified mask.
       
   261      * Always returns present actions in the following order:
       
   262      * initiate, accept.
       
   263      *
       
   264      * @param mask a specific integer action mask to translate into a string
       
   265      * @return the canonical string representation of the actions
       
   266      */
       
   267     static String getActions(int mask)
       
   268     {
       
   269         StringBuilder sb = new StringBuilder();
       
   270         boolean comma = false;
       
   271 
       
   272         if ((mask & INITIATE) == INITIATE) {
       
   273             if (comma) sb.append(',');
       
   274             else comma = true;
       
   275             sb.append("initiate");
       
   276         }
       
   277 
       
   278         if ((mask & ACCEPT) == ACCEPT) {
       
   279             if (comma) sb.append(',');
       
   280             else comma = true;
       
   281             sb.append("accept");
       
   282         }
       
   283 
       
   284         return sb.toString();
       
   285     }
       
   286 
       
   287     /**
       
   288      * Returns the canonical string representation of the actions.
       
   289      * Always returns present actions in the following order:
       
   290      * initiate, accept.
       
   291      */
       
   292     @Override
       
   293     public String getActions() {
       
   294         if (actions == null)
       
   295             actions = getActions(this.mask);
       
   296 
       
   297         return actions;
       
   298     }
       
   299 
       
   300 
       
   301     /**
       
   302      * Returns a PermissionCollection object for storing
       
   303      * ServicePermission objects.
       
   304      * <br>
       
   305      * ServicePermission objects must be stored in a manner that
       
   306      * allows them to be inserted into the collection in any order, but
       
   307      * that also enables the PermissionCollection implies method to
       
   308      * be implemented in an efficient (and consistent) manner.
       
   309      *
       
   310      * @return a new PermissionCollection object suitable for storing
       
   311      * ServicePermissions.
       
   312      */
       
   313     @Override
       
   314     public PermissionCollection newPermissionCollection() {
       
   315         return new KrbServicePermissionCollection();
       
   316     }
       
   317 
       
   318     /**
       
   319      * Return the current action mask.
       
   320      *
       
   321      * @return the actions mask.
       
   322      */
       
   323     int getMask() {
       
   324         return mask;
       
   325     }
       
   326 
       
   327     /**
       
   328      * Convert an action string to an integer actions mask.
       
   329      *
       
   330      * Note: if action is "-", action will be NONE, which means any
       
   331      * action implies it.
       
   332      *
       
   333      * @param action the action string.
       
   334      * @return the action mask
       
   335      */
       
   336     private static int getMask(String action) {
       
   337 
       
   338         if (action == null) {
       
   339             throw new NullPointerException("action can't be null");
       
   340         }
       
   341 
       
   342         if (action.equals("")) {
       
   343             throw new IllegalArgumentException("action can't be empty");
       
   344         }
       
   345 
       
   346         int mask = NONE;
       
   347 
       
   348         char[] a = action.toCharArray();
       
   349 
       
   350         if (a.length == 1 && a[0] == '-') {
       
   351             return mask;
       
   352         }
       
   353 
       
   354         int i = a.length - 1;
       
   355 
       
   356         while (i != -1) {
       
   357             char c;
       
   358 
       
   359             // skip whitespace
       
   360             while ((i!=-1) && ((c = a[i]) == ' ' ||
       
   361                                c == '\r' ||
       
   362                                c == '\n' ||
       
   363                                c == '\f' ||
       
   364                                c == '\t'))
       
   365                 i--;
       
   366 
       
   367             // check for the known strings
       
   368             int matchlen;
       
   369 
       
   370             if (i >= 7 && (a[i-7] == 'i' || a[i-7] == 'I') &&
       
   371                           (a[i-6] == 'n' || a[i-6] == 'N') &&
       
   372                           (a[i-5] == 'i' || a[i-5] == 'I') &&
       
   373                           (a[i-4] == 't' || a[i-4] == 'T') &&
       
   374                           (a[i-3] == 'i' || a[i-3] == 'I') &&
       
   375                           (a[i-2] == 'a' || a[i-2] == 'A') &&
       
   376                           (a[i-1] == 't' || a[i-1] == 'T') &&
       
   377                           (a[i] == 'e' || a[i] == 'E'))
       
   378             {
       
   379                 matchlen = 8;
       
   380                 mask |= INITIATE;
       
   381 
       
   382             } else if (i >= 5 && (a[i-5] == 'a' || a[i-5] == 'A') &&
       
   383                                  (a[i-4] == 'c' || a[i-4] == 'C') &&
       
   384                                  (a[i-3] == 'c' || a[i-3] == 'C') &&
       
   385                                  (a[i-2] == 'e' || a[i-2] == 'E') &&
       
   386                                  (a[i-1] == 'p' || a[i-1] == 'P') &&
       
   387                                  (a[i] == 't' || a[i] == 'T'))
       
   388             {
       
   389                 matchlen = 6;
       
   390                 mask |= ACCEPT;
       
   391 
       
   392             } else {
       
   393                 // parse error
       
   394                 throw new IllegalArgumentException(
       
   395                         "invalid permission: " + action);
       
   396             }
       
   397 
       
   398             // make sure we didn't just match the tail of a word
       
   399             // like "ackbarfaccept".  Also, skip to the comma.
       
   400             boolean seencomma = false;
       
   401             while (i >= matchlen && !seencomma) {
       
   402                 switch(a[i-matchlen]) {
       
   403                 case ',':
       
   404                     seencomma = true;
       
   405                     break;
       
   406                 case ' ': case '\r': case '\n':
       
   407                 case '\f': case '\t':
       
   408                     break;
       
   409                 default:
       
   410                     throw new IllegalArgumentException(
       
   411                             "invalid permission: " + action);
       
   412                 }
       
   413                 i--;
       
   414             }
       
   415 
       
   416             // point i at the location of the comma minus one (or -1).
       
   417             i -= matchlen;
       
   418         }
       
   419 
       
   420         return mask;
       
   421     }
       
   422 
       
   423 
       
   424     /**
       
   425      * WriteObject is called to save the state of the ServicePermission
       
   426      * to a stream. The actions are serialized, and the superclass
       
   427      * takes care of the name.
       
   428      */
       
   429     private void writeObject(java.io.ObjectOutputStream s)
       
   430         throws IOException
       
   431     {
       
   432         // Write out the actions. The superclass takes care of the name
       
   433         // call getActions to make sure actions field is initialized
       
   434         if (actions == null)
       
   435             getActions();
       
   436         s.defaultWriteObject();
       
   437     }
       
   438 
       
   439     /**
       
   440      * readObject is called to restore the state of the
       
   441      * ServicePermission from a stream.
       
   442      */
       
   443     private void readObject(java.io.ObjectInputStream s)
       
   444          throws IOException, ClassNotFoundException
       
   445     {
       
   446         // Read in the action, then initialize the rest
       
   447         s.defaultReadObject();
       
   448         init(getName(),getMask(actions));
       
   449     }
       
   450 
       
   451 
       
   452     /*
       
   453       public static void main(String[] args) throws Exception {
       
   454       ServicePermission this_ =
       
   455       new ServicePermission(args[0], "accept");
       
   456       ServicePermission that_ =
       
   457       new ServicePermission(args[1], "accept,initiate");
       
   458       System.out.println("-----\n");
       
   459       System.out.println("this.implies(that) = " + this_.implies(that_));
       
   460       System.out.println("-----\n");
       
   461       System.out.println("this = "+this_);
       
   462       System.out.println("-----\n");
       
   463       System.out.println("that = "+that_);
       
   464       System.out.println("-----\n");
       
   465 
       
   466       KrbServicePermissionCollection nps =
       
   467       new KrbServicePermissionCollection();
       
   468       nps.add(this_);
       
   469       nps.add(new ServicePermission("nfs/example.com@EXAMPLE.COM",
       
   470       "accept"));
       
   471       nps.add(new ServicePermission("host/example.com@EXAMPLE.COM",
       
   472       "initiate"));
       
   473       System.out.println("nps.implies(that) = " + nps.implies(that_));
       
   474       System.out.println("-----\n");
       
   475 
       
   476       Enumeration e = nps.elements();
       
   477 
       
   478       while (e.hasMoreElements()) {
       
   479       ServicePermission x =
       
   480       (ServicePermission) e.nextElement();
       
   481       System.out.println("nps.e = " + x);
       
   482       }
       
   483 
       
   484       }
       
   485     */
       
   486 
       
   487 }
       
   488 
       
   489 
       
   490 final class KrbServicePermissionCollection extends PermissionCollection
       
   491     implements java.io.Serializable {
       
   492 
       
   493     // Key is the service principal, value is the ServicePermission.
       
   494     // Not serialized; see serialization section at end of class
       
   495     private transient ConcurrentHashMap<String, Permission> perms;
       
   496 
       
   497     public KrbServicePermissionCollection() {
       
   498         perms = new ConcurrentHashMap<>();
       
   499     }
       
   500 
       
   501     /**
       
   502      * Check and see if this collection of permissions implies the permissions
       
   503      * expressed in "permission".
       
   504      *
       
   505      * @param permission the Permission object to compare
       
   506      *
       
   507      * @return true if "permission" is a proper subset of a permission in
       
   508      * the collection, false if not.
       
   509      */
       
   510     @Override
       
   511     public boolean implies(Permission permission) {
       
   512         if (! (permission instanceof ServicePermission))
       
   513             return false;
       
   514 
       
   515         ServicePermission np = (ServicePermission) permission;
       
   516         int desired = np.getMask();
       
   517 
       
   518         if (desired == 0) {
       
   519             for (Permission p: perms.values()) {
       
   520                 ServicePermission sp = (ServicePermission)p;
       
   521                 if (sp.impliesIgnoreMask(np)) {
       
   522                     return true;
       
   523                 }
       
   524             }
       
   525             return false;
       
   526         }
       
   527 
       
   528 
       
   529         // first, check for wildcard principal
       
   530         ServicePermission x = (ServicePermission)perms.get("*");
       
   531         if (x != null) {
       
   532             if ((x.getMask() & desired) == desired) {
       
   533                 return true;
       
   534             }
       
   535         }
       
   536 
       
   537         // otherwise, check for match on principal
       
   538         x = (ServicePermission)perms.get(np.getName());
       
   539         if (x != null) {
       
   540             //System.out.println("  trying "+x);
       
   541             if ((x.getMask() & desired) == desired) {
       
   542                 return true;
       
   543             }
       
   544         }
       
   545         return false;
       
   546     }
       
   547 
       
   548     /**
       
   549      * Adds a permission to the ServicePermissions. The key for
       
   550      * the hash is the name.
       
   551      *
       
   552      * @param permission the Permission object to add.
       
   553      *
       
   554      * @exception IllegalArgumentException - if the permission is not a
       
   555      *                                       ServicePermission
       
   556      *
       
   557      * @exception SecurityException - if this PermissionCollection object
       
   558      *                                has been marked readonly
       
   559      */
       
   560     @Override
       
   561     public void add(Permission permission) {
       
   562         if (! (permission instanceof ServicePermission))
       
   563             throw new IllegalArgumentException("invalid permission: "+
       
   564                                                permission);
       
   565         if (isReadOnly())
       
   566             throw new SecurityException("attempt to add a Permission to a readonly PermissionCollection");
       
   567 
       
   568         ServicePermission sp = (ServicePermission)permission;
       
   569         String princName = sp.getName();
       
   570 
       
   571         // Add permission to map if it is absent, or replace with new
       
   572         // permission if applicable. NOTE: cannot use lambda for
       
   573         // remappingFunction parameter until JDK-8076596 is fixed.
       
   574         perms.merge(princName, sp,
       
   575             new java.util.function.BiFunction<>() {
       
   576                 @Override
       
   577                 public Permission apply(Permission existingVal,
       
   578                                         Permission newVal) {
       
   579                     int oldMask = ((ServicePermission)existingVal).getMask();
       
   580                     int newMask = ((ServicePermission)newVal).getMask();
       
   581                     if (oldMask != newMask) {
       
   582                         int effective = oldMask | newMask;
       
   583                         if (effective == newMask) {
       
   584                             return newVal;
       
   585                         }
       
   586                         if (effective != oldMask) {
       
   587                             return new ServicePermission(princName, effective);
       
   588                         }
       
   589                     }
       
   590                     return existingVal;
       
   591                 }
       
   592             }
       
   593         );
       
   594     }
       
   595 
       
   596     /**
       
   597      * Returns an enumeration of all the ServicePermission objects
       
   598      * in the container.
       
   599      *
       
   600      * @return an enumeration of all the ServicePermission objects.
       
   601      */
       
   602     @Override
       
   603     public Enumeration<Permission> elements() {
       
   604         return perms.elements();
       
   605     }
       
   606 
       
   607     private static final long serialVersionUID = -4118834211490102011L;
       
   608 
       
   609     // Need to maintain serialization interoperability with earlier releases,
       
   610     // which had the serializable field:
       
   611     // private Vector permissions;
       
   612 
       
   613     /**
       
   614      * @serialField permissions java.util.Vector
       
   615      *     A list of ServicePermission objects.
       
   616      */
       
   617     private static final ObjectStreamField[] serialPersistentFields = {
       
   618         new ObjectStreamField("permissions", Vector.class),
       
   619     };
       
   620 
       
   621     /**
       
   622      * @serialData "permissions" field (a Vector containing the ServicePermissions).
       
   623      */
       
   624     /*
       
   625      * Writes the contents of the perms field out as a Vector for
       
   626      * serialization compatibility with earlier releases.
       
   627      */
       
   628     private void writeObject(ObjectOutputStream out) throws IOException {
       
   629         // Don't call out.defaultWriteObject()
       
   630 
       
   631         // Write out Vector
       
   632         Vector<Permission> permissions = new Vector<>(perms.values());
       
   633 
       
   634         ObjectOutputStream.PutField pfields = out.putFields();
       
   635         pfields.put("permissions", permissions);
       
   636         out.writeFields();
       
   637     }
       
   638 
       
   639     /*
       
   640      * Reads in a Vector of ServicePermissions and saves them in the perms field.
       
   641      */
       
   642     @SuppressWarnings("unchecked")
       
   643     private void readObject(ObjectInputStream in)
       
   644         throws IOException, ClassNotFoundException
       
   645     {
       
   646         // Don't call defaultReadObject()
       
   647 
       
   648         // Read in serialized fields
       
   649         ObjectInputStream.GetField gfields = in.readFields();
       
   650 
       
   651         // Get the one we want
       
   652         Vector<Permission> permissions =
       
   653                 (Vector<Permission>)gfields.get("permissions", null);
       
   654         perms = new ConcurrentHashMap<>(permissions.size());
       
   655         for (Permission perm : permissions) {
       
   656             perms.put(perm.getName(), perm);
       
   657         }
       
   658     }
       
   659 }