/* * Commons eID Project. * Copyright (C) 2015 e-Contract.be BVBA. * Copyright (C) 2008-2015 FedICT. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version * 3.0 as published by the Free Software Foundation. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, see * http://www.gnu.org/licenses/. */ /* * author Frank Marien */ package be.fedict.commons.eid.client.impl; import java.io.File; import be.fedict.commons.eid.client.spi.Logger; /** * Encapsulate fixes regarding the dynamic loading of the pcsclite library on * GNU/Linux Systems. statically call LibJ2PCSCGNULinuxFix.fixNativeLibrary() * before using a TerminalFactory. * * @author Frank Cornelis * @author Frank Marien */ public final class LibJ2PCSCGNULinuxFix { private static final int PCSC_LIBRARY_VERSION = 1; private static final String SMARTCARDIO_LIBRARY_PROPERTY = "sun.security.smartcardio.library"; private static final String LIBRARY_PATH_PROPERTY = "java.library.path"; private static final String GNULINUX_OS_PROPERTY_PREFIX = "Linux"; private static final String PCSC_LIBRARY_NAME = "pcsclite"; private static final String UBUNTU_MULTILIB_32_SUFFIX = "i386-linux-gnu"; private static final String UBUNTU_MULTILIB_64_SUFFIX = "x86_64-linux-gnu"; private static final String JRE_BITNESS_PROPERTY = "os.arch"; private static final String OS_NAME_PROPERTY = "os.name"; private static final String JRE_BITNESS_32_VALUE = "i386"; private static final String JRE_BITNESS_64_VALUE = "amd64"; private static enum UbuntuBitness { NA, PURE32, PURE64, MULTILIB }; private LibJ2PCSCGNULinuxFix() { super(); } /** * Make sure libpcsclite is found. The libj2pcsc.so from the JRE attempts to * dlopen using the linker name "libpcsclite.so" instead of the appropriate * "libpcsclite.so.1". This causes libpcsclite not to be found on GNU/Linux * distributions that don't have the libpcsclite.so symbolic link. This * method finds the library and forces the JRE to use it instead of * attempting to locate it by itself. See also: * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=529339 * * Does nothing if not on a GNU/Linux system */ public static void fixNativeLibrary(final Logger logger) { final String osName = System.getProperty(OS_NAME_PROPERTY); if ((osName != null) && (osName.startsWith(GNULINUX_OS_PROPERTY_PREFIX))) { logger.debug("OS is [" + osName + "]. Enabling PCSC library fix."); final File libPcscLite = findGNULinuxNativeLibrary( PCSC_LIBRARY_NAME, PCSC_LIBRARY_VERSION, logger); if (libPcscLite != null) { logger.debug("Setting [" + SMARTCARDIO_LIBRARY_PROPERTY + "] to [" + libPcscLite.getAbsolutePath() + "]"); System.setProperty(SMARTCARDIO_LIBRARY_PROPERTY, libPcscLite.getAbsolutePath()); } } else { logger.debug("OS is [" + osName + "]. Not Enabling PCSC library fix."); } } // ---------------------------------------------------------------------------------------- // -------------------------------- supporting private methods. // --------------------------- // ---------------------------------------------------------------------------------------- /* * Determine Ubuntu-type multilib configuration */ private static UbuntuBitness getUbuntuBitness() { File multilibdir = new File("/lib/" + UBUNTU_MULTILIB_32_SUFFIX); boolean has32 = multilibdir.exists() && multilibdir.isDirectory(); multilibdir = new File("/lib/" + UBUNTU_MULTILIB_64_SUFFIX); boolean has64 = multilibdir.exists() && multilibdir.isDirectory(); if (has32 && (!has64)) { return UbuntuBitness.PURE32; } else if ((!has32) && has64) { return UbuntuBitness.PURE64; } else if (has32 && has64) { return UbuntuBitness.MULTILIB; } else { return UbuntuBitness.NA; } } /* * return the path with extension appended, if it wasn't already contained * in the path */ private static String extendLibraryPath(final String libPath, final String extension) { if (libPath.contains(extension)) { return libPath; } return libPath + ":" + extension; } /* * It turns out the Ubuntu packages differ from the Debian ones, due to * Debian bug#531592 and a difference of opinion between the Debian * maintainer of PC/SC and the Ubuntu maintainers. As such, if we want to * work on both distributions, we need to add both directories. */ private static String addMultiarchPath(final String libPath, final String suffix) { String retval = extendLibraryPath(libPath, "/lib/" + suffix); return extendLibraryPath(retval, "/usr/lib/" + suffix); } /* * Oracle Java 7, java.library.path is severely limited as compared to the * OpenJDK default and doesn't contain Ubuntu 12's MULTILIB directories. * Test for Ubuntu in various configs and add the required paths */ private static String fixPathForUbuntuMultiLib(final String libraryPath, final Logger logger) { logger.debug("Looking for Debian/Ubuntu-style multilib installation."); switch (getUbuntuBitness()) { case PURE32 : // pure 32-bit Ubuntu. Add the 32-bit lib dir. logger.debug("pure 32-bit Debian/Ubuntu detected, adding library paths containing 32-bit multilib suffix: " + UBUNTU_MULTILIB_32_SUFFIX); return addMultiarchPath(libraryPath, UBUNTU_MULTILIB_32_SUFFIX); case PURE64 : // pure 64-bit Ubuntu. Add the 64-bit lib dir. logger.debug("pure 64-bit Debian/Ubuntu detected, adding library paths containing 64-bit multilib suffix: " + UBUNTU_MULTILIB_64_SUFFIX); return addMultiarchPath(libraryPath, UBUNTU_MULTILIB_64_SUFFIX); case MULTILIB : { // multilib Ubuntu. Let the currently running JRE's bitness // determine which lib dir to add. logger.debug("Multilib Ubuntu detected. Using JRE Bitness."); final String jvmBinaryArch = System .getProperty(JRE_BITNESS_PROPERTY); if (jvmBinaryArch == null) { return libraryPath; } logger.debug("JRE Bitness is [" + jvmBinaryArch + "]"); if (jvmBinaryArch.equals(JRE_BITNESS_32_VALUE)) { logger.debug("32-bit JRE, using 32-bit multilib suffix: " + UBUNTU_MULTILIB_32_SUFFIX); return addMultiarchPath(libraryPath, UBUNTU_MULTILIB_32_SUFFIX); } if (jvmBinaryArch.equals(JRE_BITNESS_64_VALUE)) { logger.debug("64-bit JRE, using 64-bit multilib suffix: " + UBUNTU_MULTILIB_64_SUFFIX); return addMultiarchPath(libraryPath, UBUNTU_MULTILIB_64_SUFFIX); } } break; default : { logger.debug("Did not find Debian/Ubuntu-style multilib."); } } return libraryPath; } /* * Finds .so.version file on GNU/Linux. avoid guessing all GNU/Linux * distros' library path configurations on 32 and 64-bit when working around * the buggy libj2pcsc.so implementation based on JRE implementations adding * the native library paths to the end of java.library.path. Fixes the path * for Oracle JRE which doesn't contain the Ubuntu MULTILIB directories */ private static File findGNULinuxNativeLibrary(final String baseName, final int version, final Logger logger) { // get java.library.path String nativeLibraryPaths = System.getProperty(LIBRARY_PATH_PROPERTY); if (nativeLibraryPaths == null) { return null; } logger.debug("Original Path=[" + nativeLibraryPaths + "]"); // when on Ubuntu, add appropriate MULTILIB path nativeLibraryPaths = fixPathForUbuntuMultiLib(nativeLibraryPaths, logger); logger.debug("Path after Ubuntu multilib Fixes=[" + nativeLibraryPaths + "]"); // scan the directories in the path and return the first library called // "baseName" with version "version" final String libFileName = System.mapLibraryName(baseName) + "." + version; logger.debug("Scanning path for [" + libFileName + "]"); for (String nativeLibraryPath : nativeLibraryPaths.split(":")) { logger.debug("Scanning [" + nativeLibraryPath + "]"); final File libraryFile = new File(nativeLibraryPath, libFileName); if (libraryFile.exists()) { logger.debug("[" + libFileName + "] found in [" + nativeLibraryPath + "]"); return libraryFile; } } logger.debug("[" + libFileName + "] not found."); return null; } }