/******************************************************************************* * Copyright (c) 2008, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Christian Georgi (SAP SE) - Bug 460430: environment variable for secure store *******************************************************************************/ package org.eclipse.equinox.internal.security.storage; import java.io.*; import java.net.URL; import java.util.*; import javax.crypto.spec.PBEKeySpec; import org.eclipse.equinox.internal.security.auth.AuthPlugin; import org.eclipse.equinox.internal.security.auth.nls.SecAuthMessages; import org.eclipse.equinox.security.storage.ISecurePreferences; import org.eclipse.equinox.security.storage.provider.IProviderHints; import org.eclipse.osgi.framework.log.FrameworkLogEntry; import org.eclipse.osgi.service.environment.EnvironmentInfo; import org.eclipse.osgi.util.NLS; public class SecurePreferencesMapper { /** * Command line argument specifying default location */ final private static String KEYRING_ARGUMENT = "-eclipse.keyring"; //$NON-NLS-1$ /** * Environment variable name for the location */ final private static String KEYRING_ENVIRONMENT = "ECLIPSE_KEYRING"; //$NON-NLS-1$ /** * Command line argument specifying default password */ final private static String PASSWORD_ARGUMENT = "-eclipse.password"; //$NON-NLS-1$ static private ISecurePreferences defaultPreferences = null; static private Map preferences = new HashMap(); // URL.toString() -> SecurePreferencesRoot final public static String USER_HOME = "user.home"; //$NON-NLS-1$ static public ISecurePreferences getDefault() { if (defaultPreferences == null) { try { defaultPreferences = open(null, null); } catch (IOException e) { AuthPlugin.getDefault().logError(SecAuthMessages.keyringNotAvailable, e); } } return defaultPreferences; } static public void clearDefault() { if (defaultPreferences == null) return; try { defaultPreferences.flush(); } catch (IOException e) { // ignore in this context } close((((SecurePreferencesWrapper) defaultPreferences).getContainer().getRootData())); defaultPreferences = null; } static public ISecurePreferences open(URL location, Map options) throws IOException { // 1) find if there are any command line arguments that need to be added EnvironmentInfo infoService = AuthPlugin.getDefault().getEnvironmentInfoService(); if (infoService != null) { String[] args = infoService.getNonFrameworkArgs(); if (args != null && args.length != 0) { for (int i = 0; i < args.length - 1; i++) { if (args[i + 1].startsWith(("-"))) //$NON-NLS-1$ continue; if (location == null && KEYRING_ARGUMENT.equalsIgnoreCase(args[i])) { location = getKeyringFile(args[i + 1]).toURL(); // don't use File.toURI().toURL() continue; } if (PASSWORD_ARGUMENT.equalsIgnoreCase(args[i])) { options = processPassword(options, args[i + 1]); continue; } } } } // 2) process location from environment String environmentKeyring = System.getenv(KEYRING_ENVIRONMENT); if (location == null && environmentKeyring != null) location = getKeyringFile(environmentKeyring).toURL(); // 3) process default location if (location == null) location = StorageUtils.getDefaultLocation(); if (!StorageUtils.isFile(location)) // at this time we only accept file URLs; check URL type right away throw new IOException(NLS.bind(SecAuthMessages.loginFileURL, location.toString())); // 3) see if there is already SecurePreferencesRoot at that location; if not open a new one String key = location.toString(); SecurePreferencesRoot root; if (preferences.containsKey(key)) root = (SecurePreferencesRoot) preferences.get(key); else { root = new SecurePreferencesRoot(location); preferences.put(key, root); } // 4) create container with the options passed in SecurePreferencesContainer container = new SecurePreferencesContainer(root, options); return container.getPreferences(); } static public void stop() { synchronized (preferences) { for (Iterator i = preferences.values().iterator(); i.hasNext();) { SecurePreferencesRoot provider = (SecurePreferencesRoot) i.next(); try { provider.flush(); } catch (IOException e) { // use FrameworkLog directly for shutdown messages - RuntimeLog // is empty by this time AuthPlugin.getDefault().frameworkLogError(SecAuthMessages.errorOnSave, FrameworkLogEntry.ERROR, e); } } preferences.clear(); } } static public void clearPasswordCache() { synchronized (preferences) { for (Iterator i = preferences.values().iterator(); i.hasNext();) { SecurePreferencesRoot provider = (SecurePreferencesRoot) i.next(); provider.clearPasswordCache(); } } } // Not exposed as API; mostly intended for testing static public void close(SecurePreferencesRoot root) { if (root == null) return; synchronized (preferences) { for (Iterator i = preferences.values().iterator(); i.hasNext();) { SecurePreferencesRoot provider = (SecurePreferencesRoot) i.next(); if (!root.equals(provider)) continue; i.remove(); break; } } } // Replace any @user.home variables found in eclipse.keyring path arg static private File getKeyringFile(String path) { if (path.startsWith('@' + USER_HOME)) return new File(System.getProperty(USER_HOME), path.substring(USER_HOME.length() + 1)); return new File(path); } static private Map processPassword(Map options, String arg) { if (arg == null || arg.length() == 0) return options; File file = new File(arg); if (!file.canRead()) { String msg = NLS.bind(SecAuthMessages.unableToReadPswdFile, arg); AuthPlugin.getDefault().logError(msg, null); return options; } BufferedReader is = null; try { is = new BufferedReader(new FileReader(file)); StringBuffer buffer = new StringBuffer(); for (;;) { // this eliminates new line characters but that's fine String tmp = is.readLine(); if (tmp == null) break; buffer.append(tmp); } if (buffer.length() == 0) return options; if (options == null) options = new HashMap(1); if (!options.containsKey(IProviderHints.DEFAULT_PASSWORD)) options.put(IProviderHints.DEFAULT_PASSWORD, new PBEKeySpec(buffer.toString().toCharArray())); } catch (IOException e) { String msg = NLS.bind(SecAuthMessages.unableToReadPswdFile, arg); AuthPlugin.getDefault().logError(msg, e); } finally { if (is != null) { try { is.close(); } catch (IOException e) { String msg = NLS.bind(SecAuthMessages.unableToReadPswdFile, arg); AuthPlugin.getDefault().logError(msg, e); } } } return options; } }