Package org.ejbca.util.keystore

Source Code of org.ejbca.util.keystore.P11Slot$SlotDataParam

/*************************************************************************
*                                                                       *
*  EJBCA: The OpenSource Certificate Authority                          *
*                                                                       *
*  This software is free software; you can redistribute it and/or       *
*  modify it under the terms of the GNU Lesser General Public           *
*  License as published by the Free Software Foundation; either         *
*  version 2.1 of the License, or any later version.                    *
*                                                                       *
*  See terms of license at gnu.org.                                     *
*                                                                       *
*************************************************************************/

package org.ejbca.util.keystore;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.AuthProvider;
import java.security.Provider;
import java.security.Security;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.security.auth.login.LoginException;

import org.apache.log4j.Logger;
import org.ejbca.core.model.InternalResources;
import org.ejbca.core.model.ca.catoken.CATokenOfflineException;

/**
* Each instance of this class represents a slot on a P11 module.
* Use an instance of this class for all your access of a specific P11 slot.
* Use {@link P11Slot#getProvider()} to get a provider for the slot.
*
* @version $Id: P11Slot.java 11228 2011-01-19 11:34:11Z anatom $
*/
public class P11Slot {
    /** Log4j instance */
    private static final Logger log = Logger.getLogger(P11Slot.class);

