Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/javax/security/auth/kerberos/ServicePermission.java
38918 views
/*1* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package javax.security.auth.kerberos;2627import java.util.*;28import java.security.Permission;29import java.security.PermissionCollection;30import java.io.ObjectStreamField;31import java.io.ObjectOutputStream;32import java.io.ObjectInputStream;33import java.io.IOException;3435/**36* This class is used to protect Kerberos services and the37* credentials necessary to access those services. There is a one to38* one mapping of a service principal and the credentials necessary39* to access the service. Therefore granting access to a service40* principal implicitly grants access to the credential necessary to41* establish a security context with the service principal. This42* applies regardless of whether the credentials are in a cache43* or acquired via an exchange with the KDC. The credential can44* be either a ticket granting ticket, a service ticket or a secret45* key from a key table.46* <p>47* A ServicePermission contains a service principal name and48* a list of actions which specify the context the credential can be49* used within.50* <p>51* The service principal name is the canonical name of the52* {@code KerberosPrincipal} supplying the service, that is53* the KerberosPrincipal represents a Kerberos service54* principal. This name is treated in a case sensitive manner.55* An asterisk may appear by itself, to signify any service principal.56* <p>57* Granting this permission implies that the caller can use a cached58* credential (TGT, service ticket or secret key) within the context59* designated by the action. In the case of the TGT, granting this60* permission also implies that the TGT can be obtained by an61* Authentication Service exchange.62* <p>63* The possible actions are:64*65* <pre>66* initiate - allow the caller to use the credential to67* initiate a security context with a service68* principal.69*70* accept - allow the caller to use the credential to71* accept security context as a particular72* principal.73* </pre>74*75* For example, to specify the permission to access to the TGT to76* initiate a security context the permission is constructed as follows:77*78* <pre>79* ServicePermission("krbtgt/[email protected]", "initiate");80* </pre>81* <p>82* To obtain a service ticket to initiate a context with the "host"83* service the permission is constructed as follows:84* <pre>85* ServicePermission("host/[email protected]", "initiate");86* </pre>87* <p>88* For a Kerberized server the action is "accept". For example, the permission89* necessary to access and use the secret key of the Kerberized "host"90* service (telnet and the likes) would be constructed as follows:91*92* <pre>93* ServicePermission("host/[email protected]", "accept");94* </pre>95*96* @since 1.497*/9899public final class ServicePermission extends Permission100implements java.io.Serializable {101102private static final long serialVersionUID = -1227585031618624935L;103104/**105* Initiate a security context to the specified service106*/107private final static int INITIATE = 0x1;108109/**110* Accept a security context111*/112private final static int ACCEPT = 0x2;113114/**115* All actions116*/117private final static int ALL = INITIATE|ACCEPT;118119/**120* No actions.121*/122private final static int NONE = 0x0;123124// the actions mask125private transient int mask;126127/**128* the actions string.129*130* @serial131*/132133private String actions; // Left null as long as possible, then134// created and re-used in the getAction function.135136/**137* Create a new {@code ServicePermission}138* with the specified {@code servicePrincipal}139* and {@code action}.140*141* @param servicePrincipal the name of the service principal.142* An asterisk may appear by itself, to signify any service principal.143* <p>144* @param action the action string145*/146public ServicePermission(String servicePrincipal, String action) {147// Note: servicePrincipal can be "@REALM" which means any principal in148// this realm implies it. action can be "-" which means any149// action implies it.150super(servicePrincipal);151init(servicePrincipal, getMask(action));152}153154155/**156* Initialize the ServicePermission object.157*/158private void init(String servicePrincipal, int mask) {159160if (servicePrincipal == null)161throw new NullPointerException("service principal can't be null");162163if ((mask & ALL) != mask)164throw new IllegalArgumentException("invalid actions mask");165166this.mask = mask;167}168169170/**171* Checks if this Kerberos service permission object "implies" the172* specified permission.173* <P>174* If none of the above are true, {@code implies} returns false.175* @param p the permission to check against.176*177* @return true if the specified permission is implied by this object,178* false if not.179*/180public boolean implies(Permission p) {181if (!(p instanceof ServicePermission))182return false;183184ServicePermission that = (ServicePermission) p;185186return ((this.mask & that.mask) == that.mask) &&187impliesIgnoreMask(that);188}189190191boolean impliesIgnoreMask(ServicePermission p) {192return ((this.getName().equals("*")) ||193this.getName().equals(p.getName()) ||194(p.getName().startsWith("@") &&195this.getName().endsWith(p.getName())));196}197198/**199* Checks two ServicePermission objects for equality.200* <P>201* @param obj the object to test for equality with this object.202*203* @return true if <i>obj</i> is a ServicePermission, and has the204* same service principal, and actions as this205* ServicePermission object.206*/207public boolean equals(Object obj) {208if (obj == this)209return true;210211if (! (obj instanceof ServicePermission))212return false;213214ServicePermission that = (ServicePermission) obj;215return ((this.mask & that.mask) == that.mask) &&216this.getName().equals(that.getName());217218219}220221/**222* Returns the hash code value for this object.223*224* @return a hash code value for this object.225*/226227public int hashCode() {228return (getName().hashCode() ^ mask);229}230231232/**233* Returns the "canonical string representation" of the actions in the234* specified mask.235* Always returns present actions in the following order:236* initiate, accept.237*238* @param mask a specific integer action mask to translate into a string239* @return the canonical string representation of the actions240*/241private static String getActions(int mask)242{243StringBuilder sb = new StringBuilder();244boolean comma = false;245246if ((mask & INITIATE) == INITIATE) {247if (comma) sb.append(',');248else comma = true;249sb.append("initiate");250}251252if ((mask & ACCEPT) == ACCEPT) {253if (comma) sb.append(',');254else comma = true;255sb.append("accept");256}257258return sb.toString();259}260261/**262* Returns the canonical string representation of the actions.263* Always returns present actions in the following order:264* initiate, accept.265*/266public String getActions() {267if (actions == null)268actions = getActions(this.mask);269270return actions;271}272273274/**275* Returns a PermissionCollection object for storing276* ServicePermission objects.277* <br>278* ServicePermission objects must be stored in a manner that279* allows them to be inserted into the collection in any order, but280* that also enables the PermissionCollection implies method to281* be implemented in an efficient (and consistent) manner.282*283* @return a new PermissionCollection object suitable for storing284* ServicePermissions.285*/286public PermissionCollection newPermissionCollection() {287return new KrbServicePermissionCollection();288}289290/**291* Return the current action mask.292*293* @return the actions mask.294*/295int getMask() {296return mask;297}298299/**300* Convert an action string to an integer actions mask.301*302* Note: if action is "-", action will be NONE, which means any303* action implies it.304*305* @param action the action string.306* @return the action mask307*/308private static int getMask(String action) {309310if (action == null) {311throw new NullPointerException("action can't be null");312}313314if (action.equals("")) {315throw new IllegalArgumentException("action can't be empty");316}317318int mask = NONE;319320char[] a = action.toCharArray();321322if (a.length == 1 && a[0] == '-') {323return mask;324}325326int i = a.length - 1;327328while (i != -1) {329char c;330331// skip whitespace332while ((i!=-1) && ((c = a[i]) == ' ' ||333c == '\r' ||334c == '\n' ||335c == '\f' ||336c == '\t'))337i--;338339// check for the known strings340int matchlen;341342if (i >= 7 && (a[i-7] == 'i' || a[i-7] == 'I') &&343(a[i-6] == 'n' || a[i-6] == 'N') &&344(a[i-5] == 'i' || a[i-5] == 'I') &&345(a[i-4] == 't' || a[i-4] == 'T') &&346(a[i-3] == 'i' || a[i-3] == 'I') &&347(a[i-2] == 'a' || a[i-2] == 'A') &&348(a[i-1] == 't' || a[i-1] == 'T') &&349(a[i] == 'e' || a[i] == 'E'))350{351matchlen = 8;352mask |= INITIATE;353354} else if (i >= 5 && (a[i-5] == 'a' || a[i-5] == 'A') &&355(a[i-4] == 'c' || a[i-4] == 'C') &&356(a[i-3] == 'c' || a[i-3] == 'C') &&357(a[i-2] == 'e' || a[i-2] == 'E') &&358(a[i-1] == 'p' || a[i-1] == 'P') &&359(a[i] == 't' || a[i] == 'T'))360{361matchlen = 6;362mask |= ACCEPT;363364} else {365// parse error366throw new IllegalArgumentException(367"invalid permission: " + action);368}369370// make sure we didn't just match the tail of a word371// like "ackbarfaccept". Also, skip to the comma.372boolean seencomma = false;373while (i >= matchlen && !seencomma) {374switch(a[i-matchlen]) {375case ',':376seencomma = true;377break;378case ' ': case '\r': case '\n':379case '\f': case '\t':380break;381default:382throw new IllegalArgumentException(383"invalid permission: " + action);384}385i--;386}387388// point i at the location of the comma minus one (or -1).389i -= matchlen;390}391392return mask;393}394395396/**397* WriteObject is called to save the state of the ServicePermission398* to a stream. The actions are serialized, and the superclass399* takes care of the name.400*/401private void writeObject(java.io.ObjectOutputStream s)402throws IOException403{404// Write out the actions. The superclass takes care of the name405// call getActions to make sure actions field is initialized406if (actions == null)407getActions();408s.defaultWriteObject();409}410411/**412* readObject is called to restore the state of the413* ServicePermission from a stream.414*/415private void readObject(java.io.ObjectInputStream s)416throws IOException, ClassNotFoundException417{418// Read in the action, then initialize the rest419s.defaultReadObject();420init(getName(),getMask(actions));421}422423424/*425public static void main(String args[]) throws Exception {426ServicePermission this_ =427new ServicePermission(args[0], "accept");428ServicePermission that_ =429new ServicePermission(args[1], "accept,initiate");430System.out.println("-----\n");431System.out.println("this.implies(that) = " + this_.implies(that_));432System.out.println("-----\n");433System.out.println("this = "+this_);434System.out.println("-----\n");435System.out.println("that = "+that_);436System.out.println("-----\n");437438KrbServicePermissionCollection nps =439new KrbServicePermissionCollection();440nps.add(this_);441nps.add(new ServicePermission("nfs/[email protected]",442"accept"));443nps.add(new ServicePermission("host/[email protected]",444"initiate"));445System.out.println("nps.implies(that) = " + nps.implies(that_));446System.out.println("-----\n");447448Enumeration e = nps.elements();449450while (e.hasMoreElements()) {451ServicePermission x =452(ServicePermission) e.nextElement();453System.out.println("nps.e = " + x);454}455456}457*/458459}460461462final class KrbServicePermissionCollection extends PermissionCollection463implements java.io.Serializable {464465// Not serialized; see serialization section at end of class466private transient List<Permission> perms;467468public KrbServicePermissionCollection() {469perms = new ArrayList<Permission>();470}471472/**473* Check and see if this collection of permissions implies the permissions474* expressed in "permission".475*476* @param permission the Permission object to compare477*478* @return true if "permission" is a proper subset of a permission in479* the collection, false if not.480*/481public boolean implies(Permission permission) {482if (! (permission instanceof ServicePermission))483return false;484485ServicePermission np = (ServicePermission) permission;486int desired = np.getMask();487488if (desired == 0) {489for (Permission p: perms) {490ServicePermission sp = (ServicePermission)p;491if (sp.impliesIgnoreMask(np)) {492return true;493}494}495return false;496}497498int effective = 0;499int needed = desired;500501synchronized (this) {502int len = perms.size();503504// need to deal with the case where the needed permission has505// more than one action and the collection has individual permissions506// that sum up to the needed.507508for (int i = 0; i < len; i++) {509ServicePermission x = (ServicePermission) perms.get(i);510511//System.out.println(" trying "+x);512if (((needed & x.getMask()) != 0) && x.impliesIgnoreMask(np)) {513effective |= x.getMask();514if ((effective & desired) == desired)515return true;516needed = (desired ^ effective);517}518}519}520return false;521}522523/**524* Adds a permission to the ServicePermissions. The key for525* the hash is the name.526*527* @param permission the Permission object to add.528*529* @exception IllegalArgumentException - if the permission is not a530* ServicePermission531*532* @exception SecurityException - if this PermissionCollection object533* has been marked readonly534*/535public void add(Permission permission) {536if (! (permission instanceof ServicePermission))537throw new IllegalArgumentException("invalid permission: "+538permission);539if (isReadOnly())540throw new SecurityException("attempt to add a Permission to a readonly PermissionCollection");541542synchronized (this) {543perms.add(0, permission);544}545}546547/**548* Returns an enumeration of all the ServicePermission objects549* in the container.550*551* @return an enumeration of all the ServicePermission objects.552*/553554public Enumeration<Permission> elements() {555// Convert Iterator into Enumeration556synchronized (this) {557return Collections.enumeration(perms);558}559}560561private static final long serialVersionUID = -4118834211490102011L;562563// Need to maintain serialization interoperability with earlier releases,564// which had the serializable field:565// private Vector permissions;566567/**568* @serialField permissions java.util.Vector569* A list of ServicePermission objects.570*/571private static final ObjectStreamField[] serialPersistentFields = {572new ObjectStreamField("permissions", Vector.class),573};574575/**576* @serialData "permissions" field (a Vector containing the ServicePermissions).577*/578/*579* Writes the contents of the perms field out as a Vector for580* serialization compatibility with earlier releases.581*/582private void writeObject(ObjectOutputStream out) throws IOException {583// Don't call out.defaultWriteObject()584585// Write out Vector586Vector<Permission> permissions = new Vector<>(perms.size());587588synchronized (this) {589permissions.addAll(perms);590}591592ObjectOutputStream.PutField pfields = out.putFields();593pfields.put("permissions", permissions);594out.writeFields();595}596597/*598* Reads in a Vector of ServicePermissions and saves them in the perms field.599*/600@SuppressWarnings("unchecked")601private void readObject(ObjectInputStream in)602throws IOException, ClassNotFoundException603{604// Don't call defaultReadObject()605606// Read in serialized fields607ObjectInputStream.GetField gfields = in.readFields();608609// Get the one we want610Vector<Permission> permissions =611(Vector<Permission>)gfields.get("permissions", null);612perms = new ArrayList<Permission>(permissions.size());613perms.addAll(permissions);614}615}616617618