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 sun.security.smartcardio; 27 28 import java.nio.ByteBuffer; 29 import java.security.AccessController; 30 import java.security.PrivilegedAction; 31 import javax.smartcardio.*; 32 import static sun.security.smartcardio.PCSC.*; 33 34 /** 35 * Card implementation. 36 * 37 * @since 1.6 38 * @author Andreas Sterbenz 39 */ 40 final class CardImpl extends Card { 41 42 private static enum State { OK, REMOVED, DISCONNECTED }; 43 44 // the terminal that created this card 45 private final TerminalImpl terminal; 46 47 // the native SCARDHANDLE 48 final long cardId; 49 50 // atr of this card 51 private final ATR atr; 52 53 // protocol in use, one of SCARD_PROTOCOL_T0 and SCARD_PROTOCOL_T1 54 final int protocol; 55 56 // the basic logical channel (channel 0) 57 private final ChannelImpl basicChannel; 58 59 // state of this card connection 60 private volatile State state; 61 62 // thread holding exclusive access to the card, or null 63 private volatile Thread exclusiveThread; 64 65 // used for platform specific logic 66 private static final boolean isWindows; 67 68 static { 69 final String osName = AccessController.doPrivileged( 70 (PrivilegedAction<String>) () -> System.getProperty("os.name")); 71 isWindows = osName.startsWith("Windows"); 72 } 73 74 CardImpl(TerminalImpl terminal, String protocol) throws PCSCException { 75 this.terminal = terminal; 76 int sharingMode = SCARD_SHARE_SHARED; 77 int connectProtocol; 78 if (protocol.equals("*")) { 79 connectProtocol = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1; 80 } else if (protocol.equalsIgnoreCase("T=0")) { 81 connectProtocol = SCARD_PROTOCOL_T0; 82 } else if (protocol.equalsIgnoreCase("T=1")) { 83 connectProtocol = SCARD_PROTOCOL_T1; 84 } else if (protocol.equalsIgnoreCase("direct")) { 85 // testing 86 87 // MSDN states that the preferred protocol can be zero, but doesn't 88 // specify whether other values are allowed. 89 // pcsc-lite implementation expects the preferred protocol to be non zero. 90 connectProtocol = isWindows ? 0 : SCARD_PROTOCOL_RAW; 91 92 sharingMode = SCARD_SHARE_DIRECT; 93 } else { 94 throw new IllegalArgumentException("Unsupported protocol " + protocol); 95 } 96 cardId = SCardConnect(terminal.contextId, terminal.name, 97 sharingMode, connectProtocol); 98 byte[] status = new byte[2]; 99 byte[] atrBytes = SCardStatus(cardId, status); 100 atr = new ATR(atrBytes); 101 this.protocol = status[1] & 0xff; 102 basicChannel = new ChannelImpl(this, 0); 103 state = State.OK; 104 } 105 106 void checkState() { 107 State s = state; 108 if (s == State.DISCONNECTED) { 109 throw new IllegalStateException("Card has been disconnected"); 110 } else if (s == State.REMOVED) { 111 throw new IllegalStateException("Card has been removed"); 112 } 113 } 114 115 boolean isValid() { 116 if (state != State.OK) { 117 return false; 118 } 119 // ping card via SCardStatus 120 try { 121 SCardStatus(cardId, new byte[2]); 122 return true; 123 } catch (PCSCException e) { 124 state = State.REMOVED; 125 return false; 126 } 127 } 128 129 private void checkSecurity(String action) { 130 SecurityManager sm = System.getSecurityManager(); 131 if (sm != null) { 132 sm.checkPermission(new CardPermission(terminal.name, action)); 133 } 134 } 135 136 void handleError(PCSCException e) { 137 if (e.code == SCARD_W_REMOVED_CARD) { 138 state = State.REMOVED; 139 } 140 } 141 142 public ATR getATR() { 143 return atr; 144 } 145 146 public String getProtocol() { 147 switch (protocol) { 148 case SCARD_PROTOCOL_T0: 149 return "T=0"; 150 case SCARD_PROTOCOL_T1: 151 return "T=1"; 152 default: 153 // should never occur 154 return "Unknown protocol " + protocol; 155 } 156 } 157 158 public CardChannel getBasicChannel() { 159 checkSecurity("getBasicChannel"); 160 checkState(); 161 return basicChannel; 162 } 163 164 private static int getSW(byte[] b) { 165 if (b.length < 2) { 166 return -1; 167 } 168 int sw1 = b[b.length - 2] & 0xff; 169 int sw2 = b[b.length - 1] & 0xff; 170 return (sw1 << 8) | sw2; 171 } 172 173 private static byte[] commandOpenChannel = new byte[] {0, 0x70, 0, 0, 1}; 174 175 public CardChannel openLogicalChannel() throws CardException { 176 checkSecurity("openLogicalChannel"); 177 checkState(); 178 checkExclusive(); 179 try { 180 byte[] response = SCardTransmit 181 (cardId, protocol, commandOpenChannel, 0, commandOpenChannel.length); 182 if ((response.length != 3) || (getSW(response) != 0x9000)) { 183 throw new CardException 184 ("openLogicalChannel() failed, card response: " 185 + PCSC.toString(response)); 186 } 187 return new ChannelImpl(this, response[0]); 188 } catch (PCSCException e) { 189 handleError(e); 190 throw new CardException("openLogicalChannel() failed", e); 191 } 192 } 193 194 void checkExclusive() throws CardException { 195 Thread t = exclusiveThread; 196 if (t == null) { 197 return; 198 } 199 if (t != Thread.currentThread()) { 200 throw new CardException("Exclusive access established by another Thread"); 201 } 202 } 203 204 public synchronized void beginExclusive() throws CardException { 205 checkSecurity("exclusive"); 206 checkState(); 207 if (exclusiveThread != null) { 208 throw new CardException 209 ("Exclusive access has already been assigned to Thread " 210 + exclusiveThread.getName()); 211 } 212 try { 213 SCardBeginTransaction(cardId); 214 } catch (PCSCException e) { 215 handleError(e); 216 throw new CardException("beginExclusive() failed", e); 217 } 218 exclusiveThread = Thread.currentThread(); 219 } 220 221 public synchronized void endExclusive() throws CardException { 222 checkState(); 223 if (exclusiveThread != Thread.currentThread()) { 224 throw new IllegalStateException 225 ("Exclusive access not assigned to current Thread"); 226 } 227 try { 228 SCardEndTransaction(cardId, SCARD_LEAVE_CARD); 229 } catch (PCSCException e) { 230 handleError(e); 231 throw new CardException("endExclusive() failed", e); 232 } finally { 233 exclusiveThread = null; 234 } 235 } 236 237 public byte[] transmitControlCommand(int controlCode, byte[] command) 238 throws CardException { 239 checkSecurity("transmitControl"); 240 checkState(); 241 checkExclusive(); 242 if (command == null) { 243 throw new NullPointerException(); 244 } 245 try { 246 byte[] r = SCardControl(cardId, controlCode, command); 247 return r; 248 } catch (PCSCException e) { 249 handleError(e); 250 throw new CardException("transmitControlCommand() failed", e); 251 } 252 } 253 254 private static final boolean invertReset = 255 Boolean.parseBoolean( 256 java.security.AccessController.doPrivileged( 257 new sun.security.action.GetPropertyAction( 258 "sun.security.smartcardio.invertCardReset", "false"))); 259 260 public void disconnect(boolean reset) throws CardException { 261 if (reset) { 262 checkSecurity("reset"); 263 } 264 if (state != State.OK) { 265 return; 266 } 267 checkExclusive(); 268 // to preserve old behaviour, don't change flag until here 269 if (invertReset) { 270 reset = !reset; 271 } 272 try { 273 SCardDisconnect(cardId, (reset ? SCARD_RESET_CARD : SCARD_LEAVE_CARD)); 274 } catch (PCSCException e) { 275 throw new CardException("disconnect() failed", e); 276 } finally { 277 state = State.DISCONNECTED; 278 exclusiveThread = null; 279 } 280 } 281 282 public String toString() { 283 return "PC/SC card in " + terminal.getName() 284 + ", protocol " + getProtocol() + ", state " + state; 285 } 286 287 protected void finalize() throws Throwable { 288 try { 289 if (state == State.OK) { 290 SCardDisconnect(cardId, SCARD_LEAVE_CARD); 291 } 292 } finally { 293 super.finalize(); 294 } 295 } 296 297 }