    /**
     * All users of the {@link P11Slot} slot must implement this interface.
     * The user may decide whether deactivation is allowed or not. Deactivation of a user is done when the {@link P11Slot} object wants to reset P11 session (disconnect and reconnect).
     * <p>
     * If deactivation is allowed and {@link #deactivate()} called the user should:<br>
     * Deactivate itself (answer false to {@link #isActive()}) and call {@link P11Slot#logoutFromSlotIfNoTokensActive()}
     * Then {@link P11Slot#getProvider()} must be called before using the provider again.
     * </p><p>
     * If deactivation is not allowed then the user may just continue to answer true to {@link #isActive()}.
     * </p>
     */
    public interface P11SlotUser {
        /**
         * Called by the {@link P11Slot} when resetting the slot.
         * @return false if any problem with the deactivation. Otherwise true.
         * @throws Exception
         */
        boolean deactivate() throws Exception;
        /**
         * The user should return true if not accepting a slot reset.
         * @return true if the slot is being used.
         */
        boolean isActive();
    }
    /** Internal localization of logs and errors */
    private static final InternalResources intres = InternalResources.getInstance();
    private final static Map<String,P11Slot> slotMap = new HashMap<String, P11Slot>();
    private final static Set<String> slotsBeingCreated = new HashSet<String>();
    private final static Map<String,Set<P11Slot>> libMap = new HashMap<String, Set<P11Slot>>();
    private final static Map<Integer, P11SlotUser> tokenMap = new HashMap<Integer, P11SlotUser>();
    final private String slotNr;
    final private String sharedLibrary;
    final private String attributesFile;
    final private boolean isIndex;
    final private Set<Integer> caids = new HashSet<Integer>();
    final private String sunP11ConfigFileName;
    final private Provider provider;
    private boolean isSettingProvider = false;
    private P11Slot(String _slotNr, String _sharedLibrary, boolean _isIndex, String _attributesFile) throws CATokenOfflineException {
        this.slotNr = _slotNr;
        this.sharedLibrary = _sharedLibrary;
        this.isIndex = _isIndex;
        this.attributesFile = _attributesFile;
        this.sunP11ConfigFileName = null;
        this.provider = createProvider();
    }
    private P11Slot( String configFileName ) throws CATokenOfflineException {
        this.sunP11ConfigFileName = configFileName;
        this.slotNr = null;
        this.sharedLibrary = null;
        this.isIndex = false;
        this.attributesFile = null;
        this.provider = createProvider();
    }
    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        if ( this.slotNr==null && this.sharedLibrary==null ) {
            return "P11, Sun configuration file name: "+this.sunP11ConfigFileName;
        }
        return "P11 slot "+(this.isIndex ? "index ":"#")+this.slotNr+" using library "+this.sharedLibrary+'.';
    }
    /**
     * Used for library key map when a sun configuration file is used to specify a token (slot). In this case only one lib could be used.
     */
    static private String ONLY_ONE = "onlyOne";
    /**
     * Reset the HSM. Could be done if it has stopped working in a try to get it working again.
     */
    public void reset() {
        final String mapName = this.sharedLibrary!=null ? new File(this.sharedLibrary).getName() : ONLY_ONE;
        final Iterator<P11Slot> i = libMap.get(mapName).iterator();
        while( i.hasNext() ) {
            Iterator<Integer> i2 = i.next().caids.iterator();
            while( i2.hasNext() ) {
                try {
                    tokenMap.get(i2.next()).deactivate();
                } catch (Exception e) {
                    log.error("Not possible to deactivate token.", e);
                }
            }
        }
    }
    /**
     * Get P11 slot instance. Only one instance (provider) will ever be created for each slot regardless of how many times this method is called.
     * @param slotNr number of the slot
     * @param sharedLibrary file path of shared
     * @param isIndex true if not slot number but index in slot list
     * @param attributesFile Attributes file. Optional. Set to null if not used
     * @param token Token that should use this object
     * @param caid unique ID of the user of the token. For EJBCA this is the caid. For the OCSP responder this is fixed since then there is only one user.
     * @return The instance.
     * @throws CATokenOfflineException if CA token can not be activated, IllegalArgumentException if sharedLibrary is null.
     */
    static public P11Slot getInstance(String slotNr, String sharedLibrary, boolean isIndex,
                                      String attributesFile, P11SlotUser token, int caid) throws CATokenOfflineException {
        if (sharedLibrary == null) {
            throw new IllegalArgumentException("sharedLibrary = null");
        }
        return getInstance(new SlotDataParam(slotNr, sharedLibrary, isIndex, attributesFile), token, caid);
    }
    /**
     * As {@link #getInstance(String, String, boolean, String, org.ejbca.util.keystore.P11Slot.P11SlotUser)} but is using config file instead parameters. Do only use this method if the P11 shared library is ony specified in this config file.
     * @param configFileName name of config file
     * @param token Token that should use this object.
     * @param caid unique ID of the user of the token. For EJBCA this is the caid. For the OCSP responder this is fixed since then there is only one user.
     * @return a new P11Slot instance
     * @throws CATokenOfflineException
     */
    static public P11Slot getInstance(String configFileName, P11SlotUser token, int caid) throws CATokenOfflineException {
        return getInstance(new SlotDataConfigFile(configFileName), token, caid);
    }
    static private P11Slot getInstance(ISlotData data, P11SlotUser token, int caid) throws CATokenOfflineException {
        tokenMap.put(Integer.valueOf(caid), token);
        P11Slot slot = slotMap.get(data.getSlotLabel());
        if (slot==null) {
            synchronized( slotsBeingCreated ) {
                // test if another thread is currently creating the slot
                if ( slotsBeingCreated.contains(data.getSlotLabel()) ) {
                    while ( true ) {
                        slot = slotMap.get(data.getSlotLabel());
                        if ( slot!=null ) {
                            break; // don't wait if we got a slot now
                        } else if ( !slotsBeingCreated.contains(data.getSlotLabel()) ) {
                            throw new CATokenOfflineException(intres.getLocalizedMessage("caadmin.errorcreatetoken")); // thread creating slot failed
                        }
                        // wait until another thread has created the slot
                        try {
                            slotsBeingCreated.wait();
                        } catch (InterruptedException e) {
                            throw new Error( "This should never happen.", e);
                        }
                        // the slot should now have been created by another thread
                    }
                } else {
                    try {
                        slotsBeingCreated.add(data.getSlotLabel());// show that this thread is creating the slot
                        slot = data.getNewP11Slot();
                        slotMap.put(data.getSlotLabel(), slot);
                    } finally {
                        if ( slot==null ) {
                            slotsBeingCreated.remove(data.getSlotLabel());// show that creating the slot failed
                        }
                        slotsBeingCreated.notifyAll();// notify that the slot is now created
                        if ( slot==null ) {
                            throw new CATokenOfflineException(intres.getLocalizedMessage("caadmin.errorcreatetoken"));
                        }
                    }
                    Set<P11Slot> libSet = libMap.get(data.getLibName());
                    if (libSet==null) {
                        libSet=new HashSet<P11Slot>();
                        libMap.put(data.getLibName(), libSet);
                    }
                    libSet.add(slot);
                }
            }
        }
        final Iterator<P11Slot> i = slotMap.values().iterator();
        while ( i.hasNext() ) {
            i.next().caids.remove(Integer.valueOf(caid));
        }
        slot.caids.add(Integer.valueOf(caid));
        return slot;
    }
    private static interface ISlotData {
        P11Slot getNewP11Slot() throws CATokenOfflineException;
        String getSlotLabel();
        String getLibName();
    }
    private static class SlotDataConfigFile implements ISlotData {
        private final String configFileName;
        SlotDataConfigFile(String _configFileName) {
            this.configFileName = _configFileName;
        }
        public String getLibName() {
            return ONLY_ONE;
        }
        public P11Slot getNewP11Slot() throws CATokenOfflineException {
            return new P11Slot(this.configFileName);
        }
        public String getSlotLabel() {
            return new File(this.configFileName).getName();
        }
    }
    private static class SlotDataParam implements ISlotData {
        private final String slotNr;
        private final String sharedLibrary;
        private final String libName;
        private final boolean isIndex;
        private final String attributesFile;
        SlotDataParam(String _slotNr, String _sharedLibrary, boolean _isIndex,
                      String _attributesFile) {
            this.slotNr = _slotNr;
            this.sharedLibrary = _sharedLibrary;
            this.isIndex = _isIndex;
            this.attributesFile = _attributesFile;
            this.libName = new File(this.sharedLibrary).getName();
        }
        public P11Slot getNewP11Slot() throws CATokenOfflineException {
            return new P11Slot(this.slotNr, this.sharedLibrary, this.isIndex, this.attributesFile);
        }
        public String getSlotLabel() {
            return this.slotNr + this.libName + this.isIndex;
        }
        public String getLibName() {
            return this.libName;
        }
    }
    /**
     * Unload if last active token on slot
     * @throws LoginException
     */
    public void logoutFromSlotIfNoTokensActive() {
        final Iterator<Integer> iTokens = this.caids.iterator();
        while( iTokens.hasNext() ) {
            if ( tokenMap.get(iTokens.next()).isActive() ) {
                return;
            }
        }
        if ( this.provider instanceof AuthProvider ) {
            try {
                ((AuthProvider)this.provider).logout();
                log.debug("P11 session terminated for \""+this+"\".");
            } catch (LoginException e) {
                log.warn("Not possible to logout from P11 Session. HW problems?", e);
            }
        } else {
            log.warn("Not possible to logout from P11 provider '"+this+"'. It is not implementing '"+AuthProvider.class.getCanonicalName()+"'.");
        }
    }
    /**
     * @return  the provider of the slot.
     * @throws CATokenOfflineException
     */
    public Provider getProvider() {
        return this.provider;
    }
    /**
     * @return  the provider of the slot.
     * @throws CATokenOfflineException
     */
    private synchronized Provider createProvider() throws CATokenOfflineException {
        final Provider tmpProvider;
        while ( this.isSettingProvider ) {
            try {
                this.wait();
            } catch (InterruptedException e1) {
                log.fatal("This should never happened", e1);
            }
        }
        try {
            this.isSettingProvider = true;
            if ( this.slotNr!=null && this.sharedLibrary!=null ) {
                tmpProvider = KeyTools.getP11Provider(this.slotNr, this.sharedLibrary,
                                                        this.isIndex, this.attributesFile);
            } else if ( this.sunP11ConfigFileName!=null ) {
                tmpProvider = KeyTools.getSunP11Provider(new FileInputStream(this.sunP11ConfigFileName));
            } else {
                throw new Error("Should never happen.");
            }
        } catch (IOException e) {
            final CATokenOfflineException e2 = new CATokenOfflineException("Not possible to create provider. See cause.");
            e2.initCause(e);
            throw e2;
        } finally {
            this.isSettingProvider = false;
            this.notifyAll();
        }
        if ( tmpProvider==null ) {
            throw new CATokenOfflineException("Provider is null");
        }
        if ( Security.getProvider(tmpProvider.getName())!=null ) {
            Security.removeProvider(tmpProvider.getName());
        }
        Security.addProvider( tmpProvider );
        log.debug("Provider successfully added: "+tmpProvider);
        return tmpProvider;
    }
    /* (non-Javadoc)
     * @see java.lang.Object#finalize()
     */
    @Override
    public void finalize() {
        try {
            super.finalize();
        } catch (Throwable e) {
            log.error("Problem. See exception.", e);
        }
        Security.removeProvider(this.provider.getName());
        log.debug("finalize called.");
    }
}
TOP

Related Classes of org.ejbca.util.keystore.P11Slot$SlotDataParam

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.