Commit c9f98d46 authored by Tobias Wich's avatar Tobias Wich
Browse files

Merge branch 'win-init'

parents bed826fa 9781639b
/**************************************************************************** /****************************************************************************
* Copyright (C) 2015 ecsec GmbH. * Copyright (C) 2015-2019 ecsec GmbH.
* All rights reserved. * All rights reserved.
* Contact: ecsec GmbH (info@ecsec.de) * Contact: ecsec GmbH (info@ecsec.de)
* *
...@@ -154,9 +154,9 @@ public enum SCIOErrorCode { ...@@ -154,9 +154,9 @@ public enum SCIOErrorCode {
/** No PIN was presented to the smart card. */ /** No PIN was presented to the smart card. */
SCARD_W_CARD_NOT_AUTHENTICATED (0x8010006F); SCARD_W_CARD_NOT_AUTHENTICATED (0x8010006F);
private final int[] codes; private final long[] codes;
private SCIOErrorCode(int... codes) { private SCIOErrorCode(long... codes) {
this.codes = codes; this.codes = codes;
} }
...@@ -166,8 +166,8 @@ public enum SCIOErrorCode { ...@@ -166,8 +166,8 @@ public enum SCIOErrorCode {
* @param code Code to test. * @param code Code to test.
* @return {@code true} if the code represents this enum instance, {@code false} otherwise. * @return {@code true} if the code represents this enum instance, {@code false} otherwise.
*/ */
public boolean matchesCode(int code) { public boolean matchesCode(long code) {
for (int c : codes) { for (long c : codes) {
if (c == code) { if (c == code) {
return true; return true;
} }
...@@ -181,15 +181,25 @@ public enum SCIOErrorCode { ...@@ -181,15 +181,25 @@ public enum SCIOErrorCode {
* @param code The code for which to look up the enum entry. * @param code The code for which to look up the enum entry.
* @return The entry matching the given code, or {@link #SCARD_F_UNKNOWN_ERROR} if no known code has been given. * @return The entry matching the given code, or {@link #SCARD_F_UNKNOWN_ERROR} if no known code has been given.
*/ */
public static SCIOErrorCode getErrorCode(int code) { public static SCIOErrorCode getErrorCode(long code) {
// no index, just walk over each code, doesn't happen so often that performance should be a problem // no index, just walk over each code, doesn't happen so often that performance should be a problem
for (SCIOErrorCode next : SCIOErrorCode.values()) { for (SCIOErrorCode next : SCIOErrorCode.values()) {
if (next.matchesCode(code)) { if (next.matchesCode(code)) {
return next; return next;
} }
} }
// no match found, unkown error // no match found, unknown error
return SCARD_F_UNKNOWN_ERROR; return SCARD_F_UNKNOWN_ERROR;
} }
public static long getLong(SCIOErrorCode code) {
for (SCIOErrorCode next : SCIOErrorCode.values()) {
if (next.name().equals(code.name())) {
return next.codes[0];
}
}
// no match found, unknown error (SCARD_F_UNKNOWN_ERROR)
return -2146435052;
}
} }
...@@ -24,6 +24,11 @@ ...@@ -24,6 +24,11 @@
<artifactId>wsdef-client</artifactId> <artifactId>wsdef-client</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>io.github.jnasmartcardio</groupId>
<artifactId>jnasmartcardio</artifactId>
<version>0.2.7</version>
</dependency>
</dependencies> </dependencies>
</project> </project>
/**************************************************************************** /****************************************************************************
* Copyright (C) 2015-2018 ecsec GmbH. * Copyright (C) 2015-2019 ecsec GmbH.
* All rights reserved. * All rights reserved.
* Contact: ecsec GmbH (info@ecsec.de) * Contact: ecsec GmbH (info@ecsec.de)
* *
...@@ -24,9 +24,8 @@ package org.openecard.scio; ...@@ -24,9 +24,8 @@ package org.openecard.scio;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.smartcardio.CardException; import javax.smartcardio.CardException;
import jnasmartcardio.Smartcardio.JnaPCSCException;
import org.openecard.common.ifd.scio.SCIOErrorCode; import org.openecard.common.ifd.scio.SCIOErrorCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
...@@ -36,50 +35,20 @@ import org.slf4j.LoggerFactory; ...@@ -36,50 +35,20 @@ import org.slf4j.LoggerFactory;
*/ */
public class PCSCExceptionExtractor { public class PCSCExceptionExtractor {
private static final Logger LOG = LoggerFactory.getLogger(PCSCExceptionExtractor.class);
public static SCIOErrorCode getCode(@Nonnull CardException mainException) { public static SCIOErrorCode getCode(@Nonnull CardException mainException) {
return getCode((Exception) mainException); return getCode((JnaPCSCException) mainException);
} }
/** /**
* Gets the actual error code from the given CardException. * Gets the actual error code from the given JnaPCSCException.
* This method uses reflections to access the actual error code which is hidden in the Java SmartcardIO. In case no * In case no error code can be found, {@link SCIOErrorCode#SCARD_F_UNKNOWN_ERROR} is returned.
* error code can be found, {@link SCIOErrorCode#SCARD_F_UNKNOWN_ERROR} is returned.
* *
* @param mainException The exception coming from the Java SmartcardIO. * @param mainException The exception coming from the Java SmartcardIO.
* @return The code extracted from the exception, or {@link SCIOErrorCode#SCARD_F_UNKNOWN_ERROR} if no code could be * @return The code extracted from the exception, or {@link SCIOErrorCode#SCARD_F_UNKNOWN_ERROR} if no code could be
* extracted. * extracted.
*/ */
public static SCIOErrorCode getCode(@Nonnull Exception mainException) { public static SCIOErrorCode getCode(@Nonnull JnaPCSCException mainException) {
Throwable cause = getPCSCException(mainException); return SCIOErrorCode.getErrorCode(mainException.code);
// check the type of the cause over reflections because these classes might not be available (sun internal)
if (cause != null) {
try {
return SCIOErrorCode.valueOf(cause.getMessage());
} catch (IllegalArgumentException ex) {
return SCIOErrorCode.SCARD_F_UNKNOWN_ERROR;
}
} else {
return SCIOErrorCode.SCARD_F_UNKNOWN_ERROR;
}
}
public static boolean hasPCSCException(Exception mainException) {
return getPCSCException(mainException) != null;
}
private static Throwable getPCSCException(Exception mainException) {
Throwable cause = mainException.getCause();
// check the type of the cause over reflections because these classes might not be available (sun internal)
if (cause != null) {
Class<?> c = cause.getClass();
if ("sun.security.smartcardio.PCSCException".equals(c.getName())) {
return cause;
}
}
return null;
} }
} }
/**************************************************************************** /****************************************************************************
* Copyright (C) 2012-2018 ecsec GmbH. * Copyright (C) 2012-2019 ecsec GmbH.
* All rights reserved. * All rights reserved.
* Contact: ecsec GmbH (info@ecsec.de) * Contact: ecsec GmbH (info@ecsec.de)
* *
...@@ -24,13 +24,9 @@ package org.openecard.scio; ...@@ -24,13 +24,9 @@ package org.openecard.scio;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import javax.smartcardio.CardTerminals;
import javax.smartcardio.TerminalFactory; import javax.smartcardio.TerminalFactory;
import org.openecard.common.ifd.scio.SCIOErrorCode; import jnasmartcardio.Smartcardio;
import org.openecard.common.ifd.scio.SCIOTerminals; import org.openecard.common.ifd.scio.SCIOTerminals;
import org.openecard.common.util.LinuxLibraryFinder; import org.openecard.common.util.LinuxLibraryFinder;
import org.slf4j.Logger; import org.slf4j.Logger;
...@@ -63,12 +59,24 @@ public class PCSCFactory implements org.openecard.common.ifd.scio.TerminalFactor ...@@ -63,12 +59,24 @@ public class PCSCFactory implements org.openecard.common.ifd.scio.TerminalFactor
File libFile = LinuxLibraryFinder.getLibraryPath("pcsclite", "1"); File libFile = LinuxLibraryFinder.getLibraryPath("pcsclite", "1");
System.setProperty("sun.security.smartcardio.library", libFile.getAbsolutePath()); System.setProperty("sun.security.smartcardio.library", libFile.getAbsolutePath());
} }
loadPCSC();
try {
LOG.info("Trying to initialize PCSC subsystem.");
terminalFactory = TerminalFactory.getInstance(ALGORITHM, null, new Smartcardio());
LOG.info("Successfully initialized PCSC subsystem.");
} catch (NoSuchAlgorithmException ex) {
LOG.error("Failed to initialize smartcard system.");
throw ex;
}
} }
@Override @Override
public String getType() { public String getType() {
return terminalFactory.getType(); if (terminalFactory != null) {
return terminalFactory.getType();
} else {
return "PC/SC";
}
} }
@Override @Override
...@@ -80,80 +88,4 @@ public class PCSCFactory implements org.openecard.common.ifd.scio.TerminalFactor ...@@ -80,80 +88,4 @@ public class PCSCFactory implements org.openecard.common.ifd.scio.TerminalFactor
return terminalFactory; return terminalFactory;
} }
private static Integer versionCompare(String str1, String str2) {
// code taken from http://stackoverflow.com/a/6702029
String[] vals1 = str1.split("\\.");
String[] vals2 = str2.split("\\.");
int i = 0;
while (i < vals1.length && i < vals2.length && vals1[i].equals(vals2[i])) {
i++;
}
if (i < vals1.length && i < vals2.length) {
return Integer.signum(Integer.valueOf(vals1[i]).compareTo(Integer.valueOf(vals2[i])));
}
return Integer.signum(vals1.length - vals2.length);
}
final void loadPCSC() throws NoSuchAlgorithmException {
terminalFactory = TerminalFactory.getInstance(ALGORITHM, null);
}
void reloadPCSC() {
try {
// code taken from http://stackoverflow.com/questions/16921785/
Class pcscterminal = Class.forName("sun.security.smartcardio.PCSCTerminals");
Field contextId = pcscterminal.getDeclaredField("contextId");
contextId.setAccessible(true);
if (contextId.getLong(pcscterminal) != 0L) {
// First get a new context value
Class pcsc = Class.forName("sun.security.smartcardio.PCSC");
Method SCardEstablishContext = pcsc.getDeclaredMethod("SCardEstablishContext", Integer.TYPE);
SCardEstablishContext.setAccessible(true);
Field SCARD_SCOPE_USER = pcsc.getDeclaredField("SCARD_SCOPE_USER");
SCARD_SCOPE_USER.setAccessible(true);
long newId = ((Long) SCardEstablishContext.invoke(pcsc, SCARD_SCOPE_USER.getInt(pcsc)));
contextId.setLong(pcscterminal, newId);
// Then clear the terminals in cache
loadPCSC();
CardTerminals terminals = terminalFactory.terminals();
Field fieldTerminals = pcscterminal.getDeclaredField("terminals");
fieldTerminals.setAccessible(true);
Class classMap = Class.forName("java.util.Map");
Method clearMap = classMap.getDeclaredMethod("clear");
clearMap.invoke(fieldTerminals.get(terminals));
}
} catch (NoSuchAlgorithmException ex) {
// if it worked once it will work again
String msg = "PCSC changed it's algorithm. There is something really wrong.";
LOG.error(msg, ex);
throw new RuntimeException("PCSC changed it's algorithm. There is something really wrong.");
} catch (ClassNotFoundException | IllegalAccessException | NoSuchFieldException | NoSuchMethodException |
SecurityException ex) {
LOG.error("Failed to perform reflection magic to reload TerminalFactory.", ex);
} catch (InvocationTargetException ex) {
if (PCSCExceptionExtractor.hasPCSCException(ex)) {
SCIOErrorCode code = PCSCExceptionExtractor.getCode(ex);
if (code == SCIOErrorCode.SCARD_E_NO_SERVICE) {
// silent drop after giving the system some time to recover for themselves
try {
Thread.sleep(5000);
} catch (InterruptedException ignore) {
Thread.currentThread().interrupt();
}
return;
}
}
LOG.error("Error while invoking PCSC restart functionality.");
}
}
} }
/**************************************************************************** /****************************************************************************
* Copyright (C) 2014-2015 TU Darmstadt. * Copyright (C) 2014-2019 TU Darmstadt.
* All rights reserved. * All rights reserved.
* Contact: ecsec GmbH (info@ecsec.de) * Contact: ecsec GmbH (info@ecsec.de)
* *
...@@ -33,6 +33,7 @@ import javax.annotation.Nonnull; ...@@ -33,6 +33,7 @@ import javax.annotation.Nonnull;
import javax.smartcardio.CardException; import javax.smartcardio.CardException;
import javax.smartcardio.CardTerminal; import javax.smartcardio.CardTerminal;
import javax.smartcardio.CardTerminals; import javax.smartcardio.CardTerminals;
import jnasmartcardio.Smartcardio;
import org.openecard.common.ifd.scio.NoSuchTerminal; import org.openecard.common.ifd.scio.NoSuchTerminal;
import org.openecard.common.ifd.scio.SCIOErrorCode; import org.openecard.common.ifd.scio.SCIOErrorCode;
import org.openecard.common.ifd.scio.SCIOException; import org.openecard.common.ifd.scio.SCIOException;
...@@ -62,16 +63,36 @@ public class PCSCTerminals implements SCIOTerminals { ...@@ -62,16 +63,36 @@ public class PCSCTerminals implements SCIOTerminals {
PCSCTerminals(@Nonnull PCSCFactory terminalFactory) { PCSCTerminals(@Nonnull PCSCFactory terminalFactory) {
this.terminalFactory = terminalFactory; this.terminalFactory = terminalFactory;
loadTerminals();
}
private void reloadFactory() { terminals = new CardTerminals() {
terminalFactory.reloadPCSC(); @Override
loadTerminals(); public List<CardTerminal> list(CardTerminals.State arg0) throws CardException {
if (loadTerminals()) {
return terminals.list(arg0);
} else {
throw new Smartcardio.JnaPCSCException(SCIOErrorCode.getLong(SCIOErrorCode.SCARD_E_NO_SERVICE), "Error loading PCSC subsystem.");
}
}
@Override
public boolean waitForChange(long arg0) throws CardException {
if (loadTerminals()) {
return terminals.waitForChange(arg0);
} else {
throw new Smartcardio.JnaPCSCException(SCIOErrorCode.getLong(SCIOErrorCode.SCARD_E_NO_SERVICE), "Error loading PCSC subsystem.");
}
}
};
} }
private void loadTerminals() { private boolean loadTerminals() {
terminals = terminalFactory.getRawFactory().terminals(); try {
terminals = terminalFactory.getRawFactory().terminals();
return true;
} catch (Smartcardio.EstablishContextException ex) {
LOG.debug("Failed to load PCSC terminals.", ex);
return false;
}
} }
@Override @Override
...@@ -101,7 +122,7 @@ public class PCSCTerminals implements SCIOTerminals { ...@@ -101,7 +122,7 @@ public class PCSCTerminals implements SCIOTerminals {
} else if (code == SCIOErrorCode.SCARD_E_NO_SERVICE || code == SCIOErrorCode.SCARD_E_SERVICE_STOPPED) { } else if (code == SCIOErrorCode.SCARD_E_NO_SERVICE || code == SCIOErrorCode.SCARD_E_SERVICE_STOPPED) {
if (firstTry) { if (firstTry) {
LOG.debug("No service available exception, reloading PCSC and trying again."); LOG.debug("No service available exception, reloading PCSC and trying again.");
reloadFactory(); loadTerminals();
return list(state, false); return list(state, false);
} else { } else {
LOG.debug("No service available exception, returning empty list."); LOG.debug("No service available exception, returning empty list.");
...@@ -222,11 +243,9 @@ public class PCSCTerminals implements SCIOTerminals { ...@@ -222,11 +243,9 @@ public class PCSCTerminals implements SCIOTerminals {
return Collections.emptyList(); return Collections.emptyList();
} else if (code == SCIOErrorCode.SCARD_E_NO_SERVICE || code == SCIOErrorCode.SCARD_E_SERVICE_STOPPED || code == SCIOErrorCode.SCARD_E_INVALID_HANDLE) { } else if (code == SCIOErrorCode.SCARD_E_NO_SERVICE || code == SCIOErrorCode.SCARD_E_SERVICE_STOPPED || code == SCIOErrorCode.SCARD_E_INVALID_HANDLE) {
LOG.debug("No service available exception, reloading PCSC and returning empty list."); LOG.debug("No service available exception, reloading PCSC and returning empty list.");
parent.reloadFactory(); parent.loadTerminals();
own.loadTerminals(); own.loadTerminals();
return Collections.emptyList(); return Collections.emptyList();
} else if (code == SCIOErrorCode.SCARD_E_INVALID_HANDLE) {
// don't log in order to prevent flooding
} else { } else {
LOG.error(msg, ex); LOG.error(msg, ex);
} }
...@@ -405,7 +424,7 @@ public class PCSCTerminals implements SCIOTerminals { ...@@ -405,7 +424,7 @@ public class PCSCTerminals implements SCIOTerminals {
case SCARD_E_NO_SERVICE: case SCARD_E_NO_SERVICE:
case SCARD_E_SERVICE_STOPPED: case SCARD_E_SERVICE_STOPPED:
LOG.debug("No service available exception, reloading PCSC."); LOG.debug("No service available exception, reloading PCSC.");
parent.reloadFactory(); parent.loadTerminals();
own.loadTerminals(); own.loadTerminals();
case SCARD_E_NO_READERS_AVAILABLE: case SCARD_E_NO_READERS_AVAILABLE:
// send events that everything is removed if there are any terminals connected right now // send events that everything is removed if there are any terminals connected right now
......
...@@ -61,4 +61,6 @@ module org.openecard.richclient { ...@@ -61,4 +61,6 @@ module org.openecard.richclient {
opens org.openecard.mdlw.sal.config to java.xml.bind; opens org.openecard.mdlw.sal.config to java.xml.bind;
opens org.openecard.addon.manifest to java.xml.bind; opens org.openecard.addon.manifest to java.xml.bind;
opens jnasmartcardio to java.base;
} }
...@@ -297,7 +297,7 @@ ...@@ -297,7 +297,7 @@
<compress>2</compress> <compress>2</compress>
<noHeaderFiles>true</noHeaderFiles> <noHeaderFiles>true</noHeaderFiles>
<noManPages>true</noManPages> <noManPages>true</noManPages>
<stripDebug>true</stripDebug> <stripDebug>false</stripDebug>
<ignoreSigningInformation>true</ignoreSigningInformation> <ignoreSigningInformation>true</ignoreSigningInformation>
<outputDirectoryImage>${project.build.directory}/open-ecard</outputDirectoryImage> <outputDirectoryImage>${project.build.directory}/open-ecard</outputDirectoryImage>
<jdkToolchain> <jdkToolchain>
......
...@@ -206,6 +206,7 @@ ...@@ -206,6 +206,7 @@
<iso:Date>2017-11-17</iso:Date> <iso:Date>2017-11-17</iso:Date>
<ATR>3B7D95000080318065B0831100C883009000</ATR> <ATR>3B7D95000080318065B0831100C883009000</ATR>
<CardImage>portuguese-eid.png</CardImage> <CardImage>portuguese-eid.png</CardImage>
<SkipContextSpecificLogin>true</SkipContextSpecificLogin>
</CardSpec> </CardSpec>
</CardConfig> </CardConfig>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment