1 /* 2 * Copyright (c) 2005, 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.smartcardio; 27 28 import java.io.*; 29 import java.util.StringJoiner; 30 import java.security.Permission; 31 32 /** 33 * A permission for Smart Card operations. A CardPermission consists of the 34 * name of the card terminal the permission applies to and a set of actions 35 * that are valid for that terminal. 36 * 37 * <p>A CardPermission with a name of <code>*</code> applies to all 38 * card terminals. The actions string is a comma separated list of the actions 39 * listed below, or <code>*</code> to signify "all actions." 40 * 41 * <p>Individual actions are: 42 * <dl> 43 * <dt>connect 44 * <dd>connect to a card using 45 * {@linkplain CardTerminal#connect CardTerminal.connect()} 46 * 47 * <dt>reset 48 * <dd>reset the card using {@linkplain Card#disconnect Card.disconnect(true)} 49 * 50 * <dt>exclusive 51 * <dd>establish exclusive access to a card using 52 * {@linkplain Card#beginExclusive} and {@linkplain Card#endExclusive 53 * endExclusive()} 54 * 55 * <dt>transmitControl 56 * <dd>transmit a control command using 57 * {@linkplain Card#transmitControlCommand Card.transmitControlCommand()} 58 * 59 * <dt>getBasicChannel 60 * <dd>obtain the basic logical channel using 61 * {@linkplain Card#getBasicChannel} 62 * 63 * <dt>openLogicalChannel 64 * <dd>open a new logical channel using 65 * {@linkplain Card#openLogicalChannel} 66 * 67 * </dl> 68 * 69 * @since 1.6 70 * @author Andreas Sterbenz 71 * @author JSR 268 Expert Group 72 */ 73 public class CardPermission extends Permission { 74 75 private static final long serialVersionUID = 7146787880530705613L; 76 77 private final static int A_CONNECT = 0x01; 78 private final static int A_EXCLUSIVE = 0x02; 79 private final static int A_GET_BASIC_CHANNEL = 0x04; 80 private final static int A_OPEN_LOGICAL_CHANNEL = 0x08; 81 private final static int A_RESET = 0x10; 82 private final static int A_TRANSMIT_CONTROL = 0x20; 83 84 // sum of all the actions above 85 private final static int A_ALL = 0x3f; 86 87 private final static int[] ARRAY_MASKS = { 88 A_ALL, 89 A_CONNECT, 90 A_EXCLUSIVE, 91 A_GET_BASIC_CHANNEL, 92 A_OPEN_LOGICAL_CHANNEL, 93 A_RESET, 94 A_TRANSMIT_CONTROL, 95 }; 96 97 private final static String S_CONNECT = "connect"; 98 private final static String S_EXCLUSIVE = "exclusive"; 99 private final static String S_GET_BASIC_CHANNEL = "getBasicChannel"; 100 private final static String S_OPEN_LOGICAL_CHANNEL = "openLogicalChannel"; 101 private final static String S_RESET = "reset"; 102 private final static String S_TRANSMIT_CONTROL = "transmitControl"; 103 104 private final static String S_ALL = "*"; 105 106 private final static String[] ARRAY_STRINGS = { 107 S_ALL, 108 S_CONNECT, 109 S_EXCLUSIVE, 110 S_GET_BASIC_CHANNEL, 111 S_OPEN_LOGICAL_CHANNEL, 112 S_RESET, 113 S_TRANSMIT_CONTROL, 114 }; 115 116 private transient int mask; 117 118 /** 119 * @serial 120 */ 121 private volatile String actions; 122 123 /** 124 * Constructs a new CardPermission with the specified actions. 125 * <code>terminalName</code> is the name of a CardTerminal or <code>*</code> 126 * if this permission applies to all terminals. <code>actions</code> 127 * contains a comma-separated list of the individual actions 128 * or <code>*</code> to signify all actions. For more information, 129 * see the documentation at the top of this {@linkplain CardPermission 130 * class}. 131 * 132 * @param terminalName the name of the card terminal, or <code>*</code> 133 * @param actions the action string (or null if the set of permitted 134 * actions is empty) 135 * 136 * @throws NullPointerException if terminalName is null 137 * @throws IllegalArgumentException if actions is an invalid actions 138 * specification 139 */ 140 public CardPermission(String terminalName, String actions) { 141 super(terminalName); 142 if (terminalName == null) { 143 throw new NullPointerException(); 144 } 145 mask = getMask(actions); 146 } 147 148 private static int getMask(String actions) { 149 if ((actions == null) || (actions.length() == 0)) { 150 throw new IllegalArgumentException("actions must not be empty"); 151 } 152 153 // try exact matches for simple actions first 154 for (int i = 0; i < ARRAY_STRINGS.length; i++) { 155 if (actions == ARRAY_STRINGS[i]) { 156 return ARRAY_MASKS[i]; 157 } 158 } 159 160 if (actions.endsWith(",")) { 161 throw new IllegalArgumentException("Invalid actions: '" + actions + "'"); 162 } 163 int mask = 0; 164 String[] split = actions.split(","); 165 outer: 166 for (String s : split) { 167 for (int i = 0; i < ARRAY_STRINGS.length; i++) { 168 if (ARRAY_STRINGS[i].equalsIgnoreCase(s)) { 169 mask |= ARRAY_MASKS[i]; 170 continue outer; 171 } 172 } 173 throw new IllegalArgumentException("Invalid action: '" + s + "'"); 174 } 175 176 return mask; 177 } 178 179 private static String getActions(int mask) { 180 if (mask == A_ALL) { 181 return S_ALL; 182 } 183 StringJoiner sj = new StringJoiner(","); 184 for (int i = 0; i < ARRAY_MASKS.length; i++) { 185 final int action = ARRAY_MASKS[i]; 186 if ((mask & action) == action) { 187 sj.add(ARRAY_STRINGS[i]); 188 } 189 } 190 return sj.toString(); 191 } 192 193 194 /** 195 * Returns the canonical string representation of the actions. 196 * It is <code>*</code> to signify all actions defined by this class or 197 * the string concatenation of the comma-separated, 198 * lexicographically sorted list of individual actions. 199 * 200 * @return the canonical string representation of the actions. 201 */ 202 public String getActions() { 203 if (actions == null) { 204 actions = getActions(mask); 205 } 206 return actions; 207 } 208 209 /** 210 * Checks if this CardPermission object implies the specified permission. 211 * That is the case, if and only if 212 * <ul> 213 * <li><p><code>permission</code> is an instance of CardPermission,</p> 214 * <li><p><code>permission</code>'s actions are a proper subset of this 215 * object's actions, and</p> 216 * <li><p>this object's <code>getName()</code> method is either 217 * <code>*</code> or equal to <code>permission</code>'s <code>name</code>. 218 * </p> 219 * </ul> 220 * 221 * @param permission the permission to check against 222 * @return true if and only if this CardPermission object implies the 223 * specified permission. 224 */ 225 public boolean implies(Permission permission) { 226 if (permission instanceof CardPermission == false) { 227 return false; 228 } 229 CardPermission other = (CardPermission)permission; 230 if ((this.mask & other.mask) != other.mask) { 231 return false; 232 } 233 String thisName = getName(); 234 if (thisName.equals("*")) { 235 return true; 236 } 237 if (thisName.equals(other.getName())) { 238 return true; 239 } 240 return false; 241 } 242 243 /** 244 * Compares the specified object with this CardPermission for equality. 245 * This CardPermission is equal to another Object <code>object</code>, if 246 * and only if 247 * <ul> 248 * <li><p><code>object</code> is an instance of CardPermission,</p> 249 * <li><p><code>this.getName()</code> is equal to 250 * <code>((CardPermission)object).getName()</code>, and</p> 251 * <li><p><code>this.getActions()</code> is equal to 252 * <code>((CardPermission)object).getActions()</code>.</p> 253 * </ul> 254 * 255 * @param obj the object to be compared for equality with this CardPermission 256 * @return true if and only if the specified object is equal to this 257 * CardPermission 258 */ 259 public boolean equals(Object obj) { 260 if (this == obj) { 261 return true; 262 } 263 if (obj instanceof CardPermission == false) { 264 return false; 265 } 266 CardPermission other = (CardPermission)obj; 267 return this.getName().equals(other.getName()) && (this.mask == other.mask); 268 } 269 270 /** 271 * Returns the hash code value for this CardPermission object. 272 * 273 * @return the hash code value for this CardPermission object. 274 */ 275 public int hashCode() { 276 return getName().hashCode() + 31 * mask; 277 } 278 279 private void writeObject(ObjectOutputStream s) throws IOException { 280 // Write out the actions. The superclass takes care of the name. 281 // Call getActions to make sure actions field is initialized 282 if (actions == null) { 283 getActions(); 284 } 285 s.defaultWriteObject(); 286 } 287 288 private void readObject(ObjectInputStream s) 289 throws IOException, ClassNotFoundException { 290 // Read in the actions, then restore the mask. 291 s.defaultReadObject(); 292 mask = getMask(actions); 293 } 294 295 }