Path: blob/master/src/java.smartcardio/share/classes/sun/security/smartcardio/CardImpl.java
41159 views
/*1* Copyright (c) 2005, 2021, 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 sun.security.smartcardio;2627import java.nio.ByteBuffer;28import java.security.AccessController;29import java.security.PrivilegedAction;30import javax.smartcardio.*;31import static sun.security.smartcardio.PCSC.*;3233/**34* Card implementation.35*36* @since 1.637* @author Andreas Sterbenz38*/39final class CardImpl extends Card {4041private static enum State { OK, REMOVED, DISCONNECTED };4243// the terminal that created this card44private final TerminalImpl terminal;4546// the native SCARDHANDLE47final long cardId;4849// atr of this card50private final ATR atr;5152// protocol in use, one of SCARD_PROTOCOL_T0 and SCARD_PROTOCOL_T153final int protocol;5455// the basic logical channel (channel 0)56private final ChannelImpl basicChannel;5758// state of this card connection59private volatile State state;6061// thread holding exclusive access to the card, or null62private volatile Thread exclusiveThread;6364// used for platform specific logic65private static final boolean isWindows;6667static {68@SuppressWarnings("removal")69final String osName = AccessController.doPrivileged(70(PrivilegedAction<String>) () -> System.getProperty("os.name"));71isWindows = osName.startsWith("Windows");72}7374CardImpl(TerminalImpl terminal, String protocol) throws PCSCException {75this.terminal = terminal;76int sharingMode = SCARD_SHARE_SHARED;77int connectProtocol;78if (protocol.equals("*")) {79connectProtocol = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1;80} else if (protocol.equalsIgnoreCase("T=0")) {81connectProtocol = SCARD_PROTOCOL_T0;82} else if (protocol.equalsIgnoreCase("T=1")) {83connectProtocol = SCARD_PROTOCOL_T1;84} else if (protocol.equalsIgnoreCase("direct")) {85// testing8687// MSDN states that the preferred protocol can be zero, but doesn't88// specify whether other values are allowed.89// pcsc-lite implementation expects the preferred protocol to be non zero.90connectProtocol = isWindows ? 0 : SCARD_PROTOCOL_RAW;9192sharingMode = SCARD_SHARE_DIRECT;93} else {94throw new IllegalArgumentException("Unsupported protocol " + protocol);95}96cardId = SCardConnect(terminal.contextId, terminal.name,97sharingMode, connectProtocol);98byte[] status = new byte[2];99byte[] atrBytes = SCardStatus(cardId, status);100atr = new ATR(atrBytes);101this.protocol = status[1] & 0xff;102basicChannel = new ChannelImpl(this, 0);103state = State.OK;104}105106void checkState() {107State s = state;108if (s == State.DISCONNECTED) {109throw new IllegalStateException("Card has been disconnected");110} else if (s == State.REMOVED) {111throw new IllegalStateException("Card has been removed");112}113}114115boolean isValid() {116if (state != State.OK) {117return false;118}119// ping card via SCardStatus120try {121SCardStatus(cardId, new byte[2]);122return true;123} catch (PCSCException e) {124state = State.REMOVED;125return false;126}127}128129private void checkSecurity(String action) {130@SuppressWarnings("removal")131SecurityManager sm = System.getSecurityManager();132if (sm != null) {133sm.checkPermission(new CardPermission(terminal.name, action));134}135}136137void handleError(PCSCException e) {138if (e.code == SCARD_W_REMOVED_CARD) {139state = State.REMOVED;140}141}142143public ATR getATR() {144return atr;145}146147public String getProtocol() {148switch (protocol) {149case SCARD_PROTOCOL_T0:150return "T=0";151case SCARD_PROTOCOL_T1:152return "T=1";153default:154// should never occur155return "Unknown protocol " + protocol;156}157}158159public CardChannel getBasicChannel() {160checkSecurity("getBasicChannel");161checkState();162return basicChannel;163}164165private static int getSW(byte[] b) {166if (b.length < 2) {167return -1;168}169int sw1 = b[b.length - 2] & 0xff;170int sw2 = b[b.length - 1] & 0xff;171return (sw1 << 8) | sw2;172}173174private static byte[] commandOpenChannel = new byte[] {0, 0x70, 0, 0, 1};175176public CardChannel openLogicalChannel() throws CardException {177checkSecurity("openLogicalChannel");178checkState();179checkExclusive();180try {181byte[] response = SCardTransmit182(cardId, protocol, commandOpenChannel, 0, commandOpenChannel.length);183if ((response.length != 3) || (getSW(response) != 0x9000)) {184throw new CardException185("openLogicalChannel() failed, card response: "186+ PCSC.toString(response));187}188return new ChannelImpl(this, response[0]);189} catch (PCSCException e) {190handleError(e);191throw new CardException("openLogicalChannel() failed", e);192}193}194195void checkExclusive() throws CardException {196Thread t = exclusiveThread;197if (t == null) {198return;199}200if (t != Thread.currentThread()) {201throw new CardException("Exclusive access established by another Thread");202}203}204205public synchronized void beginExclusive() throws CardException {206checkSecurity("exclusive");207checkState();208if (exclusiveThread != null) {209throw new CardException210("Exclusive access has already been assigned to Thread "211+ exclusiveThread.getName());212}213try {214SCardBeginTransaction(cardId);215} catch (PCSCException e) {216handleError(e);217throw new CardException("beginExclusive() failed", e);218}219exclusiveThread = Thread.currentThread();220}221222public synchronized void endExclusive() throws CardException {223checkState();224if (exclusiveThread != Thread.currentThread()) {225throw new IllegalStateException226("Exclusive access not assigned to current Thread");227}228try {229SCardEndTransaction(cardId, SCARD_LEAVE_CARD);230} catch (PCSCException e) {231handleError(e);232throw new CardException("endExclusive() failed", e);233} finally {234exclusiveThread = null;235}236}237238public byte[] transmitControlCommand(int controlCode, byte[] command)239throws CardException {240checkSecurity("transmitControl");241checkState();242checkExclusive();243if (command == null) {244throw new NullPointerException();245}246try {247byte[] r = SCardControl(cardId, controlCode, command);248return r;249} catch (PCSCException e) {250handleError(e);251throw new CardException("transmitControlCommand() failed", e);252}253}254255public void disconnect(boolean reset) throws CardException {256if (reset) {257checkSecurity("reset");258}259if (state != State.OK) {260return;261}262checkExclusive();263try {264SCardDisconnect(cardId, (reset ? SCARD_RESET_CARD : SCARD_LEAVE_CARD));265} catch (PCSCException e) {266throw new CardException("disconnect() failed", e);267} finally {268state = State.DISCONNECTED;269exclusiveThread = null;270}271}272273public String toString() {274return "PC/SC card in " + terminal.name275+ ", protocol " + getProtocol() + ", state " + state;276}277278@SuppressWarnings("deprecation")279protected void finalize() throws Throwable {280try {281if (state == State.OK) {282state = State.DISCONNECTED;283SCardDisconnect(cardId, SCARD_LEAVE_CARD);284}285} finally {286super.finalize();287}288}289290}291292293